diff -Nru a/Documentation/BK-usage/bk-kernel-howto.txt b/Documentation/BK-usage/bk-kernel-howto.txt
--- a/Documentation/BK-usage/bk-kernel-howto.txt	Mon Apr 22 15:32:18 2002
+++ b/Documentation/BK-usage/bk-kernel-howto.txt	Mon Apr 22 15:32:18 2002
@@ -27,7 +27,7 @@
 and merges.  You will need to take a few minutes to think about
 how to best work under BitKeeper, and re-optimize things a bit.
 In some sense it is a bit radical, because it might described as
-tossing changes out into a maelstrom and having them them magically
+tossing changes out into a maelstrom and having them magically
 land at the right destination... but I'm getting ahead of myself.
 
 Let's start with this progression:
diff -Nru a/Documentation/DocBook/kernel-hacking.tmpl b/Documentation/DocBook/kernel-hacking.tmpl
--- a/Documentation/DocBook/kernel-hacking.tmpl	Mon Apr 22 15:32:19 2002
+++ b/Documentation/DocBook/kernel-hacking.tmpl	Mon Apr 22 15:32:19 2002
@@ -1025,7 +1025,7 @@
     Similar to <function>EXPORT_SYMBOL()</function> except that the
     symbols exported by <function>EXPORT_SYMBOL_GPL()</function> can
     only be seen by modules with a
-    <function>MODULE_LICENCE()</function> that specifies a GPL
+    <function>MODULE_LICENSE()</function> that specifies a GPL
     compatible license.
    </para>
   </sect1>
diff -Nru a/Documentation/filesystems/Exporting b/Documentation/filesystems/Exporting
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/Documentation/filesystems/Exporting	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,176 @@
+
+Making Filesystems Exportable
+=============================
+
+Most filesystem operations require a dentry (or two) as a starting
+point.  Local applications have a reference-counted hold on suitable
+dentrys via open file descriptors or cwd/root.  However remote
+applications that access a filesystem via a remote filesystem protocol
+such as NFS may not be able to hold such a reference, and so need a
+different way to refer to a particular dentry.  As the alternative
+form of reference needs to be stable across renames, truncates, and
+server-reboot (among other things, though these tend to be the most
+problematic), there is no simple answer like 'filename'.
+
+The mechanism discussed here allows each filesystem implementation to
+specify how to generate an opaque (out side of the filesystem) byte
+string for any dentry, and how to find an appropriate dentry for any
+given opaque byte string.
+This byte string will be called a "filehandle fragment" as it
+corresponds to part of an NFS filehandle.
+
+A filesystem which supports the mapping between filehandle fragments
+and dentrys will be termed "exportable".
+
+
+
+Dcache Issues
+-------------
+
+The dcache normally contains a proper prefix of any given filesystem
+tree.  This means that if any filesystem object is in the dcache, then
+all of the ancestors of that filesystem object are also in the dcache.
+As normal access is by filename this prefix is created naturally and
+maintained easily (by each object maintaining a reference count on
+it's parent).
+
+However when objects are included into the dcache by interpreting a
+filehandle fragment, there is no automatic creation of a path prefix
+for the object.  This leads to two related but distinct features of
+the dcache that are not needed for normal filesystem access.
+
+1/ The dcache must sometimes contain objects that are not part of the
+   proper prefix. i.e that are not connected to the root.
+2/ The dcache must be prepared for a newly found (via ->lookup) directory
+   to already have a (non-connected) dentry, and must be able to move
+   that dentry into place (based on the parent and name in the
+   ->lookup).   This is particuarly needed for directories as
+   it is a dcache invarient that directories only have one dentry.
+
+To implement these features, the dcache has:
+
+a/ A dentry flag  DCACHE_DISCONNECTED which is set on
+   and dentry that might not be part of the proper prefix.
+   This is set when anonymous dentries are created, and cleared when a
+   dentry is noticed to be a child on a dentry which is in the proper
+   prefix. 
+
+b/ A per-superblock list "s_anon" of dentries which are the roots of
+   subtrees that are not in the proper prefix.  These dentries, as
+   well as the proper prefix, need to be released at unmount time.  As
+   these dentries will not be hashed, they are linked together on the
+   d_hash list_head.
+
+c/ Helper routines to allocate anonymous dentries, and to help attach
+   loose directory dentries at lookup time. They are:
+    d_alloc_anon(inode) will return a dentry for the given inode.
+      If the inode already has a dentry, one of those is returned.
+      If it doesn't, a new anonymous (IS_ROOT and
+        DCACHE_DISCONNECTED) dentry is allocated and attached.
+      In the case of a directory, care is taken that only one dentry
+      can ever be attached.
+    d_splice_alias(inode, dentry) will make sure that there is a
+      dentry with the same name and parent as the given dentry, and
+      which refers to the given inode.
+      If the inode is a directory and already has a dentry, then that
+      dentry is d_moved over the given dentry.
+      If the passed dentry gets attached, care is taken that this is
+      mutually exclusive to a d_alloc_anon operation.
+      If the passed dentry is used, NULL is returned, else the used
+      dentry is returned.  This corresponds to the calling pattern of
+      ->lookup.
+  
+ 
+Filesystem Issues
+-----------------
+
+For a filesystem to be exportable it must:
+ 
+   1/ provide the filehandle fragment routines described below.
+   2/ make sure that d_splice_alias is used rather than d_add
+      when ->lookup finds an inode for a given parent and name.
+      Typically the ->lookup routine will end:
+		if (inode)
+			return d_splice(inode, dentry);
+		d_add(dentry, inode);
+		return NULL;
+	}
+
+
+
+  A file system implementation declares that instances of the filesystem
+are exportable by setting the s_export_op field in the struct
+super_block.  This field must point to a "struct export_operations"
+struct which could potentially be full of NULLs, though normally at
+least get_parent will be set.
+
+ The primary operations are decode_fh and encode_fh.  
+decode_fh takes a filehandle fragment and tries to find or create a
+dentry for the object referred to by the filehandle.
+encode_fh takes a dentry and creates a filehandle fragment which can
+later be used to find/create a dentry for the same object.
+
+decode_fh will probably make use of "find_exported_dentry".
+This function lives in the "exportfs" module which a filesystem does
+not need unless it is being exported.  So rather that calling
+find_exported_dentry directly, each filesystem should call it through
+the find_exported_dentry pointer in it's export_operations table.
+This field is set correctly by the exporting agent (e.g. nfsd) when a
+filesystem is exported, and before any export operations are called.
+
+find_exported_dentry needs three support functions from the
+filesystem:
+  get_name.  When given a parent dentry and a child dentry, this
+    should find a name in the directory identified by the parent
+    dentry, which leads to the object identified by the child dentry.
+    If no get_name function is supplied, a default implementation
+    which used vfs_readdir to find potential names, and matches inode
+    numbers to find the correct match.
+
+  get_parent.  When given a dentry for a directory, this should return 
+    a dentry for the parent.  Quite possibly the parent dentry will
+    have been allocated by d_alloc_anon.  
+    The default get_parent function just returns an error so any
+    filehandle lookup that requires finding a parent will fail.
+    ->lookup("..") is *not* used as a default as it can leave ".."
+    entries in the dcache which are too messy to work with.
+
+  get_dentry.  When given a opaque datum, this should find the
+    implied object and create a dentry for it (possibly with
+    d_alloc_anon). 
+    The opaque datum is whatever is passed down by the decode_fh
+    function, and is often simply a fragment of the filehandle
+    fragment.
+    decode_fh passes two datums through find_exported_dentry.  One that 
+    should be used to identify the target object, and one that can be
+    used to identify the objects parent, should that be necessary.
+    The default get_dentry function assumes that the datum contains an
+    inode number and a generation number, and it attempts to get the
+    inode using "iget" and check it's validity by matching the
+    generation number.  A filesystem should only depend on the default
+    if iget can safely be used this way.
+
+If decode_fh and/or encode_fh are left as NULL, then default
+implementations are used.  These defaults are suitable for ext2 and 
+extremely similar filesystems (like ext3).
+
+The default encode_fh creates a filehandle fragment from the inode
+number and generation number of the target together with the inode
+number and generation number of the parent (if the parent is
+required).
+
+The default decode_fh extract the target and parent datums from the
+filehandle assuming the format used by the default encode_fh and
+passed them to find_exported_dentry.
+
+
+A filehandle fragment consists of an array of 1 or more 4byte words.
+Together with a one byte "type".
+The decode_fh routine should not depend on the stated size that is
+passed to it.  This size may be larger than the original filehandle
+generated by encode_fh, in which case it will have been padded with
+nuls.  Rather, the encode_fh routine should choose a "type" which
+indicates the decode_fh how much of the filehandle is valid, and how
+it should be interpreted.
+
+ 
diff -Nru a/Documentation/filesystems/devfs/ChangeLog b/Documentation/filesystems/devfs/ChangeLog
--- a/Documentation/filesystems/devfs/ChangeLog	Mon Apr 22 15:32:18 2002
+++ b/Documentation/filesystems/devfs/ChangeLog	Mon Apr 22 15:32:18 2002
@@ -1905,3 +1905,10 @@
 - Fixed bitfield data type for <devfs_*alloc_devnum>
 
 - Made major bitfield type and initialiser 64 bit safe
+===============================================================================
+Changes for patch v210
+
+- Updated fs/devfs/util.c to fix shift warning on 64 bit machines
+  Thanks to Anton Blanchard <anton@samba.org>
+
+- Updated README from master HTML file
diff -Nru a/Documentation/filesystems/devfs/README b/Documentation/filesystems/devfs/README
--- a/Documentation/filesystems/devfs/README	Mon Apr 22 15:32:19 2002
+++ b/Documentation/filesystems/devfs/README	Mon Apr 22 15:32:19 2002
@@ -3,7 +3,7 @@
 
 Linux Devfs (Device File System) FAQ
 Richard Gooch
-4-APR-2002
+7-APR-2002
 
 
 Document languages:
@@ -1589,6 +1589,20 @@
 mount devfs manually in your boot scripts with:
 mount -t none devfs /dev
 
+
+
+Mount by volume LABEL=<label> doesn't work with
+devfs
+
+Most probably you are not mounting devfs onto /dev. What
+happens is that if your kernel config has CONFIG_DEVFS_FS=y
+then the contents of /proc/partitions will have the devfs
+names (such as scsi/host0/bus0/target0/lun0/part1). The
+contents of /proc/partitions are used by mount(8) when
+mounting by volume label. If devfs is not mounted on /dev,
+then mount(8) will fail to find devices. The solution is to
+make sure that devfs is mounted on /dev. See above for how to
+do that.
 
 
 
diff -Nru a/Documentation/filesystems/porting b/Documentation/filesystems/porting
--- a/Documentation/filesystems/porting	Mon Apr 22 15:32:19 2002
+++ b/Documentation/filesystems/porting	Mon Apr 22 15:32:19 2002
@@ -1,7 +1,7 @@
 Changes since 2.5.0:
 
 --- 
-[recommeneded]
+[recommended]
 
 New helpers: sb_bread(), sb_getblk(), sb_get_hash_table(), set_bh(),
 	sb_set_blocksize() and sb_min_blocksize().
@@ -9,7 +9,7 @@
 Use them.
 
 --- 
-[recommeneded]
+[recommended]
 
 New methods: ->alloc_inode() and ->destroy_inode().
 
@@ -123,3 +123,19 @@
 ->setattr() is called without BKL now.  Caller _always_ holds ->i_sem, so
 watch for ->i_sem-grabbing code that might be used by your ->setattr().
 Callers of notify_change() need ->i_sem now.
+
+---
+[recommended]
+
+New super_block field "struct export_operations *s_export_op" for
+explicit support for exporting, e.g. via NFS.  The structure is fully
+documented at its declaration in include/linux/fs.h, and in
+Documentation/filesystems/Exporting.
+
+Briefly it allows for the definition of decode_fh and encode_fh operations
+to encode and decode filehandles, and allows the filesystem to use
+a standard helper function for decode_fh, and provide file-system specific
+support for this helper, particularly get_parent.
+
+It is planned that this will be required for exporting once the code
+settles down a bit.
diff -Nru a/Documentation/ide.txt b/Documentation/ide.txt
--- a/Documentation/ide.txt	Mon Apr 22 15:32:18 2002
+++ b/Documentation/ide.txt	Mon Apr 22 15:32:18 2002
@@ -1,71 +1,16 @@
-ide.txt -- Information regarding the Enhanced IDE drive in Linux 2.2/2.3/2.4
-===============================================================================
 
-   +-----------------------------------------------------------------+
-   |  The hdparm utility for controlling various IDE features is     |
-   |  packaged separately.  Look for it on popular linux FTP sites.  |
-   +-----------------------------------------------------------------+
-
-See description later on below for handling BIG IDE drives with >1024 cyls.
-
-Major features of the 2.1/2.2 IDE driver ("NEW!" marks changes since 2.0.xx):
-
-NEW!	- support for IDE ATAPI *floppy* drives
-	- support for IDE ATAPI *tape* drives, courtesy of Gadi Oxman
-		(re-run MAKEDEV.ide to create the tape device entries in /dev/)
-	- support for up to *four* IDE interfaces on one or more IRQs
-	- support for any mix of up to *eight* IDE drives
-	- support for reading IDE ATAPI cdrom drives (NEC,MITSUMI,VERTOS,SONY)
-	- support for audio functions
-	- auto-detection of interfaces, drives, IRQs, and disk geometries
-		- "single" drives should be jumpered as "master", not "slave"
-		  (both are now probed for)
-	- support for BIOSs which report "more than 16 heads" on disk drives
-	- uses LBA (slightly faster) on disk drives which support it
-	- support for lots of fancy (E)IDE drive functions with hdparm utility
-	- optional (compile time) support for 32-bit VLB data transfers
-	- support for IDE multiple (block) mode (same as hd.c)
-	- support for interrupt unmasking during I/O (better than hd.c)
-	- improved handshaking and error detection/recovery
-	- can co-exist with hd.c controlling the first interface
-	- run-time selectable 32bit interface support (using hdparm-2.3)
-	- support for reliable operation of buggy RZ1000 interfaces
-		- PCI support is automatic when rz1000 support is configured
-	- support for reliable operation of buggy CMD-640 interfaces
-		- PCI support is automatic when cmd640 support is configured
-		- for VLB, use kernel command line option:   ide0=cmd640_vlb
-		- this support also enables the secondary i/f when needed
-		- interface PIO timing & prefetch parameter support
-	- experimental support for UMC 8672 interfaces
-	- support for secondary interface on the FGI/Holtek HT-6560B VLB i/f
-		- use kernel command line option:   ide0=ht6560b
-	- experimental support for various IDE chipsets
-		- use appropriate kernel command line option from list below
-	- support for drives with a stuck WRERR_STAT bit
-	- support for removable devices, including door lock/unlock
-	- transparent support for DiskManager 6.0x and "Dynamic Disk Overlay"
-	- works with Linux fdisk, LILO, loadlin, bootln, etc..
-	- mostly transparent support for EZ-Drive disk translation software
-		- to use LILO with EZ, install LILO on the linux partition
-		  rather than on the master boot record, and then mark the
-		  linux partition as "bootable" or "active" using fdisk.
-		  (courtesy of Juha Laiho <jlaiho@ichaos.nullnet.fi>).
-	- auto-detect of disk translations by examining partition table
-	- ide-cd.c now compiles separate from ide.c
-	- ide-cd.c now supports door locking and auto-loading.
-		- Also preliminary support for multisession
-		  and direct reads of audio data.
-	- experimental support for Promise DC4030VL caching interface card
-		- email thanks/problems to: peterd@pnd-pc.demon.co.uk
-	- the hdparm-3.1 package can be used to set PIO modes for some chipsets.
-NEW!	- support for setting PIO modes with the OPTi 82C621, courtesy of Jaromir Koutek.
-NEW!	- support for loadable modules
-NEW!	- optional SCSI host adapter emulation for ATAPI devices
-NEW!	- generic PCI Bus-Master DMA support
-NEW!		- works with most Pentium PCI systems, chipsets, add-on cards
-NEW!		- works with regular DMA as well as Ultra DMA
-NEW!		- automatically probes for all PCI IDE interfaces
-NEW!	- generic support for using BIOS-configured Ultra-DMA (UDMA) transfers
+
+
+	Information regarding the Enhanced IDE drive in Linux 2.5
+
+
+==============================================================================
+
+   
+   The hdparm utility can be used to controll various IDE features on a
+   running system. It is packaged separately.  Please Look for it on popular
+   linux FTP sites.
+   
 
 
 ***  IMPORTANT NOTICES:  BUGGY IDE CHIPSETS CAN CORRUPT DATA!!
@@ -92,9 +37,9 @@
 ***
 ***  Use of the "serialize" option is no longer necessary.
 
-This is the multiple IDE interface driver, as evolved from hd.c.
-It supports up to six IDE interfaces, on one or more IRQs (usually 14 & 15).
-There can be up to two drives per interface, as per the ATA-2 spec.
+This is the multiple IDE interface driver, as evolved from hd.c.  It supports
+up to 9 IDE interfaces per default, on one or more IRQs (usually 14 & 15).
+There can be up to two drives per interface, as per the ATA-6 spec.
 
 Primary:    ide0, port 0x1f0; major=3;  hda is minor=0; hdb is minor=64
 Secondary:  ide1, port 0x170; major=22; hdc is minor=0; hdd is minor=64
@@ -103,16 +48,14 @@
 fifth..     ide4, usually PCI, probed
 sixth..     ide5, usually PCI, probed
 
-To access devices on interfaces > ide0, device entries must first be
-created in /dev for them.  To create such entries, simply run the included
-shell script:   /usr/src/linux/scripts/MAKEDEV.ide
-
-Apparently many older releases of Slackware had incorrect entries
-in /dev for hdc* and hdd* -- this can also be corrected by running MAKEDEV.ide
-
-ide.c automatically probes for most IDE interfaces (including all PCI ones),
-for the drives/geometries attached to those interfaces, and for the
-IRQ numbers being used by the interfaces (normally 14, 15 for ide0/ide1).
+To access devices on interfaces > ide0, device entries please make sure that
+device files for them are present in /dev.  If not, please create such
+entries, by simply running the included shell script:
+/usr/src/linux/scripts/MAKEDEV.ide
+
+This driver automatically probes for most IDE interfaces (including all PCI
+ones), for the drives/geometries attached to those interfaces, and for the IRQ
+lines being used by the interfaces (normally 14, 15 for ide0/ide1).
 
 For special cases, interfaces may be specified using kernel "command line"
 options.  For example,
@@ -170,11 +113,11 @@
 	hdc=768,16,32
 	hdc=noprobe
 
-Note that when only one IDE device is attached to an interface,
-it should be jumpered as "single" or "master", *not* "slave".
-Many folks have had "trouble" with cdroms because of this requirement,
-so ide.c now probes for both units, though success is more likely
-when the drive is jumpered correctly.
+Note that when only one IDE device is attached to an interface, it should be
+jumpered as "single" or "master", *not* "slave".  Many folks have had
+"trouble" with cdroms because of this requirement, so the driver now probes
+for both units, though success is more likely when the drive is jumpered
+correctly.
 
 Courtesy of Scott Snyder and others, the driver supports ATAPI cdrom drives
 such as the NEC-260 and the new MITSUMI triple/quad speed drives.
@@ -193,8 +136,8 @@
 (/dev/hdc).  To mount a CD in the cdrom drive, one would use something like:
 
 	ln -sf /dev/hdc /dev/cdrom
-	mkdir /cd
-	mount /dev/cdrom /cd -t iso9660 -o ro
+	mkdir /mnt/cdrom
+	mount /dev/cdrom /mnt/cdrom -t iso9660 -o ro
 
 If, after doing all of the above, mount doesn't work and you see
 errors from the driver (with dmesg) complaining about `status=0xff',
@@ -274,8 +217,6 @@
 				older/odd IDE drives.
  "hdx=slow"		: insert a huge pause after each access to the data
 				port. Should be used only as a last resort.
- "hdx=swapdata"		: when the drive is a disk, byte swap all data
-
  "hdxlun=xx"		: set the drive last logical unit
 
  "idebus=xx"		: inform IDE driver of VESA/PCI bus speed in MHz,
@@ -307,8 +248,9 @@
  "idex=reset"		: reset interface after probe
  "idex=dma"		: automatically configure/use DMA if possible.
 
- The following are valid ONLY on ide0,
- and the defaults for the base,ctl ports must not be altered.
+The following are valid ONLY on ide0, which usually corresponds to the first
+ATA interface found on the particular host, and the defaults for the base,ctl
+ports must not be altered.
 
  "ide0=dtc2278"		: probe/support DTC2278 interface
  "ide0=ht6560b"		: probe/support HT6560B interface
@@ -329,179 +271,21 @@
 IDE = Integrated Drive Electronics, meaning that each drive has a built-in
 controller, which is why an "IDE interface card" is not a "controller card".
 
-IDE drives are designed to attach almost directly to the ISA bus of an AT-style
-computer.  The typical IDE interface card merely provides I/O port address
-decoding and tri-state buffers, although several newer localbus cards go much
-beyond the basics.  When purchasing a localbus IDE interface, avoid cards with
-an onboard BIOS and those which require special drivers.  Instead, look for a
-card which uses hardware switches/jumpers to select the interface timing speed,
-to allow much faster data transfers than the original 8MHz ISA bus allows.
-
 ATA = AT (the old IBM 286 computer) Attachment Interface, a draft American
 National Standard for connecting hard drives to PCs.  This is the official
 name for "IDE".
 
-The latest standards define some enhancements, known as the ATA-2 spec,
+The latest standards define some enhancements, known as the ATA-6 spec,
 which grew out of vendor-specific "Enhanced IDE" (EIDE) implementations.
 
 ATAPI = ATA Packet Interface, a new protocol for controlling the drives,
 similar to SCSI protocols, created at the same time as the ATA2 standard.
-ATAPI is currently used for controlling CDROM and TAPE devices, and will
-likely also soon be used for Floppy drives, removable R/W cartridges,
-and for high capacity hard disk drives.
-
-How To Use *Big* ATA/IDE drives with Linux
-------------------------------------------
-The ATA Interface spec for IDE disk drives allows a total of 28 bits
-(8 bits for sector, 16 bits for cylinder, and 4 bits for head) for addressing
-individual disk sectors of 512 bytes each (in "Linear Block Address" (LBA)
-mode, there is still only a total of 28 bits available in the hardware).
-This "limits" the capacity of an IDE drive to no more than 128GB (Giga-bytes).
-All current day IDE drives are somewhat smaller than this upper limit, and
-within a few years, ATAPI disk drives will raise the limit considerably.
-
-All IDE disk drives "suffer" from a "16-heads" limitation:  the hardware has
-only a four bit field for head selection, restricting the number of "physical"
-heads to 16 or less.  Since the BIOS usually has a 63 sectors/track limit,
-this means that all IDE drivers larger than 504MB (528Meg) must use a "physical"
-geometry with more than 1024 cylinders.
-
-   (1024cyls * 16heads * 63sects * 512bytes/sector) / (1024 * 1024) == 504MB
-
-(Some BIOSs (and controllers with onboard BIOS) pretend to allow "32" or "64"
- heads per drive (discussed below), but can only do so by playing games with
- the real (hidden) geometry, which is always limited to 16 or fewer heads).
-
-This presents two problems to most systems:
-
-	1. The INT13 interface to the BIOS only allows 10-bits for cylinder
-	addresses, giving a limit of 1024cyls for programs which use it.
-
-	2. The physical geometry fields of the disk partition table only
-	allow 10-bits for cylinder addresses, giving a similar limit of 1024
-	cyls for operating systems that do not use the "sector count" fields
-	instead of the physical Cyl/Head/Sect (CHS) geometry fields.
-
-Neither of these limitations affects Linux itself, as it (1) does not use the
-BIOS for disk access, and it (2) is clever enough to use the "sector count"
-fields of the partition table instead of the physical CHS geometry fields.
-
-	a) Most folks use LILO to load linux.  LILO uses the INT13 interface
-	to the BIOS to load the kernel at boot time.  Therefore, LILO can only
-	load linux if the files it needs (usually just the kernel images) are
-	located below the magic 1024 cylinder "boundary" (more on this later).
-
-	b) Many folks also like to have bootable DOS partitions on their
-	drive(s).  DOS also uses the INT13 interface to the BIOS, not only
-	for booting, but also for operation after booting.  Therefore, DOS
-	can normally only access partitions which are contained entirely below
-	the magic 1024 cylinder "boundary".
-
-There are at least seven commonly used schemes for kludging DOS to work
-around this "limitation".  In the long term, the problem is being solved
-by introduction of an alternative BIOS interface that does not have the
-same limitations as the INT13 interface.  New versions of DOS are expected
-to detect and use this interface in systems whose BIOS provides it.
-
-But in the present day, alternative solutions are necessary.
-
-The most popular solution in newer systems is to have the BIOS shift bits
-between the cylinder and head number fields.  This is activated by entering
-a translated logical geometry into the BIOS/CMOS setup for the drive.
-Thus, if the drive has a geometry of 2100/16/63 (CHS), then the BIOS could
-present a "logical" geometry of 525/64/63 by "shifting" two bits from the
-cylinder number into the head number field for purposes of the partition table,
-CMOS setup, and INT13 interfaces.  Linux kernels 1.1.39 and higher detect and
-"handle" this translation automatically, making this a rather painless solution
-for the 1024 cyls problem.  If for some reason Linux gets confused (unlikely),
-then use the kernel command line parameters to pass the *logical* geometry,
-as in:  hda=525,64,63
-
-If the BIOS does not support this form of drive translation, then several
-options remain, listed below in order of popularity:
-
-	- use a partition below the 1024 cyl boundary to hold the linux
-	boot files (kernel images and /boot directory), and place the rest
-	of linux anywhere else on the drive.  These files can reside in a DOS
-	partition, or in a tailor-made linux boot partition.
-	- use DiskManager software from OnTrack, supplied free with
-	many new hard drive purchases.
-	- use EZ-Drive software (similar to DiskManager).  Note though,
-	that LILO must *not* use the MBR when EZ-Drive is present.
-	Instead, install LILO on the first sector of your linux partition,
-	and mark it as "active" or "bootable" with fdisk.
-	- boot from a floppy disk instead of the hard drive (takes 10 seconds).
-
-If you cannot use drive translation, *and* your BIOS also restricts you to
-entering no more than 1024 cylinders in the geometry field in the CMOS setup,
-then just set it to 1024.  As of v3.5 of this driver, Linux automatically
-determines the *real* number of cylinders for fdisk to use, allowing easy
-access to the full disk capacity without having to fiddle around.
-
-Regardless of what you do, all DOS partitions *must* be contained entirely
-within the first 1024 logical cylinders.  For a 1Gig WD disk drive, here's
-a good "half and half" partitioning scheme to start with:
-
-	geometry = 2100/16/63
-	/dev/hda1 from cyl    1 to  992		dos
-	/dev/hda2 from cyl  993 to 1023		swap
-	/dev/hda3 from cyl 1024 to 2100		linux
-
-To ensure that LILO can boot linux, the boot files (kernel and /boot/*)
-must reside within the first 1024 cylinders of the drive.  If your linux
-root partition is *not* completely within the first 1024 cyls (quite common),
-then you can use LILO to boot linux from files on your DOS partition
-by doing the following after installing Slackware (or whatever):
-
-	0. Boot from the "boot floppy" created during the installation
-        1. Mount your DOS partition as /dos (and stick it in /etc/fstab)
-        2. Move /boot to /dos/boot with:  cp -a /boot /dos ; rm -r /boot
-        3. Create a symlink for LILO to use with:  ln -s /dos/boot /boot
-        4. Move your kernel (/vmlinuz) to /boot/vmlinuz:  mv /vmlinuz /boot
-        5. Edit /etc/lilo.conf to change /vmlinuz to /boot/vmlinuz
-        6. Re-run LILO with:  lilo
-
-	A danger with this approach is that whenever an MS-DOS "defragmentation"
-	program is run (like Norton "speeddisk"), it may move the Linux boot
-	files around, confusing LILO and making the (Linux) system unbootable.
-	Be sure to keep a kernel "boot floppy" at hand for such circumstances.
-	A possible workaround is to mark the Linux files as S+H+R (System,
-	Hidden, Readonly), to prevent most defragmentation programs from
-	moving the files around.
-
-If you "don't do DOS", then partition as you please, but remember to create
-a small partition to hold the /boot directory (and vmlinuz) as described above
-such that they stay within the first 1024 cylinders.
-
-Note that when creating partitions that span beyond cylinder 1024,
-Linux fdisk will complain about "Partition X has different physical/logical
-endings" and emit messages such as "This is larger than 1024, and may cause
-problems with some software".   Ignore this for linux partitions.  The "some
-software" refers to DOS, the BIOS, and LILO, as described previously.
-
-Western Digital ships a "DiskManager 6.03" diskette with all of their big
-hard drives.  Use BIOS translation instead of this if possible, as it is a
-more generally compatible method of achieving the same results (DOS access
-to the entire disk).  However, if you must use DiskManager, it now works
-with Linux 1.3.x in most cases.  Let me know if you still have trouble.
-
-My recommendations to anyone who asks about NEW systems are:
-
-        - buy a motherboard that uses the Intel Triton chipset -- very common.
-        - use IDE for the first two drives, placing them on separate interfaces.
-		- very fast 7200rpm drives are now available
-		(though many problems have been reported with Seagate ones).
-	- place the IDE cdrom drive as slave on either interface.
-        - if additional disks are to be connected, consider your needs:
-                - fileserver?  Buy a SC200 SCSI adaptor for the next few drives.
-                - personal system?  Use IDE for the next two drives.
-                - still not enough?  Keep adding SC200 SCSI cards as needed.
-
-Most manufacturers make both IDE and SCSI versions of each of their drives.
-The IDE ones are usually as fast and cheaper, due to lower command overhead
-and the higher data transfer speed of UDMA2.  But fast/ultrawide/superlative
-SCSI is still king of the heap, especially for servers, if you've got the bucks.
+ATAPI is currently used for controlling CDROM, TAPE and FLOPPY (ZIP or
+LS120/240) devices, removable R/W cartridges, and for high capacity hard disk
+drives.
 
 mlord@pobox.com
 --
-For current maintainers of this stuff, see the linux/MAINTAINERS file.
+Wed Apr 17 22:52:44 CEST 2002 edited by Marcin Dalecki
+
+For current maintainers of this stuff, please see the linux/MAINTAINERS file.
diff -Nru a/Documentation/usb/dc2xx.txt b/Documentation/usb/dc2xx.txt
--- a/Documentation/usb/dc2xx.txt	Mon Apr 22 15:32:19 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,111 +0,0 @@
-14 April 2000
-david-b@pacbell.net
-
-This is an overview of how to use the "dc2xx" USB driver with certain
-digital still cameras from Kodak and other vendors.
-
-
-CAMERAS
-
-This driver will mostly be used with Kodak DC-2xx series digital still
-cameras, but it should be trivial to tell it about several non-Kodak
-USB-enabled cameras.
-
-You'll most likely want to hook it up to recent versions of "gPhoto"
-(www.gphoto.org), since version 0.4 and later know how to use it to talk
-to Kodak DC-240 and DC-280 cameras over USB.
-
-In addition the DC-220, DC-260, DC-265, and DC-290 are also recognized.
-However, like other cameras using the "Digita OS" (from www.flashpoint.com)
-there is no gPhoto support for this camera.  There is a python script
-for accessing these cameras (see archives of the linux-usb mailing list)
-and a "Digita Services" library that can also use this driver.
-
-The HP PhotoSmart C500 should also work, since it's another Digita camera
-with USB support.
-
-
-USB HARDWARE
-
-Recent kernels have had no particular problems using this driver with
-either OHCI or UHCI chipsets, and have worked on the PowerMac platform.
-
-Note that in some cases changes in BIOS settings may be needed before
-your USB works.  At least one user has reported a need for SMP-related 
-settings as well, and some old hardware may not handle USB correctly.
-
-
-SETUP
-
-Configure in the DC2XX USB driver, and have it in your kernel.  It works
-as a module, or compiled in directly.
-
-Create at least one device, perhaps like this (both read and write):
-
-    # mknod -m 0660 /dev/usb/dc2xx0 c 180 80
-    # mknod -m 0660 /dev/usb/dc2xx1 c 180 81
-    ...
-
-NOTE:  you would normally configure PAM so that the user logged in at
-the console is granted ownership of these devices.  console.perms(5)
-explains how to do this.
-
-The driver supports multiple device nodes.  The USB framework supports
-a maximum of sixteen device nodes (up to minor device number 96).
-
-When you plug in one camera, it will use the first device node (dc2xx0
-in the example above).  A second camera will use the second device node,
-and so on.
-
-
-SANITY TESTING
-
-First:  if you've got /proc support, make sure that the driver has hooked
-itself up correctly.
-
-    - You should see an entry in /proc/bus/usb/drivers for "dc2xx",
-      if you enabled USB /proc support and correctly mounted the
-      usbdevfs on /proc/bus/usb.
-
-Second:  when you connect your camera to the computer, does it get recognized
-by the driver?  (Make sure the camera is powered on!)
-
-    - if you've got /proc/bus/usb/devices, you should see an entry
-      something like this.  The "ProdID" may be different if you didn't
-      plug in a DC-240, as may the strings presented, but "Driver=dc2xx"
-      had better be there.
-
-	T:  Lev=01 Prnt=00 Port=00 Cnt=01 Dev#=  1 Spd=12  MxCh= 0
-	D:  Ver= 1.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs=  1
-	P:  Vendor=040a ProdID=0120 Rev= 1.08
-	S:  Manufacturer=Eastman Kodak Company
-	S:  Product=KODAK DC240 Zoom Digital Camera
-	C:* #Ifs= 1 Cfg#= 1 Atr=40 MxPwr=100mA
-	I:  If#= 0 Alt= 0 #EPs= 2 Cls=00(>ifc ) Sub=00 Prot=00 Driver=dc2xx
-	E:  Ad=01(O) Atr=02(Bulk) MxPS=  64 Ivl=  0ms
-	E:  Ad=82(I) Atr=02(Bulk) MxPS=  64 Ivl=  0ms
-
-    - see if "dmesg" output tells you that you plugged in your camera.
-
-	Manufacturer: Eastman Kodak Company
-	Product: KODAK DC240 Zoom Digital Camera
-	dc2xx.c: USB Camera #0 connected
-
-Third:  (optional) can you use gPhoto to talk to the camera?
-
-    - When you configure your camera, tell it to use "/dev/usb/dc2xx0"
-      (or whatever name you used).  Right now, gPhoto emits a diagnostic
-      message (non-GUI) saying that it since it didn't act like a TTY,
-      it's assuming it's got a USB connection.
-
-    - With the camera turned on, get the "camera summary".  It'll
-      talk to the camera -- and tell you you're using USB.
-
-If you got that far, you should be able to use everything fine.
-
-
-ADDITIONAL INFORMATION
-
-You may find that you need more driver-specific information, which is
-currently accessible through a link from http://www.linux-usb.org/
-along with other Linux USB resources.
diff -Nru a/Makefile b/Makefile
--- a/Makefile	Mon Apr 22 15:32:18 2002
+++ b/Makefile	Mon Apr 22 15:32:18 2002
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 5
-SUBLEVEL = 8
+SUBLEVEL = 9
 EXTRAVERSION =
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
@@ -347,9 +347,9 @@
 	$(MAKE) CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)" $(subst $@, _dir_$@, $@)
 
 TAGS: dummy
-	etags `find include/asm-$(ARCH) -name '*.h'`
-	find include -type d \( -name "asm-*" -o -name config \) -prune -o -name '*.h' -print | xargs etags -a
-	find $(SUBDIRS) init -name '*.[ch]' | xargs etags -a
+	{ find include/asm-${ARCH} -name '*.h' -print ; \
+	find include -type d \( -name "asm-*" -o -name config \) -prune -o -name '*.h' -print ; \
+	find $(SUBDIRS) init -name '*.[ch]' ; } | grep -v SCCS | etags -
 
 # Exuberant ctags works better with -I
 tags: dummy
diff -Nru a/Rules.make b/Rules.make
--- a/Rules.make	Mon Apr 22 15:32:19 2002
+++ b/Rules.make	Mon Apr 22 15:32:19 2002
@@ -126,8 +126,10 @@
 
 # for make >= 3.78 the following is cleaner:
 # multi-used := $(foreach m,$(obj-y) $(obj-m), $(if $($(basename $(m))-objs), $(m)))
-multi-used-y := $(sort $(foreach m,$(obj-y),$(patsubst %,$(m),$($(basename $(m))-objs))))
-multi-used-m := $(sort $(foreach m,$(obj-m),$(patsubst %,$(m),$($(basename $(m))-objs))))
+__obj-y = $(filter-out export.o,$(obj-y))
+__obj-m = $(filter-out export.o,$(obj-m))
+multi-used-y := $(sort $(foreach m,$(__obj-y),$(patsubst %,$(m),$($(basename $(m))-objs))))
+multi-used-m := $(sort $(foreach m,$(__obj-m),$(patsubst %,$(m),$($(basename $(m))-objs))))
 ld-multi-used-y := $(filter-out $(list-multi),$(multi-used-y))
 ld-multi-used-m := $(filter-out $(list-multi),$(multi-used-m))
 ld-multi-objs-y := $(foreach m, $(ld-multi-used-y), $($(basename $(m))-objs))
diff -Nru a/arch/alpha/config.in b/arch/alpha/config.in
--- a/arch/alpha/config.in	Mon Apr 22 15:32:19 2002
+++ b/arch/alpha/config.in	Mon Apr 22 15:32:19 2002
@@ -324,14 +324,7 @@
 
 source net/ax25/Config.in
 
-mainmenu_option next_comment
-comment 'ISDN subsystem'
-
-tristate 'ISDN support' CONFIG_ISDN
-if [ "$CONFIG_ISDN" != "n" ]; then
-  source drivers/isdn/Config.in
-fi
-endmenu
+source drivers/isdn/Config.in
 
 mainmenu_option next_comment
 comment 'Old CD-ROM drivers (not SCSI, not IDE)'
diff -Nru a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c
--- a/arch/alpha/kernel/osf_sys.c	Mon Apr 22 15:32:18 2002
+++ b/arch/alpha/kernel/osf_sys.c	Mon Apr 22 15:32:18 2002
@@ -219,7 +219,7 @@
 	 * isn't actually going to matter, as if the parent happens
 	 * to change we can happily return either of the pids.
 	 */
-	(&regs)->r20 = tsk->p_opptr->pid;
+	(&regs)->r20 = tsk->real_parent->pid;
 	return tsk->pid;
 }
 
diff -Nru a/arch/alpha/kernel/process.c b/arch/alpha/kernel/process.c
--- a/arch/alpha/kernel/process.c	Mon Apr 22 15:32:19 2002
+++ b/arch/alpha/kernel/process.c	Mon Apr 22 15:32:19 2002
@@ -54,15 +54,21 @@
 	return 0;
 }
 
+void default_idle(void)
+{
+	barrier();
+}
+
 void
 cpu_idle(void)
 {
 	while (1) {
+		void (*idle)(void) = default_idle;
 		/* FIXME -- EV6 and LCA45 know how to power down
 		   the CPU.  */
 
 		while (!need_resched())
-			barrier();
+			idle();
 		schedule();
 	}
 }
diff -Nru a/arch/alpha/kernel/ptrace.c b/arch/alpha/kernel/ptrace.c
--- a/arch/alpha/kernel/ptrace.c	Mon Apr 22 15:32:19 2002
+++ b/arch/alpha/kernel/ptrace.c	Mon Apr 22 15:32:19 2002
@@ -292,7 +292,7 @@
 		if (request != PTRACE_KILL)
 			goto out;
 	}
-	if (child->p_pptr != current) {
+	if (child->parent != current) {
 		DBG(DBG_MEM, ("child not parent of this process\n"));
 		goto out;
 	}
diff -Nru a/arch/alpha/kernel/semaphore.c b/arch/alpha/kernel/semaphore.c
--- a/arch/alpha/kernel/semaphore.c	Mon Apr 22 15:32:19 2002
+++ b/arch/alpha/kernel/semaphore.c	Mon Apr 22 15:32:19 2002
@@ -5,8 +5,8 @@
  * (C) Copyright 1999, 2000 Richard Henderson
  */
 
+#include <linux/errno.h>
 #include <linux/sched.h>
-
 
 /*
  * Semaphores are implemented using a two-way counter:
diff -Nru a/arch/alpha/kernel/signal.c b/arch/alpha/kernel/signal.c
--- a/arch/alpha/kernel/signal.c	Mon Apr 22 15:32:19 2002
+++ b/arch/alpha/kernel/signal.c	Mon Apr 22 15:32:19 2002
@@ -661,8 +661,8 @@
 				info.si_signo = signr;
 				info.si_errno = 0;
 				info.si_code = SI_USER;
-				info.si_pid = current->p_pptr->pid;
-				info.si_uid = current->p_pptr->uid;
+				info.si_pid = current->parent->pid;
+				info.si_uid = current->parent->uid;
 			}
 
 			/* If the (new) signal is now blocked, requeue it.  */
@@ -701,7 +701,7 @@
 			case SIGSTOP:
 				current->state = TASK_STOPPED;
 				current->exit_code = signr;
-				if (!(current->p_pptr->sig->action[SIGCHLD-1]
+				if (!(current->parent->sig->action[SIGCHLD-1]
 				      .sa.sa_flags & SA_NOCLDSTOP))
 					notify_parent(current, SIGCHLD);
 				schedule();
diff -Nru a/arch/alpha/math-emu/Makefile b/arch/alpha/math-emu/Makefile
--- a/arch/alpha/math-emu/Makefile	Mon Apr 22 15:32:18 2002
+++ b/arch/alpha/math-emu/Makefile	Mon Apr 22 15:32:18 2002
@@ -11,11 +11,8 @@
 
 else
 
-list-multi	:= math-emu.o
 math-emu-objs	:= math.o qrnnd.o
 obj-m		:= math-emu.o
-math-emu.o: $(math-emu-objs)
-	$(LD) -r -o $@ $(math-emu-objs)
 
 endif
 
diff -Nru a/arch/alpha/mm/init.c b/arch/alpha/mm/init.c
--- a/arch/alpha/mm/init.c	Mon Apr 22 15:32:19 2002
+++ b/arch/alpha/mm/init.c	Mon Apr 22 15:32:19 2002
@@ -283,7 +283,7 @@
 	unsigned long dma_pfn, high_pfn;
 
 	dma_pfn = virt_to_phys((char *)MAX_DMA_ADDRESS) >> PAGE_SHIFT;
-	high_pfn = max_low_pfn;
+	high_pfn = max_pfn = max_low_pfn;
 
 	if (dma_pfn >= high_pfn)
 		zones_size[ZONE_DMA] = high_pfn;
diff -Nru a/arch/arm/Config.help b/arch/arm/Config.help
--- a/arch/arm/Config.help	Mon Apr 22 15:32:18 2002
+++ b/arch/arm/Config.help	Mon Apr 22 15:32:18 2002
@@ -836,3 +836,153 @@
   of the BUG call as well as the EIP and oops trace.  This aids
   debugging but costs about 70-100K of memory.
 
+CONFIG_ZBOOT_ROM
+  Say Y here if you intend to execute your compressed kernel image (zImage)
+  directly from ROM or flash.  If unsure, say N.
+
+CONFIG_ZBOOT_ROM_TEXT
+  The base address for zImage.  Unless you have special requirements, you
+  should not change this value.
+
+CONFIG_ZBOOT_ROM_BSS
+  The base address of 64KiB of read/write memory, which must be available
+  while the decompressor is running.  Unless you have special requirements,
+  you should not change this value.
+
+CONFIG_CPU_FREQ
+  CPU clock scaling allows you to change the clock speed of the
+  running CPU on the fly. This is a nice method to save battery power,
+  because the lower the clock speed, the less power the CPU
+  consumes. Note that this driver doesn't automatically change the CPU
+  clock speed, you need some userland tools (which still have to be
+  written) to implement the policy. If you don't understand what this
+  is all about, it's safe to say 'N'.
+
+CONFIG_ARCH_EDB7211
+  Say Y here if you intend to run this kernel on a Cirrus Logic EDB-7211
+  evaluation board.
+
+CONFIG_SA1100_H3100
+  Say Y here if you intend to run this kernel on the Compaq iPAQ
+  H3100 handheld computer.  Information about this machine and the
+  Linux port to this machine can be found at:
+
+  <http://www.handhelds.org/Compaq/index.html#iPAQ_H3100>
+  <http://www.compaq.com/products/handhelds/pocketpc/>
+
+CONFIG_SA1100_H3800
+  Say Y here if you intend to run this kernel on the Compaq iPAQ H3800
+  series handheld computer.  Information about this machine and the
+  Linux port to this machine can be found at:
+
+  <http://www.handhelds.org/Compaq/index.html#iPAQ_H3800>
+  <http://www.compaq.com/products/handhelds/pocketpc/>
+
+CONFIG_H3600_SLEEVE
+  Choose this option to enable support for extension packs (sleeves)
+  for the Compaq iPAQ H3XXX series of handheld computers.  This option
+  is required for the CF, PCMCIA, Bluetooth and GSM/GPRS extension
+  packs.
+
+CONFIG_SA1100_GRAPHICSMASTER
+  Say Y here if you are using an Applied Data Systems Intel(R)
+  StrongARM(R) SA-1100 based Graphics Master SBC with SA-1111
+  StrongARM companion chip.  See
+  <http://www.applieddata.net/products_masterSpec.asp> for information
+  on this system.
+
+CONFIG_SA1100_ADSBITSY
+  Say Y here if you are using Applied Data Systems Intel(R)
+  StrongARM(R) 1110 based Bitsy, 3 x 5 inches in size, Compaq - IPAQ -
+  like platform. See
+  <http://www.applieddata.net/products_bitsySpec.asp> for more
+  information.
+
+CONFIG_SA1100_ITSY
+  Say Y here if you are using the Compaq Itsy experimental pocket
+  computer. See <http://research.compaq.com/wrl/projects/itsy/> for
+  more information.
+
+CONFIG_SA1100_HUW_WEBPANEL
+  Say Y here to support the HuW Webpanel produced by Hoeft & Wessel
+  AG.  English-language website is at
+  <http://www.hoeft-wessel.de/en.htm>; credits and build instructions
+  at Documentation/arm/SA1100/HUW_WEBPANEL.
+
+CONFIG_SA1100_PLEB
+  Say Y here if you are using a Portable Linux Embedded Board
+  (also known as PLEB). See <http://www.cse.unsw.edu.au/~pleb/>
+  for more information.
+
+CONFIG_SA1100_SHERMAN
+  Say Y here to support the Blazie Engineering `Sherman' StrongARM
+  1110-based SBC, used primarily in assistance products for the
+  visually impaired.  The company is now Freedom Scientific, with
+  a website at <http://www.freedomscientific.com/index.html>. The
+  Sherman product, however, appears to have been discontinued.
+
+CONFIG_SA1100_YOPY
+  Say Y here to support the Yopy PDA.  Product information at
+  <http://www.yopy.com/>.  See Documentation/arm/SA110/Yopy
+  for more.
+
+CONFIG_SA1100_CERF_CPLD
+  Say Y here to support the Linux CerfPDA development kit from
+  Intrinsyc. This is a StrongARM-1110-based reference platform for
+  designing custom PDAs.  Product info is at
+  <http://www.intrinsyc.com/products/referencedesigns/cerfpda.asp>.
+
+CONFIG_SA1100_FREEBIRD
+  Support the FreeBird board used in Coventive embedded products.  See
+  Documentation/arm/SA1100/Freebird for more.
+
+CONFIG_SA1100_PT_SYSTEM3
+   Say Y here if you intend to build a kernel suitable to run on
+   a Pruftechnik Digital Board. For more information see
+   <http://www.pruftechnik.com>
+
+CONFIG_CPU_ARM926T
+  This is a variant of the ARM920.  It has slightly different
+  instruction sequences for cache and TLB operations.  Curiously,
+  there is no documentation on it at the ARM corporate website.
+
+  Say Y if you want support for the ARM926T processor.
+  Otherwise, say N.
+
+CONFIG_SA1100_JORNADA720
+  Say Y here if you want to build a kernel for the HP Jornada 720
+  handheld computer.  See <http://www.hp.com/jornada/products/720>
+  for details.
+
+CONFIG_SA1100_OMNIMETER
+  Say Y here if you are using the inhand electronics OmniMeter.  See
+  <http://www.inhandelectronics.com/html/omni1.html> for details.
+
+CONFIG_SA1100_SIMPAD
+  The SIEMENS webpad SIMpad is based on the StrongARM 1110. There
+  are two different versions CL4 and SL4. CL4 has 32MB RAM and 16MB
+  FLASH. The SL4 version got 64 MB RAM and 32 MB FLASH and a
+  PCMCIA-Slot. The version for the Germany Telecom (DTAG) is the same
+  like CL4 in additional it has a PCMCIA-Slot. For more information
+  visit <http://www.my-siemens.com or www.siemens.ch>.
+
+CONFIG_ARCH_CDB89712
+  This is an evaluation board from Cirrus for the CS89712 processor.
+  The board includes 2 serial ports, Ethernet, IRDA, and expansion
+  headers.  It comes with 16 MB SDRAM and 8 MB flash ROM.
+
+CONFIG_ARCH_AUTCPU12
+  Say Y if you intend to run the kernel on the autronix autcpu12
+  board. This board is based on a Cirrus Logic CS89712.
+
+CONFIG_EP72XX_ROM_BOOT
+  If you say Y here, your CLPS711x-based kernel will use the bootstrap
+  mode memory map instead of the normal memory map.
+
+  Processors derived from the Cirrus CLPS-711X core support two boot
+  modes.  Normal mode boots from the external memory device at CS0.
+  Bootstrap mode rearranges parts of the memory map, placing an
+  internal 128 byte bootstrap ROM at CS0.  This option performs the
+  address map changes required to support booting in this mode.
+
+  You almost surely want to say N here.
diff -Nru a/arch/arm/boot/Makefile b/arch/arm/boot/Makefile
--- a/arch/arm/boot/Makefile	Mon Apr 22 15:32:18 2002
+++ b/arch/arm/boot/Makefile	Mon Apr 22 15:32:18 2002
@@ -5,31 +5,31 @@
 # License.  See the file "COPYING" in the main directory of this archive
 # for more details.
 #
-# Copyright (C) 1995-2000 Russell King
+# Copyright (C) 1995-2002 Russell King
 #
 
 SYSTEM	=$(TOPDIR)/vmlinux
 
 ifeq ($(CONFIG_CPU_26),y)
-ZTEXTADDR	 = 0x02080000
+ZRELADDR	 = 0x02080000
 PARAMS_PHYS	 = 0x0207c000
 INITRD_PHYS	 = 0x02180000
 INITRD_VIRT	 = 0x02180000
 endif
 
 ifeq ($(CONFIG_ARCH_RPC),y)
-ZTEXTADDR	 = 0x10008000
+ZRELADDR	 = 0x10008000
 PARAMS_PHYS	 = 0x10000100
 INITRD_PHYS	 = 0x18000000
 INITRD_VIRT	 = 0xc8000000
 endif
 
 ifeq ($(CONFIG_ARCH_CLPS7500),y)
-ZTEXTADDR	 = 0x10008000
+ZRELADDR	 = 0x10008000
 endif
 
 ifeq ($(CONFIG_ARCH_EBSA110),y)
-ZTEXTADDR	 = 0x00008000
+ZRELADDR	 = 0x00008000
 PARAMS_PHYS	 = 0x00000400
 INITRD_PHYS	 = 0x00800000
 INITRD_VIRT	 = 0xc0800000
@@ -41,41 +41,34 @@
 endif
 
 ifeq ($(CONFIG_FOOTBRIDGE),y)
-ZTEXTADDR	 = 0x00008000
+ZRELADDR	 = 0x00008000
 PARAMS_PHYS	 = 0x00000100
 INITRD_PHYS	 = 0x00800000
 INITRD_VIRT	 = 0xc0800000
 endif
 
 ifeq ($(CONFIG_ARCH_INTEGRATOR),y)
-ZTEXTADDR	 = 0x00008000
+ZRELADDR	 = 0x00008000
 PARAMS_PHYS	 = 0x00000100
 INITRD_PHYS	 = 0x00800000
 INITRD_VIRT	 = 0xc0800000
 endif
 
 ifeq ($(CONFIG_ARCH_CAMELOT),y)
-ZTEXTADDR	 = 0x00008000
+ZRELADDR	 = 0x00008000
 endif
 
 ifeq ($(CONFIG_ARCH_NEXUSPCI),y)
-ZTEXTADDR	 = 0x40008000
+ZRELADDR	 = 0x40008000
 endif
 
 ifeq ($(CONFIG_ARCH_L7200),y)
-# RAM based kernel
-#ZTEXTADDR	 = 0xf0400000
-#ZRELADDR	 = 0xf0008000
-
-# FLASH based kernel
-ZTEXTADDR	 = 0x00010000
 ZRELADDR	 = 0xf0008000
-ZBSSADDR	 = 0xf03e0000
 endif
 
 # The standard locations for stuff on CLPS711x type processors
 ifeq ($(CONFIG_ARCH_CLPS711X),y)
-ZTEXTADDR	 = 0xc0028000
+ZRELADDR	 = 0xc0028000
 PARAMS_PHYS	 = 0xc0000100
 endif
 
@@ -90,60 +83,40 @@
 endif
 
 ifeq ($(CONFIG_ARCH_SA1100),y)
-ZTEXTADDR	 = 0xc0008000
 ZRELADDR	 = 0xc0008000
-ifeq ($(CONFIG_SA1100_VICTOR),y)
-  ZTEXTADDR	 = 0x00002000
-  ZBSSADDR	 = 0xc0200000
-endif
-ifeq ($(CONFIG_SA1100_SHERMAN),y)
-  ZTEXTADDR      = 0x00050000   
-  ZBSSADDR       = 0xc0200000
-endif
-ifeq ($(CONFIG_SA1100_GRAPHICSCLIENT),y)
-  ZTEXTADDR	 = 0xC0200000
-endif
-ifeq ($(CONFIG_SA1100_GRAPHICSMASTER),y)
-  ZTEXTADDR	 = 0xC0400000
-endif
-ifeq ($(CONFIG_SA1100_ADSBITSY),y)
-  ZTEXTADDR	 = 0xC0400000
-endif
-ifeq ($(CONFIG_SA1100_YOPY),y)
-  ZTEXTADDR      = 0x00080000
-  ZBSSADDR       = 0xc0200000
-endif
+# No defconfig file to move this into...
+#ifeq ($(CONFIG_SA1100_YOPY),y)
+#  ZTEXTADDR      = 0x00080000
+#  ZBSSADDR       = 0xc0200000
+#endif
 ifeq ($(CONFIG_SA1111),y)
   ZRELADDR	 = 0xc0208000
 endif
 endif
 
 ifeq ($(CONFIG_ARCH_ANAKIN),y)
-ZTEXTADDR	 = 0x20008000
+ZRELADDR	 = 0x20008000
 endif
 
 ifeq ($(CONFIG_ARCH_IQ80310),y)
 ZRELADDR	 = 0xa0008000
-
-# for serial upload
-ZTEXTADDR	 = 0xa1008000
-
-# for direct flash execution
-# ZTEXTADDR	 = 0x00060000
-# ZBSSADDR	 = 0xa1008000
 endif
 
 ifeq ($(CONFIG_ARCH_ADIFCC),y)
 ZRELADDR	 = 0xc0008000
-ZTEXTADDR	 = 0xc1000000
 endif
 
 #
-# If you don't define ZRELADDR above,
-# then it defaults to ZTEXTADDR
+# We now have a PIC decompressor implementation.  Decompressors running
+# from RAM should not define ZTEXTADDR.  Decompressors running directly
+# from ROM or Flash must define ZTEXTADDR (preferably via the config)
 #
-ifeq ($(ZRELADDR),)
-ZRELADDR	= $(ZTEXTADDR)
+ifeq ($(CONFIG_ZBOOT_ROM),y)
+ZTEXTADDR	=0x$(CONFIG_ZBOOT_ROM_TEXT)
+ZBSSADDR	=0x$(CONFIG_ZBOOT_ROM_BSS)
+else
+ZTEXTADDR	=0
+ZBSSADDR	=ALIGN(4)
 endif
 
 export	SYSTEM ZTEXTADDR ZBSSADDR ZRELADDR INITRD_PHYS INITRD_VIRT PARAMS_PHYS
diff -Nru a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
--- a/arch/arm/boot/compressed/Makefile	Mon Apr 22 15:32:18 2002
+++ b/arch/arm/boot/compressed/Makefile	Mon Apr 22 15:32:18 2002
@@ -9,7 +9,7 @@
 
 HEAD		 = head.o
 OBJS		 = misc.o
-CFLAGS		 = $(CPPFLAGS) -O2 -DSTDC_HEADERS $(CFLAGS_BOOT)
+CFLAGS		 = $(CPPFLAGS) -O2 -DSTDC_HEADERS $(CFLAGS_BOOT) -fpic
 FONTC		 = $(TOPDIR)/drivers/video/font_acorn_8x8.c
 ZLDFLAGS	 = -p -X -T vmlinux.lds
 
@@ -65,13 +65,7 @@
 OBJS		+= head-xscale.o
 endif
 
-SEDFLAGS	= s/TEXT_START/$(ZTEXTADDR)/;s/LOAD_ADDR/$(ZRELADDR)/;
-
-ifneq ($(ZBSSADDR),)
-SEDFLAGS	+= s/BSS_START/$(ZBSSADDR)/
-else
-SEDFLAGS	+= s/BSS_START/ALIGN(4)/
-endif
+SEDFLAGS	= s/TEXT_START/$(ZTEXTADDR)/;s/LOAD_ADDR/$(ZRELADDR)/;s/BSS_START/$(ZBSSADDR)/
 
 LIBGCC		:= $(shell $(CC) $(CFLAGS) --print-libgcc-file-name)
 
diff -Nru a/arch/arm/boot/compressed/head-clps7500.S b/arch/arm/boot/compressed/head-clps7500.S
--- a/arch/arm/boot/compressed/head-clps7500.S	Mon Apr 22 15:32:19 2002
+++ b/arch/arm/boot/compressed/head-clps7500.S	Mon Apr 22 15:32:19 2002
@@ -13,7 +13,7 @@
 		/* This branch is taken if the CPU memory width matches the
 		   actual device in use.  The default at power on is 16 bits
 		   so we must be prepared for a mismatch. */
-		.section ".start", #alloc, #execinstr
+		.section ".start", "ax"
 2:
 		b	1f
 		.word	0xffff
diff -Nru a/arch/arm/boot/compressed/head-epxa10db.S b/arch/arm/boot/compressed/head-epxa10db.S
--- a/arch/arm/boot/compressed/head-epxa10db.S	Mon Apr 22 15:32:19 2002
+++ b/arch/arm/boot/compressed/head-epxa10db.S	Mon Apr 22 15:32:19 2002
@@ -1,5 +1,5 @@
 #include <asm/mach-types.h>
 #include <asm/arch/excalibur.h>
 
-		.section	".start", #alloc, #execinstr
+		.section	".start", "ax"
 		mov	r7, #MACH_TYPE_CAMELOT
diff -Nru a/arch/arm/boot/compressed/head-ftvpci.S b/arch/arm/boot/compressed/head-ftvpci.S
--- a/arch/arm/boot/compressed/head-ftvpci.S	Mon Apr 22 15:32:19 2002
+++ b/arch/arm/boot/compressed/head-ftvpci.S	Mon Apr 22 15:32:19 2002
@@ -13,7 +13,7 @@
  * 2 of the License, or (at your option) any later version.
  */
 
-	.section        ".start", #alloc, #execinstr
+	.section        ".start", "ax"
 ftv_start:
 	mcr	p15, 0, r0, c7, c5, 0		@ flush I cache
 	mrc	p15, 0, r0, c1, c0
diff -Nru a/arch/arm/boot/compressed/head-integrator.S b/arch/arm/boot/compressed/head-integrator.S
--- a/arch/arm/boot/compressed/head-integrator.S	Mon Apr 22 15:32:18 2002
+++ b/arch/arm/boot/compressed/head-integrator.S	Mon Apr 22 15:32:18 2002
@@ -1,4 +1,4 @@
 #include <asm/mach-types.h>
 
-		.section	".start", #alloc, #execinstr
+		.section	".start", "ax"
 		mov	r7, #MACH_TYPE_INTEGRATOR
diff -Nru a/arch/arm/boot/compressed/head-l7200.S b/arch/arm/boot/compressed/head-l7200.S
--- a/arch/arm/boot/compressed/head-l7200.S	Mon Apr 22 15:32:18 2002
+++ b/arch/arm/boot/compressed/head-l7200.S	Mon Apr 22 15:32:18 2002
@@ -13,7 +13,7 @@
 #error What am I doing here...
 #endif
 
-		.section        ".start", #alloc, #execinstr
+		.section        ".start", "ax"
 
 __L7200_start:
 		mov	r0, #0x00100000		@ FLASH address of initrd
diff -Nru a/arch/arm/boot/compressed/head-netwinder.S b/arch/arm/boot/compressed/head-netwinder.S
--- a/arch/arm/boot/compressed/head-netwinder.S	Mon Apr 22 15:32:19 2002
+++ b/arch/arm/boot/compressed/head-netwinder.S	Mon Apr 22 15:32:19 2002
@@ -1,50 +1,13 @@
 /*
  *  linux/arch/arm/boot/compressed/head-netwinder.S
  *
- *  Copyright (C) 2000 Russell King
+ *  Copyright (C) 2000-2002 Russell King
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
-#define K(a,b,c)	((a) << 24 | (b) << 12 | (c))
+		.section	".start", "ax"
 
-		.section	".start", #alloc, #execinstr
-
-		/*
-		 * check to see if we are running from the correct address.
-		 * If not, we move ourselves in a two stage process.  Firstly,
-		 * we copy the start of the kernel (which includes this code)
-		 * to 0x8000, and then jump to this code to continue with the
-		 * rest (since this code will get overwritten).
-		 */
-		adr	r2, 1f
-		ldmdb	r2, {r9, r10}
-		and	r3, r2, #0xc000
-		teq	r3, #0x8000	@ correctly located?
-		beq	2f		@ skip this code
-		bic	r3, r2, #0xc000
-		orr	r3, r3, #0x8000
-		mov	r0, r3		@ new address if '1'
-		mov	r4, #64		@ number of bytes to copy
-		sub	r5, r10, r9	@ total number of bytes to copy
-		b	1f
-
-		.word	_start
-		.word	__bss_start
-
-1:
-		.rept	4
-		ldmia	r2!, {r6, r9, r10, r11}
-		stmia	r3!, {r6, r9, r10, r11}
-		.endr
-		subs	r4, r4, #64
-		bcs	1b
-		movs	r4, r5		@ remaining length
-		mov	r5, #0		@ no more to copy
-		movne	pc, r0		@ jump back to 1 (in the newly copied
-					@ code)
-		mov	r7, #5		@ only here to fix NeTTroms which dont
-		mov	r8, #2 << 24		@ scheduled for removal in 2.5.xx
-		orr	r8, r8, #5 << 12
-2:
+		mov	r7, #5
+		mov	r8, #0
diff -Nru a/arch/arm/boot/compressed/head-sa1100.S b/arch/arm/boot/compressed/head-sa1100.S
--- a/arch/arm/boot/compressed/head-sa1100.S	Mon Apr 22 15:32:18 2002
+++ b/arch/arm/boot/compressed/head-sa1100.S	Mon Apr 22 15:32:18 2002
@@ -11,7 +11,7 @@
 #include <linux/linkage.h>
 #include <asm/mach-types.h>
 
-		.section        ".start", #alloc, #execinstr
+		.section        ".start", "ax"
 
 __SA1100_start:
 
diff -Nru a/arch/arm/boot/compressed/head-shark.S b/arch/arm/boot/compressed/head-shark.S
--- a/arch/arm/boot/compressed/head-shark.S	Mon Apr 22 15:32:19 2002
+++ b/arch/arm/boot/compressed/head-shark.S	Mon Apr 22 15:32:19 2002
@@ -16,7 +16,7 @@
 
 #include <asm/assembler.h>
 	
-		.section	".start", #alloc, #execinstr
+		.section	".start", "ax"
 
 		b	__beginning
 	
diff -Nru a/arch/arm/boot/compressed/head-xscale.S b/arch/arm/boot/compressed/head-xscale.S
--- a/arch/arm/boot/compressed/head-xscale.S	Mon Apr 22 15:32:19 2002
+++ b/arch/arm/boot/compressed/head-xscale.S	Mon Apr 22 15:32:19 2002
@@ -9,7 +9,7 @@
 #include <linux/linkage.h>
 #include <asm/mach-types.h>
 
-		.section        ".start", #alloc, #execinstr
+		.section        ".start", "ax"
 
 __XScale_start:
 
diff -Nru a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S
--- a/arch/arm/boot/compressed/head.S	Mon Apr 22 15:32:19 2002
+++ b/arch/arm/boot/compressed/head.S	Mon Apr 22 15:32:19 2002
@@ -132,10 +132,28 @@
 		 */
 
 		.text
-1:		adr	r2, LC0
-		ldmia	r2, {r2, r3, r4, r5, sp}
+		adr	r0, LC0
+		ldmia	r0, {r1, r2, r3, r4, r5, r6, ip, sp}
+		subs	r0, r0, r1		@ calculate the delta offset
+
+		teq	r0, #0			@ if delta is zero, we're
+		beq	not_relocated		@ running at the address we
+						@ were linked at.
+
+		add	r2, r2, r0		@ different address, so we
+		add	r3, r3, r0		@ need to fix up various
+		add	r5, r5, r0		@ pointers.
+		add	r6, r6, r0
+		add	ip, ip, r0
+		add	sp, sp, r0
+
+1:		ldr	r1, [r6, #0]		@ relocate entries in the GOT
+		add	r1, r1, r0		@ table.  This fixes up the
+		str	r1, [r6], #4		@ C references.
+		cmp	r6, ip
+		blt	1b
 
-		mov	r0, #0
+not_relocated:	mov	r0, #0
 1:		str	r0, [r2], #4		@ clear bss
 		str	r0, [r2], #4
 		str	r0, [r2], #4
@@ -149,17 +167,26 @@
 		mov	r1, sp			@ malloc space above stack
 		add	r2, sp, #0x10000	@ 64k max
 
-		teq	r4, r5			@ will we overwrite ourselves?
-		moveq	r5, r2			@ decompress after image
-		movne	r5, r4			@ decompress to final location
+/*
+ * Check to see if we will overwrite ourselves.
+ *   r4 = final kernel address
+ *   r5 = start of this image
+ *   r2 = end of malloc space (and therefore this image)
+ * We basically want:
+ *   r4 >= r2 -> OK
+ *   r4 + image length <= r5 -> OK
+ */
+		cmp	r4, r2
+		bhs	wont_overwrite
+		add	r0, r4, #4096*1024	@ 4MB largest kernel size
+		cmp	r0, r5
+		bls	wont_overwrite
 
+		mov	r5, r2			@ decompress after malloc space
 		mov	r0, r5
 		mov	r3, r7
 		bl	SYMBOL_NAME(decompress_kernel)
 
-		teq	r4, r5			@ do we need to relocate
-		beq	call_kernel		@ the kernel?
-
 		add	r0, r0, #127
 		bic	r0, r0, #127		@ align the kernel length
 /*
@@ -184,12 +211,26 @@
 		bl	cache_clean_flush
 		add	pc, r5, r0		@ call relocation code
 
+/*
+ * We're not in danger of overwriting ourselves.  Do this the simple way.
+ *
+ * r4     = kernel execution address
+ * r7     = architecture ID
+ */
+wont_overwrite:	mov	r0, r4
+		mov	r3, r7
+		bl	SYMBOL_NAME(decompress_kernel)
+		b	call_kernel
+
 		.type	LC0, #object
-LC0:		.word	__bss_start
-		.word	_end
-		.word	_load_addr
-		.word	_start
-		.word	user_stack+4096
+LC0:		.word	LC0			@ r1
+		.word	__bss_start		@ r2
+		.word	_end			@ r3
+		.word	_load_addr		@ r4
+		.word	_start			@ r5
+		.word	_got_start		@ r6
+		.word	_got_end		@ r7
+		.word	user_stack+4096		@ r8
 		.size	LC0, . - LC0
 
 /*
@@ -206,26 +247,15 @@
  *  r7 = architecture number
  *  r8 = run-time address of "start"
  * On exit,
- *  r0, r1, r2, r3, r8, r9 corrupted
+ *  r1, r2, r3, r8, r9, r12 corrupted
  * This routine must preserve:
  *  r4, r5, r6, r7
  */
 		.align	5
-cache_on:	ldr	r1, proc_sa110_type
-		eor	r1, r1, r6
-		movs	r1, r1, lsr #5		@ catch SA110 and SA1100
-		beq	1f
-		ldr     r1, proc_sa1110_type
-		eor	r1, r1, r6
-		movs	r1, r1, lsr #4
-		beq	1f
-		ldr	r1, proc_xscale_type
-		eor	r1, r1, r6
-		movs	r1, r1, lsr #16
-@		movne	pc, lr
-		bne	cache_off
-1:
-		sub	r3, r4, #16384		@ Page directory size
+cache_on:	mov	r3, #8			@ cache_on function
+		b	call_cache_fn
+
+__cache_on:	sub	r3, r4, #16384		@ Page directory size
 		bic	r3, r3, #0xff		@ Align the pointer
 		bic	r3, r3, #0x3f00
 /*
@@ -277,8 +307,9 @@
 		mov	pc, lr
 
 /*
- * This code is relocatable.  It is relocated by the above code to the end
- * of the kernel and executed there.  During this time, we have no stacks.
+ * All code following this line is relocatable.  It is relocated by
+ * the above code to the end of the decompressed kernel image and
+ * executed there.  During this time, we have no stacks.
  *
  * r0     = decompressed kernel length
  * r1-r3  = unused
@@ -309,47 +340,101 @@
 		mov	pc, r4			@ call kernel
 
 /*
- * Here follow the relocatable cache support functions for
- * the various processors.
+ * Here follow the relocatable cache support functions for the
+ * various processors.  This is a generic hook for locating an
+ * entry and jumping to an instruction at the specified offset
+ * from the start of the block.  Please note this is all position
+ * independent code.
+ *
+ *  r1  = corrupted
+ *  r2  = corrupted
+ *  r3  = block offset
+ *  r6  = CPU ID
+ *  r12 = corrupted
+ */
+
+call_cache_fn:	adr	r12, proc_types
+1:		ldr	r1, [r12, #0]		@ get value
+		ldr	r2, [r12, #4]		@ get mask
+		eor	r1, r1, r6		@ (real ^ match)
+		tst	r1, r2			@       & mask
+		addeq	pc, r12, r3		@ call cache function
+		add	r12, r12, #4*5
+		b	1b
+
+/*
+ * Table for cache operations.  This is basically:
+ *   - CPU ID match
+ *   - CPU ID mask
+ *   - 'cache on' method instruction
+ *   - 'cache off' method instruction
+ *   - 'cache flush' method instruction
+ *
+ * We match an entry using: ((real_id ^ match) & mask) == 0
+ *
+ * Writethrough caches generally only need 'on' and 'off'
+ * methods.  Writeback caches _must_ have the flush method
+ * defined.
  */
+		.type	proc_types,#object
+proc_types:
+		.word	0x41560600		@ ARM6/610
+		.word	0xffffffe0
+		b	__arm6_cache_off
+		b	__arm6_cache_off
+		mov	pc, lr
+
+		.word	0x41007000		@ ARM7/710
+		.word	0xfff8fe00
+		b	__arm7_cache_off
+		b	__arm7_cache_off
+		mov	pc, lr
+
+		.word	0x41807200		@ ARM720T (writethrough)
+		.word	0xffffff00
+		b	__cache_on
+		b	__armv4_cache_off
+		mov	pc, lr
+
+		.word	0x4401a100		@ sa110 / sa1100
+		.word	0xffffffe0
+		b	__cache_on
+		b	__armv4_cache_off
+		b	__armv4_cache_flush
+
+		.word	0x6901b110		@ sa1110
+		.word	0xfffffff0
+		b	__cache_on
+		b	__armv4_cache_off
+		b	__armv4_cache_flush
+
+		.word	0x69050000		@ xscale
+		.word	0xffff0000
+		b	__cache_on
+		b	__armv4_cache_off
+		b	__armv4_cache_flush
 
-		.type	proc_sa110_type,#object
-proc_sa110_type:
-		.word	0x4401a100
-		.size	proc_sa110_type, . - proc_sa110_type
-
-		.type	proc_sa1110_type,#object
-proc_sa1110_type:
-		.word	0x6901b110
-		.size	proc_sa1110_type, . - proc_sa1110_type
+		.word	0			@ unrecognised type
+		.word	0
+		mov	pc, lr
+		mov	pc, lr
+		mov	pc, lr
+
+		.size	proc_types, . - proc_types
 
 /*
  * Turn off the Cache and MMU.  ARMv3 does not support
  * reading the control register, but ARMv4 does.
  *
  * On entry,  r6 = processor ID
- * On exit,   r0, r1 corrupted
+ * On exit,   r0, r1, r2, r3, r12 corrupted
  * This routine must preserve: r4, r6, r7
  */
 		.align	5
-cache_off:
-#ifdef CONFIG_CPU_ARM610
-		eor	r1, r6, #0x41000000
-		eor	r1, r1, #0x00560000
-		bic	r1, r1, #0x0000001f
-		teq	r1, #0x00000600
-		mov	r0, #0x00000060		@ ARM6 control reg.
-		beq	__armv3_cache_off
-#endif
-#ifdef CONFIG_CPU_ARM710
-		eor	r1, r6, #0x41000000
-		bic	r1, r1, #0x00070000
-		bic	r1, r1, #0x000000ff
-		teq	r1, #0x00007000		@ ARM7
-		teqne	r1, #0x00007100		@ ARM710
-		mov	r0, #0x00000070		@ ARM7 control reg.
-		beq	__armv3_cache_off
-#endif
+cache_off:	mov	r3, #12			@ cache_off function
+		b	call_cache_fn
+
+__armv4_cache_off:
 		mrc	p15, 0, r0, c1, c0
 		bic	r0, r0, #0x000d
 		mcr	p15, 0, r0, c1, c0	@ turn MMU and cache off
@@ -358,6 +443,14 @@
 		mcr	p15, 0, r0, c8, c7	@ invalidate whole TLB v4
 		mov	pc, lr
 
+__arm6_cache_off:
+		mov	r0, #0x00000060		@ ARM6 control reg.
+		b	__armv3_cache_off
+
+__arm7_cache_off:
+		mov	r0, #0x00000070		@ ARM7 control reg.
+		b	__armv3_cache_off
+
 __armv3_cache_off:
 		mcr	p15, 0, r0, c1, c0	@ turn MMU and cache off
 		mov	r0, #0
@@ -371,25 +464,16 @@
  * On entry,
  *  r6 = processor ID
  * On exit,
- *  r1, r2, r12 corrupted
+ *  r1, r2, r3, r12 corrupted
  * This routine must preserve:
- *  r4, r6, r7
+ *  r0, r4, r5, r6, r7
  */
 		.align	5
 cache_clean_flush:
-		ldr	r1, proc_sa110_type
-		eor	r1, r1, r6
-		movs	r1, r1, lsr #5		@ catch SA110 and SA1100
-		beq	1f
-		ldr	r1, proc_sa1110_type
-		eor	r1, r1, r6
-		movs	r1, r1, lsr #4
-		beq	1f
-		ldr	r1, proc_xscale_type
-		eor	r1, r1, r6
-		movs	r1, r1, lsr #16
-		movne	pc, lr
-1:
+		mov	r3, #16
+		b	call_cache_fn
+
+__armv4_cache_flush:
 		bic	r1, pc, #31
 		add	r2, r1, #65536		@ 2x the largest dcache size
 1:		ldr	r12, [r1], #32		@ s/w flush D cache
@@ -400,12 +484,6 @@
 		mcr	p15, 0, r1, c7, c10, 4	@ drain WB
 		mov	pc, lr
 
-		.type	proc_xscale_type,#object
-proc_xscale_type:
-		.word	0x69050000
-		.size	proc_xscale_type, . - proc_xscale_type
-
-
 /*
  * Various debugging routines for printing hex characters and
  * memory, which again must be relocatable.
@@ -481,5 +559,5 @@
 reloc_end:
 
 		.align
-		.section	".stack"
+		.section ".stack", "aw"
 user_stack:	.space	4096
diff -Nru a/arch/arm/boot/compressed/vmlinux.lds.in b/arch/arm/boot/compressed/vmlinux.lds.in
--- a/arch/arm/boot/compressed/vmlinux.lds.in	Mon Apr 22 15:32:19 2002
+++ b/arch/arm/boot/compressed/vmlinux.lds.in	Mon Apr 22 15:32:19 2002
@@ -35,29 +35,26 @@
 
   _etext = .;
 
-  .data : {
-    *(.data)
-  }
-
+  _got_start = .;
+  .got			: { *(.got) }
+  _got_end = .;
+  .got.plt		: { *(.got.plt) }
+  .data			: { *(.data) }
   _edata = .;
 
   . = BSS_START;
   __bss_start = .;
-  .bss : {
-    *(.bss)
-  }
+  .bss			: { *(.bss) }
   _end = .;
 
-  .stack : {
-    *(.stack)
-  }
+  .stack (NOLOAD)	: { *(.stack) }
 
-  .stab 0 : { *(.stab) }
-  .stabstr 0 : { *(.stabstr) }
-  .stab.excl 0 : { *(.stab.excl) }
-  .stab.exclstr 0 : { *(.stab.exclstr) }
-  .stab.index 0 : { *(.stab.index) }
-  .stab.indexstr 0 : { *(.stab.indexstr) }
-  .comment 0 : { *(.comment) }
+  .stab 0		: { *(.stab) }
+  .stabstr 0		: { *(.stabstr) }
+  .stab.excl 0		: { *(.stab.excl) }
+  .stab.exclstr 0	: { *(.stab.exclstr) }
+  .stab.index 0		: { *(.stab.index) }
+  .stab.indexstr 0	: { *(.stab.indexstr) }
+  .comment 0		: { *(.comment) }
 }
 
diff -Nru a/arch/arm/config.in b/arch/arm/config.in
--- a/arch/arm/config.in	Mon Apr 22 15:32:19 2002
+++ b/arch/arm/config.in	Mon Apr 22 15:32:19 2002
@@ -433,6 +433,12 @@
    define_bool CONFIG_FIQ n
 fi
 
+# Compressed boot loader in ROM.  Yes, we really want to ask about
+# TEXT and BSS so we preserve their values in the config files.
+bool 'Compressed boot loader in ROM/flash' CONFIG_ZBOOT_ROM
+hex 'Compressed ROM boot loader base address' CONFIG_ZBOOT_ROM_TEXT 0
+hex 'Compressed ROM boot loader BSS address' CONFIG_ZBOOT_ROM_BSS 0
+
 if [ "$CONFIG_ARCH_SA1100" = "y" -o \
      "$CONFIG_ARCH_INTEGRATOR" = "y" ]; then
    dep_bool 'Support CPU clock change (EXPERIMENTAL)' CONFIG_CPU_FREQ $CONFIG_EXPERIMENTAL
@@ -573,14 +579,7 @@
 
 source drivers/message/i2o/Config.in
 
-mainmenu_option next_comment
-comment 'ISDN subsystem'
-
-tristate 'ISDN support' CONFIG_ISDN
-if [ "$CONFIG_ISDN" != "n" ]; then
-   source drivers/isdn/Config.in
-fi
-endmenu
+source drivers/isdn/Config.in
 
 #
 # input before char - char/joystick depends on it. As does USB.
diff -Nru a/arch/arm/def-configs/iq80310 b/arch/arm/def-configs/iq80310
--- a/arch/arm/def-configs/iq80310	Mon Apr 22 15:32:19 2002
+++ b/arch/arm/def-configs/iq80310	Mon Apr 22 15:32:19 2002
@@ -139,6 +139,9 @@
 #
 # General setup
 #
+CONFIG_ZBOOT_ROM=y
+CONFIG_ZBOOT_ROM_TEXT=0x00060000
+CONFIG_ZBOOT_ROM_BSS=0xa1008000
 CONFIG_PCI=y
 # CONFIG_ISA is not set
 # CONFIG_ISA_DMA is not set
diff -Nru a/arch/arm/def-configs/lusl7200 b/arch/arm/def-configs/lusl7200
--- a/arch/arm/def-configs/lusl7200	Mon Apr 22 15:32:19 2002
+++ b/arch/arm/def-configs/lusl7200	Mon Apr 22 15:32:19 2002
@@ -23,6 +23,9 @@
 # CONFIG_CPU_26 is not set
 CONFIG_CPU_32v4=y
 CONFIG_CPU_ARM720=y
+CONFIG_ZBOOT_ROM=y
+CONFIG_ZBOOT_ROM_TEXT=0x00010000
+CONFIG_ZBOOT_ROM_BSS=0xf03e0000
 # CONFIG_PCI is not set
 # CONFIG_ISA is not set
 # CONFIG_ISA_DMA is not set
diff -Nru a/arch/arm/def-configs/shark b/arch/arm/def-configs/shark
--- a/arch/arm/def-configs/shark	Mon Apr 22 15:32:18 2002
+++ b/arch/arm/def-configs/shark	Mon Apr 22 15:32:18 2002
@@ -15,7 +15,14 @@
 # Code maturity level options
 #
 CONFIG_EXPERIMENTAL=y
-# CONFIG_OBSOLETE is not set
+
+#
+# General setup
+#
+CONFIG_NET=y
+CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_SYSCTL=y
 
 #
 # Loadable module support
@@ -96,6 +103,7 @@
 # CONFIG_SA1100_VICTOR is not set
 # CONFIG_SA1100_XP860 is not set
 # CONFIG_SA1100_YOPY is not set
+# CONFIG_SA1100_STORK is not set
 # CONFIG_SA1100_USB is not set
 # CONFIG_SA1100_USB_NETLINK is not set
 # CONFIG_SA1100_USB_CHAR is not set
@@ -124,6 +132,7 @@
 #
 # CONFIG_CPU_32v3 is not set
 CONFIG_CPU_32v4=y
+# CONFIG_CPU_32v5 is not set
 # CONFIG_CPU_ARM610 is not set
 # CONFIG_CPU_ARM710 is not set
 # CONFIG_CPU_ARM720T is not set
@@ -133,13 +142,17 @@
 # CONFIG_CPU_ARM1020 is not set
 CONFIG_CPU_SA110=y
 # CONFIG_CPU_SA1100 is not set
+# CONFIG_CPU_XSCALE is not set
 # CONFIG_XSCALE_PMU is not set
-# CONFIG_ARM_THUMB is not set
-# CONFIG_DISCONTIGMEM is not set
+
+#
+# Processor Features
+#
 
 #
 # General setup
 #
+# CONFIG_DISCONTIGMEM is not set
 CONFIG_PCI=y
 # CONFIG_PCI_HOST_PLX90X0 is not set
 CONFIG_PCI_HOST_VIA82C505=y
@@ -149,10 +162,6 @@
 # CONFIG_PCI_NAMES is not set
 # CONFIG_HOTPLUG is not set
 # CONFIG_PCMCIA is not set
-CONFIG_NET=y
-CONFIG_SYSVIPC=y
-# CONFIG_BSD_PROCESS_ACCT is not set
-CONFIG_SYSCTL=y
 
 #
 # At least one math emulation must be selected
@@ -165,6 +174,7 @@
 CONFIG_BINFMT_ELF=y
 # CONFIG_BINFMT_MISC is not set
 # CONFIG_PM is not set
+# CONFIG_PREEMPT is not set
 # CONFIG_APM is not set
 # CONFIG_ARTHUR is not set
 CONFIG_LEDS=y
@@ -195,6 +205,7 @@
 #
 # CONFIG_PNP is not set
 # CONFIG_ISAPNP is not set
+# CONFIG_PNPBIOS is not set
 
 #
 # Block devices
@@ -229,7 +240,7 @@
 #
 CONFIG_PACKET=y
 # CONFIG_PACKET_MMAP is not set
-# CONFIG_NETLINK is not set
+# CONFIG_NETLINK_DEV is not set
 # CONFIG_NETFILTER is not set
 CONFIG_FILTER=y
 CONFIG_UNIX=y
@@ -239,6 +250,7 @@
 # CONFIG_IP_PNP is not set
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
+# CONFIG_ARPD is not set
 # CONFIG_INET_ECN is not set
 # CONFIG_SYN_COOKIES is not set
 # CONFIG_IPV6 is not set
@@ -280,6 +292,7 @@
 # CONFIG_BONDING is not set
 # CONFIG_EQUALIZER is not set
 # CONFIG_TUN is not set
+# CONFIG_ETHERTAP is not set
 
 #
 # Ethernet (10 or 100Mbit)
@@ -290,7 +303,6 @@
 # CONFIG_HAPPYMEAL is not set
 # CONFIG_SUNBMAC is not set
 # CONFIG_SUNQE is not set
-# CONFIG_SUNLANCE is not set
 # CONFIG_SUNGEM is not set
 # CONFIG_NET_VENDOR_3COM is not set
 # CONFIG_LANCE is not set
@@ -306,12 +318,9 @@
 # CONFIG_AC3200 is not set
 # CONFIG_APRICOT is not set
 CONFIG_CS89x0=y
-# CONFIG_DE2104X is not set
-# CONFIG_TULIP is not set
-# CONFIG_DE4X5 is not set
 # CONFIG_DGRS is not set
-# CONFIG_DM9102 is not set
 # CONFIG_EEPRO100 is not set
+# CONFIG_E100 is not set
 # CONFIG_LNE390 is not set
 # CONFIG_FEALNX is not set
 # CONFIG_NATSEMI is not set
@@ -323,13 +332,13 @@
 # CONFIG_8139TOO_PIO is not set
 # CONFIG_8139TOO_TUNE_TWISTER is not set
 # CONFIG_8139TOO_8129 is not set
+# CONFIG_8139_NEW_RX_RESET is not set
 # CONFIG_SIS900 is not set
 # CONFIG_EPIC100 is not set
 # CONFIG_SUNDANCE is not set
 # CONFIG_TLAN is not set
 # CONFIG_VIA_RHINE is not set
 # CONFIG_VIA_RHINE_MMIO is not set
-# CONFIG_WINBOND_840 is not set
 # CONFIG_NET_POCKET is not set
 
 #
@@ -337,11 +346,13 @@
 #
 # CONFIG_ACENIC is not set
 # CONFIG_DL2K is not set
+# CONFIG_E1000 is not set
 # CONFIG_MYRI_SBUS is not set
 # CONFIG_NS83820 is not set
 # CONFIG_HAMACHI is not set
 # CONFIG_YELLOWFIN is not set
 # CONFIG_SK98LIN is not set
+# CONFIG_TIGON3 is not set
 # CONFIG_FDDI is not set
 # CONFIG_HIPPI is not set
 # CONFIG_PLIP is not set
@@ -367,6 +378,11 @@
 # CONFIG_WAN is not set
 
 #
+# "Tulip" family network device support
+#
+# CONFIG_NET_TULIP is not set
+
+#
 # Amateur Radio support
 #
 # CONFIG_HAMRADIO is not set
@@ -382,7 +398,7 @@
 CONFIG_IDE=y
 
 #
-# IDE, ATA and ATAPI Block devices
+# ATA and ATAPI Block devices
 #
 CONFIG_BLK_DEV_IDE=y
 
@@ -393,6 +409,7 @@
 # CONFIG_BLK_DEV_HD is not set
 CONFIG_BLK_DEV_IDEDISK=y
 # CONFIG_IDEDISK_MULTI_MODE is not set
+# CONFIG_IDEDISK_STROKE is not set
 # CONFIG_BLK_DEV_IDEDISK_VENDOR is not set
 # CONFIG_BLK_DEV_IDEDISK_FUJITSU is not set
 # CONFIG_BLK_DEV_IDEDISK_IBM is not set
@@ -409,7 +426,7 @@
 # CONFIG_BLK_DEV_IDESCSI is not set
 
 #
-# IDE chipset support/bugfixes
+# IDE chipset support
 #
 # CONFIG_BLK_DEV_CMD640 is not set
 # CONFIG_BLK_DEV_CMD640_ENHANCED is not set
@@ -420,7 +437,6 @@
 # CONFIG_IDE_CHIPSETS is not set
 # CONFIG_IDEDMA_AUTO is not set
 # CONFIG_DMA_NONPCI is not set
-# CONFIG_BLK_DEV_IDE_MODES is not set
 # CONFIG_BLK_DEV_ATARAID is not set
 # CONFIG_BLK_DEV_ATARAID_PDC is not set
 # CONFIG_BLK_DEV_ATARAID_HPT is not set
@@ -520,13 +536,23 @@
 # CONFIG_ISDN is not set
 
 #
-# Input core support
+# Input device support
 #
 # CONFIG_INPUT is not set
 # CONFIG_INPUT_KEYBDEV is not set
 # CONFIG_INPUT_MOUSEDEV is not set
 # CONFIG_INPUT_JOYDEV is not set
 # CONFIG_INPUT_EVDEV is not set
+# CONFIG_GAMEPORT is not set
+CONFIG_SOUND_GAMEPORT=y
+# CONFIG_GAMEPORT_NS558 is not set
+# CONFIG_GAMEPORT_L4 is not set
+# CONFIG_INPUT_EMU10K1 is not set
+# CONFIG_GAMEPORT_PCIGAME is not set
+# CONFIG_GAMEPORT_FM801 is not set
+# CONFIG_GAMEPORT_CS461x is not set
+# CONFIG_SERIO is not set
+# CONFIG_SERIO_SERPORT is not set
 
 #
 # Character devices
@@ -541,6 +567,24 @@
 #
 # Serial drivers
 #
+# CONFIG_SERIAL_8250 is not set
+# CONFIG_SERIAL_8250_CONSOLE is not set
+# CONFIG_SERIAL_8250_EXTENDED is not set
+# CONFIG_SERIAL_8250_MANY_PORTS is not set
+# CONFIG_SERIAL_8250_SHARE_IRQ is not set
+# CONFIG_SERIAL_8250_DETECT_IRQ is not set
+# CONFIG_SERIAL_8250_MULTIPORT is not set
+# CONFIG_SERIAL_8250_RSA is not set
+
+#
+# Non-8250 serial port support
+#
+
+#
+# ARM Serial drivers
+#
+# CONFIG_ATOMWIDE_SERIAL is not set
+# CONFIG_DUALSP_SERIAL is not set
 # CONFIG_SERIAL_ANAKIN is not set
 # CONFIG_SERIAL_ANAKIN_CONSOLE is not set
 # CONFIG_SERIAL_AMBA is not set
@@ -554,16 +598,6 @@
 # CONFIG_SERIAL_UART00_CONSOLE is not set
 # CONFIG_SERIAL_SA1100 is not set
 # CONFIG_SERIAL_SA1100_CONSOLE is not set
-# CONFIG_SERIAL_8250 is not set
-# CONFIG_SERIAL_8250_CONSOLE is not set
-# CONFIG_ATOMWIDE_SERIAL is not set
-# CONFIG_DUALSP_SERIAL is not set
-# CONFIG_SERIAL_8250_EXTENDED is not set
-# CONFIG_SERIAL_8250_MANY_PORTS is not set
-# CONFIG_SERIAL_8250_SHARE_IRQ is not set
-# CONFIG_SERIAL_8250_DETECT_IRQ is not set
-# CONFIG_SERIAL_8250_MULTIPORT is not set
-# CONFIG_SERIAL_8250_RSA is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 CONFIG_PRINTER=m
@@ -596,19 +630,6 @@
 CONFIG_PSMOUSE=y
 # CONFIG_82C710_MOUSE is not set
 # CONFIG_PC110_PAD is not set
-
-#
-# Joysticks
-#
-# CONFIG_INPUT_GAMEPORT is not set
-
-#
-# Input core support is needed for gameports
-#
-
-#
-# Input core support is needed for joysticks
-#
 # CONFIG_QIC02_TAPE is not set
 
 #
@@ -664,6 +685,9 @@
 CONFIG_ISO9660_FS=y
 CONFIG_JOLIET=y
 # CONFIG_ZISOFS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_JFS_DEBUG is not set
+# CONFIG_JFS_STATISTICS is not set
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
@@ -694,6 +718,7 @@
 # CONFIG_ROOT_NFS is not set
 # CONFIG_NFSD is not set
 # CONFIG_NFSD_V3 is not set
+# CONFIG_NFSD_TCP is not set
 CONFIG_SUNRPC=y
 CONFIG_LOCKD=y
 # CONFIG_SMB_FS is not set
@@ -707,7 +732,6 @@
 # CONFIG_NCPFS_NLS is not set
 # CONFIG_NCPFS_EXTRAS is not set
 # CONFIG_ZISOFS_FS is not set
-# CONFIG_ZLIB_FS_INFLATE is not set
 
 #
 # Partition Types
@@ -797,6 +821,7 @@
 # CONFIG_FB_RADEON is not set
 # CONFIG_FB_ATY128 is not set
 # CONFIG_FB_SIS is not set
+# CONFIG_FB_NEOMAGIC is not set
 # CONFIG_FB_3DFX is not set
 # CONFIG_FB_VOODOO1 is not set
 # CONFIG_FB_VIRTUAL is not set
@@ -813,6 +838,11 @@
 # Sound
 #
 CONFIG_SOUND=m
+
+#
+# Open Sound System
+#
+CONFIG_SOUND_PRIME=m
 # CONFIG_SOUND_BT878 is not set
 # CONFIG_SOUND_CMPCI is not set
 # CONFIG_SOUND_EMU10K1 is not set
@@ -866,6 +896,11 @@
 # CONFIG_SOUND_TVMIXER is not set
 
 #
+# Advanced Linux Sound Architecture
+#
+# CONFIG_SND is not set
+
+#
 # Multimedia Capabilities Port drivers
 #
 # CONFIG_MCP is not set
@@ -875,111 +910,14 @@
 # CONFIG_MCP_UCB1200_TS is not set
 
 #
-# USB support
-#
-# CONFIG_USB is not set
-
-#
-# USB Host Controller Drivers
-#
-# CONFIG_USB_EHCI_HCD is not set
-# CONFIG_USB_UHCI is not set
-# CONFIG_USB_UHCI_ALT is not set
-# CONFIG_USB_OHCI is not set
-# CONFIG_USB_OHCI_SA1111 is not set
-
-#
-# USB Device Class drivers
-#
-# CONFIG_USB_AUDIO is not set
-# CONFIG_USB_BLUETOOTH is not set
-# CONFIG_USB_STORAGE is not set
-# CONFIG_USB_STORAGE_DEBUG is not set
-# CONFIG_USB_STORAGE_DATAFAB is not set
-# CONFIG_USB_STORAGE_FREECOM is not set
-# CONFIG_USB_STORAGE_ISD200 is not set
-# CONFIG_USB_STORAGE_DPCM is not set
-# CONFIG_USB_STORAGE_HP8200e is not set
-# CONFIG_USB_STORAGE_SDDR09 is not set
-# CONFIG_USB_STORAGE_JUMPSHOT is not set
-# CONFIG_USB_ACM is not set
-# CONFIG_USB_PRINTER is not set
-
-#
-# USB Human Interface Devices (HID)
-#
-
-#
-#   Input core support is needed for USB HID
-#
-
-#
-# USB Imaging devices
-#
-# CONFIG_USB_DC2XX is not set
-# CONFIG_USB_MDC800 is not set
-# CONFIG_USB_SCANNER is not set
-# CONFIG_USB_MICROTEK is not set
-# CONFIG_USB_HPUSBSCSI is not set
-
-#
-# USB Multimedia devices
-#
-
-#
-#   Video4Linux support is needed for USB Multimedia device support
-#
-
-#
-# USB Network adaptors
-#
-# CONFIG_USB_PEGASUS is not set
-# CONFIG_USB_KAWETH is not set
-# CONFIG_USB_CATC is not set
-# CONFIG_USB_CDCETHER is not set
-# CONFIG_USB_USBNET is not set
-
-#
-# USB port drivers
-#
-# CONFIG_USB_USS720 is not set
-
-#
-# USB Serial Converter support
+# Console Switches
 #
-# CONFIG_USB_SERIAL is not set
-# CONFIG_USB_SERIAL_GENERIC is not set
-# CONFIG_USB_SERIAL_BELKIN is not set
-# CONFIG_USB_SERIAL_WHITEHEAT is not set
-# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set
-# CONFIG_USB_SERIAL_EMPEG is not set
-# CONFIG_USB_SERIAL_FTDI_SIO is not set
-# CONFIG_USB_SERIAL_VISOR is not set
-# CONFIG_USB_SERIAL_IPAQ is not set
-# CONFIG_USB_SERIAL_IR is not set
-# CONFIG_USB_SERIAL_EDGEPORT is not set
-# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set
-# CONFIG_USB_SERIAL_KEYSPAN is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set
-# CONFIG_USB_SERIAL_MCT_U232 is not set
-# CONFIG_USB_SERIAL_KLSI is not set
-# CONFIG_USB_SERIAL_PL2303 is not set
-# CONFIG_USB_SERIAL_CYBERJACK is not set
-# CONFIG_USB_SERIAL_XIRCOM is not set
-# CONFIG_USB_SERIAL_OMNINET is not set
+# CONFIG_SWITCHES is not set
 
 #
-# USB Miscellaneous drivers
+# USB support
 #
-# CONFIG_USB_RIO500 is not set
-# CONFIG_USB_AUERSWALD is not set
+# CONFIG_USB is not set
 
 #
 # Bluetooth support
@@ -992,7 +930,6 @@
 CONFIG_NO_FRAME_POINTER=y
 CONFIG_DEBUG_USER=y
 # CONFIG_DEBUG_INFO is not set
-# CONFIG_NO_PGT_CACHE is not set
 # CONFIG_DEBUG_KERNEL is not set
 # CONFIG_DEBUG_SLAB is not set
 # CONFIG_MAGIC_SYSRQ is not set
@@ -1008,3 +945,5 @@
 # Library routines
 #
 CONFIG_CRC32=y
+# CONFIG_ZLIB_INFLATE is not set
+# CONFIG_ZLIB_DEFLATE is not set
diff -Nru a/arch/arm/def-configs/sherman b/arch/arm/def-configs/sherman
--- a/arch/arm/def-configs/sherman	Mon Apr 22 15:32:18 2002
+++ b/arch/arm/def-configs/sherman	Mon Apr 22 15:32:18 2002
@@ -47,6 +47,9 @@
 #
 # General setup
 #
+CONFIG_ZBOOT_ROM=y
+CONFIG_ZBOOT_ROM_TEXT=0x00050000
+CONFIG_ZBOOT_ROM_BSS=0xc0200000
 # CONFIG_NET is not set
 # CONFIG_SYSVIPC is not set
 # CONFIG_BSD_PROCESS_ACCT is not set
diff -Nru a/arch/arm/def-configs/victor b/arch/arm/def-configs/victor
--- a/arch/arm/def-configs/victor	Mon Apr 22 15:32:18 2002
+++ b/arch/arm/def-configs/victor	Mon Apr 22 15:32:18 2002
@@ -46,6 +46,9 @@
 #
 # General setup
 #
+CONFIG_ZBOOT_ROM=y
+CONFIG_ZBOOT_ROM_TEXT=0x00002000
+CONFIG_ZBOOT_ROM_BSS=0xc0200000
 # CONFIG_NET is not set
 # CONFIG_SYSVIPC is not set
 # CONFIG_BSD_PROCESS_ACCT is not set
diff -Nru a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c
--- a/arch/arm/kernel/armksyms.c	Mon Apr 22 15:32:19 2002
+++ b/arch/arm/kernel/armksyms.c	Mon Apr 22 15:32:19 2002
@@ -68,6 +68,7 @@
 extern void __udivmoddi4(void);
 extern void __udivsi3(void);
 extern void __umodsi3(void);
+extern void abort(void);
 
 extern void ret_from_exception(void);
 extern void fpundefinstr(void);
diff -Nru a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c
--- a/arch/arm/kernel/bios32.c	Mon Apr 22 15:32:19 2002
+++ b/arch/arm/kernel/bios32.c	Mon Apr 22 15:32:19 2002
@@ -426,6 +426,9 @@
 		pci_read_config_word(dev, PCI_COMMAND, &cmd);
 		cmd |= features;
 		pci_write_config_word(dev, PCI_COMMAND, cmd);
+
+		pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE,
+				      SMP_CACHE_BYTES >> 2);
 	}
 
 	/*
diff -Nru a/arch/arm/kernel/dma.c b/arch/arm/kernel/dma.c
--- a/arch/arm/kernel/dma.c	Mon Apr 22 15:32:19 2002
+++ b/arch/arm/kernel/dma.c	Mon Apr 22 15:32:19 2002
@@ -17,6 +17,7 @@
 #include <linux/mman.h>
 #include <linux/init.h>
 #include <linux/spinlock.h>
+#include <linux/errno.h>
 
 #include <asm/dma.h>
 
diff -Nru a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
--- a/arch/arm/kernel/entry-armv.S	Mon Apr 22 15:32:19 2002
+++ b/arch/arm/kernel/entry-armv.S	Mon Apr 22 15:32:19 2002
@@ -756,7 +756,6 @@
 #ifdef CONFIG_PREEMPT
 		ldr	r0, [r8, #TI_FLAGS]		@ get flags
 		tst	r0, #_TIF_NEED_RESCHED
-		ldrne	r6, .LCirq_stat
 		blne	svc_preempt
 preempt_return:
 		ldr	r0, [r8, #TI_PREEMPT]		@ read preempt value
@@ -770,20 +769,20 @@
 
 #ifdef CONFIG_PREEMPT
 svc_preempt:	teq	r9, #0				@ was preempt count = 0
+		ldreq	r6, .LCirq_stat
 		movne	pc, lr				@ no
 		ldr	r0, [r6, #4]			@ local_irq_count
 		ldr	r1, [r6, #8]			@ local_bh_count
 		adds	r0, r0, r1
 		movne	pc, lr
-		ldr	r1, [r8, #TI_TASK]
-		set_cpsr_c r2, #MODE_SVC		@ enable IRQs
-		str	r0, [r1, #0]			@ current->state = TASK_RUNNING
-1:		bl	SYMBOL_NAME(schedule)
+		mov	r7, #PREEMPT_ACTIVE
+		str	r7, [r8, #TI_PREEMPT]		@ set PREEMPT_ACTIVE
+1:		set_cpsr_c r2, #MODE_SVC		@ enable IRQs
+		bl	SYMBOL_NAME(schedule)
 		set_cpsr_c r0, #PSR_I_BIT | MODE_SVC	@ disable IRQs
-		ldr	r0, [r8, #TI_FLAGS]
+		ldr	r0, [r8, #TI_FLAGS]		@ get new tasks TI_FLAGS
 		tst	r0, #_TIF_NEED_RESCHED
-		beq	preempt_return
-		set_cpsr_c r0, #MODE_SVC		@ enable IRQs
+		beq	preempt_return			@ go again
 		b	1b
 #endif
 
diff -Nru a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S
--- a/arch/arm/kernel/entry-common.S	Mon Apr 22 15:32:18 2002
+++ b/arch/arm/kernel/entry-common.S	Mon Apr 22 15:32:18 2002
@@ -76,6 +76,9 @@
  * This is how we return from a fork.
  */
 ENTRY(ret_from_fork)
+#ifdef CONFIG_PREEMPT
+	bl	schedule_tail
+#endif
 	get_thread_info tsk
 	ldr	ip, [tsk, #TI_FLAGS]		@ check for syscall tracing
 	mov	why, #1
diff -Nru a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c
--- a/arch/arm/kernel/irq.c	Mon Apr 22 15:32:18 2002
+++ b/arch/arm/kernel/irq.c	Mon Apr 22 15:32:18 2002
@@ -29,6 +29,7 @@
 #include <linux/smp.h>
 #include <linux/init.h>
 #include <linux/seq_file.h>
+#include <linux/errno.h>
 
 #include <asm/irq.h>
 #include <asm/system.h>
diff -Nru a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c
--- a/arch/arm/kernel/process.c	Mon Apr 22 15:32:19 2002
+++ b/arch/arm/kernel/process.c	Mon Apr 22 15:32:19 2002
@@ -31,14 +31,6 @@
 #include <asm/processor.h>
 #include <asm/uaccess.h>
 
-/*
- * Values for cpu_do_idle()
- */
-#define IDLE_WAIT_SLOW	0
-#define IDLE_WAIT_FAST	1
-#define IDLE_CLOCK_SLOW	2
-#define IDLE_CLOCK_FAST	3
-
 extern const char *processor_modes[];
 extern void setup_mm_for_reboot(char mode);
 
@@ -78,6 +70,18 @@
 void (*pm_power_off)(void);
 
 /*
+ * This is our default idle handler.  We need to disable
+ * interrupts here to ensure we don't miss a wakeup call.
+ */
+void default_idle(void)
+{
+	__cli();
+	if (!need_resched() && !hlt_counter)
+		arch_idle();
+	__sti();
+}
+
+/*
  * The idle thread.  We try to conserve power, while trying to keep
  * overall latency low.  The architecture specific idle is passed
  * a value to indicate the level of "idleness" of the system.
@@ -89,7 +93,7 @@
 	while (1) {
 		void (*idle)(void) = pm_idle;
 		if (!idle)
-			idle = arch_idle;
+			idle = default_idle;
 		leds_event(led_idle_start);
 		while (!need_resched())
 			idle();
diff -Nru a/arch/arm/kernel/semaphore.c b/arch/arm/kernel/semaphore.c
--- a/arch/arm/kernel/semaphore.c	Mon Apr 22 15:32:19 2002
+++ b/arch/arm/kernel/semaphore.c	Mon Apr 22 15:32:19 2002
@@ -13,6 +13,7 @@
  */
 #include <linux/config.h>
 #include <linux/sched.h>
+#include <linux/errno.h>
 
 #include <asm/semaphore.h>
 
diff -Nru a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
--- a/arch/arm/kernel/setup.c	Mon Apr 22 15:32:18 2002
+++ b/arch/arm/kernel/setup.c	Mon Apr 22 15:32:18 2002
@@ -26,6 +26,7 @@
 #include <asm/procinfo.h>
 #include <asm/setup.h>
 #include <asm/mach-types.h>
+#include <asm/tlbflush.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/irq.h>
@@ -188,7 +189,7 @@
 {
 	unsigned int mult = 2 + (CACHE_M(cache) ? 1 : 0);
 
-	printk("%s size %dK associativity %d line length %d sets %d\n",
+	printk("%s: %d bytes, associativity %d, %d byte lines, %d sets\n",
 		prefix,
 		mult << (8 + CACHE_SIZE(cache)),
 		(mult << CACHE_ASSOC(cache)) >> 1,
@@ -255,7 +256,7 @@
 	cpu_user = *list->user;
 #endif
 
-	printk("Processor: %s %s revision %d\n",
+	printk("CPU: %s %s revision %d\n",
 	       proc_info.manufacturer, proc_info.cpu_name,
 	       (int)processor_id & 15);
 
diff -Nru a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c
--- a/arch/arm/kernel/signal.c	Mon Apr 22 15:32:18 2002
+++ b/arch/arm/kernel/signal.c	Mon Apr 22 15:32:18 2002
@@ -628,14 +628,12 @@
 
 			case SIGSTOP: {
 				struct signal_struct *sig;
+				current->state = TASK_STOPPED;
 				current->exit_code = signr;
 				sig = current->parent->sig;
-				preempt_disable();
-				current->state = TASK_STOPPED;
 				if (sig && !(sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP))
 					notify_parent(current, SIGCHLD);
 				schedule();
-				preempt_enable();
 				continue;
 			}
 
diff -Nru a/arch/arm/kernel/time.c b/arch/arm/kernel/time.c
--- a/arch/arm/kernel/time.c	Mon Apr 22 15:32:19 2002
+++ b/arch/arm/kernel/time.c	Mon Apr 22 15:32:19 2002
@@ -25,6 +25,7 @@
 #include <linux/init.h>
 #include <linux/smp.h>
 #include <linux/timex.h>
+#include <linux/errno.h>
 
 #include <asm/hardware.h>
 #include <asm/io.h>
diff -Nru a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
--- a/arch/arm/kernel/traps.c	Mon Apr 22 15:32:19 2002
+++ b/arch/arm/kernel/traps.c	Mon Apr 22 15:32:19 2002
@@ -546,12 +546,14 @@
 
 void __init trap_init(void)
 {
-	extern void __trap_init(void *);
+	extern void __trap_init(unsigned long);
+	unsigned long base = vectors_base();
 
-	__trap_init((void *)vectors_base());
-	if (vectors_base() != 0)
-		printk(KERN_DEBUG "Relocating machine vectors to 0x%08x\n",
-			vectors_base());
+	__trap_init(base);
+	flush_icache_range(base, base + PAGE_SIZE);
+	if (base != 0)
+		printk(KERN_DEBUG "Relocating machine vectors to 0x%08lx\n",
+			base);
 #ifdef CONFIG_CPU_32
 	modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
 #endif
diff -Nru a/arch/arm/mach-clps711x/edb7211-mm.c b/arch/arm/mach-clps711x/edb7211-mm.c
--- a/arch/arm/mach-clps711x/edb7211-mm.c	Mon Apr 22 15:32:19 2002
+++ b/arch/arm/mach-clps711x/edb7211-mm.c	Mon Apr 22 15:32:19 2002
@@ -26,11 +26,10 @@
 #include <asm/hardware.h>
 #include <asm/pgtable.h>
 #include <asm/page.h>
+#include <asm/sizes.h>
  
 #include <asm/mach/map.h>
 
-#define MB1 1048576	/* one megabyte == size of an MMU section */
-
 extern void clps711x_map_io(void);
 
 /*
@@ -56,12 +55,12 @@
  /* virtual, physical, length, domain, r, w, c, b */
 
  /* memory-mapped extra keyboard row and CS8900A Ethernet chip */
- { EP7211_VIRT_EXTKBD, EP7211_PHYS_EXTKBD, MB1, DOMAIN_IO, 0, 1, 0, 0 }, 
- { EP7211_VIRT_CS8900A, EP7211_PHYS_CS8900A, MB1, DOMAIN_IO, 0, 1, 0, 0 },
+ { EP7211_VIRT_EXTKBD,  EP7211_PHYS_EXTKBD,  SZ_1M, DOMAIN_IO, 0, 1, 0, 0 }, 
+ { EP7211_VIRT_CS8900A, EP7211_PHYS_CS8900A, SZ_1M, DOMAIN_IO, 0, 1, 0, 0 },
 
  /* flash banks */
- { EP7211_VIRT_FLASH1, EP7211_PHYS_FLASH1, MB1 * 8, DOMAIN_KERNEL, 0, 1, 0, 0 },
- { EP7211_VIRT_FLASH2, EP7211_PHYS_FLASH2, MB1 * 8, DOMAIN_KERNEL, 0, 1, 0, 0 },
+ { EP7211_VIRT_FLASH1,  EP7211_PHYS_FLASH1,  SZ_8M, DOMAIN_KERNEL, 0, 1, 0, 0 },
+ { EP7211_VIRT_FLASH2,  EP7211_PHYS_FLASH2,  SZ_8M, DOMAIN_KERNEL, 0, 1, 0, 0 },
 
  LAST_DESC
 };
diff -Nru a/arch/arm/mach-footbridge/isa-irq.c b/arch/arm/mach-footbridge/isa-irq.c
--- a/arch/arm/mach-footbridge/isa-irq.c	Mon Apr 22 15:32:19 2002
+++ b/arch/arm/mach-footbridge/isa-irq.c	Mon Apr 22 15:32:19 2002
@@ -106,8 +106,10 @@
 static struct resource pic1_resource = { "pic1", 0x20, 0x3f };
 static struct resource pic2_resource = { "pic2", 0xa0, 0xbf };
 
-void __init isa_init_irq(unsigned int irq)
+void __init isa_init_irq(unsigned int host_irq)
 {
+	unsigned int irq;
+
 	/*
 	 * Setup, and then probe for an ISA PIC
 	 * If the PIC is not there, then we
@@ -133,10 +135,10 @@
 		outb(0xff, PIC_MASK_HI);/* mask all IRQs	*/
 	} else {
 		printk(KERN_INFO "IRQ: ISA PIC not found\n");
-		irq = -1;
+		host_irq = (unsigned int)-1;
 	}
 
-	if (irq != -1) {
+	if (host_irq != (unsigned int)-1) {
 		for (irq = _ISA_IRQ(0); irq < _ISA_IRQ(8); irq++) {
 			set_irq_chip(irq, &isa_lo_chip);
 			set_irq_handler(irq, do_level_IRQ);
@@ -153,7 +155,7 @@
 		request_resource(&ioport_resource, &pic2_resource);
 		setup_irq(IRQ_ISA_CASCADE, &irq_cascade);
 
-		set_irq_chained_handler(irq, isa_irq_handler);
+		set_irq_chained_handler(host_irq, isa_irq_handler);
 
 		/*
 		 * On the NetWinder, don't automatically
diff -Nru a/arch/arm/mach-sa1100/Makefile b/arch/arm/mach-sa1100/Makefile
--- a/arch/arm/mach-sa1100/Makefile	Mon Apr 22 15:32:19 2002
+++ b/arch/arm/mach-sa1100/Makefile	Mon Apr 22 15:32:19 2002
@@ -1,9 +1,6 @@
 #
 # Makefile for the linux kernel.
 #
-# Note! Dependencies are done automagically by 'make dep', which also
-# removes any old dependencies. DON'T put your own dependencies here
-# unless it's something special (ie not a .c file).
 
 USE_STANDARD_AS_RULE := true
 
@@ -115,7 +112,6 @@
 obj-$(CONFIG_LEDS) += $(led-y)
 
 # SA1110 USB client support
-list-multi += sa1100usb_core.o
 sa1100usb_core-objs := usb_ctl.o usb_ep0.o usb_recv.o usb_send.o
 obj-$(CONFIG_SA1100_USB) += sa1100usb_core.o
 obj-$(CONFIG_SA1100_USB_NETLINK) += usb-eth.o
@@ -125,7 +121,3 @@
 obj-$(CONFIG_PM) += pm.o sleep.o
 
 include $(TOPDIR)/Rules.make
-
-sa1100usb_core.o: $(sa1100usb_core-objs)
-	$(LD) -r -o $@ $(sa1100usb_core-objs)
-
diff -Nru a/arch/arm/mach-sa1100/adsbitsy.c b/arch/arm/mach-sa1100/adsbitsy.c
--- a/arch/arm/mach-sa1100/adsbitsy.c	Mon Apr 22 15:32:18 2002
+++ b/arch/arm/mach-sa1100/adsbitsy.c	Mon Apr 22 15:32:18 2002
@@ -52,27 +52,11 @@
 	/*
 	 * Probe for SA1111.
 	 */
-	ret = sa1111_probe(0x18000000);
+	ret = sa1111_init(NULL, 0x18000000, IRQ_GPIO0);
 	if (ret < 0)
 		return ret;
 
 	/*
-	 * We found it.  Wake the chip up.
-	 */
-	sa1111_wake();
-
-	/*
-	 * The SDRAM configuration of the SA1110 and the SA1111 must
-	 * match.  This is very important to ensure that SA1111 accesses
-	 * don't corrupt the SDRAM.  Note that this ungates the SA1111's
-	 * MBGNT signal, so we must have called sa1110_mb_disable()
-	 * beforehand.
-	 */
-	sa1111_configure_smc(1,
-			     FExtr(MDCNFG, MDCNFG_SA1110_DRAC0),
-			     FExtr(MDCNFG, MDCNFG_SA1110_TDL0));
-
-	/*
 	 * Enable PWM control for LCD
 	 */
 	SKPCR |= SKPCR_PWMCLKEN;
@@ -80,20 +64,6 @@
 	SKPEN0 = 1;
 	SKPWM1 = 0x01;				// Backlight
 	SKPEN1 = 1;
-
-	/*
-	 * We only need to turn on DCLK whenever we want to use the
-	 * DMA.  It can otherwise be held firmly in the off position.
-	 */
-	SKPCR |= SKPCR_DCLKEN;
-
-	/*
-	 * Enable the SA1110 memory bus request and grant signals.
-	 */
-	sa1110_mb_enable();
-
-	set_GPIO_IRQ_edge(GPIO_GPIO0, GPIO_RISING_EDGE);
-	sa1111_init_irq(IRQ_GPIO0);
 
 	return 0;
 }
diff -Nru a/arch/arm/mach-sa1100/badge4.c b/arch/arm/mach-sa1100/badge4.c
--- a/arch/arm/mach-sa1100/badge4.c	Mon Apr 22 15:32:18 2002
+++ b/arch/arm/mach-sa1100/badge4.c	Mon Apr 22 15:32:18 2002
@@ -34,8 +34,6 @@
 
 static int __init badge4_sa1111_init(void)
 {
-	int ret;
-
 	/*
 	 * Ensure that the memory bus request/grant signals are setup,
 	 * and the grant is held in its inactive state
@@ -45,40 +43,7 @@
 	/*
 	 * Probe for SA1111.
 	 */
-	ret = sa1111_probe(BADGE4_SA1111_BASE);
-	if (ret < 0)
-		return ret;
-
-	/*
-	 * We found it.  Wake the chip up.
-	 */
-	sa1111_wake();
-
-	/*
-	 * The SDRAM configuration of the SA1110 and the SA1111 must
-	 * match.  This is very important to ensure that SA1111 accesses
-	 * don't corrupt the SDRAM.  Note that this ungates the SA1111's
-	 * MBGNT signal, so we must have called sa1110_mb_disable()
-	 * beforehand.
-	 */
-	sa1111_configure_smc(1,
-			     FExtr(MDCNFG, MDCNFG_SA1110_DRAC0),
-			     FExtr(MDCNFG, MDCNFG_SA1110_TDL0));
-
-	/*
-	 * We only need to turn on DCLK whenever we want to use the
-	 * DMA.  It can otherwise be held firmly in the off position.
-	 */
-	SKPCR |= SKPCR_DCLKEN;
-
-	/*
-	 * Enable the SA1110 memory bus request and grant signals.
-	 */
-	sa1110_mb_enable();
-
-	sa1111_init_irq(BADGE4_IRQ_GPIO_SA1111);
-
-	return 0;
+	return sa1111_init(NULL, BADGE4_SA1111_BASE, BADGE4_IRQ_GPIO_SA1111);
 }
 
 static int __init badge4_init(void)
diff -Nru a/arch/arm/mach-sa1100/graphicsmaster.c b/arch/arm/mach-sa1100/graphicsmaster.c
--- a/arch/arm/mach-sa1100/graphicsmaster.c	Mon Apr 22 15:32:19 2002
+++ b/arch/arm/mach-sa1100/graphicsmaster.c	Mon Apr 22 15:32:19 2002
@@ -42,27 +42,11 @@
 	/*
 	 * Probe for SA1111.
 	 */
-	ret = sa1111_probe(0x18000000);
+	ret = sa1111_init(NULL, 0x18000000, ADS_EXT_IRQ(0));
 	if (ret < 0)
 		return ret;
 
 	/*
-	 * We found it.  Wake the chip up.
-	 */
-	sa1111_wake();
-
-	/*
-	 * The SDRAM configuration of the SA1110 and the SA1111 must
-	 * match.  This is very important to ensure that SA1111 accesses
-	 * don't corrupt the SDRAM.  Note that this ungates the SA1111's
-	 * MBGNT signal, so we must have called sa1110_mb_disable()
-	 * beforehand.
-	 */
-	sa1111_configure_smc(1,
-			     FExtr(MDCNFG, MDCNFG_SA1110_DRAC0),
-			     FExtr(MDCNFG, MDCNFG_SA1110_TDL0));
-
-	/*
 	 * Enable PWM control for LCD
 	 */
 	SKPCR |= SKPCR_PWMCLKEN;
@@ -70,19 +54,6 @@
 	SKPEN0 = 1;
 	SKPWM1 = 0x01;				// Backlight
 	SKPEN1 = 1;
-
-	/*
-	 * We only need to turn on DCLK whenever we want to use the
-	 * DMA.  It can otherwise be held firmly in the off position.
-	 */
-	SKPCR |= SKPCR_DCLKEN;
-
-	/*
-	 * Enable the SA1110 memory bus request and grant signals.
-	 */
-	sa1110_mb_enable();
-
-	sa1111_init_irq(ADS_EXT_IRQ(0));
 
 	return 0;
 }
diff -Nru a/arch/arm/mach-sa1100/neponset.c b/arch/arm/mach-sa1100/neponset.c
--- a/arch/arm/mach-sa1100/neponset.c	Mon Apr 22 15:32:19 2002
+++ b/arch/arm/mach-sa1100/neponset.c	Mon Apr 22 15:32:19 2002
@@ -10,6 +10,7 @@
 #include <linux/tty.h>
 #include <linux/ioport.h>
 #include <linux/serial_core.h>
+#include <linux/device.h>
 
 #include <asm/hardware.h>
 #include <asm/irq.h>
@@ -21,6 +22,10 @@
 
 #include "sa1111.h"
 
+static struct device neponset_device = {
+	name:	"Neponset",
+	bus_id:	"nep_bus",
+};
 
 /*
  * Install handler for Neponset IRQ.  Note that we have to loop here
@@ -125,6 +130,10 @@
 		return -ENODEV;
 	}
 
+	ret = device_register(&neponset_device);
+	if (ret)
+		return ret;
+
 	neponset_init_irq();
 
 	/*
@@ -139,45 +148,9 @@
 	/* FIXME: setup MSC2 */
 
 	/*
-	 * Probe for a SA1111.
-	 */
-	ret = sa1111_probe(0x40000000);
-	if (ret < 0)
-		return ret;
-
-	/*
-	 * We found it.  Wake the chip up.
+	 * Probe and initialise the SA1111.
 	 */
-	sa1111_wake();
-
-	/*
-	 * The SDRAM configuration of the SA1110 and the SA1111 must
-	 * match.  This is very important to ensure that SA1111 accesses
-	 * don't corrupt the SDRAM.  Note that this ungates the SA1111's
-	 * MBGNT signal, so we must have called sa1110_mb_disable()
-	 * beforehand.
-	 */
-	sa1111_configure_smc(1,
-			     FExtr(MDCNFG, MDCNFG_SA1110_DRAC0),
-			     FExtr(MDCNFG, MDCNFG_SA1110_TDL0));
-
-	/*
-	 * We only need to turn on DCLK whenever we want to use the
-	 * DMA.  It can otherwise be held firmly in the off position.
-	 */
-	SKPCR |= SKPCR_DCLKEN;
-
-	/*
-	 * Enable the SA1110 memory bus request and grant signals.
-	 */
-	sa1110_mb_enable();
-
-	/*
-	 * Initialise SA1111 IRQs
-	 */
-	sa1111_init_irq(IRQ_NEPONSET_SA1111);
-
-	return 0;
+	return sa1111_init(&neponset_device, 0x40000000, IRQ_NEPONSET_SA1111);
 }
 
 __initcall(neponset_init);
diff -Nru a/arch/arm/mach-sa1100/pfs168.c b/arch/arm/mach-sa1100/pfs168.c
--- a/arch/arm/mach-sa1100/pfs168.c	Mon Apr 22 15:32:18 2002
+++ b/arch/arm/mach-sa1100/pfs168.c	Mon Apr 22 15:32:18 2002
@@ -35,40 +35,7 @@
 	/*
 	 * Probe for SA1111.
 	 */
-	ret = sa1111_probe(0x40000000);
-	if (ret < 0)
-		return ret;
-
-	/*
-	 * We found it.  Wake the chip up.
-	 */
-	sa1111_wake();
-
-	/*
-	 * The SDRAM configuration of the SA1110 and the SA1111 must
-	 * match.  This is very important to ensure that SA1111 accesses
-	 * don't corrupt the SDRAM.  Note that this ungates the SA1111's
-	 * MBGNT signal, so we must have called sa1110_mb_disable()
-	 * beforehand.
-	 */
-	sa1111_configure_smc(1,
-			     FExtr(MDCNFG, MDCNFG_SA1110_DRAC0),
-			     FExtr(MDCNFG, MDCNFG_SA1110_TDL0));
-
-	/*
-	 * We only need to turn on DCLK whenever we want to use the
-	 * DMA.  It can otherwise be held firmly in the off position.
-	 */
-	SKPCR |= SKPCR_DCLKEN;
-
-	/*
-	 * Enable the SA1110 memory bus request and grant signals.
-	 */
-	sa1110_mb_enable();
-
-	sa1111_init_irq(IRQ_GPIO25);	/* SA1111 IRQ on GPIO 25 */
-
-	return 0;
+	return sa1111_init(NULL, 0x40000000, IRQ_GPIO25);
 }
 
 __initcall(pfs168_init);
diff -Nru a/arch/arm/mach-sa1100/sa1111.c b/arch/arm/mach-sa1100/sa1111.c
--- a/arch/arm/mach-sa1100/sa1111.c	Mon Apr 22 15:32:19 2002
+++ b/arch/arm/mach-sa1100/sa1111.c	Mon Apr 22 15:32:19 2002
@@ -23,8 +23,11 @@
 #include <linux/ptrace.h>
 #include <linux/errno.h>
 #include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/slab.h>
 
 #include <asm/hardware.h>
+#include <asm/io.h>
 #include <asm/irq.h>
 #include <asm/mach/irq.h>
 
@@ -32,11 +35,9 @@
 
 #include "sa1111.h"
 
-struct resource sa1111_resource = {
-	name:	"SA1111",
-};
+struct sa1111_device *sa1111;
 
-EXPORT_SYMBOL(sa1111_resource);
+EXPORT_SYMBOL(sa1111);
 
 /*
  * SA1111 interrupt support.  Since clearing an IRQ while there are
@@ -65,6 +66,9 @@
 	for (i = IRQ_SA1111_START + 32; stat1; i++, stat1 >>= 1)
 		if (stat1 & 1)
 			do_edge_IRQ(i, irq_desc + i, regs);
+
+	/* For level-based interrupts */
+	desc->chip->unmask(irq);
 }
 
 #define SA1111_IRQMASK_LO(x)	(1 << (x - IRQ_SA1111_START))
@@ -198,7 +202,7 @@
 	type:		sa1111_type_highirq,
 };
 
-void __init sa1111_init_irq(int irq_nr)
+static void __init sa1111_init_irq(int irq_nr)
 {
 	unsigned int irq;
 
@@ -239,6 +243,21 @@
 	set_irq_chained_handler(irq_nr, sa1111_irq_handler);
 }
 
+static int sa1111_suspend(struct device *dev, u32 state, u32 level)
+{
+	return 0;
+}
+
+static int sa1111_resume(struct device *dev, u32 level)
+{
+	return 0;
+}
+
+static struct device_driver sa1111_device_driver = {
+	suspend:	sa1111_suspend,
+	resume:		sa1111_resume,
+};
+
 /**
  *	sa1111_probe - probe for a single SA1111 chip.
  *	@phys_addr: physical address of device.
@@ -251,38 +270,71 @@
  *	%-EBUSY		physical address already marked in-use.
  *	%0		successful.
  */
-int __init sa1111_probe(unsigned long phys_addr)
+static int __init
+sa1111_probe(struct device *parent, unsigned long phys_addr)
 {
+	struct sa1111_device *sa;
 	unsigned long id;
 	int ret = -ENODEV;
 
-	sa1111_resource.start = phys_addr;
-	sa1111_resource.end = phys_addr + 0x2000;
+	sa = kmalloc(sizeof(struct sa1111_device), GFP_KERNEL);
+	if (!sa)
+		return -ENOMEM;
+
+	memset(sa, 0, sizeof(struct sa1111_device));
 
-	if (request_resource(&iomem_resource, &sa1111_resource)) {
+	sa->resource.name  = "SA1111";
+	sa->resource.start = phys_addr;
+	sa->resource.end   = phys_addr + 0x2000;
+
+	if (request_resource(&iomem_resource, &sa->resource)) {
 		ret = -EBUSY;
 		goto out;
 	}
 
+	/* eventually ioremap... */
+	sa->base = (void *)0xf4000000;
+	if (!sa->base) {
+		ret = -ENOMEM;
+		goto release;
+	}
+
 	/*
 	 * Probe for the chip.  Only touch the SBI registers.
 	 */
-	id = SBI_SKID;
+	id = readl(sa->base + SA1111_SKID);
 	if ((id & SKID_ID_MASK) != SKID_SA1111_ID) {
 		printk(KERN_DEBUG "SA1111 not detected: ID = %08lx\n", id);
 		ret = -ENODEV;
-		goto release;
+		goto unmap;
 	}
 
+	/*
+	 * We found the chip.
+	 */
+	strcpy(sa->dev.name, "SA1111");
+	sprintf(sa->dev.bus_id, "%8.8lx", phys_addr);
+	sa->dev.parent = parent;
+	sa->dev.driver = &sa1111_device_driver;
+
+	ret = device_register(&sa->dev);
+	if (ret)
+		printk("sa1111 device_register failed: %d\n", ret);
+
 	printk(KERN_INFO "SA1111 Microprocessor Companion Chip: "
 		"silicon revision %lx, metal revision %lx\n",
 		(id & SKID_SIREV_MASK)>>4, (id & SKID_MTREV_MASK));
 
+	sa1111 = sa;
+
 	return 0;
 
+ unmap:
+//	iounmap(sa->base);
  release:
-	release_resource(&sa1111_resource);
+	release_resource(&sa->resource);
  out:
+	kfree(sa);
 	return ret;
 }
 
@@ -302,7 +354,8 @@
  */
 void sa1111_wake(void)
 {
-	unsigned long flags;
+	struct sa1111_device *sa = sa1111;
+	unsigned long flags, r;
 
 	local_irq_save(flags);
 
@@ -317,8 +370,11 @@
 	/*
 	 * Turn VCO on, and disable PLL Bypass.
 	 */
-	SBI_SKCR &= ~SKCR_VCO_OFF;
-	SBI_SKCR |= SKCR_PLL_BYPASS | SKCR_OE_EN;
+	r = readl(sa->base + SA1111_SKCR);
+	r &= ~SKCR_VCO_OFF;
+	writel(r, sa->base + SA1111_SKCR);
+	r |= SKCR_PLL_BYPASS | SKCR_OE_EN;
+	writel(r, sa->base + SA1111_SKCR);
 
 	/*
 	 * Wait lock time.  SA1111 manual _doesn't_
@@ -329,7 +385,8 @@
 	/*
 	 * Enable RCLK.  We also ensure that RDYEN is set.
 	 */
-	SBI_SKCR |= SKCR_RCLKEN | SKCR_RDYEN;
+	r |= SKCR_RCLKEN | SKCR_RDYEN;
+	writel(r, sa->base + SA1111_SKCR);
 
 	/*
 	 * Wait 14 RCLK cycles for the chip to finish coming out
@@ -340,18 +397,26 @@
 	/*
 	 * Ensure all clocks are initially off.
 	 */
-	SKPCR = 0;
+	writel(0, sa->base + SA1111_SKPCR);
 
 	local_irq_restore(flags);
 }
 
 void sa1111_doze(void)
 {
-	if (SKPCR & SKPCR_UCLKEN) {
+	struct sa1111_device *sa = sa1111;
+	unsigned long flags;
+
+	local_irq_save(flags);
+
+	if (readl(sa->base + SA1111_SKPCR) & SKPCR_UCLKEN) {
+		local_irq_restore(flags);
 		printk("SA1111 doze mode refused\n");
 		return;
 	}
-	SBI_SKCR &= ~SKCR_RCLKEN;
+
+	writel(readl(sa->base + SA1111_SKCR) & ~SKCR_RCLKEN, sa->base + SA1111_SKCR);
+	local_irq_restore(flags);
 }
 
 /*
@@ -359,12 +424,13 @@
  */
 void sa1111_configure_smc(int sdram, unsigned int drac, unsigned int cas_latency)
 {
+	struct sa1111_device *sa = sa1111;
 	unsigned int smcr = SMCR_DTIM | SMCR_MBGE | FInsrt(drac, SMCR_DRAC);
 
 	if (cas_latency == 3)
 		smcr |= SMCR_CLAT;
 
-	SBI_SMCR = smcr;
+	writel(smcr, sa->base + SA1111_SMCR);
 }
 
 /* According to the "Intel StrongARM SA-1111 Microprocessor Companion
@@ -432,3 +498,46 @@
 }
 
 EXPORT_SYMBOL(sa1111_check_dma_bug);
+
+int sa1111_init(struct device *parent, unsigned long phys, unsigned int irq)
+{
+	int ret;
+
+	ret = sa1111_probe(parent, phys);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * We found it.  Wake the chip up.
+	 */
+	sa1111_wake();
+
+	/*
+	 * The SDRAM configuration of the SA1110 and the SA1111 must
+	 * match.  This is very important to ensure that SA1111 accesses
+	 * don't corrupt the SDRAM.  Note that this ungates the SA1111's
+	 * MBGNT signal, so we must have called sa1110_mb_disable()
+	 * beforehand.
+	 */
+	sa1111_configure_smc(1,
+			     FExtr(MDCNFG, MDCNFG_SA1110_DRAC0),
+			     FExtr(MDCNFG, MDCNFG_SA1110_TDL0));
+
+	/*
+	 * We only need to turn on DCLK whenever we want to use the
+	 * DMA.  It can otherwise be held firmly in the off position.
+	 */
+	SKPCR |= SKPCR_DCLKEN;
+
+	/*
+	 * Enable the SA1110 memory bus request and grant signals.
+	 */
+	sa1110_mb_enable();
+
+	/*
+	 * Initialise SA1111 IRQs
+	 */
+	sa1111_init_irq(irq);
+
+	return 0;
+}
diff -Nru a/arch/arm/mach-sa1100/sa1111.h b/arch/arm/mach-sa1100/sa1111.h
--- a/arch/arm/mach-sa1100/sa1111.h	Mon Apr 22 15:32:19 2002
+++ b/arch/arm/mach-sa1100/sa1111.h	Mon Apr 22 15:32:19 2002
@@ -1,11 +1,13 @@
 /*
  * linux/arch/arm/mach-sa1100/sa1111.h
  */
+struct device;
 
 /*
  * Probe for a SA1111 chip.
  */
-extern int sa1111_probe(unsigned long phys);
+extern int
+sa1111_init(struct device *parent, unsigned long phys, unsigned int irq);
 
 /*
  * Wake up a SA1111 chip.
@@ -16,9 +18,3 @@
  * Doze the SA1111 chip.
  */
 extern void sa1111_doze(void);
-
-/*
- * Configure the SA1111 shared memory controller.
- */
-extern void sa1111_configure_smc(int sdram, unsigned int drac, unsigned int cas_latency);
-extern void sa1111_init_irq(int irq_nr);
diff -Nru a/arch/arm/mach-sa1100/system3.c b/arch/arm/mach-sa1100/system3.c
--- a/arch/arm/mach-sa1100/system3.c	Mon Apr 22 15:32:19 2002
+++ b/arch/arm/mach-sa1100/system3.c	Mon Apr 22 15:32:19 2002
@@ -426,44 +426,11 @@
 	/*
 	 * Probe for a SA1111.
 	 */
-	ret = sa1111_probe(PT_SA1111_BASE);
+	ret = sa1111_init(NULL, PT_SA1111_BASE, IRQ_SYSTEM3_SA1111);
 	if (ret < 0) {
 		printk( KERN_WARNING"PT Digital Board: no SA1111 found!\n" );
 		goto DONE;
 	}
-
-	/*
-	 * We found it.  Wake the chip up.
-	 */
-	sa1111_wake();
-
-	/*
-	 * The SDRAM configuration of the SA1110 and the SA1111 must
-	 * match.  This is very important to ensure that SA1111 accesses
-	 * don't corrupt the SDRAM.  Note that this ungates the SA1111's
-	 * MBGNT signal, so we must have called sa1110_mb_disable()
-	 * beforehand.
-	 */
-	sa1111_configure_smc(1,
-			     FExtr(MDCNFG, MDCNFG_SA1110_DRAC0),
-			     FExtr(MDCNFG, MDCNFG_SA1110_TDL0));
-
-	/*
-	 * We only need to turn on DCLK whenever we want to use the
-	 * DMA.  It can otherwise be held firmly in the off position.
-	 */
-	SKPCR |= SKPCR_DCLKEN;
-
-	/*
-	 * Enable the SA1110 memory bus request and grant signals.
-	 */
-	sa1110_mb_enable();
-
-	/*
-	 * Initialise SA1111 IRQs
-	 */
-	sa1111_init_irq(IRQ_SYSTEM3_SA1111);
-
 
 #if defined( CONFIG_CPU_FREQ )
 	ret = cpufreq_register_notifier(&system3_clkchg_block);
diff -Nru a/arch/arm/mach-shark/pci.c b/arch/arm/mach-shark/pci.c
--- a/arch/arm/mach-shark/pci.c	Mon Apr 22 15:32:19 2002
+++ b/arch/arm/mach-shark/pci.c	Mon Apr 22 15:32:19 2002
@@ -20,6 +20,8 @@
 	else return 255;
 }
 
+extern void __init via82c505_preinit(void *sysdata);
+
 struct hw_pci shark_pci __initdata = {
 	setup:		via82c505_setup,
 	swizzle:       	pci_std_swizzle,
diff -Nru a/arch/arm/mm/abort-ev5ej.S b/arch/arm/mm/abort-ev5ej.S
--- a/arch/arm/mm/abort-ev5ej.S	Mon Apr 22 15:32:18 2002
+++ b/arch/arm/mm/abort-ev5ej.S	Mon Apr 22 15:32:18 2002
@@ -28,7 +28,7 @@
 	ldrneh	r3, [r2]			@ read aborted thumb instruction
 	ldreq	r3, [r2]			@ read aborted ARM instruction
 	movne	r3, r3, lsl #(21 - 12)		@ move thumb bit 11 to ARM bit 20
-	tst	r2, #1 << 20			@ L = 1 -> write
+	tst	r3, #1 << 20			@ L = 1 -> write
 	orreq	r1, r1, #1 << 8			@ yes.
 1:	mov	pc, lr
 
diff -Nru a/arch/arm/mm/consistent.c b/arch/arm/mm/consistent.c
--- a/arch/arm/mm/consistent.c	Mon Apr 22 15:32:19 2002
+++ b/arch/arm/mm/consistent.c	Mon Apr 22 15:32:19 2002
@@ -25,10 +25,12 @@
 
 /*
  * This allocates one page of cache-coherent memory space and returns
- * both the virtual and a "dma" address to that space.  It is not clear
- * whether this could be called from an interrupt context or not.  For
- * now, we expressly forbid it, especially as some of the stuff we do
- * here is not interrupt context safe.
+ * both the virtual and a "dma" address to that space.
+ *
+ * We should allow this function to be called from interrupt context.
+ * However, we call ioremap, which needs to fiddle around with various
+ * things (like the vmlist_lock, and allocating page tables).  These
+ * things aren't interrupt safe (yet).
  *
  * Note that this does *not* zero the allocated area!
  */
@@ -36,8 +38,9 @@
 {
 	struct page *page, *end, *free;
 	unsigned long order;
-	void *ret, *virt;
+	void *ret;
 
+	/* FIXME */
 	if (in_interrupt())
 		BUG();
 
@@ -48,22 +51,22 @@
 	if (!page)
 		goto no_page;
 
-	/*
-	 * We could do with a page_to_phys and page_to_bus here.
-	 */
-	virt = page_address(page);
-	*dma_handle = virt_to_bus(virt);
-	ret = __ioremap(virt_to_phys(virt), size, 0);
+	*dma_handle = page_to_bus(page);
+	ret = __ioremap(page_to_phys(page), size, 0);
 	if (!ret)
 		goto no_remap;
 
 #if 0 /* ioremap_does_flush_cache_all */
-	/*
-	 * we need to ensure that there are no cachelines in use, or
-	 * worse dirty in this area.  Really, we don't need to do
-	 * this since __ioremap does a flush_cache_all() anyway. --rmk
-	 */
-	invalidate_dcache_range(virt, virt + size);
+	{
+		void *virt = page_address(page);
+
+		/*
+		 * we need to ensure that there are no cachelines in use, or
+		 * worse dirty in this area.  Really, we don't need to do
+		 * this since __ioremap does a flush_cache_all() anyway. --rmk
+		 */
+		invalidate_dcache_range(virt, virt + size);
+	}
 #endif
 
 	/*
@@ -72,7 +75,6 @@
 	 * We also mark the pages in use as reserved so that
 	 * remap_page_range works.
 	 */
-	page = virt_to_page(virt);
 	free = page + (size >> PAGE_SHIFT);
 	end  = page + (1 << order);
 
@@ -93,18 +95,12 @@
 
 void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, dma_addr_t *handle)
 {
-	void *__ret;
-	int __gfp = GFP_KERNEL;
+	int gfp = GFP_KERNEL;
 
-#ifdef CONFIG_PCI
-	if ((hwdev) == NULL ||
-	    (hwdev)->dma_mask != 0xffffffff)
-#endif
-		__gfp |= GFP_DMA;
+	if (hwdev == NULL || hwdev->dma_mask != 0xffffffff)
+		gfp |= GFP_DMA;
 
-	__ret = consistent_alloc(__gfp, (size),
-				 (handle));
-	return __ret;
+	return consistent_alloc(gfp, size, handle);
 }
 
 /*
@@ -114,19 +110,16 @@
 void consistent_free(void *vaddr, size_t size, dma_addr_t handle)
 {
 	struct page *page, *end;
-	void *virt;
 
 	if (in_interrupt())
 		BUG();
 
-	virt = bus_to_virt(handle);
-
 	/*
 	 * More messing around with the MM internals.  This is
 	 * sick, but then so is remap_page_range().
 	 */
 	size = PAGE_ALIGN(size);
-	page = virt_to_page(virt);
+	page = virt_to_page(bus_to_virt(handle));
 	end = page + (size >> PAGE_SHIFT);
 
 	for (; page < end; page++)
diff -Nru a/arch/arm/mm/minicache.c b/arch/arm/mm/minicache.c
--- a/arch/arm/mm/minicache.c	Mon Apr 22 15:32:18 2002
+++ b/arch/arm/mm/minicache.c	Mon Apr 22 15:32:18 2002
@@ -44,7 +44,7 @@
 unsigned long map_page_minicache(unsigned long virt)
 {
 	set_pte(minicache_pte, mk_pte_phys(__pa(virt), minicache_pgprot));
-	flush_kern_tlb_page(minicache_address);
+	flush_tlb_kernel_page(minicache_address);
 
 	return minicache_address;
 }
diff -Nru a/arch/arm/mm/proc-xscale.S b/arch/arm/mm/proc-xscale.S
--- a/arch/arm/mm/proc-xscale.S	Mon Apr 22 15:32:19 2002
+++ b/arch/arm/mm/proc-xscale.S	Mon Apr 22 15:32:19 2002
@@ -723,7 +723,6 @@
 	.word	cpu_xscale_set_pgd
 	.word	cpu_xscale_set_pmd
 	.word	cpu_xscale_set_pte
-
 	.size	xscale_processor_functions, . - xscale_processor_functions
 
 	.type	cpu_80200_info, #object
@@ -779,6 +778,5 @@
 	.long	xscale_processor_functions
 	.long	v4wbi_tlb_fns
 	.long	v5te_mc_user_fns
-	.size	__cotulla_proc_info, . - __cotulla_proc_info
 	.size	__pxa250_proc_info, . - __pxa250_proc_info
 
diff -Nru a/arch/arm/mm/tlb-v3.S b/arch/arm/mm/tlb-v3.S
--- a/arch/arm/mm/tlb-v3.S	Mon Apr 22 15:32:18 2002
+++ b/arch/arm/mm/tlb-v3.S	Mon Apr 22 15:32:18 2002
@@ -53,6 +53,7 @@
 	act_mm	r3				@ get current->active_mm
 	teq	r2, r3				@ == mm ?
 	movne	pc, lr				@ no, we dont do anything
+ENTRY(v3_flush_kern_tlb_range)
 	bic	r0, r0, #0x0ff
 	bic	r0, r0, #0xf00
 1:	mcr	p15, 0, r0, c6, c0, 0		@ invalidate TLB entry
@@ -87,5 +88,6 @@
 	.long	v3_flush_user_tlb_mm
 	.long	v3_flush_user_tlb_range
 	.long	v3_flush_user_tlb_page
+	.long	v3_flush_kern_tlb_range
 	.long	v3_flush_kern_tlb_page
 	.size	v3_tlb_fns, . - v3_tlb_fns
diff -Nru a/arch/arm/mm/tlb-v4.S b/arch/arm/mm/tlb-v4.S
--- a/arch/arm/mm/tlb-v4.S	Mon Apr 22 15:32:19 2002
+++ b/arch/arm/mm/tlb-v4.S	Mon Apr 22 15:32:19 2002
@@ -42,7 +42,7 @@
 /*
  *	v4_flush_user_tlb_range(start, end, mm)
  *
- *	Invalidate a range of TLB entries in the specified address space.
+ *	Invalidate a range of TLB entries in the specified user address space.
  *
  *	- start - range start address
  *	- end   - range end address
@@ -86,6 +86,27 @@
 	mov	pc, lr
 
 /*
+ *	v4_flush_kerm_tlb_range(start, end)
+ *
+ *	Invalidate a range of TLB entries in the specified kernel
+ *	address range.
+ *
+ *	- start - virtual address (may not be aligned)
+ *	- end   - virtual address (may not be aligned)
+ */
+	.align	5
+ENTRY(v4_flush_kern_tlb_range)
+	bic	r0, r0, #0x0ff
+	bic	r0, r0, #0xf00
+1:	mcr	p15, 0, r0, c8, c6, 1		@ invalidate D TLB entry
+	add	r0, r0, #PAGE_SZ
+	cmp	r0, r1
+	blo	1b
+	mcr	p15, 0, r3, c8, c5, 0		@ invalidate I TLB
+	mov	pc, lr
+
+
+/*
  *	v4_flush_kern_tlb_page(kaddr)
  *
  *	Invalidate the TLB entry for the specified page.  The address
@@ -106,5 +127,6 @@
 	.long	v4_flush_user_tlb_mm
 	.long	v4_flush_user_tlb_range
 	.long	v4_flush_user_tlb_page
+	.long	v4_flush_kern_tlb_range
 	.long	v4_flush_kern_tlb_page
 	.size	v4_tlb_fns, . - v4_tlb_fns
diff -Nru a/arch/arm/mm/tlb-v4wb.S b/arch/arm/mm/tlb-v4wb.S
--- a/arch/arm/mm/tlb-v4wb.S	Mon Apr 22 15:32:19 2002
+++ b/arch/arm/mm/tlb-v4wb.S	Mon Apr 22 15:32:19 2002
@@ -88,7 +88,41 @@
 	mcr	p15, 0, r3, c7, c10, 4		@ drain WB
 	tst	r2, #VM_EXEC
 	mcrne	p15, 0, r3, c8, c5, 0		@ invalidate I TLB
+	mcr	p15, 0, r0, c8, c6, 1		@ invalidate D TLB entry
+	mov	pc, lr
+
+/*
+ *	v4_flush_kerm_tlb_range(start, end)
+ *
+ *	Invalidate a range of TLB entries in the specified kernel
+ *	address range.
+ *
+ *	- start - virtual address (may not be aligned)
+ *	- end   - virtual address (may not be aligned)
+ */
+ENTRY(v4wb_flush_kern_tlb_range)
+	mov	r3, #0
+	mcr	p15, 0, r3, c7, c10, 4		@ drain WB
+	bic	r0, r0, #0x0ff
+	bic	r0, r0, #0xf00
+	mcr	p15, 0, r3, c8, c5, 0		@ invalidate I TLB
+1:	mcr	p15, 0, r0, c8, c6, 1		@ invalidate D TLB entry
+	add	r0, r0, #PAGE_SZ
+	cmp	r0, r1
+	blo	1b
+	mov	pc, lr
+
+/*
+ *	v4_flush_kern_tlb_page(kaddr)
+ *
+ *	Invalidate the TLB entry for the specified page.  The address
+ *	will be in the kernels virtual memory space.  Current uses
+ *	only require the D-TLB to be invalidated.
+ *
+ *	- kaddr - Kernel virtual memory address
+ */
 ENTRY(v4wb_flush_kern_tlb_page)
+	mcr	p15, 0, r3, c8, c5, 0		@ invalidate I TLB
 	mcr	p15, 0, r0, c8, c6, 1		@ invalidate D TLB entry
 	mov	pc, lr
 
@@ -107,14 +141,17 @@
  */
 	.align	5
 ENTRY(v4wbi_flush_user_tlb_range)
+	vma_vm_mm ip, r2
 	act_mm	r3				@ get current->active_mm
-	teq	r2, r3				@ == mm ?
+	eors	r3, ip, r3			@ == mm ?
 	movne	pc, lr				@ no, we dont do anything
 	mov	r3, #0
 	mcr	p15, 0, r3, c7, c10, 4		@ drain WB
+	vma_vm_flags r2, r2
 	bic	r0, r0, #0x0ff
 	bic	r0, r0, #0xf00
-1:	mcr	p15, 0, r0, c8, c5, 1		@ invalidate I TLB entry
+1:	tst	r2, #VM_EXEC
+	mcrne	p15, 0, r0, c8, c5, 1		@ invalidate I TLB entry
 	mcr	p15, 0, r0, c8, c6, 1		@ invalidate D TLB entry
 	add	r0, r0, #PAGE_SZ
 	cmp	r0, r1
@@ -140,7 +177,23 @@
 	mcr	p15, 0, r3, c7, c10, 4		@ drain WB
 	tst	r2, #VM_EXEC
 	mcrne	p15, 0, r0, c8, c5, 1		@ invalidate I TLB entry
+	mcr	p15, 0, r0, c8, c6, 1		@ invalidate D TLB entry
+	mov	pc, lr
+
+ENTRY(v4wbi_flush_kern_tlb_range)
+	mov	r3, #0
+	mcr	p15, 0, r3, c7, c10, 4		@ drain WB
+	bic	r0, r0, #0x0ff
+	bic	r0, r0, #0xf00
+1:	mcr	p15, 0, r0, c8, c5, 1		@ invalidate I TLB entry
+	mcr	p15, 0, r0, c8, c6, 1		@ invalidate D TLB entry
+	add	r0, r0, #PAGE_SZ
+	cmp	r0, r1
+	blo	1b
+	mov	pc, lr
+
 ENTRY(v4wbi_flush_kern_tlb_page)
+	mcr	p15, 0, r0, c8, c5, 1		@ invalidate I TLB entry
 	mcr	p15, 0, r0, c8, c6, 1		@ invalidate D TLB entry
 	mov	pc, lr
 
@@ -152,6 +205,7 @@
 	.long	v4wb_flush_user_tlb_mm
 	.long	v4wb_flush_user_tlb_range
 	.long	v4wb_flush_user_tlb_page
+	.long	v4wb_flush_kern_tlb_range
 	.long	v4wb_flush_kern_tlb_page
 	.size	v4wb_tlb_fns, . - v4wb_tlb_fns
 
@@ -161,5 +215,6 @@
 	.long	v4wbi_flush_user_tlb_mm
 	.long	v4wbi_flush_user_tlb_range
 	.long	v4wbi_flush_user_tlb_page
+	.long	v4wbi_flush_kern_tlb_range
 	.long	v4wbi_flush_kern_tlb_page
 	.size	v4wbi_tlb_fns, . - v4wbi_tlb_fns
diff -Nru a/arch/arm/nwfpe/Makefile b/arch/arm/nwfpe/Makefile
--- a/arch/arm/nwfpe/Makefile	Mon Apr 22 15:32:18 2002
+++ b/arch/arm/nwfpe/Makefile	Mon Apr 22 15:32:18 2002
@@ -1,6 +1,4 @@
 #
-# linux/arch/arm/nwfpe/Makefile
-#
 # Copyright (C) 1998, 1999, 2001 Philip Blundell
 #
 
@@ -12,8 +10,6 @@
 obj-m			:=
 obj-n			:=
 
-list-multi		:= nwfpe.o
-
 obj-$(CONFIG_FPE_NWFPE)	+= nwfpe.o
 
 nwfpe-objs		:= fpa11.o fpa11_cpdo.o fpa11_cpdt.o fpa11_cprt.o \
@@ -27,6 +23,3 @@
 endif
 
 include $(TOPDIR)/Rules.make
-
-nwfpe.o: $(nwfpe-objs)
-	$(LD) -r -o $@ $(nwfpe-objs)
diff -Nru a/arch/arm/nwfpe/fpmodule.c b/arch/arm/nwfpe/fpmodule.c
--- a/arch/arm/nwfpe/fpmodule.c	Mon Apr 22 15:32:19 2002
+++ b/arch/arm/nwfpe/fpmodule.c	Mon Apr 22 15:32:19 2002
@@ -28,6 +28,7 @@
 #include <linux/config.h>
 
 /* XXX */
+#include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/signal.h>
diff -Nru a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types
--- a/arch/arm/tools/mach-types	Mon Apr 22 15:32:19 2002
+++ b/arch/arm/tools/mach-types	Mon Apr 22 15:32:19 2002
@@ -6,7 +6,7 @@
 # To add an entry into this database, please see Documentation/arm/README,
 # or contact rmk@arm.linux.org.uk
 #
-# Last update: Sun Mar 24 11:48:10 2002
+# Last update: Fri Mar 29 15:51:20 2002
 #
 # machine_is_xxx	CONFIG_xxxx		MACH_TYPE_xxx		number
 #
@@ -179,3 +179,4 @@
 active			SA1100_ACTIVE		ACTIVE			168
 iq80321			ARCH_IQ80321		IQ80321			169
 wid			SA1100_WID		WID			170
+sabinal			ARCH_SABINAL		SABINAL			171
diff -Nru a/arch/cris/config.in b/arch/cris/config.in
--- a/arch/cris/config.in	Mon Apr 22 15:32:18 2002
+++ b/arch/cris/config.in	Mon Apr 22 15:32:18 2002
@@ -185,15 +185,7 @@
 
 source net/irda/Config.in
 
-mainmenu_option next_comment
-comment 'ISDN subsystem'
-if [ "$CONFIG_NET" != "n" ]; then
-   tristate 'ISDN support' CONFIG_ISDN
-   if [ "$CONFIG_ISDN" != "n" ]; then
-      source drivers/isdn/Config.in
-   fi
-fi
-endmenu
+source drivers/isdn/Config.in
 
 mainmenu_option next_comment
 comment 'Old CD-ROM drivers (not SCSI, not IDE)'
diff -Nru a/arch/cris/drivers/ide.c b/arch/cris/drivers/ide.c
--- a/arch/cris/drivers/ide.c	Mon Apr 22 15:32:19 2002
+++ b/arch/cris/drivers/ide.c	Mon Apr 22 15:32:19 2002
@@ -272,13 +272,17 @@
 	printk("ide: ETRAX 100LX built-in ATA DMA controller\n");
 
 	/* first initialize the channel interface data */
-	
+
 	for(h = 0; h < MAX_HWIFS; h++) {
 		struct ata_channel *hwif = &ide_hwifs[h];
+
 		hwif->chipset = ide_etrax100;
 		hwif->tuneproc = &tune_e100_ide;
 		hwif->dmaproc = &e100_dmaproc;
-		hwif->ideproc = &e100_ideproc;
+		hwif->ata_read = e100_ide_input_data;
+		hwif->ata_write = e100_ide_output_data;
+		hwif->atapi_read = e100_atapi_read;
+		hwif->atapi_write = e100_atapi_write;
 	}
 	/* actually reset and configure the etrax100 ide/ata interface */
 
@@ -375,12 +379,12 @@
  * so if an odd bytecount is specified, be sure that there's at least one
  * extra byte allocated for the buffer.
  */
-static void 
-e100_atapi_input_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount)
+static void
+e100_atapi_read(ide_drive_t *drive, void *buffer, unsigned int bytecount)
 {
 	ide_ioreg_t data_reg = IDE_DATA_REG;
 
-	D(printk("atapi_input_bytes, dreg 0x%x, buffer 0x%x, count %d\n",
+	D(printk("atapi_read, dreg 0x%x, buffer 0x%x, count %d\n",
 		 data_reg, buffer, bytecount));
 	
 	if(bytecount & 1) {
@@ -454,12 +458,12 @@
 #endif
 }
 
-static void 
-e100_atapi_output_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount)
+static void
+e100_atapi_write(ide_drive_t *drive, void *buffer, unsigned int bytecount)
 {
 	ide_ioreg_t data_reg = IDE_DATA_REG;
 	
-	D(printk("atapi_output_bytes, dreg 0x%x, buffer 0x%x, count %d\n",
+	D(printk("atapi_write, dreg 0x%x, buffer 0x%x, count %d\n",
 		 data_reg, buffer, bytecount));
 
 	if(bytecount & 1) {
@@ -544,7 +548,7 @@
 static void 
 e100_ide_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
 {
-	e100_atapi_input_bytes(drive, buffer, wcount << 2);
+	e100_atapi_read(drive, buffer, wcount << 2);
 }
 
 /*
@@ -553,33 +557,7 @@
 static void
 e100_ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
 {
-	e100_atapi_output_bytes(drive, buffer, wcount << 2);
-}
-
-/*
- * The multiplexor for ide_xxxput_data and atapi calls
- */
-static void 
-e100_ideproc (ide_ide_action_t func, ide_drive_t *drive,
-	      void *buffer, unsigned int length)
-{
-	switch (func) {
-		case ideproc_ide_input_data:
-			e100_ide_input_data(drive, buffer, length);
-			break;
-		case ideproc_ide_output_data:
-			e100_ide_input_data(drive, buffer, length);
-			break;
-		case ideproc_atapi_input_bytes:
-			e100_atapi_input_bytes(drive, buffer, length);
-			break;
-		case ideproc_atapi_output_bytes:
-			e100_atapi_output_bytes(drive, buffer, length);
-			break;
-		default:
-			printk("e100_ideproc: unsupported func %d!\n", func);
-			break;
-	}
+	e100_atapi_write(drive, buffer, wcount << 2);
 }
 
 /* we only have one DMA channel on the chip for ATA, so we can keep these statically */
diff -Nru a/arch/i386/config.in b/arch/i386/config.in
--- a/arch/i386/config.in	Mon Apr 22 15:32:19 2002
+++ b/arch/i386/config.in	Mon Apr 22 15:32:19 2002
@@ -88,7 +88,6 @@
    define_int  CONFIG_X86_L1_CACHE_SHIFT 5
    define_bool CONFIG_X86_TSC y
    define_bool CONFIG_X86_GOOD_APIC y
-   define_bool CONFIG_X86_PGE y
    define_bool CONFIG_X86_USE_PPRO_CHECKSUM y
    define_bool CONFIG_X86_PPRO_FENCE y
 fi
@@ -96,14 +95,12 @@
    define_int  CONFIG_X86_L1_CACHE_SHIFT 5
    define_bool CONFIG_X86_TSC y
    define_bool CONFIG_X86_GOOD_APIC y
-   define_bool CONFIG_X86_PGE y
    define_bool CONFIG_X86_USE_PPRO_CHECKSUM y
 fi
 if [ "$CONFIG_MPENTIUM4" = "y" ]; then
    define_int  CONFIG_X86_L1_CACHE_SHIFT 7
    define_bool CONFIG_X86_TSC y
    define_bool CONFIG_X86_GOOD_APIC y
-   define_bool CONFIG_X86_PGE y
    define_bool CONFIG_X86_USE_PPRO_CHECKSUM y
 fi
 if [ "$CONFIG_MK6" = "y" ]; then
@@ -117,7 +114,6 @@
    define_bool CONFIG_X86_TSC y
    define_bool CONFIG_X86_GOOD_APIC y
    define_bool CONFIG_X86_USE_3DNOW y
-   define_bool CONFIG_X86_PGE y
    define_bool CONFIG_X86_USE_PPRO_CHECKSUM y
 fi
 if [ "$CONFIG_MELAN" = "y" ]; then
@@ -348,15 +344,7 @@
 
 source net/irda/Config.in
 
-mainmenu_option next_comment
-comment 'ISDN subsystem'
-if [ "$CONFIG_NET" != "n" ]; then
-   tristate 'ISDN support' CONFIG_ISDN
-   if [ "$CONFIG_ISDN" != "n" ]; then
-      source drivers/isdn/Config.in
-   fi
-fi
-endmenu
+source drivers/isdn/Config.in
 
 mainmenu_option next_comment
 comment 'Old CD-ROM drivers (not SCSI, not IDE)'
diff -Nru a/arch/i386/defconfig b/arch/i386/defconfig
--- a/arch/i386/defconfig	Mon Apr 22 15:32:18 2002
+++ b/arch/i386/defconfig	Mon Apr 22 15:32:18 2002
@@ -56,7 +56,6 @@
 CONFIG_X86_L1_CACHE_SHIFT=5
 CONFIG_X86_TSC=y
 CONFIG_X86_GOOD_APIC=y
-CONFIG_X86_PGE=y
 CONFIG_X86_USE_PPRO_CHECKSUM=y
 CONFIG_X86_MCE=y
 # CONFIG_X86_MCE_NONFATAL is not set
@@ -529,7 +528,7 @@
 #
 # ISDN subsystem
 #
-# CONFIG_ISDN is not set
+# CONFIG_ISDN_BOOL is not set
 
 #
 # Old CD-ROM drivers (not SCSI, not IDE)
@@ -693,6 +692,7 @@
 # CONFIG_NFSD_TCP is not set
 CONFIG_SUNRPC=y
 CONFIG_LOCKD=y
+CONFIG_EXPORTFS=y
 # CONFIG_SMB_FS is not set
 # CONFIG_NCP_FS is not set
 # CONFIG_NCPFS_PACKET_SIGNING is not set
@@ -864,7 +864,6 @@
 #
 # USB Imaging devices
 #
-# CONFIG_USB_DC2XX is not set
 # CONFIG_USB_MDC800 is not set
 # CONFIG_USB_SCANNER is not set
 # CONFIG_USB_MICROTEK is not set
diff -Nru a/arch/i386/kernel/bluesmoke.c b/arch/i386/kernel/bluesmoke.c
--- a/arch/i386/kernel/bluesmoke.c	Mon Apr 22 15:32:19 2002
+++ b/arch/i386/kernel/bluesmoke.c	Mon Apr 22 15:32:19 2002
@@ -33,9 +33,9 @@
  *	P4/Xeon Thermal transition interrupt handler
  */
 
+#ifdef CONFIG_X86_LOCAL_APIC
 static void intel_thermal_interrupt(struct pt_regs *regs)
 {
-#ifdef CONFIG_X86_LOCAL_APIC
 	u32 l, h;
 	unsigned int cpu = smp_processor_id();
 
@@ -48,8 +48,8 @@
 	} else {
 		printk(KERN_INFO "CPU#%d: Temperature/speed normal\n", cpu);
 	}
-#endif
 }
+#endif
 
 static void unexpected_thermal_interrupt(struct pt_regs *regs)
 {	
@@ -76,11 +76,11 @@
 	unsigned int cpu = smp_processor_id();
 
 	/* Thermal monitoring */
-	if (!test_bit(X86_FEATURE_ACPI, &c->x86_capability))
+	if (!test_bit(X86_FEATURE_ACPI, c->x86_capability))
 		return;	/* -ENODEV */
 	
 	/* Clock modulation */
-	if (!test_bit(X86_FEATURE_ACC, &c->x86_capability))
+	if (!test_bit(X86_FEATURE_ACC, c->x86_capability))
 		return;	/* -ENODEV */
 
 	rdmsr(MSR_IA32_MISC_ENABLE, l, h);
diff -Nru a/arch/i386/kernel/i387.c b/arch/i386/kernel/i387.c
--- a/arch/i386/kernel/i387.c	Mon Apr 22 15:32:19 2002
+++ b/arch/i386/kernel/i387.c	Mon Apr 22 15:32:19 2002
@@ -31,13 +31,21 @@
  * value at reset if we support XMM instructions and then
  * remeber the current task has used the FPU.
  */
-void init_fpu(void)
+void init_fpu(struct task_struct *tsk)
 {
-	__asm__("fninit");
-	if ( cpu_has_xmm )
-		load_mxcsr(0x1f80);
-		
-	current->used_math = 1;
+	if (cpu_has_fxsr) {
+		memset(&tsk->thread.i387.fxsave, 0, sizeof(struct i387_fxsave_struct));
+		tsk->thread.i387.fxsave.cwd = 0x37f;
+		if (cpu_has_xmm)
+			tsk->thread.i387.fxsave.mxcsr = 0x1f80;
+	} else {
+		memset(&tsk->thread.i387.fsave, 0, sizeof(struct i387_fsave_struct));
+		tsk->thread.i387.fsave.cwd = 0xffff037f;
+		tsk->thread.i387.fsave.swd = 0xffff0000;
+		tsk->thread.i387.fsave.twd = 0xffffffff;
+		tsk->thread.i387.fsave.fos = 0xffff0000;
+	}
+	tsk->used_math = 1;
 }
 
 /*
diff -Nru a/arch/i386/kernel/io_apic.c b/arch/i386/kernel/io_apic.c
--- a/arch/i386/kernel/io_apic.c	Mon Apr 22 15:32:19 2002
+++ b/arch/i386/kernel/io_apic.c	Mon Apr 22 15:32:19 2002
@@ -28,6 +28,7 @@
 #include <linux/config.h>
 #include <linux/smp_lock.h>
 #include <linux/mc146818rtc.h>
+#include <linux/compiler.h>
 
 #include <asm/io.h>
 #include <asm/smp.h>
@@ -183,6 +184,86 @@
 			clear_IO_APIC_pin(apic, pin);
 }
 
+static void set_ioapic_affinity (unsigned int irq, unsigned long mask)
+{
+	unsigned long flags;
+
+	/*
+	 * Only the first 8 bits are valid.
+	 */
+	mask = mask << 24;
+	spin_lock_irqsave(&ioapic_lock, flags);
+	__DO_ACTION(1, = mask, )
+	spin_unlock_irqrestore(&ioapic_lock, flags);
+}
+
+#if CONFIG_SMP
+
+typedef struct {
+	unsigned int cpu;
+	unsigned long timestamp;
+} ____cacheline_aligned irq_balance_t;
+
+static irq_balance_t irq_balance[NR_IRQS] __cacheline_aligned
+			= { [ 0 ... NR_IRQS-1 ] = { 1, 0 } };
+
+extern unsigned long irq_affinity [NR_IRQS];
+
+#endif
+
+#define IDLE_ENOUGH(cpu,now) \
+		(idle_cpu(cpu) && ((now) - irq_stat[(cpu)].idle_timestamp > 1))
+
+#define IRQ_ALLOWED(cpu,allowed_mask) \
+		((1 << cpu) & (allowed_mask))
+
+static unsigned long move(int curr_cpu, unsigned long allowed_mask, unsigned long now, int direction)
+{
+	int search_idle = 1;
+	int cpu = curr_cpu;
+
+	goto inside;
+
+	do {
+		if (unlikely(cpu == curr_cpu))
+			search_idle = 0;
+inside:
+		if (direction == 1) {
+			cpu++;
+			if (cpu >= smp_num_cpus)
+				cpu = 0;
+		} else {
+			cpu--;
+			if (cpu == -1)
+				cpu = smp_num_cpus-1;
+		}
+	} while (!IRQ_ALLOWED(cpu,allowed_mask) ||
+			(search_idle && !IDLE_ENOUGH(cpu,now)));
+
+	return cpu;
+}
+
+static inline void balance_irq(int irq)
+{
+#if CONFIG_SMP
+	irq_balance_t *entry = irq_balance + irq;
+	unsigned long now = jiffies;
+
+	if (unlikely(entry->timestamp != now)) {
+		unsigned long allowed_mask;
+		int random_number;
+
+		rdtscl(random_number);
+		random_number &= 1;
+
+		allowed_mask = cpu_online_map & irq_affinity[irq];
+		entry->timestamp = now;
+		entry->cpu = move(entry->cpu, allowed_mask, now, random_number);
+		set_ioapic_affinity(irq, 1 << entry->cpu);
+	}
+#endif
+}
+
 /*
  * support for broken MP BIOSs, enables hand-redirection of PIRQ0-7 to
  * specific CPU-side IRQs.
@@ -672,8 +753,7 @@
 }
 
 /*
- * Set up the 8259A-master output pin as broadcast to all
- * CPUs.
+ * Set up the 8259A-master output pin:
  */
 void __init setup_ExtINT_IRQ0_pin(unsigned int pin, int vector)
 {
@@ -1193,6 +1273,7 @@
  */
 static void ack_edge_ioapic_irq(unsigned int irq)
 {
+	balance_irq(irq);
 	if ((irq_desc[irq].status & (IRQ_PENDING | IRQ_DISABLED))
 					== (IRQ_PENDING | IRQ_DISABLED))
 		mask_IO_APIC_irq(irq);
@@ -1232,6 +1313,7 @@
 	unsigned long v;
 	int i;
 
+	balance_irq(irq);
 /*
  * It appears there is an erratum which affects at least version 0x11
  * of I/O APIC (that's the 82093AA and cores integrated into various
@@ -1288,19 +1370,6 @@
 
 static void mask_and_ack_level_ioapic_irq (unsigned int irq) { /* nothing */ }
 
-static void set_ioapic_affinity (unsigned int irq, unsigned long mask)
-{
-	unsigned long flags;
-	/*
-	 * Only the first 8 bits are valid.
-	 */
-	mask = mask << 24;
-
-	spin_lock_irqsave(&ioapic_lock, flags);
-	__DO_ACTION(1, = mask, )
-	spin_unlock_irqrestore(&ioapic_lock, flags);
-}
-
 /*
  * Level and edge triggered IO-APIC interrupts need different handling,
  * so we use two separate IRQ descriptors. Edge triggered IRQs can be
@@ -1590,6 +1659,7 @@
 
 	printk(KERN_INFO "...trying to set up timer as ExtINT IRQ...");
 
+	timer_ack = 0;
 	init_8259A(0);
 	make_8259A_irq(0);
 	apic_write_around(APIC_LVT0, APIC_DM_EXTINT);
diff -Nru a/arch/i386/kernel/irq.c b/arch/i386/kernel/irq.c
--- a/arch/i386/kernel/irq.c	Mon Apr 22 15:32:18 2002
+++ b/arch/i386/kernel/irq.c	Mon Apr 22 15:32:18 2002
@@ -1073,7 +1073,7 @@
 
 static struct proc_dir_entry * smp_affinity_entry [NR_IRQS];
 
-static unsigned long irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = ~0UL };
+unsigned long irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = ~0UL };
 static int irq_affinity_read_proc (char *page, char **start, off_t off,
 			int count, int *eof, void *data)
 {
diff -Nru a/arch/i386/kernel/microcode.c b/arch/i386/kernel/microcode.c
--- a/arch/i386/kernel/microcode.c	Mon Apr 22 15:32:19 2002
+++ b/arch/i386/kernel/microcode.c	Mon Apr 22 15:32:19 2002
@@ -338,7 +338,7 @@
 		return -EINVAL;
 	}
 	if ((len >> PAGE_SHIFT) > num_physpages) {
-		printk(KERN_ERR "microcode: too much data (max %d pages)\n", num_physpages);
+		printk(KERN_ERR "microcode: too much data (max %ld pages)\n", num_physpages);
 		return -EINVAL;
 	}
 	down_write(&microcode_rwsem);
diff -Nru a/arch/i386/kernel/mpparse.c b/arch/i386/kernel/mpparse.c
--- a/arch/i386/kernel/mpparse.c	Mon Apr 22 15:32:19 2002
+++ b/arch/i386/kernel/mpparse.c	Mon Apr 22 15:32:19 2002
@@ -38,6 +38,8 @@
 int apic_version [MAX_APICS];
 int mp_bus_id_to_type [MAX_MP_BUSSES];
 int mp_bus_id_to_node [MAX_MP_BUSSES];
+int mp_bus_id_to_local [MAX_MP_BUSSES];
+int quad_local_to_mp_bus_id [NR_CPUS/4][4];
 int mp_bus_id_to_pci_bus [MAX_MP_BUSSES] = { [0 ... MAX_MP_BUSSES-1] = -1 };
 int mp_current_pci_id;
 
@@ -237,13 +239,17 @@
 static void __init MP_bus_info (struct mpc_config_bus *m)
 {
 	char str[7];
+	int quad;
 
 	memcpy(str, m->mpc_bustype, 6);
 	str[6] = 0;
 	
 	if (clustered_apic_mode) {
-		mp_bus_id_to_node[m->mpc_busid] = translation_table[mpc_record]->trans_quad;
-		printk("Bus #%d is %s (node %d)\n", m->mpc_busid, str, mp_bus_id_to_node[m->mpc_busid]);
+		quad = translation_table[mpc_record]->trans_quad;
+		mp_bus_id_to_node[m->mpc_busid] = quad;
+		mp_bus_id_to_local[m->mpc_busid] = translation_table[mpc_record]->trans_local;
+		quad_local_to_mp_bus_id[quad][translation_table[mpc_record]->trans_local] = m->mpc_busid;
+		printk("Bus #%d is %s (node %d)\n", m->mpc_busid, str, quad);
 	} else {
 		Dprintk("Bus #%d is %s\n", m->mpc_busid, str);
 	}
@@ -320,13 +326,14 @@
 
 static void __init MP_translation_info (struct mpc_config_translation *m)
 {
-	printk("Translation: record %d, type %d, quad %d, global %d, local %d\n", mpc_record, m->trans_type, 
-		m->trans_quad, m->trans_global, m->trans_local);
+	printk("Translation: record %d, type %d, quad %d, global %d, local %d\n", mpc_record, m->trans_type, m->trans_quad, m->trans_global, m->trans_local);
 
 	if (mpc_record >= MAX_MPC_ENTRY) 
 		printk("MAX_MPC_ENTRY exceeded!\n");
 	else
 		translation_table[mpc_record] = m; /* stash this for later */
+	if (m->trans_quad+1 > numnodes)
+		numnodes = m->trans_quad+1;
 }
 
 /*
@@ -491,10 +498,6 @@
 			}
 		}
 		++mpc_record;
-	}
-	if (clustered_apic_mode && nr_ioapics > 2) {
-		/* don't initialise IO apics on secondary quads */
-		nr_ioapics = 2;
 	}
 	if (!num_processors)
 		printk(KERN_ERR "SMP mptable: no processors registered!\n");
diff -Nru a/arch/i386/kernel/pci-pc.c b/arch/i386/kernel/pci-pc.c
--- a/arch/i386/kernel/pci-pc.c	Mon Apr 22 15:32:19 2002
+++ b/arch/i386/kernel/pci-pc.c	Mon Apr 22 15:32:19 2002
@@ -1091,8 +1091,9 @@
 	 */
 	int pxb, reg;
 	u8 busno, suba, subb;
+#ifdef CONFIG_MULTIQUAD
 	int quad = BUS2QUAD(d->bus->number);
-
+#endif
 	printk("PCI: Searching for i450NX host bridges on %s\n", d->slot_name);
 	reg = 0xd0;
 	for(pxb=0; pxb<2; pxb++) {
diff -Nru a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c
--- a/arch/i386/kernel/process.c	Mon Apr 22 15:32:18 2002
+++ b/arch/i386/kernel/process.c	Mon Apr 22 15:32:18 2002
@@ -142,6 +142,7 @@
 		void (*idle)(void) = pm_idle;
 		if (!idle)
 			idle = default_idle;
+		irq_stat[smp_processor_id()].idle_timestamp = jiffies;
 		while (!need_resched())
 			idle();
 		schedule();
diff -Nru a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c
--- a/arch/i386/kernel/setup.c	Mon Apr 22 15:32:19 2002
+++ b/arch/i386/kernel/setup.c	Mon Apr 22 15:32:19 2002
@@ -2153,6 +2153,11 @@
 		strcpy(c->x86_model_id, p);
 	
 #ifdef CONFIG_SMP
+	/* PGE CPUID bug: Pentium4 supports PGE, but seems to have SMP bugs.. */
+	if ( c->x86 == 15 )
+		clear_bit(X86_FEATURE_PGE, c->x86_capability);
+
+
 	if (test_bit(X86_FEATURE_HT, c->x86_capability)) {
 		extern	int phys_proc_id[NR_CPUS];
 		
diff -Nru a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c
--- a/arch/i386/kernel/traps.c	Mon Apr 22 15:32:19 2002
+++ b/arch/i386/kernel/traps.c	Mon Apr 22 15:32:19 2002
@@ -757,13 +757,12 @@
  */
 asmlinkage void math_state_restore(struct pt_regs regs)
 {
+	struct task_struct *tsk = current;
 	clts();		/* Allow maths ops (or we recurse) */
 
-	if (current->used_math) {
-		restore_fpu(current);
-	} else {
-		init_fpu();
-	}
+	if (!tsk->used_math)
+		init_fpu(tsk);
+	restore_fpu(tsk);
 	set_thread_flag(TIF_USEDFPU);	/* So we fnsave on switch_to() */
 }
 
diff -Nru a/arch/ia64/Config.help b/arch/ia64/Config.help
--- a/arch/ia64/Config.help	Mon Apr 22 15:32:19 2002
+++ b/arch/ia64/Config.help	Mon Apr 22 15:32:19 2002
@@ -414,12 +414,18 @@
 
   HP-simulator   For the HP simulator
                  (<http://software.hp.com/ia64linux/>).
+  HP-zx1         For HP zx1 platforms.
   SN1-simulator  For the SGI SN1 simulator.
   DIG-compliant  For DIG ("Developer's Interface Guide") compliant
-                 system.
+                 systems.
 
   If you don't know what to do, choose "generic".
 
+CONFIG_IA64_HP_ZX1
+  Build a kernel that runs on HP zx1-based systems.  This adds support
+  for the zx1 IOMMU and makes root bus bridges appear in PCI config space
+  (required for zx1 agpgart support).
+
 CONFIG_IA64_PAGE_SIZE_4KB
   This lets you select the page size of the kernel.  For best IA-64
   performance, a page size of 8KB or 16KB is recommended.  For best
@@ -439,6 +445,32 @@
   Select this option to build a kernel for an Itanium prototype system
   with a B-step CPU.  You have a B-step CPU if the "revision" field in
   /proc/cpuinfo has a value in the range from 1 to 4.
+
+CONFIG_IA64_SGI_AUTOTEST
+  Build a kernel used for hardware validation. If you include the
+  keyword "autotest" on the boot command line, the kernel does NOT boot.
+  Instead, it starts all cpus and runs cache coherency tests instead.
+
+  If unsure, say N.
+
+CONFIG_IA64_SGI_SN_DEBUG
+  Turns on extra debugging code in the SGI SN (Scalable NUMA) platform
+  for IA64.  Unless you are debugging problems on an SGI SN IA64 box,
+  say N.
+
+CONFIG_IA64_SGI_SN_SIM
+  If you are compiling a kernel that will run under SGI's IA64
+  simulator (Medusa) then say Y, otherwise say N.
+
+CONFIG_SERIAL_SGI_L1_PROTOCOL
+  Uses protocol mode instead of raw mode for the level 1 console on the
+  SGI SN (Scalable NUMA) platform for IA64.  If you are compiling for
+  an SGI SN box then Y is the recommended value, otherwise say N.
+
+CONFIG_PCIBA
+  IRIX PCIBA-inspired user mode PCI interface for the SGI SN (Scalable
+  NUMA) platform for IA64.  Unless you are compiling a kernel for an
+  SGI SN IA64 box, say N.
 
 CONFIG_IA64_MCA
   Say Y here to enable machine check support for IA-64.  If you're
diff -Nru a/arch/ia64/Makefile b/arch/ia64/Makefile
--- a/arch/ia64/Makefile	Mon Apr 22 15:32:18 2002
+++ b/arch/ia64/Makefile	Mon Apr 22 15:32:18 2002
@@ -22,7 +22,7 @@
 # -ffunction-sections
 CFLAGS_KERNEL := -mconstant-gp
 
-GCC_VERSION=$(shell $(CROSS_COMPILE)$(HOSTCC) -v 2>&1 | fgrep 'gcc version' | cut -f3 -d' ' | cut -f1 -d'.')
+GCC_VERSION=$(shell $(CC) -v 2>&1 | fgrep 'gcc version' | cut -f3 -d' ' | cut -f1 -d'.')
 
 ifneq ($(GCC_VERSION),2)
 	CFLAGS += -frename-registers --param max-inline-insns=2000
@@ -33,16 +33,11 @@
 endif
 
 ifdef CONFIG_IA64_GENERIC
-	CORE_FILES      :=      arch/$(ARCH)/hp/hp.a	\
-				arch/$(ARCH)/sn/sn.o	\
-				arch/$(ARCH)/dig/dig.a	\
-				arch/$(ARCH)/sn/io/sgiio.o \
+	CORE_FILES      :=      arch/$(ARCH)/hp/hp.o			\
+				arch/$(ARCH)/dig/dig.a			\
 				$(CORE_FILES)
 	SUBDIRS		:=	arch/$(ARCH)/hp		\
-				arch/$(ARCH)/sn/sn1	\
-				arch/$(ARCH)/sn		\
 				arch/$(ARCH)/dig	\
-				arch/$(ARCH)/sn/io	\
 				$(SUBDIRS)
 
 else # !GENERIC
@@ -50,7 +45,16 @@
 ifdef CONFIG_IA64_HP_SIM
         SUBDIRS         :=      arch/$(ARCH)/hp		\
                                 $(SUBDIRS)
-        CORE_FILES      :=      arch/$(ARCH)/hp/hp.a	\
+        CORE_FILES      :=      arch/$(ARCH)/hp/hp.o	\
+                                $(CORE_FILES)
+endif
+
+ifdef CONFIG_IA64_HP_ZX1
+        SUBDIRS         :=      arch/$(ARCH)/hp				\
+				arch/$(ARCH)/dig			\
+                                $(SUBDIRS)
+        CORE_FILES      :=      arch/$(ARCH)/hp/hp.o			\
+				arch/$(ARCH)/dig/dig.a			\
                                 $(CORE_FILES)
 endif
 
diff -Nru a/arch/ia64/config.in b/arch/ia64/config.in
--- a/arch/ia64/config.in	Mon Apr 22 15:32:19 2002
+++ b/arch/ia64/config.in	Mon Apr 22 15:32:19 2002
@@ -22,6 +22,7 @@
 	"generic		CONFIG_IA64_GENERIC		\
 	 DIG-compliant		CONFIG_IA64_DIG			\
 	 HP-simulator		CONFIG_IA64_HP_SIM		\
+	 HP-zx1			CONFIG_IA64_HP_ZX1		\
 	 SGI-SN1		CONFIG_IA64_SGI_SN1		\
 	 SGI-SN2		CONFIG_IA64_SGI_SN2" generic
 
@@ -56,7 +57,7 @@
 	fi
 fi
 
-if [ "$CONFIG_IA64_DIG" = "y" ]; then
+if [ "$CONFIG_IA64_GENERIC" = "y" ] || [ "$CONFIG_IA64_DIG" = "y" ] || [ "$CONFIG_IA64_HP_ZX1" = "y" ]; then
 	bool '  Enable IA-64 Machine Check Abort' CONFIG_IA64_MCA
 	define_bool CONFIG_PM y
 fi
@@ -173,14 +174,7 @@
 
 source net/ax25/Config.in
 
-mainmenu_option next_comment
-comment 'ISDN subsystem'
-
-tristate 'ISDN support' CONFIG_ISDN
-if [ "$CONFIG_ISDN" != "n" ]; then
-  source drivers/isdn/Config.in
-fi
-endmenu
+source drivers/isdn/Config.in
 
 mainmenu_option next_comment
 comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)'
diff -Nru a/arch/ia64/defconfig b/arch/ia64/defconfig
--- a/arch/ia64/defconfig	Mon Apr 22 15:32:18 2002
+++ b/arch/ia64/defconfig	Mon Apr 22 15:32:18 2002
@@ -23,7 +23,7 @@
 # CONFIG_KMOD is not set
 
 #
-# General setup
+# Processor type and features
 #
 CONFIG_IA64=y
 # CONFIG_ISA is not set
@@ -32,21 +32,22 @@
 # CONFIG_SBUS is not set
 CONFIG_RWSEM_GENERIC_SPINLOCK=y
 # CONFIG_RWSEM_XCHGADD_ALGORITHM is not set
-CONFIG_ACPI=y
-CONFIG_ACPI_EFI=y
-CONFIG_ACPI_INTERPRETER=y
-CONFIG_ACPI_KERNEL_CONFIG=y
 CONFIG_ITANIUM=y
 # CONFIG_MCKINLEY is not set
 # CONFIG_IA64_GENERIC is not set
 CONFIG_IA64_DIG=y
 # CONFIG_IA64_HP_SIM is not set
+# CONFIG_IA64_HP_ZX1 is not set
 # CONFIG_IA64_SGI_SN1 is not set
 # CONFIG_IA64_SGI_SN2 is not set
 # CONFIG_IA64_PAGE_SIZE_4KB is not set
 # CONFIG_IA64_PAGE_SIZE_8KB is not set
 CONFIG_IA64_PAGE_SIZE_16KB=y
 # CONFIG_IA64_PAGE_SIZE_64KB is not set
+CONFIG_ACPI=y
+CONFIG_ACPI_EFI=y
+CONFIG_ACPI_INTERPRETER=y
+CONFIG_ACPI_KERNEL_CONFIG=y
 CONFIG_IA64_BRL_EMU=y
 # CONFIG_ITANIUM_BSTEP_SPECIFIC is not set
 CONFIG_IA64_L1_CACHE_SHIFT=6
@@ -60,15 +61,23 @@
 CONFIG_EFI_VARS=y
 CONFIG_BINFMT_ELF=y
 # CONFIG_BINFMT_MISC is not set
-# CONFIG_ACPI_DEBUG is not set
-CONFIG_ACPI_BUSMGR=y
-CONFIG_ACPI_SYS=y
-CONFIG_ACPI_CPU=y
+
+#
+# ACPI Support
+#
+CONFIG_ACPI=y
+CONFIG_ACPI_EFI=y
+CONFIG_ACPI_BOOT=y
+CONFIG_ACPI_BUS=y
+CONFIG_ACPI_INTERPRETER=y
+CONFIG_ACPI_PCI=y
+CONFIG_ACPI_POWER=y
+CONFIG_ACPI_SYSTEM=y
 CONFIG_ACPI_BUTTON=y
-CONFIG_ACPI_AC=y
-CONFIG_ACPI_EC=y
-CONFIG_ACPI_CMBATT=y
-CONFIG_ACPI_THERMAL=y
+CONFIG_ACPI_FAN=m
+CONFIG_ACPI_PROCESSOR=m
+CONFIG_ACPI_THERMAL=m
+# CONFIG_ACPI_DEBUG is not set
 CONFIG_PCI=y
 CONFIG_PCI_NAMES=y
 # CONFIG_HOTPLUG is not set
@@ -231,15 +240,13 @@
 # CONFIG_BLK_DEV_ALI15X3 is not set
 # CONFIG_WDC_ALI15X3 is not set
 # CONFIG_BLK_DEV_AMD74XX is not set
-# CONFIG_AMD74XX_OVERRIDE is not set
 # CONFIG_BLK_DEV_CMD64X is not set
 # CONFIG_BLK_DEV_CY82C693 is not set
 # CONFIG_BLK_DEV_CS5530 is not set
 # CONFIG_BLK_DEV_HPT34X is not set
 # CONFIG_HPT34X_AUTODMA is not set
 # CONFIG_BLK_DEV_HPT366 is not set
-CONFIG_BLK_DEV_PIIX=y
-# CONFIG_PIIX_TUNING is not set
+# CONFIG_BLK_DEV_PIIX is not set
 # CONFIG_BLK_DEV_NS87415 is not set
 # CONFIG_BLK_DEV_OPTI621 is not set
 # CONFIG_BLK_DEV_PDC_ADMA is not set
@@ -254,7 +261,6 @@
 # CONFIG_IDEDMA_IVB is not set
 # CONFIG_IDEDMA_AUTO is not set
 # CONFIG_DMA_NONPCI is not set
-CONFIG_BLK_DEV_IDE_MODES=y
 # CONFIG_BLK_DEV_ATARAID is not set
 # CONFIG_BLK_DEV_ATARAID_PDC is not set
 # CONFIG_BLK_DEV_ATARAID_HPT is not set
@@ -691,8 +697,6 @@
 # CONFIG_MINIX_SUBPARTITION is not set
 # CONFIG_SOLARIS_X86_PARTITION is not set
 # CONFIG_UNIXWARE_DISKLABEL is not set
-CONFIG_EFI_PARTITION=y
-# CONFIG_DEVFS_GUID is not set
 # CONFIG_LDM_PARTITION is not set
 # CONFIG_SGI_PARTITION is not set
 # CONFIG_ULTRIX_PARTITION is not set
@@ -726,6 +730,7 @@
 # CONFIG_NLS_CODEPAGE_949 is not set
 # CONFIG_NLS_CODEPAGE_874 is not set
 # CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
 # CONFIG_NLS_CODEPAGE_1251 is not set
 # CONFIG_NLS_ISO8859_1 is not set
 # CONFIG_NLS_ISO8859_2 is not set
@@ -814,8 +819,9 @@
 # USB Device Class drivers
 #
 # CONFIG_USB_AUDIO is not set
-# CONFIG_USB_EMI26 is not set
-# CONFIG_USB_BLUETOOTH is not set
+# CONFIG_USB_BLUETOOTH_TTY is not set
+# CONFIG_USB_ACM is not set
+# CONFIG_USB_PRINTER is not set
 # CONFIG_USB_STORAGE is not set
 # CONFIG_USB_STORAGE_DEBUG is not set
 # CONFIG_USB_STORAGE_DATAFAB is not set
@@ -825,13 +831,12 @@
 # CONFIG_USB_STORAGE_HP8200e is not set
 # CONFIG_USB_STORAGE_SDDR09 is not set
 # CONFIG_USB_STORAGE_JUMPSHOT is not set
-# CONFIG_USB_ACM is not set
-# CONFIG_USB_PRINTER is not set
 
 #
 # USB Human Interface Devices (HID)
 #
 CONFIG_USB_HID=m
+CONFIG_USB_HIDINPUT=y
 CONFIG_USB_HIDDEV=y
 CONFIG_USB_KBD=m
 CONFIG_USB_MOUSE=m
@@ -849,23 +854,24 @@
 #
 # USB Multimedia devices
 #
+# CONFIG_USB_DABUSB is not set
+# CONFIG_USB_VICAM is not set
+# CONFIG_USB_DSBR is not set
 # CONFIG_USB_IBMCAM is not set
+# CONFIG_USB_KONICAWC is not set
 # CONFIG_USB_OV511 is not set
 # CONFIG_USB_PWC is not set
 # CONFIG_USB_SE401 is not set
 # CONFIG_USB_STV680 is not set
-# CONFIG_USB_VICAM is not set
-# CONFIG_USB_DSBR is not set
-# CONFIG_USB_DABUSB is not set
-# CONFIG_USB_KONICAWC is not set
 
 #
 # USB Network adaptors
 #
-# CONFIG_USB_PEGASUS is not set
-# CONFIG_USB_KAWETH is not set
 # CONFIG_USB_CATC is not set
 # CONFIG_USB_CDCETHER is not set
+# CONFIG_USB_KAWETH is not set
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_RTL8150 is not set
 # CONFIG_USB_USBNET is not set
 
 #
@@ -897,9 +903,11 @@
 # CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set
 # CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set
 # CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set
-# CONFIG_USB_SERIAL_MCT_U232 is not set
 # CONFIG_USB_SERIAL_KLSI is not set
+# CONFIG_USB_SERIAL_MCT_U232 is not set
 # CONFIG_USB_SERIAL_PL2303 is not set
+# CONFIG_USB_SERIAL_SAFE is not set
+# CONFIG_USB_SERIAL_SAFE_PADDED is not set
 # CONFIG_USB_SERIAL_CYBERJACK is not set
 # CONFIG_USB_SERIAL_XIRCOM is not set
 # CONFIG_USB_SERIAL_OMNINET is not set
@@ -907,8 +915,10 @@
 #
 # USB Miscellaneous drivers
 #
-# CONFIG_USB_RIO500 is not set
+# CONFIG_USB_EMI26 is not set
+# CONFIG_USB_TIGL is not set
 # CONFIG_USB_AUERSWALD is not set
+# CONFIG_USB_RIO500 is not set
 
 #
 # Library routines
@@ -936,11 +946,3 @@
 # CONFIG_DEBUG_SPINLOCK is not set
 # CONFIG_IA64_DEBUG_CMPXCHG is not set
 # CONFIG_IA64_DEBUG_IRQ is not set
-CONFIG_KDB=y
-# CONFIG_KDB_MODULES is not set
-# CONFIG_KDB_OFF is not set
-
-#
-#   Load all symbols for debugging is required for KDB
-#
-CONFIG_KALLSYMS=y
diff -Nru a/arch/ia64/dig/setup.c b/arch/ia64/dig/setup.c
--- a/arch/ia64/dig/setup.c	Mon Apr 22 15:32:19 2002
+++ b/arch/ia64/dig/setup.c	Mon Apr 22 15:32:19 2002
@@ -33,9 +33,6 @@
  * is sufficient (the IDE driver will autodetect the drive geometry).
  */
 char drive_info[4*16];
-extern int pcat_compat;
-
-unsigned char aux_device_present = 0xaa;	/* XXX remove this when legacy I/O is gone */
 
 void __init
 dig_setup (char **cmdline_p)
@@ -85,16 +82,4 @@
 void __init
 dig_irq_init (void)
 {
-	if (pcat_compat) {
-		/*
-		 * Disable the compatibility mode interrupts (8259 style), needs IN/OUT support
-		 * enabled.
-		 */
-		printk("%s: Disabling PC-AT compatible 8259 interrupts\n", __FUNCTION__);
-		outb(0xff, 0xA1);
-		outb(0xff, 0x21);
-	} else {
-		printk("%s: System doesn't have PC-AT compatible dual-8259 setup. "
-		       "Nothing to be done\n", __FUNCTION__);
-	}
 }
diff -Nru a/arch/ia64/hp/Config.in b/arch/ia64/hp/Config.in
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/arch/ia64/hp/Config.in	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,9 @@
+mainmenu_option next_comment
+comment 'HP Simulator drivers'
+
+bool 'Simulated Ethernet ' CONFIG_HP_SIMETH
+bool 'Simulated serial driver support' CONFIG_HP_SIMSERIAL
+if [ "$CONFIG_SCSI" != "n" ]; then
+  bool 'Simulated SCSI disk' CONFIG_HP_SIMSCSI
+fi
+endmenu
diff -Nru a/arch/ia64/hp/Makefile b/arch/ia64/hp/Makefile
--- a/arch/ia64/hp/Makefile	Mon Apr 22 15:32:19 2002
+++ b/arch/ia64/hp/Makefile	Mon Apr 22 15:32:19 2002
@@ -1,23 +1,15 @@
-#
-# ia64/platform/hp/Makefile
-#
-# Copyright (C) 2002 Hewlett-Packard Co.
-#	David Mosberger-Tang <davidm@hpl.hp.com>
-# Copyright (C) 1999 Silicon Graphics, Inc.
-# Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com)
-#
+# arch/ia64/hp/Makefile
+# Copyright (c) 2002 Matthew Wilcox for Hewlett Packard
 
-all: hp.a
+ALL_SUB_DIRS := sim zx1 common
 
-O_TARGET := hp.a
+O_TARGET := hp.o
 
-obj-y := hpsim_console.o hpsim_irq.o hpsim_setup.o
-obj-$(CONFIG_IA64_GENERIC) += hpsim_machvec.o
+subdir-$(CONFIG_IA64_GENERIC) += $(ALL_SUB_DIRS)
+subdir-$(CONFIG_IA64_HP_SIM) += sim
+subdir-$(CONFIG_IA64_HP_ZX1) += zx1 common
 
-obj-$(CONFIG_SIMETH)	+= simeth.o
-obj-$(CONFIG_SIM_SERIAL) += simserial.o
-obj-$(CONFIG_SCSI_SIM)	+= simscsi.o
-
-clean::
+SUB_DIRS := $(subdir-y)
+obj-y += $(join $(subdir-y),$(subdir-y:%=/%.o))
 
 include $(TOPDIR)/Rules.make
diff -Nru a/arch/ia64/hp/common/Makefile b/arch/ia64/hp/common/Makefile
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/arch/ia64/hp/common/Makefile	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,28 @@
+#
+# ia64/platform/hp/common/Makefile
+#
+# Copyright (C) 2002 Hewlett Packard
+# Copyright (C) Alex Williamson (alex_williamson@hp.com)
+#
+
+O_TARGET := common.o
+
+export-objs := sba_iommu.o
+
+obj-y := sba_iommu.o
+
+include $(TOPDIR)/Rules.make
+#
+# ia64/platform/hp/common/Makefile
+#
+# Copyright (C) 2002 Hewlett Packard
+# Copyright (C) Alex Williamson (alex_williamson@hp.com)
+#
+
+O_TARGET := common.o
+
+export-objs := sba_iommu.o
+
+obj-y := sba_iommu.o
+
+include $(TOPDIR)/Rules.make
diff -Nru a/arch/ia64/hp/common/sba_iommu.c b/arch/ia64/hp/common/sba_iommu.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/arch/ia64/hp/common/sba_iommu.c	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,3700 @@
+/*
+**  IA64 System Bus Adapter (SBA) I/O MMU manager
+**
+**	(c) Copyright 2002 Alex Williamson
+**	(c) Copyright 2002 Hewlett-Packard Company
+**
+**	Portions (c) 2000 Grant Grundler (from parisc I/O MMU code)
+**	Portions (c) 1999 Dave S. Miller (from sparc64 I/O MMU code)
+**
+**	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 module initializes the IOC (I/O Controller) found on HP
+** McKinley machines and their successors.
+**
+*/
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+
+#include <asm/delay.h>		/* ia64_get_itc() */
+#include <asm/io.h>
+#include <asm/page.h>		/* PAGE_OFFSET */
+#include <asm/efi.h>
+
+
+#define DRIVER_NAME "SBA"
+
+#ifndef CONFIG_IA64_HP_PROTO
+#define ALLOW_IOV_BYPASS
+#endif
+#define ENABLE_MARK_CLEAN
+/*
+** The number of debug flags is a clue - this code is fragile.
+*/
+#undef DEBUG_SBA_INIT
+#undef DEBUG_SBA_RUN
+#undef DEBUG_SBA_RUN_SG
+#undef DEBUG_SBA_RESOURCE
+#undef ASSERT_PDIR_SANITY
+#undef DEBUG_LARGE_SG_ENTRIES
+#undef DEBUG_BYPASS
+
+#define SBA_INLINE	__inline__
+/* #define SBA_INLINE */
+
+#ifdef DEBUG_SBA_INIT
+#define DBG_INIT(x...)	printk(x)
+#else
+#define DBG_INIT(x...)
+#endif
+
+#ifdef DEBUG_SBA_RUN
+#define DBG_RUN(x...)	printk(x)
+#else
+#define DBG_RUN(x...)
+#endif
+
+#ifdef DEBUG_SBA_RUN_SG
+#define DBG_RUN_SG(x...)	printk(x)
+#else
+#define DBG_RUN_SG(x...)
+#endif
+
+
+#ifdef DEBUG_SBA_RESOURCE
+#define DBG_RES(x...)	printk(x)
+#else
+#define DBG_RES(x...)
+#endif
+
+#ifdef DEBUG_BYPASS
+#define DBG_BYPASS(x...)	printk(x)
+#else
+#define DBG_BYPASS(x...)
+#endif
+
+#ifdef ASSERT_PDIR_SANITY
+#define ASSERT(expr) \
+        if(!(expr)) { \
+                printk( "\n" __FILE__ ":%d: Assertion " #expr " failed!\n",__LINE__); \
+                panic(#expr); \
+        }
+#else
+#define ASSERT(expr)
+#endif
+
+#define KB(x) ((x) * 1024)
+#define MB(x) (KB (KB (x)))
+#define GB(x) (MB (KB (x)))
+
+/*
+** The number of pdir entries to "free" before issueing
+** a read to PCOM register to flush out PCOM writes.
+** Interacts with allocation granularity (ie 4 or 8 entries
+** allocated and free'd/purged at a time might make this
+** less interesting).
+*/
+#define DELAYED_RESOURCE_CNT	16
+
+#define DEFAULT_DMA_HINT_REG	0
+
+#define ZX1_FUNC_ID_VALUE    ((PCI_DEVICE_ID_HP_ZX1_SBA << 16) | PCI_VENDOR_ID_HP)
+#define ZX1_MC_ID    ((PCI_DEVICE_ID_HP_ZX1_MC << 16) | PCI_VENDOR_ID_HP)
+
+#define SBA_FUNC_ID	0x0000	/* function id */
+#define SBA_FCLASS	0x0008	/* function class, bist, header, rev... */
+
+#define SBA_FUNC_SIZE	0x10000   /* SBA configuration function reg set */
+
+unsigned int __initdata zx1_func_offsets[] = {0x1000, 0x4000, 0x8000,
+                                              0x9000, 0xa000, -1};
+
+#define SBA_IOC_OFFSET	0x1000
+
+#define MAX_IOC		1	/* we only have 1 for now*/
+
+#define IOC_IBASE	0x300	/* IO TLB */
+#define IOC_IMASK	0x308
+#define IOC_PCOM	0x310
+#define IOC_TCNFG	0x318
+#define IOC_PDIR_BASE	0x320
+
+#define IOC_IOVA_SPACE_BASE	0x40000000 /* IOVA ranges start at 1GB */
+
+/*
+** IOC supports 4/8/16/64KB page sizes (see TCNFG register)
+** It's safer (avoid memory corruption) to keep DMA page mappings
+** equivalently sized to VM PAGE_SIZE.
+**
+** We really can't avoid generating a new mapping for each
+** page since the Virtual Coherence Index has to be generated
+** and updated for each page.
+**
+** IOVP_SIZE could only be greater than PAGE_SIZE if we are
+** confident the drivers really only touch the next physical
+** page iff that driver instance owns it.
+*/
+#define IOVP_SIZE	PAGE_SIZE
+#define IOVP_SHIFT	PAGE_SHIFT
+#define IOVP_MASK	PAGE_MASK
+
+struct ioc {
+	unsigned long	ioc_hpa;	/* I/O MMU base address */
+	char		*res_map;	/* resource map, bit == pdir entry */
+	u64		*pdir_base;	/* physical base address */
+	unsigned long	ibase;		/* pdir IOV Space base */
+	unsigned long	imask;		/* pdir IOV Space mask */
+
+	unsigned long	*res_hint;	/* next avail IOVP - circular search */
+	spinlock_t	res_lock;
+	unsigned long	hint_mask_pdir;	/* bits used for DMA hints */
+	unsigned int	res_bitshift;	/* from the RIGHT! */
+	unsigned int	res_size;	/* size of resource map in bytes */
+	unsigned int	hint_shift_pdir;
+	unsigned long	dma_mask;
+#if DELAYED_RESOURCE_CNT > 0
+	int saved_cnt;
+	struct sba_dma_pair {
+		dma_addr_t	iova;
+		size_t		size;
+	} saved[DELAYED_RESOURCE_CNT];
+#endif
+
+#ifdef CONFIG_PROC_FS
+#define SBA_SEARCH_SAMPLE	0x100
+	unsigned long avg_search[SBA_SEARCH_SAMPLE];
+	unsigned long avg_idx;	/* current index into avg_search */
+	unsigned long used_pages;
+	unsigned long msingle_calls;
+	unsigned long msingle_pages;
+	unsigned long msg_calls;
+	unsigned long msg_pages;
+	unsigned long usingle_calls;
+	unsigned long usingle_pages;
+	unsigned long usg_calls;
+	unsigned long usg_pages;
+#ifdef ALLOW_IOV_BYPASS
+	unsigned long msingle_bypass;
+	unsigned long usingle_bypass;
+	unsigned long msg_bypass;
+#endif
+#endif
+
+	/* STUFF We don't need in performance path */
+	unsigned int	pdir_size;	/* in bytes, determined by IOV Space size */
+};
+
+struct sba_device {
+	struct sba_device	*next;	/* list of SBA's in system */
+	const char 		*name;
+	unsigned long		sba_hpa; /* base address */
+	spinlock_t		sba_lock;
+	unsigned int		flags;  /* state/functionality enabled */
+	unsigned int		hw_rev;  /* HW revision of chip */
+
+	unsigned int		num_ioc;  /* number of on-board IOC's */
+	struct ioc		ioc[MAX_IOC];
+};
+
+
+static struct sba_device *sba_list;
+static int sba_count;
+static int reserve_sba_gart = 1;
+
+#define sba_sg_iova(sg) (sg->address)
+#define sba_sg_len(sg) (sg->length)
+#define sba_sg_buffer(sg) (sg->orig_address)
+
+/* REVISIT - fix me for multiple SBAs/IOCs */
+#define GET_IOC(dev) (sba_list->ioc)
+#define SBA_SET_AGP(sba_dev) (sba_dev->flags |= 0x1)
+#define SBA_GET_AGP(sba_dev) (sba_dev->flags & 0x1)
+
+/*
+** DMA_CHUNK_SIZE is used by the SCSI mid-layer to break up
+** (or rather not merge) DMA's into managable chunks.
+** On parisc, this is more of the software/tuning constraint
+** rather than the HW. I/O MMU allocation alogorithms can be
+** faster with smaller size is (to some degree).
+*/
+#define DMA_CHUNK_SIZE  (BITS_PER_LONG*PAGE_SIZE)
+
+/* Looks nice and keeps the compiler happy */
+#define SBA_DEV(d) ((struct sba_device *) (d))
+
+#define ROUNDUP(x,y) ((x + ((y)-1)) & ~((y)-1))
+
+/************************************
+** SBA register read and write support
+**
+** BE WARNED: register writes are posted.
+**  (ie follow writes which must reach HW with a read)
+**
+*/
+#define READ_REG(addr)       __raw_readq(addr)
+#define WRITE_REG(val, addr) __raw_writeq(val, addr)
+
+#ifdef DEBUG_SBA_INIT
+
+/**
+ * sba_dump_tlb - debugging only - print IOMMU operating parameters
+ * @hpa: base address of the IOMMU
+ *
+ * Print the size/location of the IO MMU PDIR.
+ */
+static void
+sba_dump_tlb(char *hpa)
+{
+	DBG_INIT("IO TLB at 0x%p\n", (void *)hpa);
+	DBG_INIT("IOC_IBASE    : %016lx\n", READ_REG(hpa+IOC_IBASE));
+	DBG_INIT("IOC_IMASK    : %016lx\n", READ_REG(hpa+IOC_IMASK));
+	DBG_INIT("IOC_TCNFG    : %016lx\n", READ_REG(hpa+IOC_TCNFG));
+	DBG_INIT("IOC_PDIR_BASE: %016lx\n", READ_REG(hpa+IOC_PDIR_BASE));
+	DBG_INIT("\n");
+}
+#endif
+
+
+#ifdef ASSERT_PDIR_SANITY
+
+/**
+ * sba_dump_pdir_entry - debugging only - print one IOMMU PDIR entry
+ * @ioc: IO MMU structure which owns the pdir we are interested in.
+ * @msg: text to print ont the output line.
+ * @pide: pdir index.
+ *
+ * Print one entry of the IO MMU PDIR in human readable form.
+ */
+static void
+sba_dump_pdir_entry(struct ioc *ioc, char *msg, uint pide)
+{
+	/* start printing from lowest pde in rval */
+	u64 *ptr = &(ioc->pdir_base[pide  & ~(BITS_PER_LONG - 1)]);
+	unsigned long *rptr = (unsigned long *) &(ioc->res_map[(pide >>3) & ~(sizeof(unsigned long) - 1)]);
+	uint rcnt;
+
+	/* printk(KERN_DEBUG "SBA: %s rp %p bit %d rval 0x%lx\n", */
+	printk("SBA: %s rp %p bit %d rval 0x%lx\n",
+		 msg, rptr, pide & (BITS_PER_LONG - 1), *rptr);
+
+	rcnt = 0;
+	while (rcnt < BITS_PER_LONG) {
+		printk("%s %2d %p %016Lx\n",
+			(rcnt == (pide & (BITS_PER_LONG - 1)))
+				? "    -->" : "       ",
+			rcnt, ptr, *ptr );
+		rcnt++;
+		ptr++;
+	}
+	printk("%s", msg);
+}
+
+
+/**
+ * sba_check_pdir - debugging only - consistency checker
+ * @ioc: IO MMU structure which owns the pdir we are interested in.
+ * @msg: text to print ont the output line.
+ *
+ * Verify the resource map and pdir state is consistent
+ */
+static int
+sba_check_pdir(struct ioc *ioc, char *msg)
+{
+	u64 *rptr_end = (u64 *) &(ioc->res_map[ioc->res_size]);
+	u64 *rptr = (u64 *) ioc->res_map;	/* resource map ptr */
+	u64 *pptr = ioc->pdir_base;	/* pdir ptr */
+	uint pide = 0;
+
+	while (rptr < rptr_end) {
+		u64 rval;
+		int rcnt; /* number of bits we might check */
+
+		rval = *rptr;
+		rcnt = 64;
+
+		while (rcnt) {
+			/* Get last byte and highest bit from that */
+			u32 pde = ((u32)((*pptr >> (63)) & 0x1));
+			if ((rval & 0x1) ^ pde)
+			{
+				/*
+				** BUMMER!  -- res_map != pdir --
+				** Dump rval and matching pdir entries
+				*/
+				sba_dump_pdir_entry(ioc, msg, pide);
+				return(1);
+			}
+			rcnt--;
+			rval >>= 1;	/* try the next bit */
+			pptr++;
+			pide++;
+		}
+		rptr++;	/* look at next word of res_map */
+	}
+	/* It'd be nice if we always got here :^) */
+	return 0;
+}
+
+
+/**
+ * sba_dump_sg - debugging only - print Scatter-Gather list
+ * @ioc: IO MMU structure which owns the pdir we are interested in.
+ * @startsg: head of the SG list
+ * @nents: number of entries in SG list
+ *
+ * print the SG list so we can verify it's correct by hand.
+ */
+static void
+sba_dump_sg( struct ioc *ioc, struct scatterlist *startsg, int nents)
+{
+	while (nents-- > 0) {
+		printk(" %d : %08lx/%05x %p\n",
+				nents,
+				(unsigned long) sba_sg_iova(startsg),
+				sba_sg_len(startsg),
+				sba_sg_buffer(startsg));
+		startsg++;
+	}
+}
+static void
+sba_check_sg( struct ioc *ioc, struct scatterlist *startsg, int nents)
+{
+	struct scatterlist *the_sg = startsg;
+	int the_nents = nents;
+
+	while (the_nents-- > 0) {
+		if (sba_sg_buffer(the_sg) == 0x0UL)
+			sba_dump_sg(NULL, startsg, nents);
+		the_sg++;
+	}
+}
+
+#endif /* ASSERT_PDIR_SANITY */
+
+
+
+
+/**************************************************************
+*
+*   I/O Pdir Resource Management
+*
+*   Bits set in the resource map are in use.
+*   Each bit can represent a number of pages.
+*   LSbs represent lower addresses (IOVA's).
+*
+***************************************************************/
+#define PAGES_PER_RANGE 1	/* could increase this to 4 or 8 if needed */
+
+/* Convert from IOVP to IOVA and vice versa. */
+#define SBA_IOVA(ioc,iovp,offset,hint_reg) ((ioc->ibase) | (iovp) | (offset) | ((hint_reg)<<(ioc->hint_shift_pdir)))
+#define SBA_IOVP(ioc,iova) (((iova) & ioc->hint_mask_pdir) & ~(ioc->ibase))
+
+/* FIXME : review these macros to verify correctness and usage */
+#define PDIR_INDEX(iovp)   ((iovp)>>IOVP_SHIFT)
+
+#define RESMAP_MASK(n)    ~(~0UL << (n))
+#define RESMAP_IDX_MASK   (sizeof(unsigned long) - 1)
+
+
+/**
+ * sba_search_bitmap - find free space in IO PDIR resource bitmap
+ * @ioc: IO MMU structure which owns the pdir we are interested in.
+ * @bits_wanted: number of entries we need.
+ *
+ * Find consecutive free bits in resource bitmap.
+ * Each bit represents one entry in the IO Pdir.
+ * Cool perf optimization: search for log2(size) bits at a time.
+ */
+static SBA_INLINE unsigned long
+sba_search_bitmap(struct ioc *ioc, unsigned long bits_wanted)
+{
+	unsigned long *res_ptr = ioc->res_hint;
+	unsigned long *res_end = (unsigned long *) &(ioc->res_map[ioc->res_size]);
+	unsigned long pide = ~0UL;
+
+	ASSERT(((unsigned long) ioc->res_hint & (sizeof(unsigned long) - 1UL)) == 0);
+	ASSERT(res_ptr < res_end);
+	if (bits_wanted > (BITS_PER_LONG/2)) {
+		/* Search word at a time - no mask needed */
+		for(; res_ptr < res_end; ++res_ptr) {
+			if (*res_ptr == 0) {
+				*res_ptr = RESMAP_MASK(bits_wanted);
+				pide = ((unsigned long)res_ptr - (unsigned long)ioc->res_map);
+				pide <<= 3;	/* convert to bit address */
+				break;
+			}
+		}
+		/* point to the next word on next pass */
+		res_ptr++;
+		ioc->res_bitshift = 0;
+	} else {
+		/*
+		** Search the resource bit map on well-aligned values.
+		** "o" is the alignment.
+		** We need the alignment to invalidate I/O TLB using
+		** SBA HW features in the unmap path.
+		*/
+		unsigned long o = 1 << get_order(bits_wanted << PAGE_SHIFT);
+		uint bitshiftcnt = ROUNDUP(ioc->res_bitshift, o);
+		unsigned long mask;
+
+		if (bitshiftcnt >= BITS_PER_LONG) {
+			bitshiftcnt = 0;
+			res_ptr++;
+		}
+		mask = RESMAP_MASK(bits_wanted) << bitshiftcnt;
+
+		DBG_RES("%s() o %ld %p", __FUNCTION__, o, res_ptr);
+		while(res_ptr < res_end)
+		{ 
+			DBG_RES("    %p %lx %lx\n", res_ptr, mask, *res_ptr);
+			ASSERT(0 != mask);
+			if(0 == ((*res_ptr) & mask)) {
+				*res_ptr |= mask;     /* mark resources busy! */
+				pide = ((unsigned long)res_ptr - (unsigned long)ioc->res_map);
+				pide <<= 3;	/* convert to bit address */
+				pide += bitshiftcnt;
+				break;
+			}
+			mask <<= o;
+			bitshiftcnt += o;
+			if (0 == mask) {
+				mask = RESMAP_MASK(bits_wanted);
+				bitshiftcnt=0;
+				res_ptr++;
+			}
+		}
+		/* look in the same word on the next pass */
+		ioc->res_bitshift = bitshiftcnt + bits_wanted;
+	}
+
+	/* wrapped ? */
+	if (res_end <= res_ptr) {
+		ioc->res_hint = (unsigned long *) ioc->res_map;
+		ioc->res_bitshift = 0;
+	} else {
+		ioc->res_hint = res_ptr;
+	}
+	return (pide);
+}
+
+
+/**
+ * sba_alloc_range - find free bits and mark them in IO PDIR resource bitmap
+ * @ioc: IO MMU structure which owns the pdir we are interested in.
+ * @size: number of bytes to create a mapping for
+ *
+ * Given a size, find consecutive unmarked and then mark those bits in the
+ * resource bit map.
+ */
+static int
+sba_alloc_range(struct ioc *ioc, size_t size)
+{
+	unsigned int pages_needed = size >> IOVP_SHIFT;
+#ifdef CONFIG_PROC_FS
+	unsigned long itc_start = ia64_get_itc();
+#endif
+	unsigned long pide;
+
+	ASSERT(pages_needed);
+	ASSERT((pages_needed * IOVP_SIZE) <= DMA_CHUNK_SIZE);
+	ASSERT(pages_needed <= BITS_PER_LONG);
+	ASSERT(0 == (size & ~IOVP_MASK));
+
+	/*
+	** "seek and ye shall find"...praying never hurts either...
+	*/
+
+	pide = sba_search_bitmap(ioc, pages_needed);
+	if (pide >= (ioc->res_size << 3)) {
+		pide = sba_search_bitmap(ioc, pages_needed);
+		if (pide >= (ioc->res_size << 3))
+			panic(__FILE__ ": I/O MMU @ %lx is out of mapping resources\n", ioc->ioc_hpa);
+	}
+
+#ifdef ASSERT_PDIR_SANITY
+	/* verify the first enable bit is clear */
+	if(0x00 != ((u8 *) ioc->pdir_base)[pide*sizeof(u64) + 7]) {
+		sba_dump_pdir_entry(ioc, "sba_search_bitmap() botched it?", pide);
+	}
+#endif
+
+	DBG_RES("%s(%x) %d -> %lx hint %x/%x\n",
+		__FUNCTION__, size, pages_needed, pide,
+		(uint) ((unsigned long) ioc->res_hint - (unsigned long) ioc->res_map),
+		ioc->res_bitshift );
+
+#ifdef CONFIG_PROC_FS
+	{
+		unsigned long itc_end = ia64_get_itc();
+		unsigned long tmp = itc_end - itc_start;
+		/* check for roll over */
+		itc_start = (itc_end < itc_start) ?  -(tmp) : (tmp);
+	}
+	ioc->avg_search[ioc->avg_idx++] = itc_start;
+	ioc->avg_idx &= SBA_SEARCH_SAMPLE - 1;
+
+	ioc->used_pages += pages_needed;
+#endif
+
+	return (pide);
+}
+
+
+/**
+ * sba_free_range - unmark bits in IO PDIR resource bitmap
+ * @ioc: IO MMU structure which owns the pdir we are interested in.
+ * @iova: IO virtual address which was previously allocated.
+ * @size: number of bytes to create a mapping for
+ *
+ * clear bits in the ioc's resource map
+ */
+static SBA_INLINE void
+sba_free_range(struct ioc *ioc, dma_addr_t iova, size_t size)
+{
+	unsigned long iovp = SBA_IOVP(ioc, iova);
+	unsigned int pide = PDIR_INDEX(iovp);
+	unsigned int ridx = pide >> 3;	/* convert bit to byte address */
+	unsigned long *res_ptr = (unsigned long *) &((ioc)->res_map[ridx & ~RESMAP_IDX_MASK]);
+
+	int bits_not_wanted = size >> IOVP_SHIFT;
+
+	/* 3-bits "bit" address plus 2 (or 3) bits for "byte" == bit in word */
+	unsigned long m = RESMAP_MASK(bits_not_wanted) << (pide & (BITS_PER_LONG - 1));
+
+	DBG_RES("%s( ,%x,%x) %x/%lx %x %p %lx\n",
+		__FUNCTION__, (uint) iova, size,
+		bits_not_wanted, m, pide, res_ptr, *res_ptr);
+
+#ifdef CONFIG_PROC_FS
+	ioc->used_pages -= bits_not_wanted;
+#endif
+
+	ASSERT(m != 0);
+	ASSERT(bits_not_wanted);
+	ASSERT((bits_not_wanted * IOVP_SIZE) <= DMA_CHUNK_SIZE);
+	ASSERT(bits_not_wanted <= BITS_PER_LONG);
+	ASSERT((*res_ptr & m) == m); /* verify same bits are set */
+	*res_ptr &= ~m;
+}
+
+
+/**************************************************************
+*
+*   "Dynamic DMA Mapping" support (aka "Coherent I/O")
+*
+***************************************************************/
+
+#define SBA_DMA_HINT(ioc, val) ((val) << (ioc)->hint_shift_pdir)
+
+
+/**
+ * sba_io_pdir_entry - fill in one IO PDIR entry
+ * @pdir_ptr:  pointer to IO PDIR entry
+ * @vba: Virtual CPU address of buffer to map
+ *
+ * SBA Mapping Routine
+ *
+ * Given a virtual address (vba, arg1) sba_io_pdir_entry()
+ * loads the I/O PDIR entry pointed to by pdir_ptr (arg0).
+ * Each IO Pdir entry consists of 8 bytes as shown below
+ * (LSB == bit 0):
+ *
+ *  63                    40                                 11    7        0
+ * +-+---------------------+----------------------------------+----+--------+
+ * |V|        U            |            PPN[39:12]            | U  |   FF   |
+ * +-+---------------------+----------------------------------+----+--------+
+ *
+ *  V  == Valid Bit
+ *  U  == Unused
+ * PPN == Physical Page Number
+ *
+ * The physical address fields are filled with the results of virt_to_phys()
+ * on the vba.
+ */
+
+#if 1
+#define sba_io_pdir_entry(pdir_ptr, vba) *pdir_ptr = ((vba & ~0xE000000000000FFFULL) | 0x80000000000000FFULL)
+#else
+void SBA_INLINE
+sba_io_pdir_entry(u64 *pdir_ptr, unsigned long vba)
+{
+	*pdir_ptr = ((vba & ~0xE000000000000FFFULL) | 0x80000000000000FFULL);
+}
+#endif
+
+#ifdef ENABLE_MARK_CLEAN
+/**
+ * Since DMA is i-cache coherent, any (complete) pages that were written via
+ * DMA can be marked as "clean" so that update_mmu_cache() doesn't have to
+ * flush them when they get mapped into an executable vm-area.
+ */
+static void
+mark_clean (void *addr, size_t size)
+{
+	unsigned long pg_addr, end;
+
+	pg_addr = PAGE_ALIGN((unsigned long) addr);
+	end = (unsigned long) addr + size;
+	while (pg_addr + PAGE_SIZE <= end) {
+		struct page *page = virt_to_page(pg_addr);
+		set_bit(PG_arch_1, &page->flags);
+		pg_addr += PAGE_SIZE;
+	}
+}
+#endif
+
+/**
+ * sba_mark_invalid - invalidate one or more IO PDIR entries
+ * @ioc: IO MMU structure which owns the pdir we are interested in.
+ * @iova:  IO Virtual Address mapped earlier
+ * @byte_cnt:  number of bytes this mapping covers.
+ *
+ * Marking the IO PDIR entry(ies) as Invalid and invalidate
+ * corresponding IO TLB entry. The PCOM (Purge Command Register)
+ * is to purge stale entries in the IO TLB when unmapping entries.
+ *
+ * The PCOM register supports purging of multiple pages, with a minium
+ * of 1 page and a maximum of 2GB. Hardware requires the address be
+ * aligned to the size of the range being purged. The size of the range
+ * must be a power of 2. The "Cool perf optimization" in the
+ * allocation routine helps keep that true.
+ */
+static SBA_INLINE void
+sba_mark_invalid(struct ioc *ioc, dma_addr_t iova, size_t byte_cnt)
+{
+	u32 iovp = (u32) SBA_IOVP(ioc,iova);
+
+	int off = PDIR_INDEX(iovp);
+
+	/* Must be non-zero and rounded up */
+	ASSERT(byte_cnt > 0);
+	ASSERT(0 == (byte_cnt & ~IOVP_MASK));
+
+#ifdef ASSERT_PDIR_SANITY
+	/* Assert first pdir entry is set */
+	if (!(ioc->pdir_base[off] >> 60)) {
+		sba_dump_pdir_entry(ioc,"sba_mark_invalid()", PDIR_INDEX(iovp));
+	}
+#endif
+
+	if (byte_cnt <= IOVP_SIZE)
+	{
+		ASSERT(off < ioc->pdir_size);
+
+		iovp |= IOVP_SHIFT;     /* set "size" field for PCOM */
+
+		/*
+		** clear I/O PDIR entry "valid" bit
+		** Do NOT clear the rest - save it for debugging.
+		** We should only clear bits that have previously
+		** been enabled.
+		*/
+		ioc->pdir_base[off] &= ~(0x80000000000000FFULL);
+	} else {
+		u32 t = get_order(byte_cnt) + PAGE_SHIFT;
+
+		iovp |= t;
+		ASSERT(t <= 31);   /* 2GB! Max value of "size" field */
+
+		do {
+			/* verify this pdir entry is enabled */
+			ASSERT(ioc->pdir_base[off]  >> 63);
+			/* clear I/O Pdir entry "valid" bit first */
+			ioc->pdir_base[off] &= ~(0x80000000000000FFULL);
+			off++;
+			byte_cnt -= IOVP_SIZE;
+		} while (byte_cnt > 0);
+	}
+
+	WRITE_REG(iovp, ioc->ioc_hpa+IOC_PCOM);
+}
+
+/**
+ * sba_map_single - map one buffer and return IOVA for DMA
+ * @dev: instance of PCI owned by the driver that's asking.
+ * @addr:  driver buffer to map.
+ * @size:  number of bytes to map in driver buffer.
+ * @direction:  R/W or both.
+ *
+ * See Documentation/DMA-mapping.txt
+ */
+dma_addr_t
+sba_map_single(struct pci_dev *dev, void *addr, size_t size, int direction)
+{
+	struct ioc *ioc;
+	unsigned long flags; 
+	dma_addr_t iovp;
+	dma_addr_t offset;
+	u64 *pdir_start;
+	int pide;
+#ifdef ALLOW_IOV_BYPASS
+	unsigned long pci_addr = virt_to_phys(addr);
+#endif
+
+	ioc = GET_IOC(dev);
+	ASSERT(ioc);
+
+#ifdef ALLOW_IOV_BYPASS
+	/*
+ 	** Check if the PCI device can DMA to ptr... if so, just return ptr
+ 	*/
+	if ((pci_addr & ~dev->dma_mask) == 0) {
+		/*
+ 		** Device is bit capable of DMA'ing to the buffer...
+		** just return the PCI address of ptr
+ 		*/
+#ifdef CONFIG_PROC_FS
+		spin_lock_irqsave(&ioc->res_lock, flags);
+		ioc->msingle_bypass++;
+		spin_unlock_irqrestore(&ioc->res_lock, flags);
+#endif
+		DBG_BYPASS("sba_map_single() bypass mask/addr: 0x%lx/0x%lx\n",
+		           dev->dma_mask, pci_addr);
+		return pci_addr;
+	}
+#endif
+
+	ASSERT(size > 0);
+	ASSERT(size <= DMA_CHUNK_SIZE);
+
+	/* save offset bits */
+	offset = ((dma_addr_t) (long) addr) & ~IOVP_MASK;
+
+	/* round up to nearest IOVP_SIZE */
+	size = (size + offset + ~IOVP_MASK) & IOVP_MASK;
+
+	spin_lock_irqsave(&ioc->res_lock, flags);
+#ifdef ASSERT_PDIR_SANITY
+	if (sba_check_pdir(ioc,"Check before sba_map_single()"))
+		panic("Sanity check failed");
+#endif
+
+#ifdef CONFIG_PROC_FS
+	ioc->msingle_calls++;
+	ioc->msingle_pages += size >> IOVP_SHIFT;
+#endif
+	pide = sba_alloc_range(ioc, size);
+	iovp = (dma_addr_t) pide << IOVP_SHIFT;
+
+	DBG_RUN("%s() 0x%p -> 0x%lx\n",
+		__FUNCTION__, addr, (long) iovp | offset);
+
+	pdir_start = &(ioc->pdir_base[pide]);
+
+	while (size > 0) {
+		ASSERT(((u8 *)pdir_start)[7] == 0); /* verify availability */
+		sba_io_pdir_entry(pdir_start, (unsigned long) addr);
+
+		DBG_RUN("     pdir 0x%p %lx\n", pdir_start, *pdir_start);
+
+		addr += IOVP_SIZE;
+		size -= IOVP_SIZE;
+		pdir_start++;
+	}
+	/* form complete address */
+#ifdef ASSERT_PDIR_SANITY
+	sba_check_pdir(ioc,"Check after sba_map_single()");
+#endif
+	spin_unlock_irqrestore(&ioc->res_lock, flags);
+	return SBA_IOVA(ioc, iovp, offset, DEFAULT_DMA_HINT_REG);
+}
+
+/**
+ * sba_unmap_single - unmap one IOVA and free resources
+ * @dev: instance of PCI owned by the driver that's asking.
+ * @iova:  IOVA of driver buffer previously mapped.
+ * @size:  number of bytes mapped in driver buffer.
+ * @direction:  R/W or both.
+ *
+ * See Documentation/DMA-mapping.txt
+ */
+void sba_unmap_single(struct pci_dev *dev, dma_addr_t iova, size_t size,
+		int direction)
+{
+	struct ioc *ioc;
+#if DELAYED_RESOURCE_CNT > 0
+	struct sba_dma_pair *d;
+#endif
+	unsigned long flags; 
+	dma_addr_t offset;
+
+	ioc = GET_IOC(dev);
+	ASSERT(ioc);
+
+#ifdef ALLOW_IOV_BYPASS
+	if ((iova & ioc->imask) != ioc->ibase) {
+		/*
+		** Address does not fall w/in IOVA, must be bypassing
+		*/
+#ifdef CONFIG_PROC_FS
+		spin_lock_irqsave(&ioc->res_lock, flags);
+		ioc->usingle_bypass++;
+		spin_unlock_irqrestore(&ioc->res_lock, flags);
+#endif
+		DBG_BYPASS("sba_unmap_single() bypass addr: 0x%lx\n", iova);
+
+#ifdef ENABLE_MARK_CLEAN
+		if (direction == PCI_DMA_FROMDEVICE) {
+			mark_clean(phys_to_virt(iova), size);
+		}
+#endif
+		return;
+	}
+#endif
+	offset = iova & ~IOVP_MASK;
+
+	DBG_RUN("%s() iovp 0x%lx/%x\n",
+		__FUNCTION__, (long) iova, size);
+
+	iova ^= offset;        /* clear offset bits */
+	size += offset;
+	size = ROUNDUP(size, IOVP_SIZE);
+
+	spin_lock_irqsave(&ioc->res_lock, flags);
+#ifdef CONFIG_PROC_FS
+	ioc->usingle_calls++;
+	ioc->usingle_pages += size >> IOVP_SHIFT;
+#endif
+
+#if DELAYED_RESOURCE_CNT > 0
+	d = &(ioc->saved[ioc->saved_cnt]);
+	d->iova = iova;
+	d->size = size;
+	if (++(ioc->saved_cnt) >= DELAYED_RESOURCE_CNT) {
+		int cnt = ioc->saved_cnt;
+		while (cnt--) {
+			sba_mark_invalid(ioc, d->iova, d->size);
+			sba_free_range(ioc, d->iova, d->size);
+			d--;
+		}
+		ioc->saved_cnt = 0;
+		READ_REG(ioc->ioc_hpa+IOC_PCOM);	/* flush purges */
+	}
+#else /* DELAYED_RESOURCE_CNT == 0 */
+	sba_mark_invalid(ioc, iova, size);
+	sba_free_range(ioc, iova, size);
+	READ_REG(ioc->ioc_hpa+IOC_PCOM);	/* flush purges */
+#endif /* DELAYED_RESOURCE_CNT == 0 */
+#ifdef ENABLE_MARK_CLEAN
+	if (direction == PCI_DMA_FROMDEVICE) {
+		u32 iovp = (u32) SBA_IOVP(ioc,iova);
+		int off = PDIR_INDEX(iovp);
+		void *addr;
+
+		if (size <= IOVP_SIZE) {
+			addr = phys_to_virt(ioc->pdir_base[off] &
+					    ~0xE000000000000FFFULL);
+			mark_clean(addr, size);
+		} else {
+			size_t byte_cnt = size;
+
+			do {
+				addr = phys_to_virt(ioc->pdir_base[off] &
+				                    ~0xE000000000000FFFULL);
+				mark_clean(addr, min(byte_cnt, IOVP_SIZE));
+				off++;
+				byte_cnt -= IOVP_SIZE;
+
+			   } while (byte_cnt > 0);
+		}
+	}
+#endif
+	spin_unlock_irqrestore(&ioc->res_lock, flags);
+
+	/* XXX REVISIT for 2.5 Linux - need syncdma for zero-copy support.
+	** For Astro based systems this isn't a big deal WRT performance.
+	** As long as 2.4 kernels copyin/copyout data from/to userspace,
+	** we don't need the syncdma. The issue here is I/O MMU cachelines
+	** are *not* coherent in all cases.  May be hwrev dependent.
+	** Need to investigate more.
+	asm volatile("syncdma");	
+	*/
+}
+
+
+/**
+ * sba_alloc_consistent - allocate/map shared mem for DMA
+ * @hwdev: instance of PCI owned by the driver that's asking.
+ * @size:  number of bytes mapped in driver buffer.
+ * @dma_handle:  IOVA of new buffer.
+ *
+ * See Documentation/DMA-mapping.txt
+ */
+void *
+sba_alloc_consistent(struct pci_dev *hwdev, size_t size, dma_addr_t *dma_handle)
+{
+	void *ret;
+
+	if (!hwdev) {
+		/* only support PCI */
+		*dma_handle = 0;
+		return 0;
+	}
+
+        ret = (void *) __get_free_pages(GFP_ATOMIC, get_order(size));
+
+	if (ret) {
+		memset(ret, 0, size);
+		*dma_handle = sba_map_single(hwdev, ret, size, 0);
+	}
+
+	return ret;
+}
+
+
+/**
+ * sba_free_consistent - free/unmap shared mem for DMA
+ * @hwdev: instance of PCI owned by the driver that's asking.
+ * @size:  number of bytes mapped in driver buffer.
+ * @vaddr:  virtual address IOVA of "consistent" buffer.
+ * @dma_handler:  IO virtual address of "consistent" buffer.
+ *
+ * See Documentation/DMA-mapping.txt
+ */
+void sba_free_consistent(struct pci_dev *hwdev, size_t size, void *vaddr,
+		dma_addr_t dma_handle)
+{
+	sba_unmap_single(hwdev, dma_handle, size, 0);
+	free_pages((unsigned long) vaddr, get_order(size));
+}
+
+
+/*
+** Since 0 is a valid pdir_base index value, can't use that
+** to determine if a value is valid or not. Use a flag to indicate
+** the SG list entry contains a valid pdir index.
+*/
+#define PIDE_FLAG 0x1UL
+
+#ifdef DEBUG_LARGE_SG_ENTRIES
+int dump_run_sg = 0;
+#endif
+
+
+/**
+ * sba_fill_pdir - write allocated SG entries into IO PDIR
+ * @ioc: IO MMU structure which owns the pdir we are interested in.
+ * @startsg:  list of IOVA/size pairs
+ * @nents: number of entries in startsg list
+ *
+ * Take preprocessed SG list and write corresponding entries
+ * in the IO PDIR.
+ */
+
+static SBA_INLINE int
+sba_fill_pdir(
+	struct ioc *ioc,
+	struct scatterlist *startsg,
+	int nents)
+{
+	struct scatterlist *dma_sg = startsg;	/* pointer to current DMA */
+	int n_mappings = 0;
+	u64 *pdirp = 0;
+	unsigned long dma_offset = 0;
+
+	dma_sg--;
+	while (nents-- > 0) {
+		int     cnt = sba_sg_len(startsg);
+		sba_sg_len(startsg) = 0;
+
+#ifdef DEBUG_LARGE_SG_ENTRIES
+		if (dump_run_sg)
+			printk(" %2d : %08lx/%05x %p\n",
+				nents,
+				(unsigned long) sba_sg_iova(startsg), cnt,
+				sba_sg_buffer(startsg)
+		);
+#else
+		DBG_RUN_SG(" %d : %08lx/%05x %p\n",
+				nents,
+				(unsigned long) sba_sg_iova(startsg), cnt,
+				sba_sg_buffer(startsg)
+		);
+#endif
+		/*
+		** Look for the start of a new DMA stream
+		*/
+		if ((u64)sba_sg_iova(startsg) & PIDE_FLAG) {
+			u32 pide = (u64)sba_sg_iova(startsg) & ~PIDE_FLAG;
+			dma_offset = (unsigned long) pide & ~IOVP_MASK;
+			sba_sg_iova(startsg) = 0;
+			dma_sg++;
+			sba_sg_iova(dma_sg) = (char *)(pide | ioc->ibase);
+			pdirp = &(ioc->pdir_base[pide >> IOVP_SHIFT]);
+			n_mappings++;
+		}
+
+		/*
+		** Look for a VCONTIG chunk
+		*/
+		if (cnt) {
+			unsigned long vaddr = (unsigned long) sba_sg_buffer(startsg);
+			ASSERT(pdirp);
+
+			/* Since multiple Vcontig blocks could make up
+			** one DMA stream, *add* cnt to dma_len.
+			*/
+			sba_sg_len(dma_sg) += cnt;
+			cnt += dma_offset;
+			dma_offset=0;	/* only want offset on first chunk */
+			cnt = ROUNDUP(cnt, IOVP_SIZE);
+#ifdef CONFIG_PROC_FS
+			ioc->msg_pages += cnt >> IOVP_SHIFT;
+#endif
+			do {
+				sba_io_pdir_entry(pdirp, vaddr);
+				vaddr += IOVP_SIZE;
+				cnt -= IOVP_SIZE;
+				pdirp++;
+			} while (cnt > 0);
+		}
+		startsg++;
+	}
+#ifdef DEBUG_LARGE_SG_ENTRIES
+	dump_run_sg = 0;
+#endif
+	return(n_mappings);
+}
+
+
+/*
+** Two address ranges are DMA contiguous *iff* "end of prev" and
+** "start of next" are both on a page boundry.
+**
+** (shift left is a quick trick to mask off upper bits)
+*/
+#define DMA_CONTIG(__X, __Y) \
+	(((((unsigned long) __X) | ((unsigned long) __Y)) << (BITS_PER_LONG - PAGE_SHIFT)) == 0UL)
+
+
+/**
+ * sba_coalesce_chunks - preprocess the SG list
+ * @ioc: IO MMU structure which owns the pdir we are interested in.
+ * @startsg:  list of IOVA/size pairs
+ * @nents: number of entries in startsg list
+ *
+ * First pass is to walk the SG list and determine where the breaks are
+ * in the DMA stream. Allocates PDIR entries but does not fill them.
+ * Returns the number of DMA chunks.
+ *
+ * Doing the fill seperate from the coalescing/allocation keeps the
+ * code simpler. Future enhancement could make one pass through
+ * the sglist do both.
+ */
+static SBA_INLINE int
+sba_coalesce_chunks( struct ioc *ioc,
+	struct scatterlist *startsg,
+	int nents)
+{
+	struct scatterlist *vcontig_sg;    /* VCONTIG chunk head */
+	unsigned long vcontig_len;         /* len of VCONTIG chunk */
+	unsigned long vcontig_end;
+	struct scatterlist *dma_sg;        /* next DMA stream head */
+	unsigned long dma_offset, dma_len; /* start/len of DMA stream */
+	int n_mappings = 0;
+
+	while (nents > 0) {
+		unsigned long vaddr = (unsigned long) (startsg->address); 
+
+		/*
+		** Prepare for first/next DMA stream
+		*/
+		dma_sg = vcontig_sg = startsg;
+		dma_len = vcontig_len = vcontig_end = sba_sg_len(startsg);
+		vcontig_end +=  vaddr;
+		dma_offset = vaddr & ~IOVP_MASK;
+
+		/* PARANOID: clear entries */
+		sba_sg_buffer(startsg) = sba_sg_iova(startsg);
+		sba_sg_iova(startsg) = 0;
+		sba_sg_len(startsg) = 0;
+
+		/*
+		** This loop terminates one iteration "early" since
+		** it's always looking one "ahead".
+		*/
+		while (--nents > 0) {
+			unsigned long vaddr;	/* tmp */
+
+			startsg++;
+
+			/* catch brokenness in SCSI layer */
+			ASSERT(startsg->length <= DMA_CHUNK_SIZE);
+
+			/*
+			** First make sure current dma stream won't
+			** exceed DMA_CHUNK_SIZE if we coalesce the
+			** next entry.
+			*/
+			if (((dma_len + dma_offset + startsg->length + ~IOVP_MASK) & IOVP_MASK) > DMA_CHUNK_SIZE)
+				break;
+
+			/*
+			** Then look for virtually contiguous blocks.
+			**
+			** append the next transaction?
+			*/
+			vaddr = (unsigned long) sba_sg_iova(startsg);
+			if  (vcontig_end == vaddr)
+			{
+				vcontig_len += sba_sg_len(startsg);
+				vcontig_end += sba_sg_len(startsg);
+				dma_len     += sba_sg_len(startsg);
+				sba_sg_buffer(startsg) = (char *)vaddr;
+				sba_sg_iova(startsg) = 0;
+				sba_sg_len(startsg) = 0;
+				continue;
+			}
+
+#ifdef DEBUG_LARGE_SG_ENTRIES
+			dump_run_sg = (vcontig_len > IOVP_SIZE);
+#endif
+
+			/*
+			** Not virtually contigous.
+			** Terminate prev chunk.
+			** Start a new chunk.
+			**
+			** Once we start a new VCONTIG chunk, dma_offset
+			** can't change. And we need the offset from the first
+			** chunk - not the last one. Ergo Successive chunks
+			** must start on page boundaries and dove tail
+			** with it's predecessor.
+			*/
+			sba_sg_len(vcontig_sg) = vcontig_len;
+
+			vcontig_sg = startsg;
+			vcontig_len = sba_sg_len(startsg);
+
+			/*
+			** 3) do the entries end/start on page boundaries?
+			**    Don't update vcontig_end until we've checked.
+			*/
+			if (DMA_CONTIG(vcontig_end, vaddr))
+			{
+				vcontig_end = vcontig_len + vaddr;
+				dma_len += vcontig_len;
+				sba_sg_buffer(startsg) = (char *)vaddr;
+				sba_sg_iova(startsg) = 0;
+				continue;
+			} else {
+				break;
+			}
+		}
+
+		/*
+		** End of DMA Stream
+		** Terminate last VCONTIG block.
+		** Allocate space for DMA stream.
+		*/
+		sba_sg_len(vcontig_sg) = vcontig_len;
+		dma_len = (dma_len + dma_offset + ~IOVP_MASK) & IOVP_MASK;
+		ASSERT(dma_len <= DMA_CHUNK_SIZE);
+		sba_sg_iova(dma_sg) = (char *) (PIDE_FLAG 
+			| (sba_alloc_range(ioc, dma_len) << IOVP_SHIFT)
+			| dma_offset);
+		n_mappings++;
+	}
+
+	return n_mappings;
+}
+
+
+/**
+ * sba_map_sg - map Scatter/Gather list
+ * @dev: instance of PCI owned by the driver that's asking.
+ * @sglist:  array of buffer/length pairs
+ * @nents:  number of entries in list
+ * @direction:  R/W or both.
+ *
+ * See Documentation/DMA-mapping.txt
+ */
+int sba_map_sg(struct pci_dev *dev, struct scatterlist *sglist, int nents,
+		int direction)
+{
+	struct ioc *ioc;
+	int coalesced, filled = 0;
+	unsigned long flags;
+#ifdef ALLOW_IOV_BYPASS
+	struct scatterlist *sg;
+#endif
+
+	DBG_RUN_SG("%s() START %d entries\n", __FUNCTION__, nents);
+	ioc = GET_IOC(dev);
+	ASSERT(ioc);
+
+#ifdef ALLOW_IOV_BYPASS
+	if (dev->dma_mask >= ioc->dma_mask) {
+		for (sg = sglist ; filled < nents ; filled++, sg++){
+			sba_sg_buffer(sg) = sba_sg_iova(sg);
+			sba_sg_iova(sg) = (char *)virt_to_phys(sba_sg_buffer(sg));
+		}
+#ifdef CONFIG_PROC_FS
+		spin_lock_irqsave(&ioc->res_lock, flags);
+		ioc->msg_bypass++;
+		spin_unlock_irqrestore(&ioc->res_lock, flags);
+#endif
+		return filled;
+	}
+#endif
+	/* Fast path single entry scatterlists. */
+	if (nents == 1) {
+		sba_sg_buffer(sglist) = sba_sg_iova(sglist);
+		sba_sg_iova(sglist) = (char *)sba_map_single(dev,
+						sba_sg_buffer(sglist),
+						sba_sg_len(sglist), direction);
+#ifdef CONFIG_PROC_FS
+		/*
+		** Should probably do some stats counting, but trying to
+		** be precise quickly starts wasting CPU time.
+		*/
+#endif
+		return 1;
+	}
+
+	spin_lock_irqsave(&ioc->res_lock, flags);
+
+#ifdef ASSERT_PDIR_SANITY
+	if (sba_check_pdir(ioc,"Check before sba_map_sg()"))
+	{
+		sba_dump_sg(ioc, sglist, nents);
+		panic("Check before sba_map_sg()");
+	}
+#endif
+
+#ifdef CONFIG_PROC_FS
+	ioc->msg_calls++;
+#endif
+
+	/*
+	** First coalesce the chunks and allocate I/O pdir space
+	**
+	** If this is one DMA stream, we can properly map using the
+	** correct virtual address associated with each DMA page.
+	** w/o this association, we wouldn't have coherent DMA!
+	** Access to the virtual address is what forces a two pass algorithm.
+	*/
+	coalesced = sba_coalesce_chunks(ioc, sglist, nents);
+ 
+	/*
+	** Program the I/O Pdir
+	**
+	** map the virtual addresses to the I/O Pdir
+	** o dma_address will contain the pdir index
+	** o dma_len will contain the number of bytes to map 
+	** o address contains the virtual address.
+	*/
+	filled = sba_fill_pdir(ioc, sglist, nents);
+
+#ifdef ASSERT_PDIR_SANITY
+	if (sba_check_pdir(ioc,"Check after sba_map_sg()"))
+	{
+		sba_dump_sg(ioc, sglist, nents);
+		panic("Check after sba_map_sg()\n");
+	}
+#endif
+
+	spin_unlock_irqrestore(&ioc->res_lock, flags);
+
+	ASSERT(coalesced == filled);
+	DBG_RUN_SG("%s() DONE %d mappings\n", __FUNCTION__, filled);
+
+	return filled;
+}
+
+
+/**
+ * sba_unmap_sg - unmap Scatter/Gather list
+ * @dev: instance of PCI owned by the driver that's asking.
+ * @sglist:  array of buffer/length pairs
+ * @nents:  number of entries in list
+ * @direction:  R/W or both.
+ *
+ * See Documentation/DMA-mapping.txt
+ */
+void sba_unmap_sg(struct pci_dev *dev, struct scatterlist *sglist, int nents,
+		int direction)
+{
+	struct ioc *ioc;
+#ifdef ASSERT_PDIR_SANITY
+	unsigned long flags;
+#endif
+
+	DBG_RUN_SG("%s() START %d entries,  %p,%x\n",
+		__FUNCTION__, nents, sba_sg_buffer(sglist), sglist->length);
+
+	ioc = GET_IOC(dev);
+	ASSERT(ioc);
+
+#ifdef CONFIG_PROC_FS
+	ioc->usg_calls++;
+#endif
+
+#ifdef ASSERT_PDIR_SANITY
+	spin_lock_irqsave(&ioc->res_lock, flags);
+	sba_check_pdir(ioc,"Check before sba_unmap_sg()");
+	spin_unlock_irqrestore(&ioc->res_lock, flags);
+#endif
+
+	while (sba_sg_len(sglist) && nents--) {
+
+		sba_unmap_single(dev, (dma_addr_t)sba_sg_iova(sglist),
+		                 sba_sg_len(sglist), direction);
+#ifdef CONFIG_PROC_FS
+		/*
+		** This leaves inconsistent data in the stats, but we can't
+		** tell which sg lists were mapped by map_single and which
+		** were coalesced to a single entry.  The stats are fun,
+		** but speed is more important.
+		*/
+		ioc->usg_pages += (((u64)sba_sg_iova(sglist) & ~IOVP_MASK) + sba_sg_len(sglist) + IOVP_SIZE - 1) >> PAGE_SHIFT;
+#endif
+		++sglist;
+	}
+
+	DBG_RUN_SG("%s() DONE (nents %d)\n", __FUNCTION__,  nents);
+
+#ifdef ASSERT_PDIR_SANITY
+	spin_lock_irqsave(&ioc->res_lock, flags);
+	sba_check_pdir(ioc,"Check after sba_unmap_sg()");
+	spin_unlock_irqrestore(&ioc->res_lock, flags);
+#endif
+
+}
+
+unsigned long
+sba_dma_address (struct scatterlist *sg)
+{
+	return ((unsigned long)sba_sg_iova(sg));
+}
+
+/**************************************************************
+*
+*   Initialization and claim
+*
+***************************************************************/
+
+
+static void
+sba_ioc_init(struct sba_device *sba_dev, struct ioc *ioc, int ioc_num)
+{
+	u32 iova_space_size, iova_space_mask;
+	void * pdir_base;
+	int pdir_size, iov_order, tcnfg;
+
+	/*
+	** Firmware programs the maximum IOV space size into the imask reg
+	*/
+	iova_space_size = ~(READ_REG(ioc->ioc_hpa + IOC_IMASK) & 0xFFFFFFFFUL) + 1;
+#ifdef CONFIG_IA64_HP_PROTO
+	if (!iova_space_size)
+		iova_space_size = GB(1);
+#endif
+
+	/*
+	** iov_order is always based on a 1GB IOVA space since we want to
+	** turn on the other half for AGP GART.
+	*/
+	iov_order = get_order(iova_space_size >> (IOVP_SHIFT-PAGE_SHIFT));
+	ioc->pdir_size = pdir_size = (iova_space_size/IOVP_SIZE) * sizeof(u64);
+
+	DBG_INIT("%s() hpa 0x%lx IOV %dMB (%d bits) PDIR size 0x%0x\n",
+		__FUNCTION__, ioc->ioc_hpa, iova_space_size>>20,
+		iov_order + PAGE_SHIFT, ioc->pdir_size);
+
+	/* FIXME : DMA HINTs not used */
+	ioc->hint_shift_pdir = iov_order + PAGE_SHIFT;
+	ioc->hint_mask_pdir = ~(0x3 << (iov_order + PAGE_SHIFT));
+
+	ioc->pdir_base =
+	pdir_base = (void *) __get_free_pages(GFP_KERNEL, get_order(pdir_size));
+	if (NULL == pdir_base)
+	{
+		panic(__FILE__ ":%s() could not allocate I/O Page Table\n", __FUNCTION__);
+	}
+	memset(pdir_base, 0, pdir_size);
+
+	DBG_INIT("%s() pdir %p size %x hint_shift_pdir %x hint_mask_pdir %lx\n",
+		__FUNCTION__, pdir_base, pdir_size,
+		ioc->hint_shift_pdir, ioc->hint_mask_pdir);
+
+	ASSERT((((unsigned long) pdir_base) & PAGE_MASK) == (unsigned long) pdir_base);
+	WRITE_REG(virt_to_phys(pdir_base), ioc->ioc_hpa + IOC_PDIR_BASE);
+
+	DBG_INIT(" base %p\n", pdir_base);
+
+	/* build IMASK for IOC and Elroy */
+	iova_space_mask =  0xffffffff;
+	iova_space_mask <<= (iov_order + PAGE_SHIFT);
+
+#ifdef CONFIG_IA64_HP_PROTO
+	/*
+	** REVISIT - this is a kludge, but we won't be supporting anything but
+	** zx1 2.0 or greater for real.  When fw is in shape, ibase will
+	** be preprogrammed w/ the IOVA hole base and imask will give us
+	** the size.
+	*/
+	if ((sba_dev->hw_rev & 0xFF) < 0x20) {
+		DBG_INIT("%s() Found SBA rev < 2.0, setting IOVA base to 0.  This device will not be supported in the future.\n", __FUNCTION__);
+		ioc->ibase = 0x0;
+	} else
+#endif
+	ioc->ibase = READ_REG(ioc->ioc_hpa + IOC_IBASE) & 0xFFFFFFFEUL;
+
+	ioc->imask = iova_space_mask;	/* save it */
+
+	DBG_INIT("%s() IOV base 0x%lx mask 0x%0lx\n",
+		__FUNCTION__, ioc->ibase, ioc->imask);
+
+	/*
+	** FIXME: Hint registers are programmed with default hint
+	** values during boot, so hints should be sane even if we
+	** can't reprogram them the way drivers want.
+	*/
+
+	WRITE_REG(ioc->imask, ioc->ioc_hpa+IOC_IMASK);
+
+	/*
+	** Setting the upper bits makes checking for bypass addresses
+	** a little faster later on.
+	*/
+	ioc->imask |= 0xFFFFFFFF00000000UL;
+
+	/* Set I/O PDIR Page size to system page size */
+	switch (PAGE_SHIFT) {
+		case 12: /* 4K */
+			tcnfg = 0;
+			break;
+		case 13: /* 8K */
+			tcnfg = 1;
+			break;
+		case 14: /* 16K */
+			tcnfg = 2;
+			break;
+		case 16: /* 64K */
+			tcnfg = 3;
+			break;
+	}
+	WRITE_REG(tcnfg, ioc->ioc_hpa+IOC_TCNFG);
+
+	/*
+	** Program the IOC's ibase and enable IOVA translation
+	** Bit zero == enable bit.
+	*/
+	WRITE_REG(ioc->ibase | 1, ioc->ioc_hpa+IOC_IBASE);
+
+	/*
+	** Clear I/O TLB of any possible entries.
+	** (Yes. This is a bit paranoid...but so what)
+	*/
+	WRITE_REG(0 | 31, ioc->ioc_hpa+IOC_PCOM);
+
+	/*
+	** If an AGP device is present, only use half of the IOV space
+	** for PCI DMA.  Unfortunately we can't know ahead of time
+	** whether GART support will actually be used, for now we
+	** can just key on an AGP device found in the system.
+	** We program the next pdir index after we stop w/ a key for
+	** the GART code to handshake on.
+	*/
+	if (SBA_GET_AGP(sba_dev)) {
+		DBG_INIT("%s() AGP Device found, reserving 512MB for GART support\n", __FUNCTION__);
+		ioc->pdir_size /= 2;
+		((u64 *)pdir_base)[PDIR_INDEX(iova_space_size/2)] = 0x0000badbadc0ffeeULL;
+	}
+
+	DBG_INIT("%s() DONE\n", __FUNCTION__);
+}
+
+
+
+/**************************************************************************
+**
+**   SBA initialization code (HW and SW)
+**
+**   o identify SBA chip itself
+**   o FIXME: initialize DMA hints for reasonable defaults
+**
+**************************************************************************/
+
+static void
+sba_hw_init(struct sba_device *sba_dev)
+{ 
+	int i;
+	int num_ioc;
+	u64 dma_mask;
+	u32 func_id;
+
+	/*
+	** Identify the SBA so we can set the dma_mask.  We can make a virtual
+	** dma_mask of the memory subsystem such that devices not implmenting
+	** a full 64bit mask might still be able to bypass efficiently.
+	*/
+	func_id = READ_REG(sba_dev->sba_hpa + SBA_FUNC_ID);
+
+	if (func_id == ZX1_FUNC_ID_VALUE) {
+		dma_mask = 0xFFFFFFFFFFUL;
+	} else {
+		dma_mask = 0xFFFFFFFFFFFFFFFFUL;
+	}
+
+	DBG_INIT("%s(): ioc->dma_mask == 0x%lx\n", __FUNCTION__, dma_mask);
+	
+	/*
+	** Leaving in the multiple ioc code from parisc for the future,
+	** currently there are no muli-ioc mckinley sbas
+	*/
+	sba_dev->ioc[0].ioc_hpa = SBA_IOC_OFFSET;
+	num_ioc = 1;
+
+	sba_dev->num_ioc = num_ioc;
+	for (i = 0; i < num_ioc; i++) {
+		sba_dev->ioc[i].dma_mask = dma_mask;
+		sba_dev->ioc[i].ioc_hpa += sba_dev->sba_hpa;
+		sba_ioc_init(sba_dev, &(sba_dev->ioc[i]), i);
+	}
+}
+
+static void
+sba_common_init(struct sba_device *sba_dev)
+{
+	int i;
+
+	/* add this one to the head of the list (order doesn't matter)
+	** This will be useful for debugging - especially if we get coredumps
+	*/
+	sba_dev->next = sba_list;
+	sba_list = sba_dev;
+	sba_count++;
+
+	for(i=0; i< sba_dev->num_ioc; i++) {
+		int res_size;
+
+		/* resource map size dictated by pdir_size */
+		res_size = sba_dev->ioc[i].pdir_size/sizeof(u64); /* entries */
+		res_size >>= 3;  /* convert bit count to byte count */
+		DBG_INIT("%s() res_size 0x%x\n",
+			__FUNCTION__, res_size);
+
+		sba_dev->ioc[i].res_size = res_size;
+		sba_dev->ioc[i].res_map = (char *) __get_free_pages(GFP_KERNEL, get_order(res_size));
+
+		if (NULL == sba_dev->ioc[i].res_map)
+		{
+			panic(__FILE__ ":%s() could not allocate resource map\n", __FUNCTION__ );
+		}
+
+		memset(sba_dev->ioc[i].res_map, 0, res_size);
+		/* next available IOVP - circular search */
+		if ((sba_dev->hw_rev & 0xFF) >= 0x20) {
+			sba_dev->ioc[i].res_hint = (unsigned long *)
+			    sba_dev->ioc[i].res_map;
+		} else {
+			u64 reserved_iov;
+			
+			/* Yet another 1.x hack */
+			printk("zx1 1.x: Starting resource hint offset into IOV space to avoid initial zero value IOVA\n");
+			sba_dev->ioc[i].res_hint = (unsigned long *)
+			    &(sba_dev->ioc[i].res_map[L1_CACHE_BYTES]);
+
+			sba_dev->ioc[i].res_map[0] = 0x1;
+			sba_dev->ioc[i].pdir_base[0] = 0x8000badbadc0ffeeULL;
+
+			for (reserved_iov = 0xA0000 ; reserved_iov < 0xC0000 ; reserved_iov += IOVP_SIZE) {
+				u64 *res_ptr = sba_dev->ioc[i].res_map;
+				int index = PDIR_INDEX(reserved_iov);
+				int res_word;
+				u64 mask;
+
+				res_word = (int)(index / BITS_PER_LONG);
+				mask =  0x1UL << (index - (res_word * BITS_PER_LONG));
+				res_ptr[res_word] |= mask;
+				sba_dev->ioc[i].pdir_base[PDIR_INDEX(reserved_iov)] = (0x80000000000000FFULL | reserved_iov);
+
+			}
+		}
+
+#ifdef ASSERT_PDIR_SANITY
+		/* Mark first bit busy - ie no IOVA 0 */
+		sba_dev->ioc[i].res_map[0] = 0x1;
+		sba_dev->ioc[i].pdir_base[0] = 0x8000badbadc0ffeeULL;
+#endif
+
+		DBG_INIT("%s() %d res_map %x %p\n", __FUNCTION__,
+		         i, res_size, (void *)sba_dev->ioc[i].res_map);
+	}
+
+	sba_dev->sba_lock = SPIN_LOCK_UNLOCKED;
+}
+
+#ifdef CONFIG_PROC_FS
+static int sba_proc_info(char *buf, char **start, off_t offset, int len)
+{
+	struct sba_device *sba_dev = sba_list;
+	struct ioc *ioc = &sba_dev->ioc[0];	/* FIXME: Multi-IOC support! */
+	int total_pages = (int) (ioc->res_size << 3); /* 8 bits per byte */
+	unsigned long i = 0, avg = 0, min, max;
+
+	sprintf(buf, "%s rev %d.%d\n",
+		"Hewlett Packard zx1 SBA",
+		((sba_dev->hw_rev >> 4) & 0xF),
+		(sba_dev->hw_rev & 0xF)
+		);
+	sprintf(buf, "%sIO PDIR size    : %d bytes (%d entries)\n",
+		buf,
+		(int) ((ioc->res_size << 3) * sizeof(u64)), /* 8 bits/byte */
+		total_pages);
+
+	sprintf(buf, "%sIO PDIR entries : %ld free  %ld used (%d%%)\n", buf,
+		total_pages - ioc->used_pages, ioc->used_pages,
+		(int) (ioc->used_pages * 100 / total_pages));
+	
+	sprintf(buf, "%sResource bitmap : %d bytes (%d pages)\n", 
+		buf, ioc->res_size, ioc->res_size << 3);   /* 8 bits per byte */
+
+	min = max = ioc->avg_search[0];
+	for (i = 0; i < SBA_SEARCH_SAMPLE; i++) {
+		avg += ioc->avg_search[i];
+		if (ioc->avg_search[i] > max) max = ioc->avg_search[i];
+		if (ioc->avg_search[i] < min) min = ioc->avg_search[i];
+	}
+	avg /= SBA_SEARCH_SAMPLE;
+	sprintf(buf, "%s  Bitmap search : %ld/%ld/%ld (min/avg/max CPU Cycles)\n",
+		buf, min, avg, max);
+
+	sprintf(buf, "%spci_map_single(): %12ld calls  %12ld pages (avg %d/1000)\n",
+		buf, ioc->msingle_calls, ioc->msingle_pages,
+		(int) ((ioc->msingle_pages * 1000)/ioc->msingle_calls));
+#ifdef ALLOW_IOV_BYPASS
+	sprintf(buf, "%spci_map_single(): %12ld bypasses\n",
+	        buf, ioc->msingle_bypass);
+#endif
+
+	sprintf(buf, "%spci_unmap_single: %12ld calls  %12ld pages (avg %d/1000)\n",
+		buf, ioc->usingle_calls, ioc->usingle_pages,
+		(int) ((ioc->usingle_pages * 1000)/ioc->usingle_calls));
+#ifdef ALLOW_IOV_BYPASS
+	sprintf(buf, "%spci_unmap_single: %12ld bypasses\n",
+	        buf, ioc->usingle_bypass);
+#endif
+
+	sprintf(buf, "%spci_map_sg()    : %12ld calls  %12ld pages (avg %d/1000)\n",
+		buf, ioc->msg_calls, ioc->msg_pages,
+		(int) ((ioc->msg_pages * 1000)/ioc->msg_calls));
+#ifdef ALLOW_IOV_BYPASS
+	sprintf(buf, "%spci_map_sg()    : %12ld bypasses\n",
+	        buf, ioc->msg_bypass);
+#endif
+
+	sprintf(buf, "%spci_unmap_sg()  : %12ld calls  %12ld pages (avg %d/1000)\n",
+		buf, ioc->usg_calls, ioc->usg_pages,
+		(int) ((ioc->usg_pages * 1000)/ioc->usg_calls));
+
+	return strlen(buf);
+}
+
+static int
+sba_resource_map(char *buf, char **start, off_t offset, int len)
+{
+	struct ioc *ioc = sba_list->ioc;	/* FIXME: Multi-IOC support! */
+	unsigned int *res_ptr = (unsigned int *)ioc->res_map;
+	int i;
+
+	buf[0] = '\0';
+	for(i = 0; i < (ioc->res_size / sizeof(unsigned int)); ++i, ++res_ptr) {
+		if ((i & 7) == 0)
+		    strcat(buf,"\n   ");
+		sprintf(buf, "%s %08x", buf, *res_ptr);
+	}
+	strcat(buf, "\n");
+
+	return strlen(buf);
+}
+#endif
+
+/*
+** Determine if sba should claim this chip (return 0) or not (return 1).
+** If so, initialize the chip and tell other partners in crime they
+** have work to do.
+*/
+void __init sba_init(void)
+{
+	struct sba_device *sba_dev;
+	u32 func_id, hw_rev;
+	u32 *func_offset = NULL;
+	int i, agp_found = 0;
+	static char sba_rev[6];
+	struct pci_dev *device = NULL;
+	u64 hpa = 0;
+
+	if (!(device = pci_find_device(PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_ZX1_SBA, NULL)))
+		return;
+
+	for (i = 0; i < PCI_NUM_RESOURCES; i++) {
+		if (pci_resource_flags(device, i) == IORESOURCE_MEM) {
+			hpa = ioremap(pci_resource_start(device, i),
+				pci_resource_len(device, i));
+			break;
+		}
+	}
+
+	func_id = READ_REG(hpa + SBA_FUNC_ID);
+
+	if (func_id == ZX1_FUNC_ID_VALUE) {
+		(void)strcpy(sba_rev, "zx1");
+		func_offset = zx1_func_offsets;
+	} else {
+		return;
+	}
+
+	/* Read HW Rev First */
+	hw_rev = READ_REG(hpa + SBA_FCLASS) & 0xFFUL;
+
+	/*
+	 * Not all revision registers of the chipset are updated on every
+	 * turn.  Must scan through all functions looking for the highest rev
+	 */
+	if (func_offset) {
+		for (i = 0 ; func_offset[i] != -1 ; i++) {
+			u32 func_rev;
+
+			func_rev = READ_REG(hpa + SBA_FCLASS + func_offset[i]) & 0xFFUL;
+			DBG_INIT("%s() func offset: 0x%x rev: 0x%x\n",
+			         __FUNCTION__, func_offset[i], func_rev);
+			if (func_rev > hw_rev)
+				hw_rev = func_rev;
+		}
+	}
+
+	printk(KERN_INFO "%s found %s %d.%d at %s, HPA 0x%lx\n", DRIVER_NAME,
+	       sba_rev, ((hw_rev >> 4) & 0xF), (hw_rev & 0xF),
+	       device->slot_name, hpa);
+
+	if ((hw_rev & 0xFF) < 0x20) {
+		printk(KERN_INFO "%s WARNING rev 2.0 or greater will be required for IO MMU support in the future\n", DRIVER_NAME);
+#ifndef CONFIG_IA64_HP_PROTO
+		panic("%s: CONFIG_IA64_HP_PROTO MUST be enabled to support SBA rev less than 2.0", DRIVER_NAME);
+#endif
+	}
+
+	sba_dev = kmalloc(sizeof(struct sba_device), GFP_KERNEL);
+	if (NULL == sba_dev) {
+		printk(KERN_ERR DRIVER_NAME " - couldn't alloc sba_device\n");
+		return;
+	}
+
+	memset(sba_dev, 0, sizeof(struct sba_device));
+
+	for(i=0; i<MAX_IOC; i++)
+		spin_lock_init(&(sba_dev->ioc[i].res_lock));
+
+	sba_dev->hw_rev = hw_rev;
+	sba_dev->sba_hpa = hpa;
+
+	/*
+	 * We need to check for an AGP device, if we find one, then only
+	 * use part of the IOVA space for PCI DMA, the rest is for GART.
+	 * REVISIT for multiple IOC.
+	 */
+	pci_for_each_dev(device)
+		agp_found |= pci_find_capability(device, PCI_CAP_ID_AGP);
+
+	if (agp_found && reserve_sba_gart)
+		SBA_SET_AGP(sba_dev);
+
+	sba_hw_init(sba_dev);
+	sba_common_init(sba_dev);
+
+#ifdef CONFIG_PROC_FS
+	{
+		struct proc_dir_entry * proc_mckinley_root;
+
+		proc_mckinley_root = proc_mkdir("bus/mckinley",0);
+		create_proc_info_entry(sba_rev, 0, proc_mckinley_root, sba_proc_info);
+		create_proc_info_entry("bitmap", 0, proc_mckinley_root, sba_resource_map);
+	}
+#endif
+}
+
+static int __init
+nosbagart (char *str)
+{
+	reserve_sba_gart = 0;
+	return 1;
+}
+
+__setup("nosbagart",nosbagart);
+
+EXPORT_SYMBOL(sba_init);
+EXPORT_SYMBOL(sba_map_single);
+EXPORT_SYMBOL(sba_unmap_single);
+EXPORT_SYMBOL(sba_map_sg);
+EXPORT_SYMBOL(sba_unmap_sg);
+EXPORT_SYMBOL(sba_dma_address);
+EXPORT_SYMBOL(sba_alloc_consistent);
+EXPORT_SYMBOL(sba_free_consistent);
+/*
+**  IA64 System Bus Adapter (SBA) I/O MMU manager
+**
+**	(c) Copyright 2002 Alex Williamson
+**	(c) Copyright 2002 Hewlett-Packard Company
+**
+**	Portions (c) 2000 Grant Grundler (from parisc I/O MMU code)
+**	Portions (c) 1999 Dave S. Miller (from sparc64 I/O MMU code)
+**
+**	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 module initializes the IOC (I/O Controller) found on HP
+** McKinley machines and their successors.
+**
+*/
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+
+#include <asm/delay.h>		/* ia64_get_itc() */
+#include <asm/io.h>
+#include <asm/page.h>		/* PAGE_OFFSET */
+#include <asm/efi.h>
+
+
+#define DRIVER_NAME "SBA"
+
+#ifndef CONFIG_IA64_HP_PROTO
+#define ALLOW_IOV_BYPASS
+#endif
+#define ENABLE_MARK_CLEAN
+/*
+** The number of debug flags is a clue - this code is fragile.
+*/
+#undef DEBUG_SBA_INIT
+#undef DEBUG_SBA_RUN
+#undef DEBUG_SBA_RUN_SG
+#undef DEBUG_SBA_RESOURCE
+#undef ASSERT_PDIR_SANITY
+#undef DEBUG_LARGE_SG_ENTRIES
+#undef DEBUG_BYPASS
+
+#define SBA_INLINE	__inline__
+/* #define SBA_INLINE */
+
+#ifdef DEBUG_SBA_INIT
+#define DBG_INIT(x...)	printk(x)
+#else
+#define DBG_INIT(x...)
+#endif
+
+#ifdef DEBUG_SBA_RUN
+#define DBG_RUN(x...)	printk(x)
+#else
+#define DBG_RUN(x...)
+#endif
+
+#ifdef DEBUG_SBA_RUN_SG
+#define DBG_RUN_SG(x...)	printk(x)
+#else
+#define DBG_RUN_SG(x...)
+#endif
+
+
+#ifdef DEBUG_SBA_RESOURCE
+#define DBG_RES(x...)	printk(x)
+#else
+#define DBG_RES(x...)
+#endif
+
+#ifdef DEBUG_BYPASS
+#define DBG_BYPASS(x...)	printk(x)
+#else
+#define DBG_BYPASS(x...)
+#endif
+
+#ifdef ASSERT_PDIR_SANITY
+#define ASSERT(expr) \
+        if(!(expr)) { \
+                printk( "\n" __FILE__ ":%d: Assertion " #expr " failed!\n",__LINE__); \
+                panic(#expr); \
+        }
+#else
+#define ASSERT(expr)
+#endif
+
+#define KB(x) ((x) * 1024)
+#define MB(x) (KB (KB (x)))
+#define GB(x) (MB (KB (x)))
+
+/*
+** The number of pdir entries to "free" before issueing
+** a read to PCOM register to flush out PCOM writes.
+** Interacts with allocation granularity (ie 4 or 8 entries
+** allocated and free'd/purged at a time might make this
+** less interesting).
+*/
+#define DELAYED_RESOURCE_CNT	16
+
+#define DEFAULT_DMA_HINT_REG	0
+
+#define ZX1_FUNC_ID_VALUE    ((PCI_DEVICE_ID_HP_ZX1_SBA << 16) | PCI_VENDOR_ID_HP)
+#define ZX1_MC_ID    ((PCI_DEVICE_ID_HP_ZX1_MC << 16) | PCI_VENDOR_ID_HP)
+
+#define SBA_FUNC_ID	0x0000	/* function id */
+#define SBA_FCLASS	0x0008	/* function class, bist, header, rev... */
+
+#define SBA_FUNC_SIZE	0x10000   /* SBA configuration function reg set */
+
+unsigned int __initdata zx1_func_offsets[] = {0x1000, 0x4000, 0x8000,
+                                              0x9000, 0xa000, -1};
+
+#define SBA_IOC_OFFSET	0x1000
+
+#define MAX_IOC		1	/* we only have 1 for now*/
+
+#define IOC_IBASE	0x300	/* IO TLB */
+#define IOC_IMASK	0x308
+#define IOC_PCOM	0x310
+#define IOC_TCNFG	0x318
+#define IOC_PDIR_BASE	0x320
+
+#define IOC_IOVA_SPACE_BASE	0x40000000 /* IOVA ranges start at 1GB */
+
+/*
+** IOC supports 4/8/16/64KB page sizes (see TCNFG register)
+** It's safer (avoid memory corruption) to keep DMA page mappings
+** equivalently sized to VM PAGE_SIZE.
+**
+** We really can't avoid generating a new mapping for each
+** page since the Virtual Coherence Index has to be generated
+** and updated for each page.
+**
+** IOVP_SIZE could only be greater than PAGE_SIZE if we are
+** confident the drivers really only touch the next physical
+** page iff that driver instance owns it.
+*/
+#define IOVP_SIZE	PAGE_SIZE
+#define IOVP_SHIFT	PAGE_SHIFT
+#define IOVP_MASK	PAGE_MASK
+
+struct ioc {
+	unsigned long	ioc_hpa;	/* I/O MMU base address */
+	char		*res_map;	/* resource map, bit == pdir entry */
+	u64		*pdir_base;	/* physical base address */
+	unsigned long	ibase;		/* pdir IOV Space base */
+	unsigned long	imask;		/* pdir IOV Space mask */
+
+	unsigned long	*res_hint;	/* next avail IOVP - circular search */
+	spinlock_t	res_lock;
+	unsigned long	hint_mask_pdir;	/* bits used for DMA hints */
+	unsigned int	res_bitshift;	/* from the RIGHT! */
+	unsigned int	res_size;	/* size of resource map in bytes */
+	unsigned int	hint_shift_pdir;
+	unsigned long	dma_mask;
+#if DELAYED_RESOURCE_CNT > 0
+	int saved_cnt;
+	struct sba_dma_pair {
+		dma_addr_t	iova;
+		size_t		size;
+	} saved[DELAYED_RESOURCE_CNT];
+#endif
+
+#ifdef CONFIG_PROC_FS
+#define SBA_SEARCH_SAMPLE	0x100
+	unsigned long avg_search[SBA_SEARCH_SAMPLE];
+	unsigned long avg_idx;	/* current index into avg_search */
+	unsigned long used_pages;
+	unsigned long msingle_calls;
+	unsigned long msingle_pages;
+	unsigned long msg_calls;
+	unsigned long msg_pages;
+	unsigned long usingle_calls;
+	unsigned long usingle_pages;
+	unsigned long usg_calls;
+	unsigned long usg_pages;
+#ifdef ALLOW_IOV_BYPASS
+	unsigned long msingle_bypass;
+	unsigned long usingle_bypass;
+	unsigned long msg_bypass;
+#endif
+#endif
+
+	/* STUFF We don't need in performance path */
+	unsigned int	pdir_size;	/* in bytes, determined by IOV Space size */
+};
+
+struct sba_device {
+	struct sba_device	*next;	/* list of SBA's in system */
+	const char 		*name;
+	unsigned long		sba_hpa; /* base address */
+	spinlock_t		sba_lock;
+	unsigned int		flags;  /* state/functionality enabled */
+	unsigned int		hw_rev;  /* HW revision of chip */
+
+	unsigned int		num_ioc;  /* number of on-board IOC's */
+	struct ioc		ioc[MAX_IOC];
+};
+
+
+static struct sba_device *sba_list;
+static int sba_count;
+static int reserve_sba_gart = 1;
+
+#define sba_sg_iova(sg) (sg->address)
+#define sba_sg_len(sg) (sg->length)
+#define sba_sg_buffer(sg) (sg->orig_address)
+
+/* REVISIT - fix me for multiple SBAs/IOCs */
+#define GET_IOC(dev) (sba_list->ioc)
+#define SBA_SET_AGP(sba_dev) (sba_dev->flags |= 0x1)
+#define SBA_GET_AGP(sba_dev) (sba_dev->flags & 0x1)
+
+/*
+** DMA_CHUNK_SIZE is used by the SCSI mid-layer to break up
+** (or rather not merge) DMA's into managable chunks.
+** On parisc, this is more of the software/tuning constraint
+** rather than the HW. I/O MMU allocation alogorithms can be
+** faster with smaller size is (to some degree).
+*/
+#define DMA_CHUNK_SIZE  (BITS_PER_LONG*PAGE_SIZE)
+
+/* Looks nice and keeps the compiler happy */
+#define SBA_DEV(d) ((struct sba_device *) (d))
+
+#define ROUNDUP(x,y) ((x + ((y)-1)) & ~((y)-1))
+
+/************************************
+** SBA register read and write support
+**
+** BE WARNED: register writes are posted.
+**  (ie follow writes which must reach HW with a read)
+**
+*/
+#define READ_REG(addr)       __raw_readq(addr)
+#define WRITE_REG(val, addr) __raw_writeq(val, addr)
+
+#ifdef DEBUG_SBA_INIT
+
+/**
+ * sba_dump_tlb - debugging only - print IOMMU operating parameters
+ * @hpa: base address of the IOMMU
+ *
+ * Print the size/location of the IO MMU PDIR.
+ */
+static void
+sba_dump_tlb(char *hpa)
+{
+	DBG_INIT("IO TLB at 0x%p\n", (void *)hpa);
+	DBG_INIT("IOC_IBASE    : %016lx\n", READ_REG(hpa+IOC_IBASE));
+	DBG_INIT("IOC_IMASK    : %016lx\n", READ_REG(hpa+IOC_IMASK));
+	DBG_INIT("IOC_TCNFG    : %016lx\n", READ_REG(hpa+IOC_TCNFG));
+	DBG_INIT("IOC_PDIR_BASE: %016lx\n", READ_REG(hpa+IOC_PDIR_BASE));
+	DBG_INIT("\n");
+}
+#endif
+
+
+#ifdef ASSERT_PDIR_SANITY
+
+/**
+ * sba_dump_pdir_entry - debugging only - print one IOMMU PDIR entry
+ * @ioc: IO MMU structure which owns the pdir we are interested in.
+ * @msg: text to print ont the output line.
+ * @pide: pdir index.
+ *
+ * Print one entry of the IO MMU PDIR in human readable form.
+ */
+static void
+sba_dump_pdir_entry(struct ioc *ioc, char *msg, uint pide)
+{
+	/* start printing from lowest pde in rval */
+	u64 *ptr = &(ioc->pdir_base[pide  & ~(BITS_PER_LONG - 1)]);
+	unsigned long *rptr = (unsigned long *) &(ioc->res_map[(pide >>3) & ~(sizeof(unsigned long) - 1)]);
+	uint rcnt;
+
+	/* printk(KERN_DEBUG "SBA: %s rp %p bit %d rval 0x%lx\n", */
+	printk("SBA: %s rp %p bit %d rval 0x%lx\n",
+		 msg, rptr, pide & (BITS_PER_LONG - 1), *rptr);
+
+	rcnt = 0;
+	while (rcnt < BITS_PER_LONG) {
+		printk("%s %2d %p %016Lx\n",
+			(rcnt == (pide & (BITS_PER_LONG - 1)))
+				? "    -->" : "       ",
+			rcnt, ptr, *ptr );
+		rcnt++;
+		ptr++;
+	}
+	printk("%s", msg);
+}
+
+
+/**
+ * sba_check_pdir - debugging only - consistency checker
+ * @ioc: IO MMU structure which owns the pdir we are interested in.
+ * @msg: text to print ont the output line.
+ *
+ * Verify the resource map and pdir state is consistent
+ */
+static int
+sba_check_pdir(struct ioc *ioc, char *msg)
+{
+	u64 *rptr_end = (u64 *) &(ioc->res_map[ioc->res_size]);
+	u64 *rptr = (u64 *) ioc->res_map;	/* resource map ptr */
+	u64 *pptr = ioc->pdir_base;	/* pdir ptr */
+	uint pide = 0;
+
+	while (rptr < rptr_end) {
+		u64 rval;
+		int rcnt; /* number of bits we might check */
+
+		rval = *rptr;
+		rcnt = 64;
+
+		while (rcnt) {
+			/* Get last byte and highest bit from that */
+			u32 pde = ((u32)((*pptr >> (63)) & 0x1));
+			if ((rval & 0x1) ^ pde)
+			{
+				/*
+				** BUMMER!  -- res_map != pdir --
+				** Dump rval and matching pdir entries
+				*/
+				sba_dump_pdir_entry(ioc, msg, pide);
+				return(1);
+			}
+			rcnt--;
+			rval >>= 1;	/* try the next bit */
+			pptr++;
+			pide++;
+		}
+		rptr++;	/* look at next word of res_map */
+	}
+	/* It'd be nice if we always got here :^) */
+	return 0;
+}
+
+
+/**
+ * sba_dump_sg - debugging only - print Scatter-Gather list
+ * @ioc: IO MMU structure which owns the pdir we are interested in.
+ * @startsg: head of the SG list
+ * @nents: number of entries in SG list
+ *
+ * print the SG list so we can verify it's correct by hand.
+ */
+static void
+sba_dump_sg( struct ioc *ioc, struct scatterlist *startsg, int nents)
+{
+	while (nents-- > 0) {
+		printk(" %d : %08lx/%05x %p\n",
+				nents,
+				(unsigned long) sba_sg_iova(startsg),
+				sba_sg_len(startsg),
+				sba_sg_buffer(startsg));
+		startsg++;
+	}
+}
+static void
+sba_check_sg( struct ioc *ioc, struct scatterlist *startsg, int nents)
+{
+	struct scatterlist *the_sg = startsg;
+	int the_nents = nents;
+
+	while (the_nents-- > 0) {
+		if (sba_sg_buffer(the_sg) == 0x0UL)
+			sba_dump_sg(NULL, startsg, nents);
+		the_sg++;
+	}
+}
+
+#endif /* ASSERT_PDIR_SANITY */
+
+
+
+
+/**************************************************************
+*
+*   I/O Pdir Resource Management
+*
+*   Bits set in the resource map are in use.
+*   Each bit can represent a number of pages.
+*   LSbs represent lower addresses (IOVA's).
+*
+***************************************************************/
+#define PAGES_PER_RANGE 1	/* could increase this to 4 or 8 if needed */
+
+/* Convert from IOVP to IOVA and vice versa. */
+#define SBA_IOVA(ioc,iovp,offset,hint_reg) ((ioc->ibase) | (iovp) | (offset) | ((hint_reg)<<(ioc->hint_shift_pdir)))
+#define SBA_IOVP(ioc,iova) (((iova) & ioc->hint_mask_pdir) & ~(ioc->ibase))
+
+/* FIXME : review these macros to verify correctness and usage */
+#define PDIR_INDEX(iovp)   ((iovp)>>IOVP_SHIFT)
+
+#define RESMAP_MASK(n)    ~(~0UL << (n))
+#define RESMAP_IDX_MASK   (sizeof(unsigned long) - 1)
+
+
+/**
+ * sba_search_bitmap - find free space in IO PDIR resource bitmap
+ * @ioc: IO MMU structure which owns the pdir we are interested in.
+ * @bits_wanted: number of entries we need.
+ *
+ * Find consecutive free bits in resource bitmap.
+ * Each bit represents one entry in the IO Pdir.
+ * Cool perf optimization: search for log2(size) bits at a time.
+ */
+static SBA_INLINE unsigned long
+sba_search_bitmap(struct ioc *ioc, unsigned long bits_wanted)
+{
+	unsigned long *res_ptr = ioc->res_hint;
+	unsigned long *res_end = (unsigned long *) &(ioc->res_map[ioc->res_size]);
+	unsigned long pide = ~0UL;
+
+	ASSERT(((unsigned long) ioc->res_hint & (sizeof(unsigned long) - 1UL)) == 0);
+	ASSERT(res_ptr < res_end);
+	if (bits_wanted > (BITS_PER_LONG/2)) {
+		/* Search word at a time - no mask needed */
+		for(; res_ptr < res_end; ++res_ptr) {
+			if (*res_ptr == 0) {
+				*res_ptr = RESMAP_MASK(bits_wanted);
+				pide = ((unsigned long)res_ptr - (unsigned long)ioc->res_map);
+				pide <<= 3;	/* convert to bit address */
+				break;
+			}
+		}
+		/* point to the next word on next pass */
+		res_ptr++;
+		ioc->res_bitshift = 0;
+	} else {
+		/*
+		** Search the resource bit map on well-aligned values.
+		** "o" is the alignment.
+		** We need the alignment to invalidate I/O TLB using
+		** SBA HW features in the unmap path.
+		*/
+		unsigned long o = 1 << get_order(bits_wanted << PAGE_SHIFT);
+		uint bitshiftcnt = ROUNDUP(ioc->res_bitshift, o);
+		unsigned long mask;
+
+		if (bitshiftcnt >= BITS_PER_LONG) {
+			bitshiftcnt = 0;
+			res_ptr++;
+		}
+		mask = RESMAP_MASK(bits_wanted) << bitshiftcnt;
+
+		DBG_RES("%s() o %ld %p", __FUNCTION__, o, res_ptr);
+		while(res_ptr < res_end)
+		{ 
+			DBG_RES("    %p %lx %lx\n", res_ptr, mask, *res_ptr);
+			ASSERT(0 != mask);
+			if(0 == ((*res_ptr) & mask)) {
+				*res_ptr |= mask;     /* mark resources busy! */
+				pide = ((unsigned long)res_ptr - (unsigned long)ioc->res_map);
+				pide <<= 3;	/* convert to bit address */
+				pide += bitshiftcnt;
+				break;
+			}
+			mask <<= o;
+			bitshiftcnt += o;
+			if (0 == mask) {
+				mask = RESMAP_MASK(bits_wanted);
+				bitshiftcnt=0;
+				res_ptr++;
+			}
+		}
+		/* look in the same word on the next pass */
+		ioc->res_bitshift = bitshiftcnt + bits_wanted;
+	}
+
+	/* wrapped ? */
+	if (res_end <= res_ptr) {
+		ioc->res_hint = (unsigned long *) ioc->res_map;
+		ioc->res_bitshift = 0;
+	} else {
+		ioc->res_hint = res_ptr;
+	}
+	return (pide);
+}
+
+
+/**
+ * sba_alloc_range - find free bits and mark them in IO PDIR resource bitmap
+ * @ioc: IO MMU structure which owns the pdir we are interested in.
+ * @size: number of bytes to create a mapping for
+ *
+ * Given a size, find consecutive unmarked and then mark those bits in the
+ * resource bit map.
+ */
+static int
+sba_alloc_range(struct ioc *ioc, size_t size)
+{
+	unsigned int pages_needed = size >> IOVP_SHIFT;
+#ifdef CONFIG_PROC_FS
+	unsigned long itc_start = ia64_get_itc();
+#endif
+	unsigned long pide;
+
+	ASSERT(pages_needed);
+	ASSERT((pages_needed * IOVP_SIZE) <= DMA_CHUNK_SIZE);
+	ASSERT(pages_needed <= BITS_PER_LONG);
+	ASSERT(0 == (size & ~IOVP_MASK));
+
+	/*
+	** "seek and ye shall find"...praying never hurts either...
+	*/
+
+	pide = sba_search_bitmap(ioc, pages_needed);
+	if (pide >= (ioc->res_size << 3)) {
+		pide = sba_search_bitmap(ioc, pages_needed);
+		if (pide >= (ioc->res_size << 3))
+			panic(__FILE__ ": I/O MMU @ %lx is out of mapping resources\n", ioc->ioc_hpa);
+	}
+
+#ifdef ASSERT_PDIR_SANITY
+	/* verify the first enable bit is clear */
+	if(0x00 != ((u8 *) ioc->pdir_base)[pide*sizeof(u64) + 7]) {
+		sba_dump_pdir_entry(ioc, "sba_search_bitmap() botched it?", pide);
+	}
+#endif
+
+	DBG_RES("%s(%x) %d -> %lx hint %x/%x\n",
+		__FUNCTION__, size, pages_needed, pide,
+		(uint) ((unsigned long) ioc->res_hint - (unsigned long) ioc->res_map),
+		ioc->res_bitshift );
+
+#ifdef CONFIG_PROC_FS
+	{
+		unsigned long itc_end = ia64_get_itc();
+		unsigned long tmp = itc_end - itc_start;
+		/* check for roll over */
+		itc_start = (itc_end < itc_start) ?  -(tmp) : (tmp);
+	}
+	ioc->avg_search[ioc->avg_idx++] = itc_start;
+	ioc->avg_idx &= SBA_SEARCH_SAMPLE - 1;
+
+	ioc->used_pages += pages_needed;
+#endif
+
+	return (pide);
+}
+
+
+/**
+ * sba_free_range - unmark bits in IO PDIR resource bitmap
+ * @ioc: IO MMU structure which owns the pdir we are interested in.
+ * @iova: IO virtual address which was previously allocated.
+ * @size: number of bytes to create a mapping for
+ *
+ * clear bits in the ioc's resource map
+ */
+static SBA_INLINE void
+sba_free_range(struct ioc *ioc, dma_addr_t iova, size_t size)
+{
+	unsigned long iovp = SBA_IOVP(ioc, iova);
+	unsigned int pide = PDIR_INDEX(iovp);
+	unsigned int ridx = pide >> 3;	/* convert bit to byte address */
+	unsigned long *res_ptr = (unsigned long *) &((ioc)->res_map[ridx & ~RESMAP_IDX_MASK]);
+
+	int bits_not_wanted = size >> IOVP_SHIFT;
+
+	/* 3-bits "bit" address plus 2 (or 3) bits for "byte" == bit in word */
+	unsigned long m = RESMAP_MASK(bits_not_wanted) << (pide & (BITS_PER_LONG - 1));
+
+	DBG_RES("%s( ,%x,%x) %x/%lx %x %p %lx\n",
+		__FUNCTION__, (uint) iova, size,
+		bits_not_wanted, m, pide, res_ptr, *res_ptr);
+
+#ifdef CONFIG_PROC_FS
+	ioc->used_pages -= bits_not_wanted;
+#endif
+
+	ASSERT(m != 0);
+	ASSERT(bits_not_wanted);
+	ASSERT((bits_not_wanted * IOVP_SIZE) <= DMA_CHUNK_SIZE);
+	ASSERT(bits_not_wanted <= BITS_PER_LONG);
+	ASSERT((*res_ptr & m) == m); /* verify same bits are set */
+	*res_ptr &= ~m;
+}
+
+
+/**************************************************************
+*
+*   "Dynamic DMA Mapping" support (aka "Coherent I/O")
+*
+***************************************************************/
+
+#define SBA_DMA_HINT(ioc, val) ((val) << (ioc)->hint_shift_pdir)
+
+
+/**
+ * sba_io_pdir_entry - fill in one IO PDIR entry
+ * @pdir_ptr:  pointer to IO PDIR entry
+ * @vba: Virtual CPU address of buffer to map
+ *
+ * SBA Mapping Routine
+ *
+ * Given a virtual address (vba, arg1) sba_io_pdir_entry()
+ * loads the I/O PDIR entry pointed to by pdir_ptr (arg0).
+ * Each IO Pdir entry consists of 8 bytes as shown below
+ * (LSB == bit 0):
+ *
+ *  63                    40                                 11    7        0
+ * +-+---------------------+----------------------------------+----+--------+
+ * |V|        U            |            PPN[39:12]            | U  |   FF   |
+ * +-+---------------------+----------------------------------+----+--------+
+ *
+ *  V  == Valid Bit
+ *  U  == Unused
+ * PPN == Physical Page Number
+ *
+ * The physical address fields are filled with the results of virt_to_phys()
+ * on the vba.
+ */
+
+#if 1
+#define sba_io_pdir_entry(pdir_ptr, vba) *pdir_ptr = ((vba & ~0xE000000000000FFFULL) | 0x80000000000000FFULL)
+#else
+void SBA_INLINE
+sba_io_pdir_entry(u64 *pdir_ptr, unsigned long vba)
+{
+	*pdir_ptr = ((vba & ~0xE000000000000FFFULL) | 0x80000000000000FFULL);
+}
+#endif
+
+#ifdef ENABLE_MARK_CLEAN
+/**
+ * Since DMA is i-cache coherent, any (complete) pages that were written via
+ * DMA can be marked as "clean" so that update_mmu_cache() doesn't have to
+ * flush them when they get mapped into an executable vm-area.
+ */
+static void
+mark_clean (void *addr, size_t size)
+{
+	unsigned long pg_addr, end;
+
+	pg_addr = PAGE_ALIGN((unsigned long) addr);
+	end = (unsigned long) addr + size;
+	while (pg_addr + PAGE_SIZE <= end) {
+		struct page *page = virt_to_page(pg_addr);
+		set_bit(PG_arch_1, &page->flags);
+		pg_addr += PAGE_SIZE;
+	}
+}
+#endif
+
+/**
+ * sba_mark_invalid - invalidate one or more IO PDIR entries
+ * @ioc: IO MMU structure which owns the pdir we are interested in.
+ * @iova:  IO Virtual Address mapped earlier
+ * @byte_cnt:  number of bytes this mapping covers.
+ *
+ * Marking the IO PDIR entry(ies) as Invalid and invalidate
+ * corresponding IO TLB entry. The PCOM (Purge Command Register)
+ * is to purge stale entries in the IO TLB when unmapping entries.
+ *
+ * The PCOM register supports purging of multiple pages, with a minium
+ * of 1 page and a maximum of 2GB. Hardware requires the address be
+ * aligned to the size of the range being purged. The size of the range
+ * must be a power of 2. The "Cool perf optimization" in the
+ * allocation routine helps keep that true.
+ */
+static SBA_INLINE void
+sba_mark_invalid(struct ioc *ioc, dma_addr_t iova, size_t byte_cnt)
+{
+	u32 iovp = (u32) SBA_IOVP(ioc,iova);
+
+	int off = PDIR_INDEX(iovp);
+
+	/* Must be non-zero and rounded up */
+	ASSERT(byte_cnt > 0);
+	ASSERT(0 == (byte_cnt & ~IOVP_MASK));
+
+#ifdef ASSERT_PDIR_SANITY
+	/* Assert first pdir entry is set */
+	if (!(ioc->pdir_base[off] >> 60)) {
+		sba_dump_pdir_entry(ioc,"sba_mark_invalid()", PDIR_INDEX(iovp));
+	}
+#endif
+
+	if (byte_cnt <= IOVP_SIZE)
+	{
+		ASSERT(off < ioc->pdir_size);
+
+		iovp |= IOVP_SHIFT;     /* set "size" field for PCOM */
+
+		/*
+		** clear I/O PDIR entry "valid" bit
+		** Do NOT clear the rest - save it for debugging.
+		** We should only clear bits that have previously
+		** been enabled.
+		*/
+		ioc->pdir_base[off] &= ~(0x80000000000000FFULL);
+	} else {
+		u32 t = get_order(byte_cnt) + PAGE_SHIFT;
+
+		iovp |= t;
+		ASSERT(t <= 31);   /* 2GB! Max value of "size" field */
+
+		do {
+			/* verify this pdir entry is enabled */
+			ASSERT(ioc->pdir_base[off]  >> 63);
+			/* clear I/O Pdir entry "valid" bit first */
+			ioc->pdir_base[off] &= ~(0x80000000000000FFULL);
+			off++;
+			byte_cnt -= IOVP_SIZE;
+		} while (byte_cnt > 0);
+	}
+
+	WRITE_REG(iovp, ioc->ioc_hpa+IOC_PCOM);
+}
+
+/**
+ * sba_map_single - map one buffer and return IOVA for DMA
+ * @dev: instance of PCI owned by the driver that's asking.
+ * @addr:  driver buffer to map.
+ * @size:  number of bytes to map in driver buffer.
+ * @direction:  R/W or both.
+ *
+ * See Documentation/DMA-mapping.txt
+ */
+dma_addr_t
+sba_map_single(struct pci_dev *dev, void *addr, size_t size, int direction)
+{
+	struct ioc *ioc;
+	unsigned long flags; 
+	dma_addr_t iovp;
+	dma_addr_t offset;
+	u64 *pdir_start;
+	int pide;
+#ifdef ALLOW_IOV_BYPASS
+	unsigned long pci_addr = virt_to_phys(addr);
+#endif
+
+	ioc = GET_IOC(dev);
+	ASSERT(ioc);
+
+#ifdef ALLOW_IOV_BYPASS
+	/*
+ 	** Check if the PCI device can DMA to ptr... if so, just return ptr
+ 	*/
+	if ((pci_addr & ~dev->dma_mask) == 0) {
+		/*
+ 		** Device is bit capable of DMA'ing to the buffer...
+		** just return the PCI address of ptr
+ 		*/
+#ifdef CONFIG_PROC_FS
+		spin_lock_irqsave(&ioc->res_lock, flags);
+		ioc->msingle_bypass++;
+		spin_unlock_irqrestore(&ioc->res_lock, flags);
+#endif
+		DBG_BYPASS("sba_map_single() bypass mask/addr: 0x%lx/0x%lx\n",
+		           dev->dma_mask, pci_addr);
+		return pci_addr;
+	}
+#endif
+
+	ASSERT(size > 0);
+	ASSERT(size <= DMA_CHUNK_SIZE);
+
+	/* save offset bits */
+	offset = ((dma_addr_t) (long) addr) & ~IOVP_MASK;
+
+	/* round up to nearest IOVP_SIZE */
+	size = (size + offset + ~IOVP_MASK) & IOVP_MASK;
+
+	spin_lock_irqsave(&ioc->res_lock, flags);
+#ifdef ASSERT_PDIR_SANITY
+	if (sba_check_pdir(ioc,"Check before sba_map_single()"))
+		panic("Sanity check failed");
+#endif
+
+#ifdef CONFIG_PROC_FS
+	ioc->msingle_calls++;
+	ioc->msingle_pages += size >> IOVP_SHIFT;
+#endif
+	pide = sba_alloc_range(ioc, size);
+	iovp = (dma_addr_t) pide << IOVP_SHIFT;
+
+	DBG_RUN("%s() 0x%p -> 0x%lx\n",
+		__FUNCTION__, addr, (long) iovp | offset);
+
+	pdir_start = &(ioc->pdir_base[pide]);
+
+	while (size > 0) {
+		ASSERT(((u8 *)pdir_start)[7] == 0); /* verify availability */
+		sba_io_pdir_entry(pdir_start, (unsigned long) addr);
+
+		DBG_RUN("     pdir 0x%p %lx\n", pdir_start, *pdir_start);
+
+		addr += IOVP_SIZE;
+		size -= IOVP_SIZE;
+		pdir_start++;
+	}
+	/* form complete address */
+#ifdef ASSERT_PDIR_SANITY
+	sba_check_pdir(ioc,"Check after sba_map_single()");
+#endif
+	spin_unlock_irqrestore(&ioc->res_lock, flags);
+	return SBA_IOVA(ioc, iovp, offset, DEFAULT_DMA_HINT_REG);
+}
+
+/**
+ * sba_unmap_single - unmap one IOVA and free resources
+ * @dev: instance of PCI owned by the driver that's asking.
+ * @iova:  IOVA of driver buffer previously mapped.
+ * @size:  number of bytes mapped in driver buffer.
+ * @direction:  R/W or both.
+ *
+ * See Documentation/DMA-mapping.txt
+ */
+void sba_unmap_single(struct pci_dev *dev, dma_addr_t iova, size_t size,
+		int direction)
+{
+	struct ioc *ioc;
+#if DELAYED_RESOURCE_CNT > 0
+	struct sba_dma_pair *d;
+#endif
+	unsigned long flags; 
+	dma_addr_t offset;
+
+	ioc = GET_IOC(dev);
+	ASSERT(ioc);
+
+#ifdef ALLOW_IOV_BYPASS
+	if ((iova & ioc->imask) != ioc->ibase) {
+		/*
+		** Address does not fall w/in IOVA, must be bypassing
+		*/
+#ifdef CONFIG_PROC_FS
+		spin_lock_irqsave(&ioc->res_lock, flags);
+		ioc->usingle_bypass++;
+		spin_unlock_irqrestore(&ioc->res_lock, flags);
+#endif
+		DBG_BYPASS("sba_unmap_single() bypass addr: 0x%lx\n", iova);
+
+#ifdef ENABLE_MARK_CLEAN
+		if (direction == PCI_DMA_FROMDEVICE) {
+			mark_clean(phys_to_virt(iova), size);
+		}
+#endif
+		return;
+	}
+#endif
+	offset = iova & ~IOVP_MASK;
+
+	DBG_RUN("%s() iovp 0x%lx/%x\n",
+		__FUNCTION__, (long) iova, size);
+
+	iova ^= offset;        /* clear offset bits */
+	size += offset;
+	size = ROUNDUP(size, IOVP_SIZE);
+
+	spin_lock_irqsave(&ioc->res_lock, flags);
+#ifdef CONFIG_PROC_FS
+	ioc->usingle_calls++;
+	ioc->usingle_pages += size >> IOVP_SHIFT;
+#endif
+
+#if DELAYED_RESOURCE_CNT > 0
+	d = &(ioc->saved[ioc->saved_cnt]);
+	d->iova = iova;
+	d->size = size;
+	if (++(ioc->saved_cnt) >= DELAYED_RESOURCE_CNT) {
+		int cnt = ioc->saved_cnt;
+		while (cnt--) {
+			sba_mark_invalid(ioc, d->iova, d->size);
+			sba_free_range(ioc, d->iova, d->size);
+			d--;
+		}
+		ioc->saved_cnt = 0;
+		READ_REG(ioc->ioc_hpa+IOC_PCOM);	/* flush purges */
+	}
+#else /* DELAYED_RESOURCE_CNT == 0 */
+	sba_mark_invalid(ioc, iova, size);
+	sba_free_range(ioc, iova, size);
+	READ_REG(ioc->ioc_hpa+IOC_PCOM);	/* flush purges */
+#endif /* DELAYED_RESOURCE_CNT == 0 */
+#ifdef ENABLE_MARK_CLEAN
+	if (direction == PCI_DMA_FROMDEVICE) {
+		u32 iovp = (u32) SBA_IOVP(ioc,iova);
+		int off = PDIR_INDEX(iovp);
+		void *addr;
+
+		if (size <= IOVP_SIZE) {
+			addr = phys_to_virt(ioc->pdir_base[off] &
+					    ~0xE000000000000FFFULL);
+			mark_clean(addr, size);
+		} else {
+			size_t byte_cnt = size;
+
+			do {
+				addr = phys_to_virt(ioc->pdir_base[off] &
+				                    ~0xE000000000000FFFULL);
+				mark_clean(addr, min(byte_cnt, IOVP_SIZE));
+				off++;
+				byte_cnt -= IOVP_SIZE;
+
+			   } while (byte_cnt > 0);
+		}
+	}
+#endif
+	spin_unlock_irqrestore(&ioc->res_lock, flags);
+
+	/* XXX REVISIT for 2.5 Linux - need syncdma for zero-copy support.
+	** For Astro based systems this isn't a big deal WRT performance.
+	** As long as 2.4 kernels copyin/copyout data from/to userspace,
+	** we don't need the syncdma. The issue here is I/O MMU cachelines
+	** are *not* coherent in all cases.  May be hwrev dependent.
+	** Need to investigate more.
+	asm volatile("syncdma");	
+	*/
+}
+
+
+/**
+ * sba_alloc_consistent - allocate/map shared mem for DMA
+ * @hwdev: instance of PCI owned by the driver that's asking.
+ * @size:  number of bytes mapped in driver buffer.
+ * @dma_handle:  IOVA of new buffer.
+ *
+ * See Documentation/DMA-mapping.txt
+ */
+void *
+sba_alloc_consistent(struct pci_dev *hwdev, size_t size, dma_addr_t *dma_handle)
+{
+	void *ret;
+
+	if (!hwdev) {
+		/* only support PCI */
+		*dma_handle = 0;
+		return 0;
+	}
+
+        ret = (void *) __get_free_pages(GFP_ATOMIC, get_order(size));
+
+	if (ret) {
+		memset(ret, 0, size);
+		*dma_handle = sba_map_single(hwdev, ret, size, 0);
+	}
+
+	return ret;
+}
+
+
+/**
+ * sba_free_consistent - free/unmap shared mem for DMA
+ * @hwdev: instance of PCI owned by the driver that's asking.
+ * @size:  number of bytes mapped in driver buffer.
+ * @vaddr:  virtual address IOVA of "consistent" buffer.
+ * @dma_handler:  IO virtual address of "consistent" buffer.
+ *
+ * See Documentation/DMA-mapping.txt
+ */
+void sba_free_consistent(struct pci_dev *hwdev, size_t size, void *vaddr,
+		dma_addr_t dma_handle)
+{
+	sba_unmap_single(hwdev, dma_handle, size, 0);
+	free_pages((unsigned long) vaddr, get_order(size));
+}
+
+
+/*
+** Since 0 is a valid pdir_base index value, can't use that
+** to determine if a value is valid or not. Use a flag to indicate
+** the SG list entry contains a valid pdir index.
+*/
+#define PIDE_FLAG 0x1UL
+
+#ifdef DEBUG_LARGE_SG_ENTRIES
+int dump_run_sg = 0;
+#endif
+
+
+/**
+ * sba_fill_pdir - write allocated SG entries into IO PDIR
+ * @ioc: IO MMU structure which owns the pdir we are interested in.
+ * @startsg:  list of IOVA/size pairs
+ * @nents: number of entries in startsg list
+ *
+ * Take preprocessed SG list and write corresponding entries
+ * in the IO PDIR.
+ */
+
+static SBA_INLINE int
+sba_fill_pdir(
+	struct ioc *ioc,
+	struct scatterlist *startsg,
+	int nents)
+{
+	struct scatterlist *dma_sg = startsg;	/* pointer to current DMA */
+	int n_mappings = 0;
+	u64 *pdirp = 0;
+	unsigned long dma_offset = 0;
+
+	dma_sg--;
+	while (nents-- > 0) {
+		int     cnt = sba_sg_len(startsg);
+		sba_sg_len(startsg) = 0;
+
+#ifdef DEBUG_LARGE_SG_ENTRIES
+		if (dump_run_sg)
+			printk(" %2d : %08lx/%05x %p\n",
+				nents,
+				(unsigned long) sba_sg_iova(startsg), cnt,
+				sba_sg_buffer(startsg)
+		);
+#else
+		DBG_RUN_SG(" %d : %08lx/%05x %p\n",
+				nents,
+				(unsigned long) sba_sg_iova(startsg), cnt,
+				sba_sg_buffer(startsg)
+		);
+#endif
+		/*
+		** Look for the start of a new DMA stream
+		*/
+		if ((u64)sba_sg_iova(startsg) & PIDE_FLAG) {
+			u32 pide = (u64)sba_sg_iova(startsg) & ~PIDE_FLAG;
+			dma_offset = (unsigned long) pide & ~IOVP_MASK;
+			sba_sg_iova(startsg) = 0;
+			dma_sg++;
+			sba_sg_iova(dma_sg) = (char *)(pide | ioc->ibase);
+			pdirp = &(ioc->pdir_base[pide >> IOVP_SHIFT]);
+			n_mappings++;
+		}
+
+		/*
+		** Look for a VCONTIG chunk
+		*/
+		if (cnt) {
+			unsigned long vaddr = (unsigned long) sba_sg_buffer(startsg);
+			ASSERT(pdirp);
+
+			/* Since multiple Vcontig blocks could make up
+			** one DMA stream, *add* cnt to dma_len.
+			*/
+			sba_sg_len(dma_sg) += cnt;
+			cnt += dma_offset;
+			dma_offset=0;	/* only want offset on first chunk */
+			cnt = ROUNDUP(cnt, IOVP_SIZE);
+#ifdef CONFIG_PROC_FS
+			ioc->msg_pages += cnt >> IOVP_SHIFT;
+#endif
+			do {
+				sba_io_pdir_entry(pdirp, vaddr);
+				vaddr += IOVP_SIZE;
+				cnt -= IOVP_SIZE;
+				pdirp++;
+			} while (cnt > 0);
+		}
+		startsg++;
+	}
+#ifdef DEBUG_LARGE_SG_ENTRIES
+	dump_run_sg = 0;
+#endif
+	return(n_mappings);
+}
+
+
+/*
+** Two address ranges are DMA contiguous *iff* "end of prev" and
+** "start of next" are both on a page boundry.
+**
+** (shift left is a quick trick to mask off upper bits)
+*/
+#define DMA_CONTIG(__X, __Y) \
+	(((((unsigned long) __X) | ((unsigned long) __Y)) << (BITS_PER_LONG - PAGE_SHIFT)) == 0UL)
+
+
+/**
+ * sba_coalesce_chunks - preprocess the SG list
+ * @ioc: IO MMU structure which owns the pdir we are interested in.
+ * @startsg:  list of IOVA/size pairs
+ * @nents: number of entries in startsg list
+ *
+ * First pass is to walk the SG list and determine where the breaks are
+ * in the DMA stream. Allocates PDIR entries but does not fill them.
+ * Returns the number of DMA chunks.
+ *
+ * Doing the fill seperate from the coalescing/allocation keeps the
+ * code simpler. Future enhancement could make one pass through
+ * the sglist do both.
+ */
+static SBA_INLINE int
+sba_coalesce_chunks( struct ioc *ioc,
+	struct scatterlist *startsg,
+	int nents)
+{
+	struct scatterlist *vcontig_sg;    /* VCONTIG chunk head */
+	unsigned long vcontig_len;         /* len of VCONTIG chunk */
+	unsigned long vcontig_end;
+	struct scatterlist *dma_sg;        /* next DMA stream head */
+	unsigned long dma_offset, dma_len; /* start/len of DMA stream */
+	int n_mappings = 0;
+
+	while (nents > 0) {
+		unsigned long vaddr = (unsigned long) (startsg->address); 
+
+		/*
+		** Prepare for first/next DMA stream
+		*/
+		dma_sg = vcontig_sg = startsg;
+		dma_len = vcontig_len = vcontig_end = sba_sg_len(startsg);
+		vcontig_end +=  vaddr;
+		dma_offset = vaddr & ~IOVP_MASK;
+
+		/* PARANOID: clear entries */
+		sba_sg_buffer(startsg) = sba_sg_iova(startsg);
+		sba_sg_iova(startsg) = 0;
+		sba_sg_len(startsg) = 0;
+
+		/*
+		** This loop terminates one iteration "early" since
+		** it's always looking one "ahead".
+		*/
+		while (--nents > 0) {
+			unsigned long vaddr;	/* tmp */
+
+			startsg++;
+
+			/* catch brokenness in SCSI layer */
+			ASSERT(startsg->length <= DMA_CHUNK_SIZE);
+
+			/*
+			** First make sure current dma stream won't
+			** exceed DMA_CHUNK_SIZE if we coalesce the
+			** next entry.
+			*/
+			if (((dma_len + dma_offset + startsg->length + ~IOVP_MASK) & IOVP_MASK) > DMA_CHUNK_SIZE)
+				break;
+
+			/*
+			** Then look for virtually contiguous blocks.
+			**
+			** append the next transaction?
+			*/
+			vaddr = (unsigned long) sba_sg_iova(startsg);
+			if  (vcontig_end == vaddr)
+			{
+				vcontig_len += sba_sg_len(startsg);
+				vcontig_end += sba_sg_len(startsg);
+				dma_len     += sba_sg_len(startsg);
+				sba_sg_buffer(startsg) = (char *)vaddr;
+				sba_sg_iova(startsg) = 0;
+				sba_sg_len(startsg) = 0;
+				continue;
+			}
+
+#ifdef DEBUG_LARGE_SG_ENTRIES
+			dump_run_sg = (vcontig_len > IOVP_SIZE);
+#endif
+
+			/*
+			** Not virtually contigous.
+			** Terminate prev chunk.
+			** Start a new chunk.
+			**
+			** Once we start a new VCONTIG chunk, dma_offset
+			** can't change. And we need the offset from the first
+			** chunk - not the last one. Ergo Successive chunks
+			** must start on page boundaries and dove tail
+			** with it's predecessor.
+			*/
+			sba_sg_len(vcontig_sg) = vcontig_len;
+
+			vcontig_sg = startsg;
+			vcontig_len = sba_sg_len(startsg);
+
+			/*
+			** 3) do the entries end/start on page boundaries?
+			**    Don't update vcontig_end until we've checked.
+			*/
+			if (DMA_CONTIG(vcontig_end, vaddr))
+			{
+				vcontig_end = vcontig_len + vaddr;
+				dma_len += vcontig_len;
+				sba_sg_buffer(startsg) = (char *)vaddr;
+				sba_sg_iova(startsg) = 0;
+				continue;
+			} else {
+				break;
+			}
+		}
+
+		/*
+		** End of DMA Stream
+		** Terminate last VCONTIG block.
+		** Allocate space for DMA stream.
+		*/
+		sba_sg_len(vcontig_sg) = vcontig_len;
+		dma_len = (dma_len + dma_offset + ~IOVP_MASK) & IOVP_MASK;
+		ASSERT(dma_len <= DMA_CHUNK_SIZE);
+		sba_sg_iova(dma_sg) = (char *) (PIDE_FLAG 
+			| (sba_alloc_range(ioc, dma_len) << IOVP_SHIFT)
+			| dma_offset);
+		n_mappings++;
+	}
+
+	return n_mappings;
+}
+
+
+/**
+ * sba_map_sg - map Scatter/Gather list
+ * @dev: instance of PCI owned by the driver that's asking.
+ * @sglist:  array of buffer/length pairs
+ * @nents:  number of entries in list
+ * @direction:  R/W or both.
+ *
+ * See Documentation/DMA-mapping.txt
+ */
+int sba_map_sg(struct pci_dev *dev, struct scatterlist *sglist, int nents,
+		int direction)
+{
+	struct ioc *ioc;
+	int coalesced, filled = 0;
+	unsigned long flags;
+#ifdef ALLOW_IOV_BYPASS
+	struct scatterlist *sg;
+#endif
+
+	DBG_RUN_SG("%s() START %d entries\n", __FUNCTION__, nents);
+	ioc = GET_IOC(dev);
+	ASSERT(ioc);
+
+#ifdef ALLOW_IOV_BYPASS
+	if (dev->dma_mask >= ioc->dma_mask) {
+		for (sg = sglist ; filled < nents ; filled++, sg++){
+			sba_sg_buffer(sg) = sba_sg_iova(sg);
+			sba_sg_iova(sg) = (char *)virt_to_phys(sba_sg_buffer(sg));
+		}
+#ifdef CONFIG_PROC_FS
+		spin_lock_irqsave(&ioc->res_lock, flags);
+		ioc->msg_bypass++;
+		spin_unlock_irqrestore(&ioc->res_lock, flags);
+#endif
+		return filled;
+	}
+#endif
+	/* Fast path single entry scatterlists. */
+	if (nents == 1) {
+		sba_sg_buffer(sglist) = sba_sg_iova(sglist);
+		sba_sg_iova(sglist) = (char *)sba_map_single(dev,
+						sba_sg_buffer(sglist),
+						sba_sg_len(sglist), direction);
+#ifdef CONFIG_PROC_FS
+		/*
+		** Should probably do some stats counting, but trying to
+		** be precise quickly starts wasting CPU time.
+		*/
+#endif
+		return 1;
+	}
+
+	spin_lock_irqsave(&ioc->res_lock, flags);
+
+#ifdef ASSERT_PDIR_SANITY
+	if (sba_check_pdir(ioc,"Check before sba_map_sg()"))
+	{
+		sba_dump_sg(ioc, sglist, nents);
+		panic("Check before sba_map_sg()");
+	}
+#endif
+
+#ifdef CONFIG_PROC_FS
+	ioc->msg_calls++;
+#endif
+
+	/*
+	** First coalesce the chunks and allocate I/O pdir space
+	**
+	** If this is one DMA stream, we can properly map using the
+	** correct virtual address associated with each DMA page.
+	** w/o this association, we wouldn't have coherent DMA!
+	** Access to the virtual address is what forces a two pass algorithm.
+	*/
+	coalesced = sba_coalesce_chunks(ioc, sglist, nents);
+ 
+	/*
+	** Program the I/O Pdir
+	**
+	** map the virtual addresses to the I/O Pdir
+	** o dma_address will contain the pdir index
+	** o dma_len will contain the number of bytes to map 
+	** o address contains the virtual address.
+	*/
+	filled = sba_fill_pdir(ioc, sglist, nents);
+
+#ifdef ASSERT_PDIR_SANITY
+	if (sba_check_pdir(ioc,"Check after sba_map_sg()"))
+	{
+		sba_dump_sg(ioc, sglist, nents);
+		panic("Check after sba_map_sg()\n");
+	}
+#endif
+
+	spin_unlock_irqrestore(&ioc->res_lock, flags);
+
+	ASSERT(coalesced == filled);
+	DBG_RUN_SG("%s() DONE %d mappings\n", __FUNCTION__, filled);
+
+	return filled;
+}
+
+
+/**
+ * sba_unmap_sg - unmap Scatter/Gather list
+ * @dev: instance of PCI owned by the driver that's asking.
+ * @sglist:  array of buffer/length pairs
+ * @nents:  number of entries in list
+ * @direction:  R/W or both.
+ *
+ * See Documentation/DMA-mapping.txt
+ */
+void sba_unmap_sg(struct pci_dev *dev, struct scatterlist *sglist, int nents,
+		int direction)
+{
+	struct ioc *ioc;
+#ifdef ASSERT_PDIR_SANITY
+	unsigned long flags;
+#endif
+
+	DBG_RUN_SG("%s() START %d entries,  %p,%x\n",
+		__FUNCTION__, nents, sba_sg_buffer(sglist), sglist->length);
+
+	ioc = GET_IOC(dev);
+	ASSERT(ioc);
+
+#ifdef CONFIG_PROC_FS
+	ioc->usg_calls++;
+#endif
+
+#ifdef ASSERT_PDIR_SANITY
+	spin_lock_irqsave(&ioc->res_lock, flags);
+	sba_check_pdir(ioc,"Check before sba_unmap_sg()");
+	spin_unlock_irqrestore(&ioc->res_lock, flags);
+#endif
+
+	while (sba_sg_len(sglist) && nents--) {
+
+		sba_unmap_single(dev, (dma_addr_t)sba_sg_iova(sglist),
+		                 sba_sg_len(sglist), direction);
+#ifdef CONFIG_PROC_FS
+		/*
+		** This leaves inconsistent data in the stats, but we can't
+		** tell which sg lists were mapped by map_single and which
+		** were coalesced to a single entry.  The stats are fun,
+		** but speed is more important.
+		*/
+		ioc->usg_pages += (((u64)sba_sg_iova(sglist) & ~IOVP_MASK) + sba_sg_len(sglist) + IOVP_SIZE - 1) >> PAGE_SHIFT;
+#endif
+		++sglist;
+	}
+
+	DBG_RUN_SG("%s() DONE (nents %d)\n", __FUNCTION__,  nents);
+
+#ifdef ASSERT_PDIR_SANITY
+	spin_lock_irqsave(&ioc->res_lock, flags);
+	sba_check_pdir(ioc,"Check after sba_unmap_sg()");
+	spin_unlock_irqrestore(&ioc->res_lock, flags);
+#endif
+
+}
+
+unsigned long
+sba_dma_address (struct scatterlist *sg)
+{
+	return ((unsigned long)sba_sg_iova(sg));
+}
+
+/**************************************************************
+*
+*   Initialization and claim
+*
+***************************************************************/
+
+
+static void
+sba_ioc_init(struct sba_device *sba_dev, struct ioc *ioc, int ioc_num)
+{
+	u32 iova_space_size, iova_space_mask;
+	void * pdir_base;
+	int pdir_size, iov_order, tcnfg;
+
+	/*
+	** Firmware programs the maximum IOV space size into the imask reg
+	*/
+	iova_space_size = ~(READ_REG(ioc->ioc_hpa + IOC_IMASK) & 0xFFFFFFFFUL) + 1;
+#ifdef CONFIG_IA64_HP_PROTO
+	if (!iova_space_size)
+		iova_space_size = GB(1);
+#endif
+
+	/*
+	** iov_order is always based on a 1GB IOVA space since we want to
+	** turn on the other half for AGP GART.
+	*/
+	iov_order = get_order(iova_space_size >> (IOVP_SHIFT-PAGE_SHIFT));
+	ioc->pdir_size = pdir_size = (iova_space_size/IOVP_SIZE) * sizeof(u64);
+
+	DBG_INIT("%s() hpa 0x%lx IOV %dMB (%d bits) PDIR size 0x%0x\n",
+		__FUNCTION__, ioc->ioc_hpa, iova_space_size>>20,
+		iov_order + PAGE_SHIFT, ioc->pdir_size);
+
+	/* FIXME : DMA HINTs not used */
+	ioc->hint_shift_pdir = iov_order + PAGE_SHIFT;
+	ioc->hint_mask_pdir = ~(0x3 << (iov_order + PAGE_SHIFT));
+
+	ioc->pdir_base =
+	pdir_base = (void *) __get_free_pages(GFP_KERNEL, get_order(pdir_size));
+	if (NULL == pdir_base)
+	{
+		panic(__FILE__ ":%s() could not allocate I/O Page Table\n", __FUNCTION__);
+	}
+	memset(pdir_base, 0, pdir_size);
+
+	DBG_INIT("%s() pdir %p size %x hint_shift_pdir %x hint_mask_pdir %lx\n",
+		__FUNCTION__, pdir_base, pdir_size,
+		ioc->hint_shift_pdir, ioc->hint_mask_pdir);
+
+	ASSERT((((unsigned long) pdir_base) & PAGE_MASK) == (unsigned long) pdir_base);
+	WRITE_REG(virt_to_phys(pdir_base), ioc->ioc_hpa + IOC_PDIR_BASE);
+
+	DBG_INIT(" base %p\n", pdir_base);
+
+	/* build IMASK for IOC and Elroy */
+	iova_space_mask =  0xffffffff;
+	iova_space_mask <<= (iov_order + PAGE_SHIFT);
+
+#ifdef CONFIG_IA64_HP_PROTO
+	/*
+	** REVISIT - this is a kludge, but we won't be supporting anything but
+	** zx1 2.0 or greater for real.  When fw is in shape, ibase will
+	** be preprogrammed w/ the IOVA hole base and imask will give us
+	** the size.
+	*/
+	if ((sba_dev->hw_rev & 0xFF) < 0x20) {
+		DBG_INIT("%s() Found SBA rev < 2.0, setting IOVA base to 0.  This device will not be supported in the future.\n", __FUNCTION__);
+		ioc->ibase = 0x0;
+	} else
+#endif
+	ioc->ibase = READ_REG(ioc->ioc_hpa + IOC_IBASE) & 0xFFFFFFFEUL;
+
+	ioc->imask = iova_space_mask;	/* save it */
+
+	DBG_INIT("%s() IOV base 0x%lx mask 0x%0lx\n",
+		__FUNCTION__, ioc->ibase, ioc->imask);
+
+	/*
+	** FIXME: Hint registers are programmed with default hint
+	** values during boot, so hints should be sane even if we
+	** can't reprogram them the way drivers want.
+	*/
+
+	WRITE_REG(ioc->imask, ioc->ioc_hpa+IOC_IMASK);
+
+	/*
+	** Setting the upper bits makes checking for bypass addresses
+	** a little faster later on.
+	*/
+	ioc->imask |= 0xFFFFFFFF00000000UL;
+
+	/* Set I/O PDIR Page size to system page size */
+	switch (PAGE_SHIFT) {
+		case 12: /* 4K */
+			tcnfg = 0;
+			break;
+		case 13: /* 8K */
+			tcnfg = 1;
+			break;
+		case 14: /* 16K */
+			tcnfg = 2;
+			break;
+		case 16: /* 64K */
+			tcnfg = 3;
+			break;
+	}
+	WRITE_REG(tcnfg, ioc->ioc_hpa+IOC_TCNFG);
+
+	/*
+	** Program the IOC's ibase and enable IOVA translation
+	** Bit zero == enable bit.
+	*/
+	WRITE_REG(ioc->ibase | 1, ioc->ioc_hpa+IOC_IBASE);
+
+	/*
+	** Clear I/O TLB of any possible entries.
+	** (Yes. This is a bit paranoid...but so what)
+	*/
+	WRITE_REG(0 | 31, ioc->ioc_hpa+IOC_PCOM);
+
+	/*
+	** If an AGP device is present, only use half of the IOV space
+	** for PCI DMA.  Unfortunately we can't know ahead of time
+	** whether GART support will actually be used, for now we
+	** can just key on an AGP device found in the system.
+	** We program the next pdir index after we stop w/ a key for
+	** the GART code to handshake on.
+	*/
+	if (SBA_GET_AGP(sba_dev)) {
+		DBG_INIT("%s() AGP Device found, reserving 512MB for GART support\n", __FUNCTION__);
+		ioc->pdir_size /= 2;
+		((u64 *)pdir_base)[PDIR_INDEX(iova_space_size/2)] = 0x0000badbadc0ffeeULL;
+	}
+
+	DBG_INIT("%s() DONE\n", __FUNCTION__);
+}
+
+
+
+/**************************************************************************
+**
+**   SBA initialization code (HW and SW)
+**
+**   o identify SBA chip itself
+**   o FIXME: initialize DMA hints for reasonable defaults
+**
+**************************************************************************/
+
+static void
+sba_hw_init(struct sba_device *sba_dev)
+{ 
+	int i;
+	int num_ioc;
+	u64 dma_mask;
+	u32 func_id;
+
+	/*
+	** Identify the SBA so we can set the dma_mask.  We can make a virtual
+	** dma_mask of the memory subsystem such that devices not implmenting
+	** a full 64bit mask might still be able to bypass efficiently.
+	*/
+	func_id = READ_REG(sba_dev->sba_hpa + SBA_FUNC_ID);
+
+	if (func_id == ZX1_FUNC_ID_VALUE) {
+		dma_mask = 0xFFFFFFFFFFUL;
+	} else {
+		dma_mask = 0xFFFFFFFFFFFFFFFFUL;
+	}
+
+	DBG_INIT("%s(): ioc->dma_mask == 0x%lx\n", __FUNCTION__, dma_mask);
+	
+	/*
+	** Leaving in the multiple ioc code from parisc for the future,
+	** currently there are no muli-ioc mckinley sbas
+	*/
+	sba_dev->ioc[0].ioc_hpa = SBA_IOC_OFFSET;
+	num_ioc = 1;
+
+	sba_dev->num_ioc = num_ioc;
+	for (i = 0; i < num_ioc; i++) {
+		sba_dev->ioc[i].dma_mask = dma_mask;
+		sba_dev->ioc[i].ioc_hpa += sba_dev->sba_hpa;
+		sba_ioc_init(sba_dev, &(sba_dev->ioc[i]), i);
+	}
+}
+
+static void
+sba_common_init(struct sba_device *sba_dev)
+{
+	int i;
+
+	/* add this one to the head of the list (order doesn't matter)
+	** This will be useful for debugging - especially if we get coredumps
+	*/
+	sba_dev->next = sba_list;
+	sba_list = sba_dev;
+	sba_count++;
+
+	for(i=0; i< sba_dev->num_ioc; i++) {
+		int res_size;
+
+		/* resource map size dictated by pdir_size */
+		res_size = sba_dev->ioc[i].pdir_size/sizeof(u64); /* entries */
+		res_size >>= 3;  /* convert bit count to byte count */
+		DBG_INIT("%s() res_size 0x%x\n",
+			__FUNCTION__, res_size);
+
+		sba_dev->ioc[i].res_size = res_size;
+		sba_dev->ioc[i].res_map = (char *) __get_free_pages(GFP_KERNEL, get_order(res_size));
+
+		if (NULL == sba_dev->ioc[i].res_map)
+		{
+			panic(__FILE__ ":%s() could not allocate resource map\n", __FUNCTION__ );
+		}
+
+		memset(sba_dev->ioc[i].res_map, 0, res_size);
+		/* next available IOVP - circular search */
+		if ((sba_dev->hw_rev & 0xFF) >= 0x20) {
+			sba_dev->ioc[i].res_hint = (unsigned long *)
+			    sba_dev->ioc[i].res_map;
+		} else {
+			u64 reserved_iov;
+			
+			/* Yet another 1.x hack */
+			printk("zx1 1.x: Starting resource hint offset into IOV space to avoid initial zero value IOVA\n");
+			sba_dev->ioc[i].res_hint = (unsigned long *)
+			    &(sba_dev->ioc[i].res_map[L1_CACHE_BYTES]);
+
+			sba_dev->ioc[i].res_map[0] = 0x1;
+			sba_dev->ioc[i].pdir_base[0] = 0x8000badbadc0ffeeULL;
+
+			for (reserved_iov = 0xA0000 ; reserved_iov < 0xC0000 ; reserved_iov += IOVP_SIZE) {
+				u64 *res_ptr = sba_dev->ioc[i].res_map;
+				int index = PDIR_INDEX(reserved_iov);
+				int res_word;
+				u64 mask;
+
+				res_word = (int)(index / BITS_PER_LONG);
+				mask =  0x1UL << (index - (res_word * BITS_PER_LONG));
+				res_ptr[res_word] |= mask;
+				sba_dev->ioc[i].pdir_base[PDIR_INDEX(reserved_iov)] = (0x80000000000000FFULL | reserved_iov);
+
+			}
+		}
+
+#ifdef ASSERT_PDIR_SANITY
+		/* Mark first bit busy - ie no IOVA 0 */
+		sba_dev->ioc[i].res_map[0] = 0x1;
+		sba_dev->ioc[i].pdir_base[0] = 0x8000badbadc0ffeeULL;
+#endif
+
+		DBG_INIT("%s() %d res_map %x %p\n", __FUNCTION__,
+		         i, res_size, (void *)sba_dev->ioc[i].res_map);
+	}
+
+	sba_dev->sba_lock = SPIN_LOCK_UNLOCKED;
+}
+
+#ifdef CONFIG_PROC_FS
+static int sba_proc_info(char *buf, char **start, off_t offset, int len)
+{
+	struct sba_device *sba_dev = sba_list;
+	struct ioc *ioc = &sba_dev->ioc[0];	/* FIXME: Multi-IOC support! */
+	int total_pages = (int) (ioc->res_size << 3); /* 8 bits per byte */
+	unsigned long i = 0, avg = 0, min, max;
+
+	sprintf(buf, "%s rev %d.%d\n",
+		"Hewlett Packard zx1 SBA",
+		((sba_dev->hw_rev >> 4) & 0xF),
+		(sba_dev->hw_rev & 0xF)
+		);
+	sprintf(buf, "%sIO PDIR size    : %d bytes (%d entries)\n",
+		buf,
+		(int) ((ioc->res_size << 3) * sizeof(u64)), /* 8 bits/byte */
+		total_pages);
+
+	sprintf(buf, "%sIO PDIR entries : %ld free  %ld used (%d%%)\n", buf,
+		total_pages - ioc->used_pages, ioc->used_pages,
+		(int) (ioc->used_pages * 100 / total_pages));
+	
+	sprintf(buf, "%sResource bitmap : %d bytes (%d pages)\n", 
+		buf, ioc->res_size, ioc->res_size << 3);   /* 8 bits per byte */
+
+	min = max = ioc->avg_search[0];
+	for (i = 0; i < SBA_SEARCH_SAMPLE; i++) {
+		avg += ioc->avg_search[i];
+		if (ioc->avg_search[i] > max) max = ioc->avg_search[i];
+		if (ioc->avg_search[i] < min) min = ioc->avg_search[i];
+	}
+	avg /= SBA_SEARCH_SAMPLE;
+	sprintf(buf, "%s  Bitmap search : %ld/%ld/%ld (min/avg/max CPU Cycles)\n",
+		buf, min, avg, max);
+
+	sprintf(buf, "%spci_map_single(): %12ld calls  %12ld pages (avg %d/1000)\n",
+		buf, ioc->msingle_calls, ioc->msingle_pages,
+		(int) ((ioc->msingle_pages * 1000)/ioc->msingle_calls));
+#ifdef ALLOW_IOV_BYPASS
+	sprintf(buf, "%spci_map_single(): %12ld bypasses\n",
+	        buf, ioc->msingle_bypass);
+#endif
+
+	sprintf(buf, "%spci_unmap_single: %12ld calls  %12ld pages (avg %d/1000)\n",
+		buf, ioc->usingle_calls, ioc->usingle_pages,
+		(int) ((ioc->usingle_pages * 1000)/ioc->usingle_calls));
+#ifdef ALLOW_IOV_BYPASS
+	sprintf(buf, "%spci_unmap_single: %12ld bypasses\n",
+	        buf, ioc->usingle_bypass);
+#endif
+
+	sprintf(buf, "%spci_map_sg()    : %12ld calls  %12ld pages (avg %d/1000)\n",
+		buf, ioc->msg_calls, ioc->msg_pages,
+		(int) ((ioc->msg_pages * 1000)/ioc->msg_calls));
+#ifdef ALLOW_IOV_BYPASS
+	sprintf(buf, "%spci_map_sg()    : %12ld bypasses\n",
+	        buf, ioc->msg_bypass);
+#endif
+
+	sprintf(buf, "%spci_unmap_sg()  : %12ld calls  %12ld pages (avg %d/1000)\n",
+		buf, ioc->usg_calls, ioc->usg_pages,
+		(int) ((ioc->usg_pages * 1000)/ioc->usg_calls));
+
+	return strlen(buf);
+}
+
+static int
+sba_resource_map(char *buf, char **start, off_t offset, int len)
+{
+	struct ioc *ioc = sba_list->ioc;	/* FIXME: Multi-IOC support! */
+	unsigned int *res_ptr = (unsigned int *)ioc->res_map;
+	int i;
+
+	buf[0] = '\0';
+	for(i = 0; i < (ioc->res_size / sizeof(unsigned int)); ++i, ++res_ptr) {
+		if ((i & 7) == 0)
+		    strcat(buf,"\n   ");
+		sprintf(buf, "%s %08x", buf, *res_ptr);
+	}
+	strcat(buf, "\n");
+
+	return strlen(buf);
+}
+#endif
+
+/*
+** Determine if sba should claim this chip (return 0) or not (return 1).
+** If so, initialize the chip and tell other partners in crime they
+** have work to do.
+*/
+void __init sba_init(void)
+{
+	struct sba_device *sba_dev;
+	u32 func_id, hw_rev;
+	u32 *func_offset = NULL;
+	int i, agp_found = 0;
+	static char sba_rev[6];
+	struct pci_dev *device = NULL;
+	u64 hpa = 0;
+
+	if (!(device = pci_find_device(PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_ZX1_SBA, NULL)))
+		return;
+
+	for (i = 0; i < PCI_NUM_RESOURCES; i++) {
+		if (pci_resource_flags(device, i) == IORESOURCE_MEM) {
+			hpa = ioremap(pci_resource_start(device, i),
+				pci_resource_len(device, i));
+			break;
+		}
+	}
+
+	func_id = READ_REG(hpa + SBA_FUNC_ID);
+
+	if (func_id == ZX1_FUNC_ID_VALUE) {
+		(void)strcpy(sba_rev, "zx1");
+		func_offset = zx1_func_offsets;
+	} else {
+		return;
+	}
+
+	/* Read HW Rev First */
+	hw_rev = READ_REG(hpa + SBA_FCLASS) & 0xFFUL;
+
+	/*
+	 * Not all revision registers of the chipset are updated on every
+	 * turn.  Must scan through all functions looking for the highest rev
+	 */
+	if (func_offset) {
+		for (i = 0 ; func_offset[i] != -1 ; i++) {
+			u32 func_rev;
+
+			func_rev = READ_REG(hpa + SBA_FCLASS + func_offset[i]) & 0xFFUL;
+			DBG_INIT("%s() func offset: 0x%x rev: 0x%x\n",
+			         __FUNCTION__, func_offset[i], func_rev);
+			if (func_rev > hw_rev)
+				hw_rev = func_rev;
+		}
+	}
+
+	printk(KERN_INFO "%s found %s %d.%d at %s, HPA 0x%lx\n", DRIVER_NAME,
+	       sba_rev, ((hw_rev >> 4) & 0xF), (hw_rev & 0xF),
+	       device->slot_name, hpa);
+
+	if ((hw_rev & 0xFF) < 0x20) {
+		printk(KERN_INFO "%s WARNING rev 2.0 or greater will be required for IO MMU support in the future\n", DRIVER_NAME);
+#ifndef CONFIG_IA64_HP_PROTO
+		panic("%s: CONFIG_IA64_HP_PROTO MUST be enabled to support SBA rev less than 2.0", DRIVER_NAME);
+#endif
+	}
+
+	sba_dev = kmalloc(sizeof(struct sba_device), GFP_KERNEL);
+	if (NULL == sba_dev) {
+		printk(KERN_ERR DRIVER_NAME " - couldn't alloc sba_device\n");
+		return;
+	}
+
+	memset(sba_dev, 0, sizeof(struct sba_device));
+
+	for(i=0; i<MAX_IOC; i++)
+		spin_lock_init(&(sba_dev->ioc[i].res_lock));
+
+	sba_dev->hw_rev = hw_rev;
+	sba_dev->sba_hpa = hpa;
+
+	/*
+	 * We need to check for an AGP device, if we find one, then only
+	 * use part of the IOVA space for PCI DMA, the rest is for GART.
+	 * REVISIT for multiple IOC.
+	 */
+	pci_for_each_dev(device)
+		agp_found |= pci_find_capability(device, PCI_CAP_ID_AGP);
+
+	if (agp_found && reserve_sba_gart)
+		SBA_SET_AGP(sba_dev);
+
+	sba_hw_init(sba_dev);
+	sba_common_init(sba_dev);
+
+#ifdef CONFIG_PROC_FS
+	{
+		struct proc_dir_entry * proc_mckinley_root;
+
+		proc_mckinley_root = proc_mkdir("bus/mckinley",0);
+		create_proc_info_entry(sba_rev, 0, proc_mckinley_root, sba_proc_info);
+		create_proc_info_entry("bitmap", 0, proc_mckinley_root, sba_resource_map);
+	}
+#endif
+}
+
+static int __init
+nosbagart (char *str)
+{
+	reserve_sba_gart = 0;
+	return 1;
+}
+
+__setup("nosbagart",nosbagart);
+
+EXPORT_SYMBOL(sba_init);
+EXPORT_SYMBOL(sba_map_single);
+EXPORT_SYMBOL(sba_unmap_single);
+EXPORT_SYMBOL(sba_map_sg);
+EXPORT_SYMBOL(sba_unmap_sg);
+EXPORT_SYMBOL(sba_dma_address);
+EXPORT_SYMBOL(sba_alloc_consistent);
+EXPORT_SYMBOL(sba_free_consistent);
diff -Nru a/arch/ia64/hp/hpsim_console.c b/arch/ia64/hp/hpsim_console.c
--- a/arch/ia64/hp/hpsim_console.c	Mon Apr 22 15:32:19 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,64 +0,0 @@
-/*
- * Platform dependent support for HP simulator.
- *
- * Copyright (C) 1998, 1999, 2002 Hewlett-Packard Co
- *	David Mosberger-Tang <davidm@hpl.hp.com>
- * Copyright (C) 1999 Vijay Chander <vijay@engr.sgi.com>
- */
-#include <linux/config.h>
-
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/param.h>
-#include <linux/string.h>
-#include <linux/types.h>
-#include <linux/tty.h>
-#include <linux/kdev_t.h>
-#include <linux/console.h>
-
-#include <asm/delay.h>
-#include <asm/irq.h>
-#include <asm/pal.h>
-#include <asm/machvec.h>
-#include <asm/pgtable.h>
-#include <asm/sal.h>
-
-#include "hpsim_ssc.h"
-
-static int simcons_init (struct console *, char *);
-static void simcons_write (struct console *, const char *, unsigned);
-static kdev_t simcons_console_device (struct console *);
-
-struct console hpsim_cons = {
-	name:		"simcons",
-	write:		simcons_write,
-	device:		simcons_console_device,
-	setup:		simcons_init,
-	flags:		CON_PRINTBUFFER,
-	index:		-1,
-};
-
-static int
-simcons_init (struct console *cons, char *options)
-{
-	return 0;
-}
-
-static void
-simcons_write (struct console *cons, const char *buf, unsigned count)
-{
-	unsigned long ch;
-
-	while (count-- > 0) {
-		ch = *buf++;
-		ia64_ssc(ch, 0, 0, 0, SSC_PUTCHAR);
-		if (ch == '\n')
-		  ia64_ssc('\r', 0, 0, 0, SSC_PUTCHAR);
-	}
-}
-
-static kdev_t
-simcons_console_device (struct console *c)
-{
-	return mk_kdev(TTY_MAJOR, 64 + c->index);
-}
diff -Nru a/arch/ia64/hp/hpsim_irq.c b/arch/ia64/hp/hpsim_irq.c
--- a/arch/ia64/hp/hpsim_irq.c	Mon Apr 22 15:32:19 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,46 +0,0 @@
-/*
- * Platform dependent support for HP simulator.
- *
- * Copyright (C) 1998-2001 Hewlett-Packard Co
- * Copyright (C) 1998-2001 David Mosberger-Tang <davidm@hpl.hp.com>
- */
-
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/irq.h>
-
-static unsigned int
-hpsim_irq_startup (unsigned int irq)
-{
-	return 0;
-}
-
-static void
-hpsim_irq_noop (unsigned int irq)
-{
-}
-
-static struct hw_interrupt_type irq_type_hp_sim = {
-	typename:	"hpsim",
-	startup:	hpsim_irq_startup,
-	shutdown:	hpsim_irq_noop,
-	enable:		hpsim_irq_noop,
-	disable:	hpsim_irq_noop,
-	ack:		hpsim_irq_noop,
-	end:		hpsim_irq_noop,
-	set_affinity:	(void (*)(unsigned int, unsigned long)) hpsim_irq_noop,
-};
-
-void __init
-hpsim_irq_init (void)
-{
-	irq_desc_t *idesc;
-	int i;
-
-	for (i = 0; i < NR_IRQS; ++i) {
-		idesc = irq_desc(i);
-		if (idesc->handler == &no_irq_type)
-			idesc->handler = &irq_type_hp_sim;
-	}
-}
diff -Nru a/arch/ia64/hp/hpsim_machvec.c b/arch/ia64/hp/hpsim_machvec.c
--- a/arch/ia64/hp/hpsim_machvec.c	Mon Apr 22 15:32:19 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,2 +0,0 @@
-#define MACHVEC_PLATFORM_NAME	hpsim
-#include <asm/machvec_init.h>
diff -Nru a/arch/ia64/hp/hpsim_setup.c b/arch/ia64/hp/hpsim_setup.c
--- a/arch/ia64/hp/hpsim_setup.c	Mon Apr 22 15:32:19 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,58 +0,0 @@
-/*
- * Platform dependent support for HP simulator.
- *
- * Copyright (C) 1998, 1999 Hewlett-Packard Co
- * Copyright (C) 1998, 1999 David Mosberger-Tang <davidm@hpl.hp.com>
- * Copyright (C) 1999 Vijay Chander <vijay@engr.sgi.com>
- */
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/param.h>
-#include <linux/string.h>
-#include <linux/types.h>
-#include <linux/kdev_t.h>
-#include <linux/console.h>
-
-#include <asm/delay.h>
-#include <asm/irq.h>
-#include <asm/pal.h>
-#include <asm/machvec.h>
-#include <asm/pgtable.h>
-#include <asm/sal.h>
-
-#include "hpsim_ssc.h"
-
-extern struct console hpsim_cons;
-
-/*
- * Simulator system call.
- */
-asm (".text\n"
-     ".align 32\n"
-     ".global ia64_ssc\n"
-     ".proc ia64_ssc\n"
-     "ia64_ssc:\n"
-     "mov r15=r36\n"
-     "break 0x80001\n"
-     "br.ret.sptk.many rp\n"
-     ".endp\n");
-
-void
-ia64_ssc_connect_irq (long intr, long irq)
-{
-	ia64_ssc(intr, irq, 0, 0, SSC_CONNECT_INTERRUPT);
-}
-
-void
-ia64_ctl_trace (long on)
-{
-	ia64_ssc(on, 0, 0, 0, SSC_CTL_TRACE);
-}
-
-void __init
-hpsim_setup (char **cmdline_p)
-{
-	ROOT_DEV = to_kdev_t(0x0801);		/* default to first SCSI drive */
-
-	register_console (&hpsim_cons);
-}
diff -Nru a/arch/ia64/hp/hpsim_ssc.h b/arch/ia64/hp/hpsim_ssc.h
--- a/arch/ia64/hp/hpsim_ssc.h	Mon Apr 22 15:32:19 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,36 +0,0 @@
-/*
- * Platform dependent support for HP simulator.
- *
- * Copyright (C) 1998, 1999 Hewlett-Packard Co
- * Copyright (C) 1998, 1999 David Mosberger-Tang <davidm@hpl.hp.com>
- * Copyright (C) 1999 Vijay Chander <vijay@engr.sgi.com>
- */
-#ifndef _IA64_PLATFORM_HPSIM_SSC_H
-#define _IA64_PLATFORM_HPSIM_SSC_H
-
-/* Simulator system calls: */
-
-#define SSC_CONSOLE_INIT		20
-#define SSC_GETCHAR			21
-#define SSC_PUTCHAR			31
-#define SSC_CONNECT_INTERRUPT		58
-#define SSC_GENERATE_INTERRUPT		59
-#define SSC_SET_PERIODIC_INTERRUPT	60
-#define SSC_GET_RTC			65
-#define SSC_EXIT			66
-#define SSC_LOAD_SYMBOLS		69
-#define SSC_GET_TOD			74
-#define SSC_CTL_TRACE			76
-
-#define SSC_NETDEV_PROBE		100
-#define SSC_NETDEV_SEND			101
-#define SSC_NETDEV_RECV			102
-#define SSC_NETDEV_ATTACH		103
-#define SSC_NETDEV_DETACH		104
-
-/*
- * Simulator system call.
- */
-extern long ia64_ssc (long arg0, long arg1, long arg2, long arg3, int nr);
-
-#endif /* _IA64_PLATFORM_HPSIM_SSC_H */
diff -Nru a/arch/ia64/hp/sim/Makefile b/arch/ia64/hp/sim/Makefile
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/arch/ia64/hp/sim/Makefile	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,19 @@
+#
+# ia64/platform/hp/sim/Makefile
+#
+# Copyright (C) 2002 Hewlett-Packard Co.
+#	David Mosberger-Tang <davidm@hpl.hp.com>
+# Copyright (C) 1999 Silicon Graphics, Inc.
+# Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com)
+#
+
+O_TARGET := sim.o
+
+obj-y := hpsim_console.o hpsim_irq.o hpsim_setup.o
+obj-$(CONFIG_IA64_GENERIC) += hpsim_machvec.o
+
+obj-$(CONFIG_HP_SIMETH)	+= simeth.o
+obj-$(CONFIG_HP_SIMSERIAL) += simserial.o
+obj-$(CONFIG_HP_SIMSCSI) += simscsi.o
+
+include $(TOPDIR)/Rules.make
diff -Nru a/arch/ia64/hp/sim/hpsim_console.c b/arch/ia64/hp/sim/hpsim_console.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/arch/ia64/hp/sim/hpsim_console.c	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,64 @@
+/*
+ * Platform dependent support for HP simulator.
+ *
+ * Copyright (C) 1998, 1999, 2002 Hewlett-Packard Co
+ *	David Mosberger-Tang <davidm@hpl.hp.com>
+ * Copyright (C) 1999 Vijay Chander <vijay@engr.sgi.com>
+ */
+#include <linux/config.h>
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/tty.h>
+#include <linux/kdev_t.h>
+#include <linux/console.h>
+
+#include <asm/delay.h>
+#include <asm/irq.h>
+#include <asm/pal.h>
+#include <asm/machvec.h>
+#include <asm/pgtable.h>
+#include <asm/sal.h>
+
+#include "hpsim_ssc.h"
+
+static int simcons_init (struct console *, char *);
+static void simcons_write (struct console *, const char *, unsigned);
+static kdev_t simcons_console_device (struct console *);
+
+struct console hpsim_cons = {
+	name:		"simcons",
+	write:		simcons_write,
+	device:		simcons_console_device,
+	setup:		simcons_init,
+	flags:		CON_PRINTBUFFER,
+	index:		-1,
+};
+
+static int
+simcons_init (struct console *cons, char *options)
+{
+	return 0;
+}
+
+static void
+simcons_write (struct console *cons, const char *buf, unsigned count)
+{
+	unsigned long ch;
+
+	while (count-- > 0) {
+		ch = *buf++;
+		ia64_ssc(ch, 0, 0, 0, SSC_PUTCHAR);
+		if (ch == '\n')
+		  ia64_ssc('\r', 0, 0, 0, SSC_PUTCHAR);
+	}
+}
+
+static kdev_t
+simcons_console_device (struct console *c)
+{
+	return mk_kdev(TTY_MAJOR, 64 + c->index);
+}
diff -Nru a/arch/ia64/hp/sim/hpsim_irq.c b/arch/ia64/hp/sim/hpsim_irq.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/arch/ia64/hp/sim/hpsim_irq.c	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,46 @@
+/*
+ * Platform dependent support for HP simulator.
+ *
+ * Copyright (C) 1998-2001 Hewlett-Packard Co
+ * Copyright (C) 1998-2001 David Mosberger-Tang <davidm@hpl.hp.com>
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/irq.h>
+
+static unsigned int
+hpsim_irq_startup (unsigned int irq)
+{
+	return 0;
+}
+
+static void
+hpsim_irq_noop (unsigned int irq)
+{
+}
+
+static struct hw_interrupt_type irq_type_hp_sim = {
+	typename:	"hpsim",
+	startup:	hpsim_irq_startup,
+	shutdown:	hpsim_irq_noop,
+	enable:		hpsim_irq_noop,
+	disable:	hpsim_irq_noop,
+	ack:		hpsim_irq_noop,
+	end:		hpsim_irq_noop,
+	set_affinity:	(void (*)(unsigned int, unsigned long)) hpsim_irq_noop,
+};
+
+void __init
+hpsim_irq_init (void)
+{
+	irq_desc_t *idesc;
+	int i;
+
+	for (i = 0; i < NR_IRQS; ++i) {
+		idesc = irq_desc(i);
+		if (idesc->handler == &no_irq_type)
+			idesc->handler = &irq_type_hp_sim;
+	}
+}
diff -Nru a/arch/ia64/hp/sim/hpsim_machvec.c b/arch/ia64/hp/sim/hpsim_machvec.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/arch/ia64/hp/sim/hpsim_machvec.c	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,2 @@
+#define MACHVEC_PLATFORM_NAME	hpsim
+#include <asm/machvec_init.h>
diff -Nru a/arch/ia64/hp/sim/hpsim_setup.c b/arch/ia64/hp/sim/hpsim_setup.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/arch/ia64/hp/sim/hpsim_setup.c	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,58 @@
+/*
+ * Platform dependent support for HP simulator.
+ *
+ * Copyright (C) 1998, 1999 Hewlett-Packard Co
+ * Copyright (C) 1998, 1999 David Mosberger-Tang <davidm@hpl.hp.com>
+ * Copyright (C) 1999 Vijay Chander <vijay@engr.sgi.com>
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+#include <linux/console.h>
+
+#include <asm/delay.h>
+#include <asm/irq.h>
+#include <asm/pal.h>
+#include <asm/machvec.h>
+#include <asm/pgtable.h>
+#include <asm/sal.h>
+
+#include "hpsim_ssc.h"
+
+extern struct console hpsim_cons;
+
+/*
+ * Simulator system call.
+ */
+asm (".text\n"
+     ".align 32\n"
+     ".global ia64_ssc\n"
+     ".proc ia64_ssc\n"
+     "ia64_ssc:\n"
+     "mov r15=r36\n"
+     "break 0x80001\n"
+     "br.ret.sptk.many rp\n"
+     ".endp\n");
+
+void
+ia64_ssc_connect_irq (long intr, long irq)
+{
+	ia64_ssc(intr, irq, 0, 0, SSC_CONNECT_INTERRUPT);
+}
+
+void
+ia64_ctl_trace (long on)
+{
+	ia64_ssc(on, 0, 0, 0, SSC_CTL_TRACE);
+}
+
+void __init
+hpsim_setup (char **cmdline_p)
+{
+	ROOT_DEV = to_kdev_t(0x0801);		/* default to first SCSI drive */
+
+	register_console (&hpsim_cons);
+}
diff -Nru a/arch/ia64/hp/sim/hpsim_ssc.h b/arch/ia64/hp/sim/hpsim_ssc.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/arch/ia64/hp/sim/hpsim_ssc.h	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,36 @@
+/*
+ * Platform dependent support for HP simulator.
+ *
+ * Copyright (C) 1998, 1999 Hewlett-Packard Co
+ * Copyright (C) 1998, 1999 David Mosberger-Tang <davidm@hpl.hp.com>
+ * Copyright (C) 1999 Vijay Chander <vijay@engr.sgi.com>
+ */
+#ifndef _IA64_PLATFORM_HPSIM_SSC_H
+#define _IA64_PLATFORM_HPSIM_SSC_H
+
+/* Simulator system calls: */
+
+#define SSC_CONSOLE_INIT		20
+#define SSC_GETCHAR			21
+#define SSC_PUTCHAR			31
+#define SSC_CONNECT_INTERRUPT		58
+#define SSC_GENERATE_INTERRUPT		59
+#define SSC_SET_PERIODIC_INTERRUPT	60
+#define SSC_GET_RTC			65
+#define SSC_EXIT			66
+#define SSC_LOAD_SYMBOLS		69
+#define SSC_GET_TOD			74
+#define SSC_CTL_TRACE			76
+
+#define SSC_NETDEV_PROBE		100
+#define SSC_NETDEV_SEND			101
+#define SSC_NETDEV_RECV			102
+#define SSC_NETDEV_ATTACH		103
+#define SSC_NETDEV_DETACH		104
+
+/*
+ * Simulator system call.
+ */
+extern long ia64_ssc (long arg0, long arg1, long arg2, long arg3, int nr);
+
+#endif /* _IA64_PLATFORM_HPSIM_SSC_H */
diff -Nru a/arch/ia64/hp/sim/simeth.c b/arch/ia64/hp/sim/simeth.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/arch/ia64/hp/sim/simeth.c	Mon Apr 22 15:32:18 2002
@@ -0,0 +1,533 @@
+/*
+ * Simulated Ethernet Driver
+ *
+ * Copyright (C) 1999-2001 Hewlett-Packard Co
+ *	Stephane Eranian <eranian@hpl.hp.com>
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <linux/notifier.h>
+#include <asm/bitops.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+
+#define SIMETH_RECV_MAX	10
+
+/*
+ * Maximum possible received frame for Ethernet.
+ * We preallocate an sk_buff of that size to avoid costly
+ * memcpy for temporary buffer into sk_buff. We do basically
+ * what's done in other drivers, like eepro with a ring.
+ * The difference is, of course, that we don't have real DMA !!!
+ */
+#define SIMETH_FRAME_SIZE	ETH_FRAME_LEN
+
+
+#define SSC_NETDEV_PROBE		100
+#define SSC_NETDEV_SEND			101
+#define SSC_NETDEV_RECV			102
+#define SSC_NETDEV_ATTACH		103
+#define SSC_NETDEV_DETACH		104
+
+#define NETWORK_INTR			8
+
+struct simeth_local {
+	struct net_device_stats stats;
+	int 			simfd;	 /* descriptor in the simulator */
+};
+
+static int simeth_probe1(void);
+static int simeth_open(struct net_device *dev);
+static int simeth_close(struct net_device *dev);
+static int simeth_tx(struct sk_buff *skb, struct net_device *dev);
+static int simeth_rx(struct net_device *dev);
+static struct net_device_stats *simeth_get_stats(struct net_device *dev);
+static void simeth_interrupt(int irq, void *dev_id, struct pt_regs * regs);
+static void set_multicast_list(struct net_device *dev);
+static int simeth_device_event(struct notifier_block *this,unsigned long event, void *ptr);
+
+static char *simeth_version="0.3";
+
+/*
+ * This variable is used to establish a mapping between the Linux/ia64 kernel
+ * and the host linux kernel.
+ *
+ * As of today, we support only one card, even though most of the code
+ * is ready for many more. The mapping is then:
+ *	linux/ia64 -> linux/x86
+ * 	   eth0    -> eth1
+ *
+ * In the future, we some string operations, we could easily support up
+ * to 10 cards (0-9).
+ *
+ * The default mapping can be changed on the kernel command line by
+ * specifying simeth=ethX (or whatever string you want).
+ */
+static char *simeth_device="eth0";	 /* default host interface to use */
+
+
+
+static volatile unsigned int card_count; /* how many cards "found" so far */
+static int simeth_debug;		/* set to 1 to get debug information */
+
+/*
+ * Used to catch IFF_UP & IFF_DOWN events
+ */
+static struct notifier_block simeth_dev_notifier = {
+	simeth_device_event,
+	0
+};
+
+
+/*
+ * Function used when using a kernel command line option.
+ *
+ * Format: simeth=interface_name (like eth0)
+ */
+static int __init
+simeth_setup(char *str)
+{
+	simeth_device = str;
+	return 1;
+}
+
+__setup("simeth=", simeth_setup);
+
+/*
+ * Function used to probe for simeth devices when not installed
+ * as a loadable module
+ */
+
+int __init
+simeth_probe (void)
+{
+	int r;
+
+	printk("simeth: v%s\n", simeth_version);
+
+	r = simeth_probe1();
+
+	if (r == 0) register_netdevice_notifier(&simeth_dev_notifier);
+
+	return r;
+}
+
+extern long ia64_ssc (long, long, long, long, int);
+extern void ia64_ssc_connect_irq (long intr, long irq);
+
+static inline int
+netdev_probe(char *name, unsigned char *ether)
+{
+	return ia64_ssc(__pa(name), __pa(ether), 0,0, SSC_NETDEV_PROBE);
+}
+
+
+static inline int
+netdev_connect(int irq)
+{
+	/* XXX Fix me
+	 * this does not support multiple cards
+	 * also no return value
+	 */
+	ia64_ssc_connect_irq(NETWORK_INTR, irq);
+	return 0;
+}
+
+static inline int
+netdev_attach(int fd, int irq, unsigned int ipaddr)
+{
+	/* this puts the host interface in the right mode (start interupting) */
+	return ia64_ssc(fd, ipaddr, 0,0, SSC_NETDEV_ATTACH);
+}
+
+
+static inline int
+netdev_detach(int fd)
+{
+	/*
+	 * inactivate the host interface (don't interrupt anymore) */
+	return ia64_ssc(fd, 0,0,0, SSC_NETDEV_DETACH);
+}
+
+static inline int
+netdev_send(int fd, unsigned char *buf, unsigned int len)
+{
+	return ia64_ssc(fd, __pa(buf), len, 0, SSC_NETDEV_SEND);
+}
+
+static inline int
+netdev_read(int fd, unsigned char *buf, unsigned int len)
+{
+	return ia64_ssc(fd, __pa(buf), len, 0, SSC_NETDEV_RECV);
+}
+
+/*
+ * Function shared with module code, so cannot be in init section
+ *
+ * So far this function "detects" only one card (test_&_set) but could
+ * be extended easily.
+ *
+ * Return:
+ * 	- -ENODEV is no device found
+ *	- -ENOMEM is no more memory
+ *	- 0 otherwise
+ */
+static int
+simeth_probe1(void)
+{
+	unsigned char mac_addr[ETH_ALEN];
+	struct simeth_local *local;
+	struct net_device *dev;
+	int fd, i;
+
+	/*
+	 * XXX Fix me
+	 * let's support just one card for now
+	 */
+	if (test_and_set_bit(0, &card_count))
+		return -ENODEV;
+
+	/*
+	 * check with the simulator for the device
+	 */
+	fd = netdev_probe(simeth_device, mac_addr);
+	if (fd == -1)
+		return -ENODEV;
+
+	dev = init_etherdev(NULL, sizeof(struct simeth_local));
+	if (!dev)
+		return -ENOMEM;
+
+	memcpy(dev->dev_addr, mac_addr, sizeof(mac_addr));
+
+	dev->irq = ia64_alloc_irq();
+
+	/*
+	 * attach the interrupt in the simulator, this does enable interrupts
+	 * until a netdev_attach() is called
+	 */
+	netdev_connect(dev->irq);
+
+	memset(dev->priv, 0, sizeof(struct simeth_local));
+
+	local = dev->priv;
+	local->simfd = fd; /* keep track of underlying file descriptor */
+
+	dev->open		= simeth_open;
+	dev->stop		= simeth_close;
+	dev->hard_start_xmit	= simeth_tx;
+	dev->get_stats		= simeth_get_stats;
+	dev->set_multicast_list = set_multicast_list; /* no yet used */
+
+	/* Fill in the fields of the device structure with ethernet-generic values. */
+	ether_setup(dev);
+
+	printk("%s: hosteth=%s simfd=%d, HwAddr", dev->name, simeth_device, local->simfd);
+	for(i = 0; i < ETH_ALEN; i++) {
+		printk(" %2.2x", dev->dev_addr[i]);
+	}
+	printk(", IRQ %d\n", dev->irq);
+
+		return 0;
+}
+
+/*
+ * actually binds the device to an interrupt vector
+ */
+static int
+simeth_open(struct net_device *dev)
+{
+	if (request_irq(dev->irq, simeth_interrupt, 0, "simeth", dev)) {
+		printk ("simeth: unable to get IRQ %d.\n", dev->irq);
+		return -EAGAIN;
+	}
+
+	netif_start_queue(dev);
+
+	return 0;
+}
+
+/* copied from lapbether.c */
+static __inline__ int dev_is_ethdev(struct net_device *dev)
+{
+       return ( dev->type == ARPHRD_ETHER && strncmp(dev->name, "dummy", 5));
+}
+
+
+/*
+ * Handler for IFF_UP or IFF_DOWN
+ *
+ * The reason for that is that we don't want to be interrupted when the
+ * interface is down. There is no way to unconnect in the simualtor. Instead
+ * we use this function to shutdown packet processing in the frame filter
+ * in the simulator. Thus no interrupts are generated
+ *
+ *
+ * That's also the place where we pass the IP address of this device to the
+ * simulator so that that we can start filtering packets for it
+ *
+ * There may be a better way of doing this, but I don't know which yet.
+ */
+static int
+simeth_device_event(struct notifier_block *this,unsigned long event, void *ptr)
+{
+	struct net_device *dev = (struct net_device *)ptr;
+	struct simeth_local *local;
+	struct in_device *in_dev;
+	struct in_ifaddr **ifap = NULL;
+	struct in_ifaddr *ifa = NULL;
+	int r;
+
+
+	if ( ! dev ) {
+		printk(KERN_WARNING "simeth_device_event dev=0\n");
+		return NOTIFY_DONE;
+	}
+
+	if ( event != NETDEV_UP && event != NETDEV_DOWN ) return NOTIFY_DONE;
+
+	/*
+	 * Check whether or not it's for an ethernet device
+	 *
+	 * XXX Fixme: This works only as long as we support one
+	 * type of ethernet device.
+	 */
+	if ( !dev_is_ethdev(dev) ) return NOTIFY_DONE;
+
+	if ((in_dev=dev->ip_ptr) != NULL) {
+		for (ifap=&in_dev->ifa_list; (ifa=*ifap) != NULL; ifap=&ifa->ifa_next)
+			if (strcmp(dev->name, ifa->ifa_label) == 0) break;
+	}
+	if ( ifa == NULL ) {
+		printk("simeth_open: can't find device %s's ifa\n", dev->name);
+		return NOTIFY_DONE;
+	}
+
+	printk("simeth_device_event: %s ipaddr=0x%x\n", dev->name, htonl(ifa->ifa_local));
+
+	/*
+	 * XXX Fix me
+	 * if the device was up, and we're simply reconfiguring it, not sure
+	 * we get DOWN then UP.
+	 */
+
+	local = dev->priv;
+	/* now do it for real */
+	r = event == NETDEV_UP ?
+		netdev_attach(local->simfd, dev->irq, htonl(ifa->ifa_local)):
+		netdev_detach(local->simfd);
+
+	printk("simeth: netdev_attach/detach: event=%s ->%d\n", event == NETDEV_UP ? "attach":"detach", r);
+
+	return NOTIFY_DONE;
+}
+
+static int
+simeth_close(struct net_device *dev)
+{
+	netif_stop_queue(dev);
+
+	free_irq(dev->irq, dev);
+
+	return 0;
+}
+
+/*
+ * Only used for debug
+ */
+static void
+frame_print(unsigned char *from, unsigned char *frame, int len)
+{
+	int i;
+
+	printk("%s: (%d) %02x", from, len, frame[0] & 0xff);
+	for(i=1; i < 6; i++ ) {
+		printk(":%02x", frame[i] &0xff);
+	}
+	printk(" %2x", frame[6] &0xff);
+	for(i=7; i < 12; i++ ) {
+		printk(":%02x", frame[i] &0xff);
+	}
+	printk(" [%02x%02x]\n", frame[12], frame[13]);
+
+	for(i=14; i < len; i++ ) {
+		printk("%02x ", frame[i] &0xff);
+		if ( (i%10)==0) printk("\n");
+	}
+	printk("\n");
+}
+
+
+/*
+ * Function used to transmit of frame, very last one on the path before
+ * going to the simulator.
+ */
+static int
+simeth_tx(struct sk_buff *skb, struct net_device *dev)
+{
+	struct simeth_local *local = (struct simeth_local *)dev->priv;
+
+#if 0
+	/* ensure we have at least ETH_ZLEN bytes (min frame size) */
+	unsigned int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+	/* Where do the extra padding bytes comes from inthe skbuff ? */
+#else
+	/* the real driver in the host system is going to take care of that
+	 * or maybe it's the NIC itself.
+	 */
+	unsigned int length = skb->len;
+#endif
+
+	local->stats.tx_bytes += skb->len;
+	local->stats.tx_packets++;
+
+
+	if (simeth_debug > 5) frame_print("simeth_tx", skb->data, length);
+
+	netdev_send(local->simfd, skb->data, length);
+
+	/*
+	 * we are synchronous on write, so we don't simulate a
+	 * trasnmit complete interrupt, thus we don't need to arm a tx
+	 */
+
+	dev_kfree_skb(skb);
+	return 0;
+}
+
+static inline struct sk_buff *
+make_new_skb(struct net_device *dev)
+{
+	struct sk_buff *nskb;
+
+	/*
+	 * The +2 is used to make sure that the IP header is nicely
+	 * aligned (on 4byte boundary I assume 14+2=16)
+	 */
+	nskb = dev_alloc_skb(SIMETH_FRAME_SIZE + 2);
+	if ( nskb == NULL ) {
+		printk(KERN_NOTICE "%s: memory squeeze. dropping packet.\n", dev->name);
+		return NULL;
+	}
+	nskb->dev = dev;
+
+	skb_reserve(nskb, 2);	/* Align IP on 16 byte boundaries */
+
+	skb_put(nskb,SIMETH_FRAME_SIZE);
+
+	return nskb;
+}
+
+/*
+ * called from interrupt handler to process a received frame
+ */
+static int
+simeth_rx(struct net_device *dev)
+{
+	struct simeth_local	*local;
+	struct sk_buff		*skb;
+	int			len;
+	int			rcv_count = SIMETH_RECV_MAX;
+
+	local = (struct simeth_local *)dev->priv;
+	/*
+	 * the loop concept has been borrowed from other drivers
+	 * looks to me like it's a throttling thing to avoid pushing to many
+	 * packets at one time into the stack. Making sure we can process them
+	 * upstream and make forward progress overall
+	 */
+	do {
+		if ( (skb=make_new_skb(dev)) == NULL ) {
+			printk(KERN_NOTICE "%s: memory squeeze. dropping packet.\n", dev->name);
+			local->stats.rx_dropped++;
+			return 0;
+		}
+		/*
+		 * Read only one frame at a time
+		 */
+		len = netdev_read(local->simfd, skb->data, SIMETH_FRAME_SIZE);
+		if ( len == 0 ) {
+			if ( simeth_debug > 0 ) printk(KERN_WARNING "%s: count=%d netdev_read=0\n", dev->name, SIMETH_RECV_MAX-rcv_count);
+			break;
+		}
+#if 0
+		/*
+		 * XXX Fix me
+		 * Should really do a csum+copy here
+		 */
+		memcpy(skb->data, frame, len);
+#endif
+		skb->protocol = eth_type_trans(skb, dev);
+
+		if ( simeth_debug > 6 ) frame_print("simeth_rx", skb->data, len);
+
+		/*
+		 * push the packet up & trigger software interrupt
+		 */
+		netif_rx(skb);
+
+		local->stats.rx_packets++;
+		local->stats.rx_bytes += len;
+
+	} while ( --rcv_count );
+
+	return len; /* 0 = nothing left to read, otherwise, we can try again */
+}
+
+/*
+ * Interrupt handler (Yes, we can do it too !!!)
+ */
+static void
+simeth_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+	struct net_device *dev = dev_id;
+
+	if ( dev == NULL ) {
+		printk(KERN_WARNING "simeth: irq %d for unknown device\n", irq);
+		return;
+	}
+
+	/*
+	 * very simple loop because we get interrupts only when receving
+	 */
+	while (simeth_rx(dev));
+}
+
+static struct net_device_stats *
+simeth_get_stats(struct net_device *dev)
+{
+	struct simeth_local  *local = (struct simeth_local *) dev->priv;
+
+	return &local->stats;
+}
+
+/* fake multicast ability */
+static void
+set_multicast_list(struct net_device *dev)
+{
+	printk(KERN_WARNING "%s: set_multicast_list called\n", dev->name);
+}
+
+#ifdef CONFIG_NET_FASTROUTE
+static int
+simeth_accept_fastpath(struct net_device *dev, struct dst_entry *dst)
+{
+	printk(KERN_WARNING "%s: simeth_accept_fastpath called\n", dev->name);
+	return -1;
+}
+#endif
+
+__initcall(simeth_probe);
diff -Nru a/arch/ia64/hp/sim/simscsi.c b/arch/ia64/hp/sim/simscsi.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/arch/ia64/hp/sim/simscsi.c	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,368 @@
+/*
+ * Simulated SCSI driver.
+ *
+ * Copyright (C) 1999, 2001-2002 Hewlett-Packard Co
+ *	David Mosberger-Tang <davidm@hpl.hp.com>
+ *	Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * 02/01/15 David Mosberger	Updated for v2.5.1
+ * 99/12/18 David Mosberger	Added support for READ10/WRITE10 needed by linux v2.3.33
+ */
+#include <linux/config.h>
+#include <linux/blk.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+
+#include <scsi/scsi.h>
+
+#include <asm/irq.h>
+
+#include "../drivers/scsi/scsi.h"
+#include "../drivers/scsi/sd.h"
+#include "../drivers/scsi/hosts.h"
+#include "simscsi.h"
+
+#define DEBUG_SIMSCSI	1
+
+/* Simulator system calls: */
+
+#define SSC_OPEN			50
+#define SSC_CLOSE			51
+#define SSC_READ			52
+#define SSC_WRITE			53
+#define SSC_GET_COMPLETION		54
+#define SSC_WAIT_COMPLETION		55
+
+#define SSC_WRITE_ACCESS		2
+#define SSC_READ_ACCESS			1
+
+#if DEBUG_SIMSCSI
+  int simscsi_debug;
+# define DBG	simscsi_debug
+#else
+# define DBG	0
+#endif
+
+static struct Scsi_Host *host;
+
+static void simscsi_interrupt (unsigned long val);
+DECLARE_TASKLET(simscsi_tasklet, simscsi_interrupt, 0);
+
+struct disk_req {
+	unsigned long addr;
+	unsigned len;
+};
+
+struct disk_stat {
+	int fd;
+	unsigned count;
+};
+
+extern long ia64_ssc (long arg0, long arg1, long arg2, long arg3, int nr);
+
+static int desc[8] = { -1, -1, -1, -1, -1, -1, -1, -1 };
+
+static struct queue_entry {
+	Scsi_Cmnd *sc;
+} queue[SIMSCSI_REQ_QUEUE_LEN];
+
+static int rd, wr;
+static atomic_t num_reqs = ATOMIC_INIT(0);
+
+/* base name for default disks */
+static char *simscsi_root = DEFAULT_SIMSCSI_ROOT;
+
+#define MAX_ROOT_LEN	128
+
+/*
+ * used to setup a new base for disk images
+ * to use /foo/bar/disk[a-z] as disk images
+ * you have to specify simscsi=/foo/bar/disk on the command line
+ */
+static int __init
+simscsi_setup (char *s)
+{
+	/* XXX Fix me we may need to strcpy() ? */
+	if (strlen(s) > MAX_ROOT_LEN) {
+		printk("simscsi_setup: prefix too long---using default %s\n", simscsi_root);
+	}
+	simscsi_root = s;
+	return 1;
+}
+
+__setup("simscsi=", simscsi_setup);
+
+static void
+simscsi_interrupt (unsigned long val)
+{
+	Scsi_Cmnd *sc;
+
+	while ((sc = queue[rd].sc) != 0) {
+		atomic_dec(&num_reqs);
+		queue[rd].sc = 0;
+		if (DBG)
+			printk("simscsi_interrupt: done with %ld\n", sc->serial_number);
+		(*sc->scsi_done)(sc);
+		rd = (rd + 1) % SIMSCSI_REQ_QUEUE_LEN;
+	}
+}
+
+int
+simscsi_detect (Scsi_Host_Template *templ)
+{
+	templ->proc_name = "simscsi";
+	host = scsi_register(templ, 0);
+	return 1;	/* fake one SCSI host adapter */
+}
+
+int
+simscsi_release (struct Scsi_Host *host)
+{
+	return 0;	/* this is easy...  */
+}
+
+const char *
+simscsi_info (struct Scsi_Host *host)
+{
+	return "simulated SCSI host adapter";
+}
+
+int
+simscsi_abort (Scsi_Cmnd *cmd)
+{
+	printk ("simscsi_abort: unimplemented\n");
+	return SCSI_ABORT_SUCCESS;
+}
+
+int
+simscsi_reset (Scsi_Cmnd *cmd, unsigned int reset_flags)
+{
+	printk ("simscsi_reset: unimplemented\n");
+	return SCSI_RESET_SUCCESS;
+}
+
+int
+simscsi_biosparam (Disk *disk, kdev_t n, int ip[])
+{
+	int size = disk->capacity;
+
+	ip[0] = 64;
+	ip[1] = 32;
+	ip[2] = size >> 11;
+	return 0;
+}
+
+static void
+simscsi_readwrite (Scsi_Cmnd *sc, int mode, unsigned long offset, unsigned long len)
+{
+	struct disk_stat stat;
+	struct disk_req req;
+
+	req.addr = __pa(sc->request_buffer);
+	req.len  = len;			/* # of bytes to transfer */
+
+	if (sc->request_bufflen < req.len)
+		return;
+
+	stat.fd = desc[sc->target];
+	if (DBG)
+		printk("simscsi_%s @ %lx (off %lx)\n",
+		       mode == SSC_READ ? "read":"write", req.addr, offset);
+	ia64_ssc(stat.fd, 1, __pa(&req), offset, mode);
+	ia64_ssc(__pa(&stat), 0, 0, 0, SSC_WAIT_COMPLETION);
+
+	if (stat.count == req.len) {
+		sc->result = GOOD;
+	} else {
+		sc->result = DID_ERROR << 16;
+	}
+}
+
+static void
+simscsi_sg_readwrite (Scsi_Cmnd *sc, int mode, unsigned long offset)
+{
+	int list_len = sc->use_sg;
+	struct scatterlist *sl = (struct scatterlist *)sc->buffer;
+	struct disk_stat stat;
+	struct disk_req req;
+
+	stat.fd = desc[sc->target];
+
+	while (list_len) {
+		req.addr = __pa(page_address(sl->page) + sl->offset);
+		req.len  = sl->length;
+		if (DBG)
+			printk("simscsi_sg_%s @ %lx (off %lx) use_sg=%d len=%d\n",
+			       mode == SSC_READ ? "read":"write", req.addr, offset,
+			       list_len, sl->length);
+		ia64_ssc(stat.fd, 1, __pa(&req), offset, mode);
+		ia64_ssc(__pa(&stat), 0, 0, 0, SSC_WAIT_COMPLETION);
+
+		/* should not happen in our case */
+		if (stat.count != req.len) {
+			sc->result = DID_ERROR << 16;
+			return;
+		}
+		offset +=  sl->length;
+		sl++;
+		list_len--;
+	}
+	sc->result = GOOD;
+}
+
+/*
+ * function handling both READ_6/WRITE_6 (non-scatter/gather mode)
+ * commands.
+ * Added 02/26/99 S.Eranian
+ */
+static void
+simscsi_readwrite6 (Scsi_Cmnd *sc, int mode)
+{
+	unsigned long offset;
+
+	offset = (((sc->cmnd[1] & 0x1f) << 16) | (sc->cmnd[2] << 8) | sc->cmnd[3])*512;
+	if (sc->use_sg > 0)
+		simscsi_sg_readwrite(sc, mode, offset);
+	else
+		simscsi_readwrite(sc, mode, offset, sc->cmnd[4]*512);
+}
+
+
+static void
+simscsi_readwrite10 (Scsi_Cmnd *sc, int mode)
+{
+	unsigned long offset;
+
+	offset = (  (sc->cmnd[2] << 24) | (sc->cmnd[3] << 16)
+		  | (sc->cmnd[4] <<  8) | (sc->cmnd[5] <<  0))*512;
+	if (sc->use_sg > 0)
+		simscsi_sg_readwrite(sc, mode, offset);
+	else
+		simscsi_readwrite(sc, mode, offset, ((sc->cmnd[7] << 8) | sc->cmnd[8])*512);
+}
+
+int
+simscsi_queuecommand (Scsi_Cmnd *sc, void (*done)(Scsi_Cmnd *))
+{
+	char fname[MAX_ROOT_LEN+16];
+	char *buf;
+#if DEBUG_SIMSCSI
+	register long sp asm ("sp");
+
+	if (DBG)
+		printk("simscsi_queuecommand: target=%d,cmnd=%u,sc=%lu,sp=%lx,done=%p\n",
+		       sc->target, sc->cmnd[0], sc->serial_number, sp, done);
+#endif
+
+	sc->result = DID_BAD_TARGET << 16;
+	sc->scsi_done = done;
+	if (sc->target <= 7 && sc->lun == 0) {
+		switch (sc->cmnd[0]) {
+		      case INQUIRY:
+			if (sc->request_bufflen < 35) {
+				break;
+			}
+			sprintf (fname, "%s%c", simscsi_root, 'a' + sc->target);
+			desc[sc->target] = ia64_ssc (__pa(fname), SSC_READ_ACCESS|SSC_WRITE_ACCESS,
+						     0, 0, SSC_OPEN);
+			if (desc[sc->target] < 0) {
+				/* disk doesn't exist... */
+				break;
+			}
+			buf = sc->request_buffer;
+			buf[0] = 0;	/* magnetic disk */
+			buf[1] = 0;	/* not a removable medium */
+			buf[2] = 2;	/* SCSI-2 compliant device */
+			buf[3] = 2;	/* SCSI-2 response data format */
+			buf[4] = 31;	/* additional length (bytes) */
+			buf[5] = 0;	/* reserved */
+			buf[6] = 0;	/* reserved */
+			buf[7] = 0;	/* various flags */
+			memcpy(buf + 8, "HP      SIMULATED DISK  0.00",  28);
+			sc->result = GOOD;
+			break;
+
+		      case TEST_UNIT_READY:
+			sc->result = GOOD;
+			break;
+
+		      case READ_6:
+			if (desc[sc->target] < 0 )
+				break;
+			simscsi_readwrite6(sc, SSC_READ);
+			break;
+
+		      case READ_10:
+			if (desc[sc->target] < 0 )
+				break;
+			simscsi_readwrite10(sc, SSC_READ);
+			break;
+
+		      case WRITE_6:
+			if (desc[sc->target] < 0)
+				break;
+			simscsi_readwrite6(sc, SSC_WRITE);
+			break;
+
+		      case WRITE_10:
+			if (desc[sc->target] < 0)
+				break;
+			simscsi_readwrite10(sc, SSC_WRITE);
+			break;
+
+
+		      case READ_CAPACITY:
+			if (desc[sc->target] < 0 || sc->request_bufflen < 8) {
+				break;
+			}
+			buf = sc->request_buffer;
+
+			/* pretend to be a 1GB disk (partition table contains real stuff): */
+			buf[0] = 0x00;
+			buf[1] = 0x1f;
+			buf[2] = 0xff;
+			buf[3] = 0xff;
+			/* set block size of 512 bytes: */
+			buf[4] = 0;
+			buf[5] = 0;
+			buf[6] = 2;
+			buf[7] = 0;
+			sc->result = GOOD;
+			break;
+
+		      case MODE_SENSE:
+			printk("MODE_SENSE\n");
+			break;
+
+		      case START_STOP:
+			printk("START_STOP\n");
+			break;
+
+		      default:
+			panic("simscsi: unknown SCSI command %u\n", sc->cmnd[0]);
+		}
+	}
+	if (sc->result == DID_BAD_TARGET) {
+		sc->result |= DRIVER_SENSE << 24;
+		sc->sense_buffer[0] = 0x70;
+		sc->sense_buffer[2] = 0x00;
+	}
+	if (atomic_read(&num_reqs) >= SIMSCSI_REQ_QUEUE_LEN) {
+		panic("Attempt to queue command while command is pending!!");
+	}
+	atomic_inc(&num_reqs);
+	queue[wr].sc = sc;
+	wr = (wr + 1) % SIMSCSI_REQ_QUEUE_LEN;
+
+	tasklet_schedule(&simscsi_tasklet);
+	return 0;
+}
+
+
+static Scsi_Host_Template driver_template = SIMSCSI;
+
+#define __initcall(fn)	late_initcall(fn)
+
+#include "../drivers/scsi/scsi_module.c"
diff -Nru a/arch/ia64/hp/sim/simscsi.h b/arch/ia64/hp/sim/simscsi.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/arch/ia64/hp/sim/simscsi.h	Mon Apr 22 15:32:18 2002
@@ -0,0 +1,39 @@
+/*
+ * Simulated SCSI driver.
+ *
+ * Copyright (C) 1999 Hewlett-Packard Co
+ *	David Mosberger-Tang <davidm@hpl.hp.com>
+ */
+#ifndef SIMSCSI_H
+#define SIMSCSI_H
+
+#define SIMSCSI_REQ_QUEUE_LEN	64
+
+#define DEFAULT_SIMSCSI_ROOT	"/var/ski-disks/sd"
+
+extern int simscsi_detect (Scsi_Host_Template *);
+extern int simscsi_release (struct Scsi_Host *);
+extern const char *simscsi_info (struct Scsi_Host *);
+extern int simscsi_queuecommand (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+extern int simscsi_abort (Scsi_Cmnd *);
+extern int simscsi_reset (Scsi_Cmnd *, unsigned int);
+extern int simscsi_biosparam (Disk *, kdev_t, int[]);
+
+#define SIMSCSI {					\
+	detect:			simscsi_detect,		\
+	release:		simscsi_release,	\
+	info:			simscsi_info,		\
+	queuecommand:		simscsi_queuecommand,	\
+	abort:			simscsi_abort,		\
+	reset:			simscsi_reset,		\
+	bios_param:		simscsi_biosparam,	\
+	can_queue:		SIMSCSI_REQ_QUEUE_LEN,	\
+	this_id:		-1,			\
+	sg_tablesize:		SG_ALL,			\
+	cmd_per_lun:		SIMSCSI_REQ_QUEUE_LEN,	\
+	present:		0,			\
+	unchecked_isa_dma:	0,			\
+	use_clustering:		DISABLE_CLUSTERING	\
+}
+
+#endif /* SIMSCSI_H */
diff -Nru a/arch/ia64/hp/sim/simserial.c b/arch/ia64/hp/sim/simserial.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/arch/ia64/hp/sim/simserial.c	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,1104 @@
+/*
+ * Simulated Serial Driver (fake serial)
+ *
+ * This driver is mostly used for bringup purposes and will go away.
+ * It has a strong dependency on the system console. All outputs
+ * are rerouted to the same facility as the one used by printk which, in our
+ * case means sys_sim.c console (goes via the simulator). The code hereafter
+ * is completely leveraged from the serial.c driver.
+ *
+ * Copyright (C) 1999-2000 Hewlett-Packard Co
+ * Copyright (C) 1999 Stephane Eranian <eranian@hpl.hp.com>
+ * Copyright (C) 2000 David Mosberger-Tang <davidm@hpl.hp.com>
+ *
+ * 02/04/00 D. Mosberger	Merged in serial.c bug fixes in rs_close().
+ * 02/25/00 D. Mosberger	Synced up with 2.3.99pre-5 version of serial.c.
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/console.h>
+#include <linux/module.h>
+#include <linux/serial.h>
+#include <linux/serialP.h>
+
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+#ifdef CONFIG_KDB
+# include <linux/kdb.h>
+#endif
+
+#undef SIMSERIAL_DEBUG	/* define this to get some debug information */
+
+#define KEYBOARD_INTR	3	/* must match with simulator! */
+
+#define NR_PORTS	1	/* only one port for now */
+#define SERIAL_INLINE	1
+
+#ifdef SERIAL_INLINE
+#define _INLINE_ inline
+#endif
+
+#ifndef MIN
+#define MIN(a,b)	((a) < (b) ? (a) : (b))
+#endif
+
+#define IRQ_T(info) ((info->flags & ASYNC_SHARE_IRQ) ? SA_SHIRQ : SA_INTERRUPT)
+
+#define SSC_GETCHAR	21
+
+extern long ia64_ssc (long, long, long, long, int);
+extern void ia64_ssc_connect_irq (long intr, long irq);
+
+static char *serial_name = "SimSerial driver";
+static char *serial_version = "0.6";
+
+/*
+ * This has been extracted from asm/serial.h. We need one eventually but
+ * I don't know exactly what we're going to put in it so just fake one
+ * for now.
+ */
+#define BASE_BAUD ( 1843200 / 16 )
+
+#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST)
+
+/*
+ * Most of the values here are meaningless to this particular driver.
+ * However some values must be preserved for the code (leveraged from serial.c
+ * to work correctly).
+ * port must not be 0
+ * type must not be UNKNOWN
+ * So I picked arbitrary (guess from where?) values instead
+ */
+static struct serial_state rs_table[NR_PORTS]={
+  /* UART CLK   PORT IRQ     FLAGS        */
+  { 0, BASE_BAUD, 0x3F8, 0, STD_COM_FLAGS,0,PORT_16550 }  /* ttyS0 */
+};
+
+/*
+ * Just for the fun of it !
+ */
+static struct serial_uart_config uart_config[] = {
+	{ "unknown", 1, 0 },
+	{ "8250", 1, 0 },
+	{ "16450", 1, 0 },
+	{ "16550", 1, 0 },
+	{ "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO },
+	{ "cirrus", 1, 0 },
+	{ "ST16650", 1, UART_CLEAR_FIFO | UART_STARTECH },
+	{ "ST16650V2", 32, UART_CLEAR_FIFO | UART_USE_FIFO |
+		  UART_STARTECH },
+	{ "TI16750", 64, UART_CLEAR_FIFO | UART_USE_FIFO},
+	{ 0, 0}
+};
+
+static struct tty_driver serial_driver, callout_driver;
+static int serial_refcount;
+
+static struct async_struct *IRQ_ports[NR_IRQS];
+static struct tty_struct *serial_table[NR_PORTS];
+static struct termios *serial_termios[NR_PORTS];
+static struct termios *serial_termios_locked[NR_PORTS];
+
+static struct console *console;
+
+static unsigned char *tmp_buf;
+static DECLARE_MUTEX(tmp_buf_sem);
+
+extern struct console *console_drivers; /* from kernel/printk.c */
+
+/*
+ * ------------------------------------------------------------
+ * rs_stop() and rs_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * They enable or disable transmitter interrupts, as necessary.
+ * ------------------------------------------------------------
+ */
+static void rs_stop(struct tty_struct *tty)
+{
+#ifdef SIMSERIAL_DEBUG
+	printk("rs_stop: tty->stopped=%d tty->hw_stopped=%d tty->flow_stopped=%d\n",
+		tty->stopped, tty->hw_stopped, tty->flow_stopped);
+#endif
+
+}
+
+static void rs_start(struct tty_struct *tty)
+{
+#if SIMSERIAL_DEBUG
+	printk("rs_start: tty->stopped=%d tty->hw_stopped=%d tty->flow_stopped=%d\n",
+		tty->stopped, tty->hw_stopped, tty->flow_stopped);
+#endif
+}
+
+static  void receive_chars(struct tty_struct *tty, struct pt_regs *regs)
+{
+	unsigned char ch;
+	static unsigned char seen_esc = 0;
+
+	while ( (ch = ia64_ssc(0, 0, 0, 0, SSC_GETCHAR)) ) {
+		if ( ch == 27 && seen_esc == 0 ) {
+			seen_esc = 1;
+			continue;
+		} else {
+			if ( seen_esc==1 && ch == 'O' ) {
+				seen_esc = 2;
+				continue;
+			} else if ( seen_esc == 2 ) {
+				if ( ch == 'P' ) show_state();		/* F1 key */
+				if ( ch == 'Q' ) show_buffers();	/* F2 key */
+#ifdef CONFIG_KDB
+				if ( ch == 'S' )
+					kdb(KDB_REASON_KEYBOARD, 0, (kdb_eframe_t) regs);
+#endif
+
+				seen_esc = 0;
+				continue;
+			}
+		}
+		seen_esc = 0;
+		if (tty->flip.count >= TTY_FLIPBUF_SIZE) break;
+
+		*tty->flip.char_buf_ptr = ch;
+
+		*tty->flip.flag_buf_ptr = 0;
+
+		tty->flip.flag_buf_ptr++;
+		tty->flip.char_buf_ptr++;
+		tty->flip.count++;
+	}
+	tty_flip_buffer_push(tty);
+}
+
+/*
+ * This is the serial driver's interrupt routine for a single port
+ */
+static void rs_interrupt_single(int irq, void *dev_id, struct pt_regs * regs)
+{
+	struct async_struct * info;
+
+	/*
+	 * I don't know exactly why they don't use the dev_id opaque data
+	 * pointer instead of this extra lookup table
+	 */
+	info = IRQ_ports[irq];
+	if (!info || !info->tty) {
+		printk("simrs_interrupt_single: info|tty=0 info=%p problem\n", info);
+		return;
+	}
+	/*
+	 * pretty simple in our case, because we only get interrupts
+	 * on inbound traffic
+	 */
+	receive_chars(info->tty, regs);
+}
+
+/*
+ * -------------------------------------------------------------------
+ * Here ends the serial interrupt routines.
+ * -------------------------------------------------------------------
+ */
+
+#if 0
+/*
+ * not really used in our situation so keep them commented out for now
+ */
+static DECLARE_TASK_QUEUE(tq_serial); /* used to be at the top of the file */
+static void do_serial_bh(void)
+{
+	run_task_queue(&tq_serial);
+	printk("do_serial_bh: called\n");
+}
+#endif
+
+static void do_softint(void *private_)
+{
+	printk("simserial: do_softint called\n");
+}
+
+static void rs_put_char(struct tty_struct *tty, unsigned char ch)
+{
+	struct async_struct *info = (struct async_struct *)tty->driver_data;
+	unsigned long flags;
+
+	if (!tty || !info->xmit.buf) return;
+
+	save_flags(flags); cli();
+	if (CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE) == 0) {
+		restore_flags(flags);
+		return;
+	}
+	info->xmit.buf[info->xmit.head] = ch;
+	info->xmit.head = (info->xmit.head + 1) & (SERIAL_XMIT_SIZE-1);
+	restore_flags(flags);
+}
+
+static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done)
+{
+	int count;
+	unsigned long flags;
+
+	save_flags(flags); cli();
+
+	if (info->x_char) {
+		char c = info->x_char;
+
+		console->write(console, &c, 1);
+
+		info->state->icount.tx++;
+		info->x_char = 0;
+
+		goto out;
+	}
+
+	if (info->xmit.head == info->xmit.tail || info->tty->stopped || info->tty->hw_stopped) {
+#ifdef SIMSERIAL_DEBUG
+		printk("transmit_chars: head=%d, tail=%d, stopped=%d\n",
+		       info->xmit.head, info->xmit.tail, info->tty->stopped);
+#endif
+		goto out;
+	}
+	/*
+	 * We removed the loop and try to do it in to chunks. We need
+	 * 2 operations maximum because it's a ring buffer.
+	 *
+	 * First from current to tail if possible.
+	 * Then from the beginning of the buffer until necessary
+	 */
+
+	count = MIN(CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE),
+		    SERIAL_XMIT_SIZE - info->xmit.tail);
+	console->write(console, info->xmit.buf+info->xmit.tail, count);
+
+	info->xmit.tail = (info->xmit.tail+count) & (SERIAL_XMIT_SIZE-1);
+
+	/*
+	 * We have more at the beginning of the buffer
+	 */
+	count = CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+	if (count) {
+		console->write(console, info->xmit.buf, count);
+		info->xmit.tail += count;
+	}
+out:
+	restore_flags(flags);
+}
+
+static void rs_flush_chars(struct tty_struct *tty)
+{
+	struct async_struct *info = (struct async_struct *)tty->driver_data;
+
+	if (info->xmit.head == info->xmit.tail || tty->stopped || tty->hw_stopped ||
+	    !info->xmit.buf)
+		return;
+
+	transmit_chars(info, NULL);
+}
+
+
+static int rs_write(struct tty_struct * tty, int from_user,
+		    const unsigned char *buf, int count)
+{
+	int	c, ret = 0;
+	struct async_struct *info = (struct async_struct *)tty->driver_data;
+	unsigned long flags;
+
+	if (!tty || !info->xmit.buf || !tmp_buf) return 0;
+
+	save_flags(flags);
+	if (from_user) {
+		down(&tmp_buf_sem);
+		while (1) {
+			int c1;
+			c = CIRC_SPACE_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+			if (count < c)
+				c = count;
+			if (c <= 0)
+				break;
+
+			c -= copy_from_user(tmp_buf, buf, c);
+			if (!c) {
+				if (!ret)
+					ret = -EFAULT;
+				break;
+			}
+			cli();
+			c1 = CIRC_SPACE_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+			if (c1 < c)
+				c = c1;
+			memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c);
+			info->xmit.head = ((info->xmit.head + c) &
+					   (SERIAL_XMIT_SIZE-1));
+			restore_flags(flags);
+			buf += c;
+			count -= c;
+			ret += c;
+		}
+		up(&tmp_buf_sem);
+	} else {
+		cli();
+		while (1) {
+			c = CIRC_SPACE_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+			if (count < c)
+				c = count;
+			if (c <= 0) {
+				break;
+			}
+			memcpy(info->xmit.buf + info->xmit.head, buf, c);
+			info->xmit.head = ((info->xmit.head + c) &
+					   (SERIAL_XMIT_SIZE-1));
+			buf += c;
+			count -= c;
+			ret += c;
+		}
+		restore_flags(flags);
+	}
+	/*
+	 * Hey, we transmit directly from here in our case
+	 */
+	if (CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE)
+	    && !tty->stopped && !tty->hw_stopped) {
+		transmit_chars(info, NULL);
+	}
+	return ret;
+}
+
+static int rs_write_room(struct tty_struct *tty)
+{
+	struct async_struct *info = (struct async_struct *)tty->driver_data;
+
+	return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+}
+
+static int rs_chars_in_buffer(struct tty_struct *tty)
+{
+	struct async_struct *info = (struct async_struct *)tty->driver_data;
+
+	return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+}
+
+static void rs_flush_buffer(struct tty_struct *tty)
+{
+	struct async_struct *info = (struct async_struct *)tty->driver_data;
+	unsigned long flags;
+
+	save_flags(flags); cli();
+	info->xmit.head = info->xmit.tail = 0;
+	restore_flags(flags);
+
+	wake_up_interruptible(&tty->write_wait);
+
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+	    tty->ldisc.write_wakeup)
+		(tty->ldisc.write_wakeup)(tty);
+}
+
+/*
+ * This function is used to send a high-priority XON/XOFF character to
+ * the device
+ */
+static void rs_send_xchar(struct tty_struct *tty, char ch)
+{
+	struct async_struct *info = (struct async_struct *)tty->driver_data;
+
+	info->x_char = ch;
+	if (ch) {
+		/*
+		 * I guess we could call console->write() directly but
+		 * let's do that for now.
+		 */
+		transmit_chars(info, NULL);
+	}
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_throttle()
+ *
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ * ------------------------------------------------------------
+ */
+static void rs_throttle(struct tty_struct * tty)
+{
+	if (I_IXOFF(tty)) rs_send_xchar(tty, STOP_CHAR(tty));
+
+	printk("simrs_throttle called\n");
+}
+
+static void rs_unthrottle(struct tty_struct * tty)
+{
+	struct async_struct *info = (struct async_struct *)tty->driver_data;
+
+	if (I_IXOFF(tty)) {
+		if (info->x_char)
+			info->x_char = 0;
+		else
+			rs_send_xchar(tty, START_CHAR(tty));
+	}
+	printk("simrs_unthrottle called\n");
+}
+
+/*
+ * rs_break() --- routine which turns the break handling on or off
+ */
+static void rs_break(struct tty_struct *tty, int break_state)
+{
+}
+
+static int rs_ioctl(struct tty_struct *tty, struct file * file,
+		    unsigned int cmd, unsigned long arg)
+{
+	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+	    (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&
+	    (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
+		if (tty->flags & (1 << TTY_IO_ERROR))
+		    return -EIO;
+	}
+
+	switch (cmd) {
+		case TIOCMGET:
+			printk("rs_ioctl: TIOCMGET called\n");
+			return -EINVAL;
+		case TIOCMBIS:
+		case TIOCMBIC:
+		case TIOCMSET:
+			printk("rs_ioctl: TIOCMBIS/BIC/SET called\n");
+			return -EINVAL;
+		case TIOCGSERIAL:
+			printk("simrs_ioctl TIOCGSERIAL called\n");
+			return 0;
+		case TIOCSSERIAL:
+			printk("simrs_ioctl TIOCSSERIAL called\n");
+			return 0;
+		case TIOCSERCONFIG:
+			printk("rs_ioctl: TIOCSERCONFIG called\n");
+			return -EINVAL;
+
+		case TIOCSERGETLSR: /* Get line status register */
+			printk("rs_ioctl: TIOCSERGETLSR called\n");
+			return  -EINVAL;
+
+		case TIOCSERGSTRUCT:
+			printk("rs_ioctl: TIOCSERGSTRUCT called\n");
+#if 0
+			if (copy_to_user((struct async_struct *) arg,
+					 info, sizeof(struct async_struct)))
+				return -EFAULT;
+#endif
+			return 0;
+
+		/*
+		 * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
+		 * - mask passed in arg for lines of interest
+		 *   (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
+		 * Caller should use TIOCGICOUNT to see which one it was
+		 */
+		case TIOCMIWAIT:
+			printk("rs_ioctl: TIOCMIWAIT: called\n");
+			return 0;
+		/*
+		 * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
+		 * Return: write counters to the user passed counter struct
+		 * NB: both 1->0 and 0->1 transitions are counted except for
+		 *     RI where only 0->1 is counted.
+		 */
+		case TIOCGICOUNT:
+			printk("rs_ioctl: TIOCGICOUNT called\n");
+			return 0;
+
+		case TIOCSERGWILD:
+		case TIOCSERSWILD:
+			/* "setserial -W" is called in Debian boot */
+			printk ("TIOCSER?WILD ioctl obsolete, ignored.\n");
+			return 0;
+
+		default:
+			return -ENOIOCTLCMD;
+		}
+	return 0;
+}
+
+#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+
+static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+	unsigned int cflag = tty->termios->c_cflag;
+
+	if (   (cflag == old_termios->c_cflag)
+	    && (   RELEVANT_IFLAG(tty->termios->c_iflag)
+		== RELEVANT_IFLAG(old_termios->c_iflag)))
+	  return;
+
+
+	/* Handle turning off CRTSCTS */
+	if ((old_termios->c_cflag & CRTSCTS) &&
+	    !(tty->termios->c_cflag & CRTSCTS)) {
+		tty->hw_stopped = 0;
+		rs_start(tty);
+	}
+}
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void shutdown(struct async_struct * info)
+{
+	unsigned long	flags;
+	struct serial_state *state;
+	int		retval;
+
+	if (!(info->flags & ASYNC_INITIALIZED)) return;
+
+	state = info->state;
+
+#ifdef SIMSERIAL_DEBUG
+	printk("Shutting down serial port %d (irq %d)....", info->line,
+	       state->irq);
+#endif
+
+	save_flags(flags); cli(); /* Disable interrupts */
+
+	/*
+	 * First unlink the serial port from the IRQ chain...
+	 */
+	if (info->next_port)
+		info->next_port->prev_port = info->prev_port;
+	if (info->prev_port)
+		info->prev_port->next_port = info->next_port;
+	else
+		IRQ_ports[state->irq] = info->next_port;
+
+	/*
+	 * Free the IRQ, if necessary
+	 */
+	if (state->irq && (!IRQ_ports[state->irq] ||
+			  !IRQ_ports[state->irq]->next_port)) {
+		if (IRQ_ports[state->irq]) {
+			free_irq(state->irq, NULL);
+			retval = request_irq(state->irq, rs_interrupt_single,
+					     IRQ_T(info), "serial", NULL);
+
+			if (retval)
+				printk("serial shutdown: request_irq: error %d"
+				       "  Couldn't reacquire IRQ.\n", retval);
+		} else
+			free_irq(state->irq, NULL);
+	}
+
+	if (info->xmit.buf) {
+		free_page((unsigned long) info->xmit.buf);
+		info->xmit.buf = 0;
+	}
+
+	if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+	info->flags &= ~ASYNC_INITIALIZED;
+	restore_flags(flags);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_close()
+ *
+ * This routine is called when the serial port gets closed.  First, we
+ * wait for the last remaining data to be sent.  Then, we unlink its
+ * async structure from the interrupt chain if necessary, and we free
+ * that IRQ if nothing is left in the chain.
+ * ------------------------------------------------------------
+ */
+static void rs_close(struct tty_struct *tty, struct file * filp)
+{
+	struct async_struct * info = (struct async_struct *)tty->driver_data;
+	struct serial_state *state;
+	unsigned long flags;
+
+	if (!info ) return;
+
+	state = info->state;
+
+	save_flags(flags); cli();
+
+	if (tty_hung_up_p(filp)) {
+#ifdef SIMSERIAL_DEBUG
+		printk("rs_close: hung_up\n");
+#endif
+		MOD_DEC_USE_COUNT;
+		restore_flags(flags);
+		return;
+	}
+#ifdef SIMSERIAL_DEBUG
+	printk("rs_close ttys%d, count = %d\n", info->line, state->count);
+#endif
+	if ((tty->count == 1) && (state->count != 1)) {
+		/*
+		 * Uh, oh.  tty->count is 1, which means that the tty
+		 * structure will be freed.  state->count should always
+		 * be one in these conditions.  If it's greater than
+		 * one, we've got real problems, since it means the
+		 * serial port won't be shutdown.
+		 */
+		printk("rs_close: bad serial port count; tty->count is 1, "
+		       "state->count is %d\n", state->count);
+		state->count = 1;
+	}
+	if (--state->count < 0) {
+		printk("rs_close: bad serial port count for ttys%d: %d\n",
+		       info->line, state->count);
+		state->count = 0;
+	}
+	if (state->count) {
+		MOD_DEC_USE_COUNT;
+		restore_flags(flags);
+		return;
+	}
+	info->flags |= ASYNC_CLOSING;
+	restore_flags(flags);
+
+	/*
+	 * Now we wait for the transmit buffer to clear; and we notify
+	 * the line discipline to only process XON/XOFF characters.
+	 */
+	shutdown(info);
+	if (tty->driver.flush_buffer) tty->driver.flush_buffer(tty);
+	if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty);
+	info->event = 0;
+	info->tty = 0;
+	if (info->blocked_open) {
+		if (info->close_delay) {
+			current->state = TASK_INTERRUPTIBLE;
+			schedule_timeout(info->close_delay);
+		}
+		wake_up_interruptible(&info->open_wait);
+	}
+	info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|ASYNC_CLOSING);
+	wake_up_interruptible(&info->close_wait);
+	MOD_DEC_USE_COUNT;
+}
+
+/*
+ * rs_wait_until_sent() --- wait until the transmitter is empty
+ */
+static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+}
+
+
+/*
+ * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+static void rs_hangup(struct tty_struct *tty)
+{
+	struct async_struct * info = (struct async_struct *)tty->driver_data;
+	struct serial_state *state = info->state;
+
+#ifdef SIMSERIAL_DEBUG
+	printk("rs_hangup: called\n");
+#endif
+
+	state = info->state;
+
+	rs_flush_buffer(tty);
+	if (info->flags & ASYNC_CLOSING)
+		return;
+	shutdown(info);
+
+	info->event = 0;
+	state->count = 0;
+	info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
+	info->tty = 0;
+	wake_up_interruptible(&info->open_wait);
+}
+
+
+static int get_async_struct(int line, struct async_struct **ret_info)
+{
+	struct async_struct *info;
+	struct serial_state *sstate;
+
+	sstate = rs_table + line;
+	sstate->count++;
+	if (sstate->info) {
+		*ret_info = sstate->info;
+		return 0;
+	}
+	info = kmalloc(sizeof(struct async_struct), GFP_KERNEL);
+	if (!info) {
+		sstate->count--;
+		return -ENOMEM;
+	}
+	memset(info, 0, sizeof(struct async_struct));
+	init_waitqueue_head(&info->open_wait);
+	init_waitqueue_head(&info->close_wait);
+	init_waitqueue_head(&info->delta_msr_wait);
+	info->magic = SERIAL_MAGIC;
+	info->port = sstate->port;
+	info->flags = sstate->flags;
+	info->xmit_fifo_size = sstate->xmit_fifo_size;
+	info->line = line;
+	info->tqueue.routine = do_softint;
+	info->tqueue.data = info;
+	info->state = sstate;
+	if (sstate->info) {
+		kfree(info);
+		*ret_info = sstate->info;
+		return 0;
+	}
+	*ret_info = sstate->info = info;
+	return 0;
+}
+
+static int
+startup(struct async_struct *info)
+{
+	unsigned long flags;
+	int	retval=0;
+	void (*handler)(int, void *, struct pt_regs *);
+	struct serial_state *state= info->state;
+	unsigned long page;
+
+	page = get_free_page(GFP_KERNEL);
+	if (!page)
+		return -ENOMEM;
+
+	save_flags(flags); cli();
+
+	if (info->flags & ASYNC_INITIALIZED) {
+		free_page(page);
+		goto errout;
+	}
+
+	if (!state->port || !state->type) {
+		if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags);
+		free_page(page);
+		goto errout;
+	}
+	if (info->xmit.buf)
+		free_page(page);
+	else
+		info->xmit.buf = (unsigned char *) page;
+
+#ifdef SIMSERIAL_DEBUG
+	printk("startup: ttys%d (irq %d)...", info->line, state->irq);
+#endif
+
+	/*
+	 * Allocate the IRQ if necessary
+	 */
+	if (state->irq && (!IRQ_ports[state->irq] ||
+			  !IRQ_ports[state->irq]->next_port)) {
+		if (IRQ_ports[state->irq]) {
+			retval = -EBUSY;
+			goto errout;
+		} else
+			handler = rs_interrupt_single;
+
+		retval = request_irq(state->irq, handler, IRQ_T(info),
+				     "simserial", NULL);
+		if (retval) {
+			if (capable(CAP_SYS_ADMIN)) {
+				if (info->tty)
+					set_bit(TTY_IO_ERROR,
+						&info->tty->flags);
+				retval = 0;
+			}
+			goto errout;
+		}
+	}
+
+	/*
+	 * Insert serial port into IRQ chain.
+	 */
+	info->prev_port = 0;
+	info->next_port = IRQ_ports[state->irq];
+	if (info->next_port)
+		info->next_port->prev_port = info;
+	IRQ_ports[state->irq] = info;
+
+	if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags);
+
+	info->xmit.head = info->xmit.tail = 0;
+
+#if 0
+	/*
+	 * Set up serial timers...
+	 */
+	timer_table[RS_TIMER].expires = jiffies + 2*HZ/100;
+	timer_active |= 1 << RS_TIMER;
+#endif
+
+	/*
+	 * Set up the tty->alt_speed kludge
+	 */
+	if (info->tty) {
+		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+			info->tty->alt_speed = 57600;
+		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+			info->tty->alt_speed = 115200;
+		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+			info->tty->alt_speed = 230400;
+		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+			info->tty->alt_speed = 460800;
+	}
+
+	info->flags |= ASYNC_INITIALIZED;
+	restore_flags(flags);
+	return 0;
+
+errout:
+	restore_flags(flags);
+	return retval;
+}
+
+
+/*
+ * This routine is called whenever a serial port is opened.  It
+ * enables interrupts for a serial port, linking in its async structure into
+ * the IRQ chain.   It also performs the serial-specific
+ * initialization for the tty structure.
+ */
+static int rs_open(struct tty_struct *tty, struct file * filp)
+{
+	struct async_struct	*info;
+	int			retval, line;
+	unsigned long		page;
+
+	MOD_INC_USE_COUNT;
+	line = minor(tty->device) - tty->driver.minor_start;
+	if ((line < 0) || (line >= NR_PORTS)) {
+		MOD_DEC_USE_COUNT;
+		return -ENODEV;
+	}
+	retval = get_async_struct(line, &info);
+	if (retval) {
+		MOD_DEC_USE_COUNT;
+		return retval;
+	}
+	tty->driver_data = info;
+	info->tty = tty;
+
+#ifdef SIMSERIAL_DEBUG
+	printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line,
+	       info->state->count);
+#endif
+	info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+
+	if (!tmp_buf) {
+		page = get_free_page(GFP_KERNEL);
+		if (!page) {
+			/* MOD_DEC_USE_COUNT; "info->tty" will cause this? */
+			return -ENOMEM;
+		}
+		if (tmp_buf)
+			free_page(page);
+		else
+			tmp_buf = (unsigned char *) page;
+	}
+
+	/*
+	 * If the port is the middle of closing, bail out now
+	 */
+	if (tty_hung_up_p(filp) ||
+	    (info->flags & ASYNC_CLOSING)) {
+		if (info->flags & ASYNC_CLOSING)
+			interruptible_sleep_on(&info->close_wait);
+		/* MOD_DEC_USE_COUNT; "info->tty" will cause this? */
+#ifdef SERIAL_DO_RESTART
+		return ((info->flags & ASYNC_HUP_NOTIFY) ?
+			-EAGAIN : -ERESTARTSYS);
+#else
+		return -EAGAIN;
+#endif
+	}
+
+	/*
+	 * Start up serial port
+	 */
+	retval = startup(info);
+	if (retval) {
+		/* MOD_DEC_USE_COUNT; "info->tty" will cause this? */
+		return retval;
+	}
+
+	if ((info->state->count == 1) &&
+	    (info->flags & ASYNC_SPLIT_TERMIOS)) {
+		if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+			*tty->termios = info->state->normal_termios;
+		else
+			*tty->termios = info->state->callout_termios;
+	}
+
+	/*
+	 * figure out which console to use (should be one already)
+	 */
+	console = console_drivers;
+	while (console) {
+		if ((console->flags & CON_ENABLED) && console->write) break;
+		console = console->next;
+	}
+
+	info->session = current->session;
+	info->pgrp = current->pgrp;
+
+#ifdef SIMSERIAL_DEBUG
+	printk("rs_open ttys%d successful\n", info->line);
+#endif
+	return 0;
+}
+
+/*
+ * /proc fs routines....
+ */
+
+static inline int line_info(char *buf, struct serial_state *state)
+{
+	return sprintf(buf, "%d: uart:%s port:%lX irq:%d\n",
+		       state->line, uart_config[state->type].name,
+		       state->port, state->irq);
+}
+
+static int rs_read_proc(char *page, char **start, off_t off, int count,
+		 int *eof, void *data)
+{
+	int i, len = 0, l;
+	off_t	begin = 0;
+
+	len += sprintf(page, "simserinfo:1.0 driver:%s\n", serial_version);
+	for (i = 0; i < NR_PORTS && len < 4000; i++) {
+		l = line_info(page + len, &rs_table[i]);
+		len += l;
+		if (len+begin > off+count)
+			goto done;
+		if (len+begin < off) {
+			begin += len;
+			len = 0;
+		}
+	}
+	*eof = 1;
+done:
+	if (off >= len+begin)
+		return 0;
+	*start = page + (begin-off);
+	return ((count < begin+len-off) ? count : begin+len-off);
+}
+
+/*
+ * ---------------------------------------------------------------------
+ * rs_init() and friends
+ *
+ * rs_init() is called at boot-time to initialize the serial driver.
+ * ---------------------------------------------------------------------
+ */
+
+/*
+ * This routine prints out the appropriate serial driver version
+ * number, and identifies which options were configured into this
+ * driver.
+ */
+static inline void show_serial_version(void)
+{
+	printk(KERN_INFO "%s version %s with", serial_name, serial_version);
+	printk(" no serial options enabled\n");
+}
+
+/*
+ * The serial driver boot-time initialization code!
+ */
+static int __init
+simrs_init (void)
+{
+	int			i;
+	struct serial_state	*state;
+
+	show_serial_version();
+
+	/* Initialize the tty_driver structure */
+
+	memset(&serial_driver, 0, sizeof(struct tty_driver));
+	serial_driver.magic = TTY_DRIVER_MAGIC;
+	serial_driver.driver_name = "simserial";
+	serial_driver.name = "ttyS";
+	serial_driver.major = TTY_MAJOR;
+	serial_driver.minor_start = 64;
+	serial_driver.num = 1;
+	serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
+	serial_driver.subtype = SERIAL_TYPE_NORMAL;
+	serial_driver.init_termios = tty_std_termios;
+	serial_driver.init_termios.c_cflag =
+		B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+	serial_driver.flags = TTY_DRIVER_REAL_RAW;
+	serial_driver.refcount = &serial_refcount;
+	serial_driver.table = serial_table;
+	serial_driver.termios = serial_termios;
+	serial_driver.termios_locked = serial_termios_locked;
+
+	serial_driver.open = rs_open;
+	serial_driver.close = rs_close;
+	serial_driver.write = rs_write;
+	serial_driver.put_char = rs_put_char;
+	serial_driver.flush_chars = rs_flush_chars;
+	serial_driver.write_room = rs_write_room;
+	serial_driver.chars_in_buffer = rs_chars_in_buffer;
+	serial_driver.flush_buffer = rs_flush_buffer;
+	serial_driver.ioctl = rs_ioctl;
+	serial_driver.throttle = rs_throttle;
+	serial_driver.unthrottle = rs_unthrottle;
+	serial_driver.send_xchar = rs_send_xchar;
+	serial_driver.set_termios = rs_set_termios;
+	serial_driver.stop = rs_stop;
+	serial_driver.start = rs_start;
+	serial_driver.hangup = rs_hangup;
+	serial_driver.break_ctl = rs_break;
+	serial_driver.wait_until_sent = rs_wait_until_sent;
+	serial_driver.read_proc = rs_read_proc;
+
+	/*
+	 * Let's have a little bit of fun !
+	 */
+	for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) {
+
+		if (state->type == PORT_UNKNOWN) continue;
+
+		if (!state->irq) {
+			state->irq = ia64_alloc_irq();
+			ia64_ssc_connect_irq(KEYBOARD_INTR, state->irq);
+		}
+
+		printk(KERN_INFO "ttyS%02d at 0x%04lx (irq = %d) is a %s\n",
+		       state->line,
+		       state->port, state->irq,
+		       uart_config[state->type].name);
+	}
+	/*
+	 * The callout device is just like normal device except for
+	 * major number and the subtype code.
+	 */
+	callout_driver = serial_driver;
+	callout_driver.name = "cua";
+	callout_driver.major = TTYAUX_MAJOR;
+	callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+	callout_driver.read_proc = 0;
+	callout_driver.proc_entry = 0;
+
+	if (tty_register_driver(&serial_driver))
+		panic("Couldn't register simserial driver\n");
+
+	if (tty_register_driver(&callout_driver))
+		panic("Couldn't register callout driver\n");
+
+	return 0;
+}
+
+#ifndef MODULE
+__initcall(simrs_init);
+#endif
diff -Nru a/arch/ia64/hp/simeth.c b/arch/ia64/hp/simeth.c
--- a/arch/ia64/hp/simeth.c	Mon Apr 22 15:32:18 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,533 +0,0 @@
-/*
- * Simulated Ethernet Driver
- *
- * Copyright (C) 1999-2001 Hewlett-Packard Co
- *	Stephane Eranian <eranian@hpl.hp.com>
- */
-#include <linux/config.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/in.h>
-#include <linux/string.h>
-#include <linux/init.h>
-#include <linux/errno.h>
-#include <linux/interrupt.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/inetdevice.h>
-#include <linux/if_ether.h>
-#include <linux/if_arp.h>
-#include <linux/skbuff.h>
-#include <linux/notifier.h>
-#include <asm/bitops.h>
-#include <asm/system.h>
-#include <asm/irq.h>
-
-#define SIMETH_RECV_MAX	10
-
-/*
- * Maximum possible received frame for Ethernet.
- * We preallocate an sk_buff of that size to avoid costly
- * memcpy for temporary buffer into sk_buff. We do basically
- * what's done in other drivers, like eepro with a ring.
- * The difference is, of course, that we don't have real DMA !!!
- */
-#define SIMETH_FRAME_SIZE	ETH_FRAME_LEN
-
-
-#define SSC_NETDEV_PROBE		100
-#define SSC_NETDEV_SEND			101
-#define SSC_NETDEV_RECV			102
-#define SSC_NETDEV_ATTACH		103
-#define SSC_NETDEV_DETACH		104
-
-#define NETWORK_INTR			8
-
-struct simeth_local {
-	struct net_device_stats stats;
-	int 			simfd;	 /* descriptor in the simulator */
-};
-
-static int simeth_probe1(void);
-static int simeth_open(struct net_device *dev);
-static int simeth_close(struct net_device *dev);
-static int simeth_tx(struct sk_buff *skb, struct net_device *dev);
-static int simeth_rx(struct net_device *dev);
-static struct net_device_stats *simeth_get_stats(struct net_device *dev);
-static void simeth_interrupt(int irq, void *dev_id, struct pt_regs * regs);
-static void set_multicast_list(struct net_device *dev);
-static int simeth_device_event(struct notifier_block *this,unsigned long event, void *ptr);
-
-static char *simeth_version="0.3";
-
-/*
- * This variable is used to establish a mapping between the Linux/ia64 kernel
- * and the host linux kernel.
- *
- * As of today, we support only one card, even though most of the code
- * is ready for many more. The mapping is then:
- *	linux/ia64 -> linux/x86
- * 	   eth0    -> eth1
- *
- * In the future, we some string operations, we could easily support up
- * to 10 cards (0-9).
- *
- * The default mapping can be changed on the kernel command line by
- * specifying simeth=ethX (or whatever string you want).
- */
-static char *simeth_device="eth0";	 /* default host interface to use */
-
-
-
-static volatile unsigned int card_count; /* how many cards "found" so far */
-static int simeth_debug;		/* set to 1 to get debug information */
-
-/*
- * Used to catch IFF_UP & IFF_DOWN events
- */
-static struct notifier_block simeth_dev_notifier = {
-	simeth_device_event,
-	0
-};
-
-
-/*
- * Function used when using a kernel command line option.
- *
- * Format: simeth=interface_name (like eth0)
- */
-static int __init
-simeth_setup(char *str)
-{
-	simeth_device = str;
-	return 1;
-}
-
-__setup("simeth=", simeth_setup);
-
-/*
- * Function used to probe for simeth devices when not installed
- * as a loadable module
- */
-
-int __init
-simeth_probe (void)
-{
-	int r;
-
-	printk("simeth: v%s\n", simeth_version);
-
-	r = simeth_probe1();
-
-	if (r == 0) register_netdevice_notifier(&simeth_dev_notifier);
-
-	return r;
-}
-
-extern long ia64_ssc (long, long, long, long, int);
-extern void ia64_ssc_connect_irq (long intr, long irq);
-
-static inline int
-netdev_probe(char *name, unsigned char *ether)
-{
-	return ia64_ssc(__pa(name), __pa(ether), 0,0, SSC_NETDEV_PROBE);
-}
-
-
-static inline int
-netdev_connect(int irq)
-{
-	/* XXX Fix me
-	 * this does not support multiple cards
-	 * also no return value
-	 */
-	ia64_ssc_connect_irq(NETWORK_INTR, irq);
-	return 0;
-}
-
-static inline int
-netdev_attach(int fd, int irq, unsigned int ipaddr)
-{
-	/* this puts the host interface in the right mode (start interupting) */
-	return ia64_ssc(fd, ipaddr, 0,0, SSC_NETDEV_ATTACH);
-}
-
-
-static inline int
-netdev_detach(int fd)
-{
-	/*
-	 * inactivate the host interface (don't interrupt anymore) */
-	return ia64_ssc(fd, 0,0,0, SSC_NETDEV_DETACH);
-}
-
-static inline int
-netdev_send(int fd, unsigned char *buf, unsigned int len)
-{
-	return ia64_ssc(fd, __pa(buf), len, 0, SSC_NETDEV_SEND);
-}
-
-static inline int
-netdev_read(int fd, unsigned char *buf, unsigned int len)
-{
-	return ia64_ssc(fd, __pa(buf), len, 0, SSC_NETDEV_RECV);
-}
-
-/*
- * Function shared with module code, so cannot be in init section
- *
- * So far this function "detects" only one card (test_&_set) but could
- * be extended easily.
- *
- * Return:
- * 	- -ENODEV is no device found
- *	- -ENOMEM is no more memory
- *	- 0 otherwise
- */
-static int
-simeth_probe1(void)
-{
-	unsigned char mac_addr[ETH_ALEN];
-	struct simeth_local *local;
-	struct net_device *dev;
-	int fd, i;
-
-	/*
-	 * XXX Fix me
-	 * let's support just one card for now
-	 */
-	if (test_and_set_bit(0, &card_count))
-		return -ENODEV;
-
-	/*
-	 * check with the simulator for the device
-	 */
-	fd = netdev_probe(simeth_device, mac_addr);
-	if (fd == -1)
-		return -ENODEV;
-
-	dev = init_etherdev(NULL, sizeof(struct simeth_local));
-	if (!dev)
-		return -ENOMEM;
-
-	memcpy(dev->dev_addr, mac_addr, sizeof(mac_addr));
-
-	dev->irq = ia64_alloc_irq();
-
-	/*
-	 * attach the interrupt in the simulator, this does enable interrupts
-	 * until a netdev_attach() is called
-	 */
-	netdev_connect(dev->irq);
-
-	memset(dev->priv, 0, sizeof(struct simeth_local));
-
-	local = dev->priv;
-	local->simfd = fd; /* keep track of underlying file descriptor */
-
-	dev->open		= simeth_open;
-	dev->stop		= simeth_close;
-	dev->hard_start_xmit	= simeth_tx;
-	dev->get_stats		= simeth_get_stats;
-	dev->set_multicast_list = set_multicast_list; /* no yet used */
-
-	/* Fill in the fields of the device structure with ethernet-generic values. */
-	ether_setup(dev);
-
-	printk("%s: hosteth=%s simfd=%d, HwAddr", dev->name, simeth_device, local->simfd);
-	for(i = 0; i < ETH_ALEN; i++) {
-		printk(" %2.2x", dev->dev_addr[i]);
-	}
-	printk(", IRQ %d\n", dev->irq);
-
-		return 0;
-}
-
-/*
- * actually binds the device to an interrupt vector
- */
-static int
-simeth_open(struct net_device *dev)
-{
-	if (request_irq(dev->irq, simeth_interrupt, 0, "simeth", dev)) {
-		printk ("simeth: unable to get IRQ %d.\n", dev->irq);
-		return -EAGAIN;
-	}
-
-	netif_start_queue(dev);
-
-	return 0;
-}
-
-/* copied from lapbether.c */
-static __inline__ int dev_is_ethdev(struct net_device *dev)
-{
-       return ( dev->type == ARPHRD_ETHER && strncmp(dev->name, "dummy", 5));
-}
-
-
-/*
- * Handler for IFF_UP or IFF_DOWN
- *
- * The reason for that is that we don't want to be interrupted when the
- * interface is down. There is no way to unconnect in the simualtor. Instead
- * we use this function to shutdown packet processing in the frame filter
- * in the simulator. Thus no interrupts are generated
- *
- *
- * That's also the place where we pass the IP address of this device to the
- * simulator so that that we can start filtering packets for it
- *
- * There may be a better way of doing this, but I don't know which yet.
- */
-static int
-simeth_device_event(struct notifier_block *this,unsigned long event, void *ptr)
-{
-	struct net_device *dev = (struct net_device *)ptr;
-	struct simeth_local *local;
-	struct in_device *in_dev;
-	struct in_ifaddr **ifap = NULL;
-	struct in_ifaddr *ifa = NULL;
-	int r;
-
-
-	if ( ! dev ) {
-		printk(KERN_WARNING "simeth_device_event dev=0\n");
-		return NOTIFY_DONE;
-	}
-
-	if ( event != NETDEV_UP && event != NETDEV_DOWN ) return NOTIFY_DONE;
-
-	/*
-	 * Check whether or not it's for an ethernet device
-	 *
-	 * XXX Fixme: This works only as long as we support one
-	 * type of ethernet device.
-	 */
-	if ( !dev_is_ethdev(dev) ) return NOTIFY_DONE;
-
-	if ((in_dev=dev->ip_ptr) != NULL) {
-		for (ifap=&in_dev->ifa_list; (ifa=*ifap) != NULL; ifap=&ifa->ifa_next)
-			if (strcmp(dev->name, ifa->ifa_label) == 0) break;
-	}
-	if ( ifa == NULL ) {
-		printk("simeth_open: can't find device %s's ifa\n", dev->name);
-		return NOTIFY_DONE;
-	}
-
-	printk("simeth_device_event: %s ipaddr=0x%x\n", dev->name, htonl(ifa->ifa_local));
-
-	/*
-	 * XXX Fix me
-	 * if the device was up, and we're simply reconfiguring it, not sure
-	 * we get DOWN then UP.
-	 */
-
-	local = dev->priv;
-	/* now do it for real */
-	r = event == NETDEV_UP ?
-		netdev_attach(local->simfd, dev->irq, htonl(ifa->ifa_local)):
-		netdev_detach(local->simfd);
-
-	printk("simeth: netdev_attach/detach: event=%s ->%d\n", event == NETDEV_UP ? "attach":"detach", r);
-
-	return NOTIFY_DONE;
-}
-
-static int
-simeth_close(struct net_device *dev)
-{
-	netif_stop_queue(dev);
-
-	free_irq(dev->irq, dev);
-
-	return 0;
-}
-
-/*
- * Only used for debug
- */
-static void
-frame_print(unsigned char *from, unsigned char *frame, int len)
-{
-	int i;
-
-	printk("%s: (%d) %02x", from, len, frame[0] & 0xff);
-	for(i=1; i < 6; i++ ) {
-		printk(":%02x", frame[i] &0xff);
-	}
-	printk(" %2x", frame[6] &0xff);
-	for(i=7; i < 12; i++ ) {
-		printk(":%02x", frame[i] &0xff);
-	}
-	printk(" [%02x%02x]\n", frame[12], frame[13]);
-
-	for(i=14; i < len; i++ ) {
-		printk("%02x ", frame[i] &0xff);
-		if ( (i%10)==0) printk("\n");
-	}
-	printk("\n");
-}
-
-
-/*
- * Function used to transmit of frame, very last one on the path before
- * going to the simulator.
- */
-static int
-simeth_tx(struct sk_buff *skb, struct net_device *dev)
-{
-	struct simeth_local *local = (struct simeth_local *)dev->priv;
-
-#if 0
-	/* ensure we have at least ETH_ZLEN bytes (min frame size) */
-	unsigned int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
-	/* Where do the extra padding bytes comes from inthe skbuff ? */
-#else
-	/* the real driver in the host system is going to take care of that
-	 * or maybe it's the NIC itself.
-	 */
-	unsigned int length = skb->len;
-#endif
-
-	local->stats.tx_bytes += skb->len;
-	local->stats.tx_packets++;
-
-
-	if (simeth_debug > 5) frame_print("simeth_tx", skb->data, length);
-
-	netdev_send(local->simfd, skb->data, length);
-
-	/*
-	 * we are synchronous on write, so we don't simulate a
-	 * trasnmit complete interrupt, thus we don't need to arm a tx
-	 */
-
-	dev_kfree_skb(skb);
-	return 0;
-}
-
-static inline struct sk_buff *
-make_new_skb(struct net_device *dev)
-{
-	struct sk_buff *nskb;
-
-	/*
-	 * The +2 is used to make sure that the IP header is nicely
-	 * aligned (on 4byte boundary I assume 14+2=16)
-	 */
-	nskb = dev_alloc_skb(SIMETH_FRAME_SIZE + 2);
-	if ( nskb == NULL ) {
-		printk(KERN_NOTICE "%s: memory squeeze. dropping packet.\n", dev->name);
-		return NULL;
-	}
-	nskb->dev = dev;
-
-	skb_reserve(nskb, 2);	/* Align IP on 16 byte boundaries */
-
-	skb_put(nskb,SIMETH_FRAME_SIZE);
-
-	return nskb;
-}
-
-/*
- * called from interrupt handler to process a received frame
- */
-static int
-simeth_rx(struct net_device *dev)
-{
-	struct simeth_local	*local;
-	struct sk_buff		*skb;
-	int			len;
-	int			rcv_count = SIMETH_RECV_MAX;
-
-	local = (struct simeth_local *)dev->priv;
-	/*
-	 * the loop concept has been borrowed from other drivers
-	 * looks to me like it's a throttling thing to avoid pushing to many
-	 * packets at one time into the stack. Making sure we can process them
-	 * upstream and make forward progress overall
-	 */
-	do {
-		if ( (skb=make_new_skb(dev)) == NULL ) {
-			printk(KERN_NOTICE "%s: memory squeeze. dropping packet.\n", dev->name);
-			local->stats.rx_dropped++;
-			return 0;
-		}
-		/*
-		 * Read only one frame at a time
-		 */
-		len = netdev_read(local->simfd, skb->data, SIMETH_FRAME_SIZE);
-		if ( len == 0 ) {
-			if ( simeth_debug > 0 ) printk(KERN_WARNING "%s: count=%d netdev_read=0\n", dev->name, SIMETH_RECV_MAX-rcv_count);
-			break;
-		}
-#if 0
-		/*
-		 * XXX Fix me
-		 * Should really do a csum+copy here
-		 */
-		memcpy(skb->data, frame, len);
-#endif
-		skb->protocol = eth_type_trans(skb, dev);
-
-		if ( simeth_debug > 6 ) frame_print("simeth_rx", skb->data, len);
-
-		/*
-		 * push the packet up & trigger software interrupt
-		 */
-		netif_rx(skb);
-
-		local->stats.rx_packets++;
-		local->stats.rx_bytes += len;
-
-	} while ( --rcv_count );
-
-	return len; /* 0 = nothing left to read, otherwise, we can try again */
-}
-
-/*
- * Interrupt handler (Yes, we can do it too !!!)
- */
-static void
-simeth_interrupt(int irq, void *dev_id, struct pt_regs * regs)
-{
-	struct net_device *dev = dev_id;
-
-	if ( dev == NULL ) {
-		printk(KERN_WARNING "simeth: irq %d for unknown device\n", irq);
-		return;
-	}
-
-	/*
-	 * very simple loop because we get interrupts only when receving
-	 */
-	while (simeth_rx(dev));
-}
-
-static struct net_device_stats *
-simeth_get_stats(struct net_device *dev)
-{
-	struct simeth_local  *local = (struct simeth_local *) dev->priv;
-
-	return &local->stats;
-}
-
-/* fake multicast ability */
-static void
-set_multicast_list(struct net_device *dev)
-{
-	printk(KERN_WARNING "%s: set_multicast_list called\n", dev->name);
-}
-
-#ifdef CONFIG_NET_FASTROUTE
-static int
-simeth_accept_fastpath(struct net_device *dev, struct dst_entry *dst)
-{
-	printk(KERN_WARNING "%s: simeth_accept_fastpath called\n", dev->name);
-	return -1;
-}
-#endif
-
-__initcall(simeth_probe);
diff -Nru a/arch/ia64/hp/simscsi.c b/arch/ia64/hp/simscsi.c
--- a/arch/ia64/hp/simscsi.c	Mon Apr 22 15:32:19 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,368 +0,0 @@
-/*
- * Simulated SCSI driver.
- *
- * Copyright (C) 1999, 2001-2002 Hewlett-Packard Co
- *	David Mosberger-Tang <davidm@hpl.hp.com>
- *	Stephane Eranian <eranian@hpl.hp.com>
- *
- * 02/01/15 David Mosberger	Updated for v2.5.1
- * 99/12/18 David Mosberger	Added support for READ10/WRITE10 needed by linux v2.3.33
- */
-#include <linux/config.h>
-#include <linux/blk.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/timer.h>
-
-#include <scsi/scsi.h>
-
-#include <asm/irq.h>
-
-#include "../drivers/scsi/scsi.h"
-#include "../drivers/scsi/sd.h"
-#include "../drivers/scsi/hosts.h"
-#include "simscsi.h"
-
-#define DEBUG_SIMSCSI	1
-
-/* Simulator system calls: */
-
-#define SSC_OPEN			50
-#define SSC_CLOSE			51
-#define SSC_READ			52
-#define SSC_WRITE			53
-#define SSC_GET_COMPLETION		54
-#define SSC_WAIT_COMPLETION		55
-
-#define SSC_WRITE_ACCESS		2
-#define SSC_READ_ACCESS			1
-
-#if DEBUG_SIMSCSI
-  int simscsi_debug;
-# define DBG	simscsi_debug
-#else
-# define DBG	0
-#endif
-
-static struct Scsi_Host *host;
-
-static void simscsi_interrupt (unsigned long val);
-DECLARE_TASKLET(simscsi_tasklet, simscsi_interrupt, 0);
-
-struct disk_req {
-	unsigned long addr;
-	unsigned len;
-};
-
-struct disk_stat {
-	int fd;
-	unsigned count;
-};
-
-extern long ia64_ssc (long arg0, long arg1, long arg2, long arg3, int nr);
-
-static int desc[8] = { -1, -1, -1, -1, -1, -1, -1, -1 };
-
-static struct queue_entry {
-	Scsi_Cmnd *sc;
-} queue[SIMSCSI_REQ_QUEUE_LEN];
-
-static int rd, wr;
-static atomic_t num_reqs = ATOMIC_INIT(0);
-
-/* base name for default disks */
-static char *simscsi_root = DEFAULT_SIMSCSI_ROOT;
-
-#define MAX_ROOT_LEN	128
-
-/*
- * used to setup a new base for disk images
- * to use /foo/bar/disk[a-z] as disk images
- * you have to specify simscsi=/foo/bar/disk on the command line
- */
-static int __init
-simscsi_setup (char *s)
-{
-	/* XXX Fix me we may need to strcpy() ? */
-	if (strlen(s) > MAX_ROOT_LEN) {
-		printk("simscsi_setup: prefix too long---using default %s\n", simscsi_root);
-	}
-	simscsi_root = s;
-	return 1;
-}
-
-__setup("simscsi=", simscsi_setup);
-
-static void
-simscsi_interrupt (unsigned long val)
-{
-	Scsi_Cmnd *sc;
-
-	while ((sc = queue[rd].sc) != 0) {
-		atomic_dec(&num_reqs);
-		queue[rd].sc = 0;
-		if (DBG)
-			printk("simscsi_interrupt: done with %ld\n", sc->serial_number);
-		(*sc->scsi_done)(sc);
-		rd = (rd + 1) % SIMSCSI_REQ_QUEUE_LEN;
-	}
-}
-
-int
-simscsi_detect (Scsi_Host_Template *templ)
-{
-	templ->proc_name = "simscsi";
-	host = scsi_register(templ, 0);
-	return 1;	/* fake one SCSI host adapter */
-}
-
-int
-simscsi_release (struct Scsi_Host *host)
-{
-	return 0;	/* this is easy...  */
-}
-
-const char *
-simscsi_info (struct Scsi_Host *host)
-{
-	return "simulated SCSI host adapter";
-}
-
-int
-simscsi_abort (Scsi_Cmnd *cmd)
-{
-	printk ("simscsi_abort: unimplemented\n");
-	return SCSI_ABORT_SUCCESS;
-}
-
-int
-simscsi_reset (Scsi_Cmnd *cmd, unsigned int reset_flags)
-{
-	printk ("simscsi_reset: unimplemented\n");
-	return SCSI_RESET_SUCCESS;
-}
-
-int
-simscsi_biosparam (Disk *disk, kdev_t n, int ip[])
-{
-	int size = disk->capacity;
-
-	ip[0] = 64;
-	ip[1] = 32;
-	ip[2] = size >> 11;
-	return 0;
-}
-
-static void
-simscsi_readwrite (Scsi_Cmnd *sc, int mode, unsigned long offset, unsigned long len)
-{
-	struct disk_stat stat;
-	struct disk_req req;
-
-	req.addr = __pa(sc->request_buffer);
-	req.len  = len;			/* # of bytes to transfer */
-
-	if (sc->request_bufflen < req.len)
-		return;
-
-	stat.fd = desc[sc->target];
-	if (DBG)
-		printk("simscsi_%s @ %lx (off %lx)\n",
-		       mode == SSC_READ ? "read":"write", req.addr, offset);
-	ia64_ssc(stat.fd, 1, __pa(&req), offset, mode);
-	ia64_ssc(__pa(&stat), 0, 0, 0, SSC_WAIT_COMPLETION);
-
-	if (stat.count == req.len) {
-		sc->result = GOOD;
-	} else {
-		sc->result = DID_ERROR << 16;
-	}
-}
-
-static void
-simscsi_sg_readwrite (Scsi_Cmnd *sc, int mode, unsigned long offset)
-{
-	int list_len = sc->use_sg;
-	struct scatterlist *sl = (struct scatterlist *)sc->buffer;
-	struct disk_stat stat;
-	struct disk_req req;
-
-	stat.fd = desc[sc->target];
-
-	while (list_len) {
-		req.addr = __pa(page_address(sl->page) + sl->offset);
-		req.len  = sl->length;
-		if (DBG)
-			printk("simscsi_sg_%s @ %lx (off %lx) use_sg=%d len=%d\n",
-			       mode == SSC_READ ? "read":"write", req.addr, offset,
-			       list_len, sl->length);
-		ia64_ssc(stat.fd, 1, __pa(&req), offset, mode);
-		ia64_ssc(__pa(&stat), 0, 0, 0, SSC_WAIT_COMPLETION);
-
-		/* should not happen in our case */
-		if (stat.count != req.len) {
-			sc->result = DID_ERROR << 16;
-			return;
-		}
-		offset +=  sl->length;
-		sl++;
-		list_len--;
-	}
-	sc->result = GOOD;
-}
-
-/*
- * function handling both READ_6/WRITE_6 (non-scatter/gather mode)
- * commands.
- * Added 02/26/99 S.Eranian
- */
-static void
-simscsi_readwrite6 (Scsi_Cmnd *sc, int mode)
-{
-	unsigned long offset;
-
-	offset = (((sc->cmnd[1] & 0x1f) << 16) | (sc->cmnd[2] << 8) | sc->cmnd[3])*512;
-	if (sc->use_sg > 0)
-		simscsi_sg_readwrite(sc, mode, offset);
-	else
-		simscsi_readwrite(sc, mode, offset, sc->cmnd[4]*512);
-}
-
-
-static void
-simscsi_readwrite10 (Scsi_Cmnd *sc, int mode)
-{
-	unsigned long offset;
-
-	offset = (  (sc->cmnd[2] << 24) | (sc->cmnd[3] << 16)
-		  | (sc->cmnd[4] <<  8) | (sc->cmnd[5] <<  0))*512;
-	if (sc->use_sg > 0)
-		simscsi_sg_readwrite(sc, mode, offset);
-	else
-		simscsi_readwrite(sc, mode, offset, ((sc->cmnd[7] << 8) | sc->cmnd[8])*512);
-}
-
-int
-simscsi_queuecommand (Scsi_Cmnd *sc, void (*done)(Scsi_Cmnd *))
-{
-	char fname[MAX_ROOT_LEN+16];
-	char *buf;
-#if DEBUG_SIMSCSI
-	register long sp asm ("sp");
-
-	if (DBG)
-		printk("simscsi_queuecommand: target=%d,cmnd=%u,sc=%lu,sp=%lx,done=%p\n",
-		       sc->target, sc->cmnd[0], sc->serial_number, sp, done);
-#endif
-
-	sc->result = DID_BAD_TARGET << 16;
-	sc->scsi_done = done;
-	if (sc->target <= 7 && sc->lun == 0) {
-		switch (sc->cmnd[0]) {
-		      case INQUIRY:
-			if (sc->request_bufflen < 35) {
-				break;
-			}
-			sprintf (fname, "%s%c", simscsi_root, 'a' + sc->target);
-			desc[sc->target] = ia64_ssc (__pa(fname), SSC_READ_ACCESS|SSC_WRITE_ACCESS,
-						     0, 0, SSC_OPEN);
-			if (desc[sc->target] < 0) {
-				/* disk doesn't exist... */
-				break;
-			}
-			buf = sc->request_buffer;
-			buf[0] = 0;	/* magnetic disk */
-			buf[1] = 0;	/* not a removable medium */
-			buf[2] = 2;	/* SCSI-2 compliant device */
-			buf[3] = 2;	/* SCSI-2 response data format */
-			buf[4] = 31;	/* additional length (bytes) */
-			buf[5] = 0;	/* reserved */
-			buf[6] = 0;	/* reserved */
-			buf[7] = 0;	/* various flags */
-			memcpy(buf + 8, "HP      SIMULATED DISK  0.00",  28);
-			sc->result = GOOD;
-			break;
-
-		      case TEST_UNIT_READY:
-			sc->result = GOOD;
-			break;
-
-		      case READ_6:
-			if (desc[sc->target] < 0 )
-				break;
-			simscsi_readwrite6(sc, SSC_READ);
-			break;
-
-		      case READ_10:
-			if (desc[sc->target] < 0 )
-				break;
-			simscsi_readwrite10(sc, SSC_READ);
-			break;
-
-		      case WRITE_6:
-			if (desc[sc->target] < 0)
-				break;
-			simscsi_readwrite6(sc, SSC_WRITE);
-			break;
-
-		      case WRITE_10:
-			if (desc[sc->target] < 0)
-				break;
-			simscsi_readwrite10(sc, SSC_WRITE);
-			break;
-
-
-		      case READ_CAPACITY:
-			if (desc[sc->target] < 0 || sc->request_bufflen < 8) {
-				break;
-			}
-			buf = sc->request_buffer;
-
-			/* pretend to be a 1GB disk (partition table contains real stuff): */
-			buf[0] = 0x00;
-			buf[1] = 0x1f;
-			buf[2] = 0xff;
-			buf[3] = 0xff;
-			/* set block size of 512 bytes: */
-			buf[4] = 0;
-			buf[5] = 0;
-			buf[6] = 2;
-			buf[7] = 0;
-			sc->result = GOOD;
-			break;
-
-		      case MODE_SENSE:
-			printk("MODE_SENSE\n");
-			break;
-
-		      case START_STOP:
-			printk("START_STOP\n");
-			break;
-
-		      default:
-			panic("simscsi: unknown SCSI command %u\n", sc->cmnd[0]);
-		}
-	}
-	if (sc->result == DID_BAD_TARGET) {
-		sc->result |= DRIVER_SENSE << 24;
-		sc->sense_buffer[0] = 0x70;
-		sc->sense_buffer[2] = 0x00;
-	}
-	if (atomic_read(&num_reqs) >= SIMSCSI_REQ_QUEUE_LEN) {
-		panic("Attempt to queue command while command is pending!!");
-	}
-	atomic_inc(&num_reqs);
-	queue[wr].sc = sc;
-	wr = (wr + 1) % SIMSCSI_REQ_QUEUE_LEN;
-
-	tasklet_schedule(&simscsi_tasklet);
-	return 0;
-}
-
-
-static Scsi_Host_Template driver_template = SIMSCSI;
-
-#define __initcall(fn)	late_initcall(fn)
-
-#include "../drivers/scsi/scsi_module.c"
diff -Nru a/arch/ia64/hp/simscsi.h b/arch/ia64/hp/simscsi.h
--- a/arch/ia64/hp/simscsi.h	Mon Apr 22 15:32:18 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,39 +0,0 @@
-/*
- * Simulated SCSI driver.
- *
- * Copyright (C) 1999 Hewlett-Packard Co
- *	David Mosberger-Tang <davidm@hpl.hp.com>
- */
-#ifndef SIMSCSI_H
-#define SIMSCSI_H
-
-#define SIMSCSI_REQ_QUEUE_LEN	64
-
-#define DEFAULT_SIMSCSI_ROOT	"/var/ski-disks/sd"
-
-extern int simscsi_detect (Scsi_Host_Template *);
-extern int simscsi_release (struct Scsi_Host *);
-extern const char *simscsi_info (struct Scsi_Host *);
-extern int simscsi_queuecommand (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
-extern int simscsi_abort (Scsi_Cmnd *);
-extern int simscsi_reset (Scsi_Cmnd *, unsigned int);
-extern int simscsi_biosparam (Disk *, kdev_t, int[]);
-
-#define SIMSCSI {					\
-	detect:			simscsi_detect,		\
-	release:		simscsi_release,	\
-	info:			simscsi_info,		\
-	queuecommand:		simscsi_queuecommand,	\
-	abort:			simscsi_abort,		\
-	reset:			simscsi_reset,		\
-	bios_param:		simscsi_biosparam,	\
-	can_queue:		SIMSCSI_REQ_QUEUE_LEN,	\
-	this_id:		-1,			\
-	sg_tablesize:		SG_ALL,			\
-	cmd_per_lun:		SIMSCSI_REQ_QUEUE_LEN,	\
-	present:		0,			\
-	unchecked_isa_dma:	0,			\
-	use_clustering:		DISABLE_CLUSTERING	\
-}
-
-#endif /* SIMSCSI_H */
diff -Nru a/arch/ia64/hp/simserial.c b/arch/ia64/hp/simserial.c
--- a/arch/ia64/hp/simserial.c	Mon Apr 22 15:32:19 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,1104 +0,0 @@
-/*
- * Simulated Serial Driver (fake serial)
- *
- * This driver is mostly used for bringup purposes and will go away.
- * It has a strong dependency on the system console. All outputs
- * are rerouted to the same facility as the one used by printk which, in our
- * case means sys_sim.c console (goes via the simulator). The code hereafter
- * is completely leveraged from the serial.c driver.
- *
- * Copyright (C) 1999-2000 Hewlett-Packard Co
- * Copyright (C) 1999 Stephane Eranian <eranian@hpl.hp.com>
- * Copyright (C) 2000 David Mosberger-Tang <davidm@hpl.hp.com>
- *
- * 02/04/00 D. Mosberger	Merged in serial.c bug fixes in rs_close().
- * 02/25/00 D. Mosberger	Synced up with 2.3.99pre-5 version of serial.c.
- */
-
-#include <linux/config.h>
-#include <linux/init.h>
-#include <linux/errno.h>
-#include <linux/sched.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/major.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/malloc.h>
-#include <linux/console.h>
-#include <linux/module.h>
-#include <linux/serial.h>
-#include <linux/serialP.h>
-
-#include <asm/irq.h>
-#include <asm/uaccess.h>
-
-#ifdef CONFIG_KDB
-# include <linux/kdb.h>
-#endif
-
-#undef SIMSERIAL_DEBUG	/* define this to get some debug information */
-
-#define KEYBOARD_INTR	3	/* must match with simulator! */
-
-#define NR_PORTS	1	/* only one port for now */
-#define SERIAL_INLINE	1
-
-#ifdef SERIAL_INLINE
-#define _INLINE_ inline
-#endif
-
-#ifndef MIN
-#define MIN(a,b)	((a) < (b) ? (a) : (b))
-#endif
-
-#define IRQ_T(info) ((info->flags & ASYNC_SHARE_IRQ) ? SA_SHIRQ : SA_INTERRUPT)
-
-#define SSC_GETCHAR	21
-
-extern long ia64_ssc (long, long, long, long, int);
-extern void ia64_ssc_connect_irq (long intr, long irq);
-
-static char *serial_name = "SimSerial driver";
-static char *serial_version = "0.6";
-
-/*
- * This has been extracted from asm/serial.h. We need one eventually but
- * I don't know exactly what we're going to put in it so just fake one
- * for now.
- */
-#define BASE_BAUD ( 1843200 / 16 )
-
-#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST)
-
-/*
- * Most of the values here are meaningless to this particular driver.
- * However some values must be preserved for the code (leveraged from serial.c
- * to work correctly).
- * port must not be 0
- * type must not be UNKNOWN
- * So I picked arbitrary (guess from where?) values instead
- */
-static struct serial_state rs_table[NR_PORTS]={
-  /* UART CLK   PORT IRQ     FLAGS        */
-  { 0, BASE_BAUD, 0x3F8, 0, STD_COM_FLAGS,0,PORT_16550 }  /* ttyS0 */
-};
-
-/*
- * Just for the fun of it !
- */
-static struct serial_uart_config uart_config[] = {
-	{ "unknown", 1, 0 },
-	{ "8250", 1, 0 },
-	{ "16450", 1, 0 },
-	{ "16550", 1, 0 },
-	{ "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO },
-	{ "cirrus", 1, 0 },
-	{ "ST16650", 1, UART_CLEAR_FIFO | UART_STARTECH },
-	{ "ST16650V2", 32, UART_CLEAR_FIFO | UART_USE_FIFO |
-		  UART_STARTECH },
-	{ "TI16750", 64, UART_CLEAR_FIFO | UART_USE_FIFO},
-	{ 0, 0}
-};
-
-static struct tty_driver serial_driver, callout_driver;
-static int serial_refcount;
-
-static struct async_struct *IRQ_ports[NR_IRQS];
-static struct tty_struct *serial_table[NR_PORTS];
-static struct termios *serial_termios[NR_PORTS];
-static struct termios *serial_termios_locked[NR_PORTS];
-
-static struct console *console;
-
-static unsigned char *tmp_buf;
-static DECLARE_MUTEX(tmp_buf_sem);
-
-extern struct console *console_drivers; /* from kernel/printk.c */
-
-/*
- * ------------------------------------------------------------
- * rs_stop() and rs_start()
- *
- * This routines are called before setting or resetting tty->stopped.
- * They enable or disable transmitter interrupts, as necessary.
- * ------------------------------------------------------------
- */
-static void rs_stop(struct tty_struct *tty)
-{
-#ifdef SIMSERIAL_DEBUG
-	printk("rs_stop: tty->stopped=%d tty->hw_stopped=%d tty->flow_stopped=%d\n",
-		tty->stopped, tty->hw_stopped, tty->flow_stopped);
-#endif
-
-}
-
-static void rs_start(struct tty_struct *tty)
-{
-#if SIMSERIAL_DEBUG
-	printk("rs_start: tty->stopped=%d tty->hw_stopped=%d tty->flow_stopped=%d\n",
-		tty->stopped, tty->hw_stopped, tty->flow_stopped);
-#endif
-}
-
-static  void receive_chars(struct tty_struct *tty, struct pt_regs *regs)
-{
-	unsigned char ch;
-	static unsigned char seen_esc = 0;
-
-	while ( (ch = ia64_ssc(0, 0, 0, 0, SSC_GETCHAR)) ) {
-		if ( ch == 27 && seen_esc == 0 ) {
-			seen_esc = 1;
-			continue;
-		} else {
-			if ( seen_esc==1 && ch == 'O' ) {
-				seen_esc = 2;
-				continue;
-			} else if ( seen_esc == 2 ) {
-				if ( ch == 'P' ) show_state();		/* F1 key */
-				if ( ch == 'Q' ) show_buffers();	/* F2 key */
-#ifdef CONFIG_KDB
-				if ( ch == 'S' )
-					kdb(KDB_REASON_KEYBOARD, 0, (kdb_eframe_t) regs);
-#endif
-
-				seen_esc = 0;
-				continue;
-			}
-		}
-		seen_esc = 0;
-		if (tty->flip.count >= TTY_FLIPBUF_SIZE) break;
-
-		*tty->flip.char_buf_ptr = ch;
-
-		*tty->flip.flag_buf_ptr = 0;
-
-		tty->flip.flag_buf_ptr++;
-		tty->flip.char_buf_ptr++;
-		tty->flip.count++;
-	}
-	tty_flip_buffer_push(tty);
-}
-
-/*
- * This is the serial driver's interrupt routine for a single port
- */
-static void rs_interrupt_single(int irq, void *dev_id, struct pt_regs * regs)
-{
-	struct async_struct * info;
-
-	/*
-	 * I don't know exactly why they don't use the dev_id opaque data
-	 * pointer instead of this extra lookup table
-	 */
-	info = IRQ_ports[irq];
-	if (!info || !info->tty) {
-		printk("simrs_interrupt_single: info|tty=0 info=%p problem\n", info);
-		return;
-	}
-	/*
-	 * pretty simple in our case, because we only get interrupts
-	 * on inbound traffic
-	 */
-	receive_chars(info->tty, regs);
-}
-
-/*
- * -------------------------------------------------------------------
- * Here ends the serial interrupt routines.
- * -------------------------------------------------------------------
- */
-
-#if 0
-/*
- * not really used in our situation so keep them commented out for now
- */
-static DECLARE_TASK_QUEUE(tq_serial); /* used to be at the top of the file */
-static void do_serial_bh(void)
-{
-	run_task_queue(&tq_serial);
-	printk("do_serial_bh: called\n");
-}
-#endif
-
-static void do_softint(void *private_)
-{
-	printk("simserial: do_softint called\n");
-}
-
-static void rs_put_char(struct tty_struct *tty, unsigned char ch)
-{
-	struct async_struct *info = (struct async_struct *)tty->driver_data;
-	unsigned long flags;
-
-	if (!tty || !info->xmit.buf) return;
-
-	save_flags(flags); cli();
-	if (CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE) == 0) {
-		restore_flags(flags);
-		return;
-	}
-	info->xmit.buf[info->xmit.head] = ch;
-	info->xmit.head = (info->xmit.head + 1) & (SERIAL_XMIT_SIZE-1);
-	restore_flags(flags);
-}
-
-static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done)
-{
-	int count;
-	unsigned long flags;
-
-	save_flags(flags); cli();
-
-	if (info->x_char) {
-		char c = info->x_char;
-
-		console->write(console, &c, 1);
-
-		info->state->icount.tx++;
-		info->x_char = 0;
-
-		goto out;
-	}
-
-	if (info->xmit.head == info->xmit.tail || info->tty->stopped || info->tty->hw_stopped) {
-#ifdef SIMSERIAL_DEBUG
-		printk("transmit_chars: head=%d, tail=%d, stopped=%d\n",
-		       info->xmit.head, info->xmit.tail, info->tty->stopped);
-#endif
-		goto out;
-	}
-	/*
-	 * We removed the loop and try to do it in to chunks. We need
-	 * 2 operations maximum because it's a ring buffer.
-	 *
-	 * First from current to tail if possible.
-	 * Then from the beginning of the buffer until necessary
-	 */
-
-	count = MIN(CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE),
-		    SERIAL_XMIT_SIZE - info->xmit.tail);
-	console->write(console, info->xmit.buf+info->xmit.tail, count);
-
-	info->xmit.tail = (info->xmit.tail+count) & (SERIAL_XMIT_SIZE-1);
-
-	/*
-	 * We have more at the beginning of the buffer
-	 */
-	count = CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
-	if (count) {
-		console->write(console, info->xmit.buf, count);
-		info->xmit.tail += count;
-	}
-out:
-	restore_flags(flags);
-}
-
-static void rs_flush_chars(struct tty_struct *tty)
-{
-	struct async_struct *info = (struct async_struct *)tty->driver_data;
-
-	if (info->xmit.head == info->xmit.tail || tty->stopped || tty->hw_stopped ||
-	    !info->xmit.buf)
-		return;
-
-	transmit_chars(info, NULL);
-}
-
-
-static int rs_write(struct tty_struct * tty, int from_user,
-		    const unsigned char *buf, int count)
-{
-	int	c, ret = 0;
-	struct async_struct *info = (struct async_struct *)tty->driver_data;
-	unsigned long flags;
-
-	if (!tty || !info->xmit.buf || !tmp_buf) return 0;
-
-	save_flags(flags);
-	if (from_user) {
-		down(&tmp_buf_sem);
-		while (1) {
-			int c1;
-			c = CIRC_SPACE_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
-			if (count < c)
-				c = count;
-			if (c <= 0)
-				break;
-
-			c -= copy_from_user(tmp_buf, buf, c);
-			if (!c) {
-				if (!ret)
-					ret = -EFAULT;
-				break;
-			}
-			cli();
-			c1 = CIRC_SPACE_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
-			if (c1 < c)
-				c = c1;
-			memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c);
-			info->xmit.head = ((info->xmit.head + c) &
-					   (SERIAL_XMIT_SIZE-1));
-			restore_flags(flags);
-			buf += c;
-			count -= c;
-			ret += c;
-		}
-		up(&tmp_buf_sem);
-	} else {
-		cli();
-		while (1) {
-			c = CIRC_SPACE_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
-			if (count < c)
-				c = count;
-			if (c <= 0) {
-				break;
-			}
-			memcpy(info->xmit.buf + info->xmit.head, buf, c);
-			info->xmit.head = ((info->xmit.head + c) &
-					   (SERIAL_XMIT_SIZE-1));
-			buf += c;
-			count -= c;
-			ret += c;
-		}
-		restore_flags(flags);
-	}
-	/*
-	 * Hey, we transmit directly from here in our case
-	 */
-	if (CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE)
-	    && !tty->stopped && !tty->hw_stopped) {
-		transmit_chars(info, NULL);
-	}
-	return ret;
-}
-
-static int rs_write_room(struct tty_struct *tty)
-{
-	struct async_struct *info = (struct async_struct *)tty->driver_data;
-
-	return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
-}
-
-static int rs_chars_in_buffer(struct tty_struct *tty)
-{
-	struct async_struct *info = (struct async_struct *)tty->driver_data;
-
-	return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
-}
-
-static void rs_flush_buffer(struct tty_struct *tty)
-{
-	struct async_struct *info = (struct async_struct *)tty->driver_data;
-	unsigned long flags;
-
-	save_flags(flags); cli();
-	info->xmit.head = info->xmit.tail = 0;
-	restore_flags(flags);
-
-	wake_up_interruptible(&tty->write_wait);
-
-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
-	    tty->ldisc.write_wakeup)
-		(tty->ldisc.write_wakeup)(tty);
-}
-
-/*
- * This function is used to send a high-priority XON/XOFF character to
- * the device
- */
-static void rs_send_xchar(struct tty_struct *tty, char ch)
-{
-	struct async_struct *info = (struct async_struct *)tty->driver_data;
-
-	info->x_char = ch;
-	if (ch) {
-		/*
-		 * I guess we could call console->write() directly but
-		 * let's do that for now.
-		 */
-		transmit_chars(info, NULL);
-	}
-}
-
-/*
- * ------------------------------------------------------------
- * rs_throttle()
- *
- * This routine is called by the upper-layer tty layer to signal that
- * incoming characters should be throttled.
- * ------------------------------------------------------------
- */
-static void rs_throttle(struct tty_struct * tty)
-{
-	if (I_IXOFF(tty)) rs_send_xchar(tty, STOP_CHAR(tty));
-
-	printk("simrs_throttle called\n");
-}
-
-static void rs_unthrottle(struct tty_struct * tty)
-{
-	struct async_struct *info = (struct async_struct *)tty->driver_data;
-
-	if (I_IXOFF(tty)) {
-		if (info->x_char)
-			info->x_char = 0;
-		else
-			rs_send_xchar(tty, START_CHAR(tty));
-	}
-	printk("simrs_unthrottle called\n");
-}
-
-/*
- * rs_break() --- routine which turns the break handling on or off
- */
-static void rs_break(struct tty_struct *tty, int break_state)
-{
-}
-
-static int rs_ioctl(struct tty_struct *tty, struct file * file,
-		    unsigned int cmd, unsigned long arg)
-{
-	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
-	    (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&
-	    (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
-		if (tty->flags & (1 << TTY_IO_ERROR))
-		    return -EIO;
-	}
-
-	switch (cmd) {
-		case TIOCMGET:
-			printk("rs_ioctl: TIOCMGET called\n");
-			return -EINVAL;
-		case TIOCMBIS:
-		case TIOCMBIC:
-		case TIOCMSET:
-			printk("rs_ioctl: TIOCMBIS/BIC/SET called\n");
-			return -EINVAL;
-		case TIOCGSERIAL:
-			printk("simrs_ioctl TIOCGSERIAL called\n");
-			return 0;
-		case TIOCSSERIAL:
-			printk("simrs_ioctl TIOCSSERIAL called\n");
-			return 0;
-		case TIOCSERCONFIG:
-			printk("rs_ioctl: TIOCSERCONFIG called\n");
-			return -EINVAL;
-
-		case TIOCSERGETLSR: /* Get line status register */
-			printk("rs_ioctl: TIOCSERGETLSR called\n");
-			return  -EINVAL;
-
-		case TIOCSERGSTRUCT:
-			printk("rs_ioctl: TIOCSERGSTRUCT called\n");
-#if 0
-			if (copy_to_user((struct async_struct *) arg,
-					 info, sizeof(struct async_struct)))
-				return -EFAULT;
-#endif
-			return 0;
-
-		/*
-		 * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
-		 * - mask passed in arg for lines of interest
-		 *   (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
-		 * Caller should use TIOCGICOUNT to see which one it was
-		 */
-		case TIOCMIWAIT:
-			printk("rs_ioctl: TIOCMIWAIT: called\n");
-			return 0;
-		/*
-		 * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
-		 * Return: write counters to the user passed counter struct
-		 * NB: both 1->0 and 0->1 transitions are counted except for
-		 *     RI where only 0->1 is counted.
-		 */
-		case TIOCGICOUNT:
-			printk("rs_ioctl: TIOCGICOUNT called\n");
-			return 0;
-
-		case TIOCSERGWILD:
-		case TIOCSERSWILD:
-			/* "setserial -W" is called in Debian boot */
-			printk ("TIOCSER?WILD ioctl obsolete, ignored.\n");
-			return 0;
-
-		default:
-			return -ENOIOCTLCMD;
-		}
-	return 0;
-}
-
-#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
-
-static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
-{
-	unsigned int cflag = tty->termios->c_cflag;
-
-	if (   (cflag == old_termios->c_cflag)
-	    && (   RELEVANT_IFLAG(tty->termios->c_iflag)
-		== RELEVANT_IFLAG(old_termios->c_iflag)))
-	  return;
-
-
-	/* Handle turning off CRTSCTS */
-	if ((old_termios->c_cflag & CRTSCTS) &&
-	    !(tty->termios->c_cflag & CRTSCTS)) {
-		tty->hw_stopped = 0;
-		rs_start(tty);
-	}
-}
-/*
- * This routine will shutdown a serial port; interrupts are disabled, and
- * DTR is dropped if the hangup on close termio flag is on.
- */
-static void shutdown(struct async_struct * info)
-{
-	unsigned long	flags;
-	struct serial_state *state;
-	int		retval;
-
-	if (!(info->flags & ASYNC_INITIALIZED)) return;
-
-	state = info->state;
-
-#ifdef SIMSERIAL_DEBUG
-	printk("Shutting down serial port %d (irq %d)....", info->line,
-	       state->irq);
-#endif
-
-	save_flags(flags); cli(); /* Disable interrupts */
-
-	/*
-	 * First unlink the serial port from the IRQ chain...
-	 */
-	if (info->next_port)
-		info->next_port->prev_port = info->prev_port;
-	if (info->prev_port)
-		info->prev_port->next_port = info->next_port;
-	else
-		IRQ_ports[state->irq] = info->next_port;
-
-	/*
-	 * Free the IRQ, if necessary
-	 */
-	if (state->irq && (!IRQ_ports[state->irq] ||
-			  !IRQ_ports[state->irq]->next_port)) {
-		if (IRQ_ports[state->irq]) {
-			free_irq(state->irq, NULL);
-			retval = request_irq(state->irq, rs_interrupt_single,
-					     IRQ_T(info), "serial", NULL);
-
-			if (retval)
-				printk("serial shutdown: request_irq: error %d"
-				       "  Couldn't reacquire IRQ.\n", retval);
-		} else
-			free_irq(state->irq, NULL);
-	}
-
-	if (info->xmit.buf) {
-		free_page((unsigned long) info->xmit.buf);
-		info->xmit.buf = 0;
-	}
-
-	if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags);
-
-	info->flags &= ~ASYNC_INITIALIZED;
-	restore_flags(flags);
-}
-
-/*
- * ------------------------------------------------------------
- * rs_close()
- *
- * This routine is called when the serial port gets closed.  First, we
- * wait for the last remaining data to be sent.  Then, we unlink its
- * async structure from the interrupt chain if necessary, and we free
- * that IRQ if nothing is left in the chain.
- * ------------------------------------------------------------
- */
-static void rs_close(struct tty_struct *tty, struct file * filp)
-{
-	struct async_struct * info = (struct async_struct *)tty->driver_data;
-	struct serial_state *state;
-	unsigned long flags;
-
-	if (!info ) return;
-
-	state = info->state;
-
-	save_flags(flags); cli();
-
-	if (tty_hung_up_p(filp)) {
-#ifdef SIMSERIAL_DEBUG
-		printk("rs_close: hung_up\n");
-#endif
-		MOD_DEC_USE_COUNT;
-		restore_flags(flags);
-		return;
-	}
-#ifdef SIMSERIAL_DEBUG
-	printk("rs_close ttys%d, count = %d\n", info->line, state->count);
-#endif
-	if ((tty->count == 1) && (state->count != 1)) {
-		/*
-		 * Uh, oh.  tty->count is 1, which means that the tty
-		 * structure will be freed.  state->count should always
-		 * be one in these conditions.  If it's greater than
-		 * one, we've got real problems, since it means the
-		 * serial port won't be shutdown.
-		 */
-		printk("rs_close: bad serial port count; tty->count is 1, "
-		       "state->count is %d\n", state->count);
-		state->count = 1;
-	}
-	if (--state->count < 0) {
-		printk("rs_close: bad serial port count for ttys%d: %d\n",
-		       info->line, state->count);
-		state->count = 0;
-	}
-	if (state->count) {
-		MOD_DEC_USE_COUNT;
-		restore_flags(flags);
-		return;
-	}
-	info->flags |= ASYNC_CLOSING;
-	restore_flags(flags);
-
-	/*
-	 * Now we wait for the transmit buffer to clear; and we notify
-	 * the line discipline to only process XON/XOFF characters.
-	 */
-	shutdown(info);
-	if (tty->driver.flush_buffer) tty->driver.flush_buffer(tty);
-	if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty);
-	info->event = 0;
-	info->tty = 0;
-	if (info->blocked_open) {
-		if (info->close_delay) {
-			current->state = TASK_INTERRUPTIBLE;
-			schedule_timeout(info->close_delay);
-		}
-		wake_up_interruptible(&info->open_wait);
-	}
-	info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|ASYNC_CLOSING);
-	wake_up_interruptible(&info->close_wait);
-	MOD_DEC_USE_COUNT;
-}
-
-/*
- * rs_wait_until_sent() --- wait until the transmitter is empty
- */
-static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
-{
-}
-
-
-/*
- * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
- */
-static void rs_hangup(struct tty_struct *tty)
-{
-	struct async_struct * info = (struct async_struct *)tty->driver_data;
-	struct serial_state *state = info->state;
-
-#ifdef SIMSERIAL_DEBUG
-	printk("rs_hangup: called\n");
-#endif
-
-	state = info->state;
-
-	rs_flush_buffer(tty);
-	if (info->flags & ASYNC_CLOSING)
-		return;
-	shutdown(info);
-
-	info->event = 0;
-	state->count = 0;
-	info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
-	info->tty = 0;
-	wake_up_interruptible(&info->open_wait);
-}
-
-
-static int get_async_struct(int line, struct async_struct **ret_info)
-{
-	struct async_struct *info;
-	struct serial_state *sstate;
-
-	sstate = rs_table + line;
-	sstate->count++;
-	if (sstate->info) {
-		*ret_info = sstate->info;
-		return 0;
-	}
-	info = kmalloc(sizeof(struct async_struct), GFP_KERNEL);
-	if (!info) {
-		sstate->count--;
-		return -ENOMEM;
-	}
-	memset(info, 0, sizeof(struct async_struct));
-	init_waitqueue_head(&info->open_wait);
-	init_waitqueue_head(&info->close_wait);
-	init_waitqueue_head(&info->delta_msr_wait);
-	info->magic = SERIAL_MAGIC;
-	info->port = sstate->port;
-	info->flags = sstate->flags;
-	info->xmit_fifo_size = sstate->xmit_fifo_size;
-	info->line = line;
-	info->tqueue.routine = do_softint;
-	info->tqueue.data = info;
-	info->state = sstate;
-	if (sstate->info) {
-		kfree(info);
-		*ret_info = sstate->info;
-		return 0;
-	}
-	*ret_info = sstate->info = info;
-	return 0;
-}
-
-static int
-startup(struct async_struct *info)
-{
-	unsigned long flags;
-	int	retval=0;
-	void (*handler)(int, void *, struct pt_regs *);
-	struct serial_state *state= info->state;
-	unsigned long page;
-
-	page = get_free_page(GFP_KERNEL);
-	if (!page)
-		return -ENOMEM;
-
-	save_flags(flags); cli();
-
-	if (info->flags & ASYNC_INITIALIZED) {
-		free_page(page);
-		goto errout;
-	}
-
-	if (!state->port || !state->type) {
-		if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags);
-		free_page(page);
-		goto errout;
-	}
-	if (info->xmit.buf)
-		free_page(page);
-	else
-		info->xmit.buf = (unsigned char *) page;
-
-#ifdef SIMSERIAL_DEBUG
-	printk("startup: ttys%d (irq %d)...", info->line, state->irq);
-#endif
-
-	/*
-	 * Allocate the IRQ if necessary
-	 */
-	if (state->irq && (!IRQ_ports[state->irq] ||
-			  !IRQ_ports[state->irq]->next_port)) {
-		if (IRQ_ports[state->irq]) {
-			retval = -EBUSY;
-			goto errout;
-		} else
-			handler = rs_interrupt_single;
-
-		retval = request_irq(state->irq, handler, IRQ_T(info),
-				     "simserial", NULL);
-		if (retval) {
-			if (capable(CAP_SYS_ADMIN)) {
-				if (info->tty)
-					set_bit(TTY_IO_ERROR,
-						&info->tty->flags);
-				retval = 0;
-			}
-			goto errout;
-		}
-	}
-
-	/*
-	 * Insert serial port into IRQ chain.
-	 */
-	info->prev_port = 0;
-	info->next_port = IRQ_ports[state->irq];
-	if (info->next_port)
-		info->next_port->prev_port = info;
-	IRQ_ports[state->irq] = info;
-
-	if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags);
-
-	info->xmit.head = info->xmit.tail = 0;
-
-#if 0
-	/*
-	 * Set up serial timers...
-	 */
-	timer_table[RS_TIMER].expires = jiffies + 2*HZ/100;
-	timer_active |= 1 << RS_TIMER;
-#endif
-
-	/*
-	 * Set up the tty->alt_speed kludge
-	 */
-	if (info->tty) {
-		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
-			info->tty->alt_speed = 57600;
-		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
-			info->tty->alt_speed = 115200;
-		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
-			info->tty->alt_speed = 230400;
-		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
-			info->tty->alt_speed = 460800;
-	}
-
-	info->flags |= ASYNC_INITIALIZED;
-	restore_flags(flags);
-	return 0;
-
-errout:
-	restore_flags(flags);
-	return retval;
-}
-
-
-/*
- * This routine is called whenever a serial port is opened.  It
- * enables interrupts for a serial port, linking in its async structure into
- * the IRQ chain.   It also performs the serial-specific
- * initialization for the tty structure.
- */
-static int rs_open(struct tty_struct *tty, struct file * filp)
-{
-	struct async_struct	*info;
-	int			retval, line;
-	unsigned long		page;
-
-	MOD_INC_USE_COUNT;
-	line = minor(tty->device) - tty->driver.minor_start;
-	if ((line < 0) || (line >= NR_PORTS)) {
-		MOD_DEC_USE_COUNT;
-		return -ENODEV;
-	}
-	retval = get_async_struct(line, &info);
-	if (retval) {
-		MOD_DEC_USE_COUNT;
-		return retval;
-	}
-	tty->driver_data = info;
-	info->tty = tty;
-
-#ifdef SIMSERIAL_DEBUG
-	printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line,
-	       info->state->count);
-#endif
-	info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
-
-	if (!tmp_buf) {
-		page = get_free_page(GFP_KERNEL);
-		if (!page) {
-			/* MOD_DEC_USE_COUNT; "info->tty" will cause this? */
-			return -ENOMEM;
-		}
-		if (tmp_buf)
-			free_page(page);
-		else
-			tmp_buf = (unsigned char *) page;
-	}
-
-	/*
-	 * If the port is the middle of closing, bail out now
-	 */
-	if (tty_hung_up_p(filp) ||
-	    (info->flags & ASYNC_CLOSING)) {
-		if (info->flags & ASYNC_CLOSING)
-			interruptible_sleep_on(&info->close_wait);
-		/* MOD_DEC_USE_COUNT; "info->tty" will cause this? */
-#ifdef SERIAL_DO_RESTART
-		return ((info->flags & ASYNC_HUP_NOTIFY) ?
-			-EAGAIN : -ERESTARTSYS);
-#else
-		return -EAGAIN;
-#endif
-	}
-
-	/*
-	 * Start up serial port
-	 */
-	retval = startup(info);
-	if (retval) {
-		/* MOD_DEC_USE_COUNT; "info->tty" will cause this? */
-		return retval;
-	}
-
-	if ((info->state->count == 1) &&
-	    (info->flags & ASYNC_SPLIT_TERMIOS)) {
-		if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
-			*tty->termios = info->state->normal_termios;
-		else
-			*tty->termios = info->state->callout_termios;
-	}
-
-	/*
-	 * figure out which console to use (should be one already)
-	 */
-	console = console_drivers;
-	while (console) {
-		if ((console->flags & CON_ENABLED) && console->write) break;
-		console = console->next;
-	}
-
-	info->session = current->session;
-	info->pgrp = current->pgrp;
-
-#ifdef SIMSERIAL_DEBUG
-	printk("rs_open ttys%d successful\n", info->line);
-#endif
-	return 0;
-}
-
-/*
- * /proc fs routines....
- */
-
-static inline int line_info(char *buf, struct serial_state *state)
-{
-	return sprintf(buf, "%d: uart:%s port:%lX irq:%d\n",
-		       state->line, uart_config[state->type].name,
-		       state->port, state->irq);
-}
-
-static int rs_read_proc(char *page, char **start, off_t off, int count,
-		 int *eof, void *data)
-{
-	int i, len = 0, l;
-	off_t	begin = 0;
-
-	len += sprintf(page, "simserinfo:1.0 driver:%s\n", serial_version);
-	for (i = 0; i < NR_PORTS && len < 4000; i++) {
-		l = line_info(page + len, &rs_table[i]);
-		len += l;
-		if (len+begin > off+count)
-			goto done;
-		if (len+begin < off) {
-			begin += len;
-			len = 0;
-		}
-	}
-	*eof = 1;
-done:
-	if (off >= len+begin)
-		return 0;
-	*start = page + (begin-off);
-	return ((count < begin+len-off) ? count : begin+len-off);
-}
-
-/*
- * ---------------------------------------------------------------------
- * rs_init() and friends
- *
- * rs_init() is called at boot-time to initialize the serial driver.
- * ---------------------------------------------------------------------
- */
-
-/*
- * This routine prints out the appropriate serial driver version
- * number, and identifies which options were configured into this
- * driver.
- */
-static inline void show_serial_version(void)
-{
-	printk(KERN_INFO "%s version %s with", serial_name, serial_version);
-	printk(" no serial options enabled\n");
-}
-
-/*
- * The serial driver boot-time initialization code!
- */
-static int __init
-simrs_init (void)
-{
-	int			i;
-	struct serial_state	*state;
-
-	show_serial_version();
-
-	/* Initialize the tty_driver structure */
-
-	memset(&serial_driver, 0, sizeof(struct tty_driver));
-	serial_driver.magic = TTY_DRIVER_MAGIC;
-	serial_driver.driver_name = "simserial";
-	serial_driver.name = "ttyS";
-	serial_driver.major = TTY_MAJOR;
-	serial_driver.minor_start = 64;
-	serial_driver.num = 1;
-	serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
-	serial_driver.subtype = SERIAL_TYPE_NORMAL;
-	serial_driver.init_termios = tty_std_termios;
-	serial_driver.init_termios.c_cflag =
-		B9600 | CS8 | CREAD | HUPCL | CLOCAL;
-	serial_driver.flags = TTY_DRIVER_REAL_RAW;
-	serial_driver.refcount = &serial_refcount;
-	serial_driver.table = serial_table;
-	serial_driver.termios = serial_termios;
-	serial_driver.termios_locked = serial_termios_locked;
-
-	serial_driver.open = rs_open;
-	serial_driver.close = rs_close;
-	serial_driver.write = rs_write;
-	serial_driver.put_char = rs_put_char;
-	serial_driver.flush_chars = rs_flush_chars;
-	serial_driver.write_room = rs_write_room;
-	serial_driver.chars_in_buffer = rs_chars_in_buffer;
-	serial_driver.flush_buffer = rs_flush_buffer;
-	serial_driver.ioctl = rs_ioctl;
-	serial_driver.throttle = rs_throttle;
-	serial_driver.unthrottle = rs_unthrottle;
-	serial_driver.send_xchar = rs_send_xchar;
-	serial_driver.set_termios = rs_set_termios;
-	serial_driver.stop = rs_stop;
-	serial_driver.start = rs_start;
-	serial_driver.hangup = rs_hangup;
-	serial_driver.break_ctl = rs_break;
-	serial_driver.wait_until_sent = rs_wait_until_sent;
-	serial_driver.read_proc = rs_read_proc;
-
-	/*
-	 * Let's have a little bit of fun !
-	 */
-	for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) {
-
-		if (state->type == PORT_UNKNOWN) continue;
-
-		if (!state->irq) {
-			state->irq = ia64_alloc_irq();
-			ia64_ssc_connect_irq(KEYBOARD_INTR, state->irq);
-		}
-
-		printk(KERN_INFO "ttyS%02d at 0x%04lx (irq = %d) is a %s\n",
-		       state->line,
-		       state->port, state->irq,
-		       uart_config[state->type].name);
-	}
-	/*
-	 * The callout device is just like normal device except for
-	 * major number and the subtype code.
-	 */
-	callout_driver = serial_driver;
-	callout_driver.name = "cua";
-	callout_driver.major = TTYAUX_MAJOR;
-	callout_driver.subtype = SERIAL_TYPE_CALLOUT;
-	callout_driver.read_proc = 0;
-	callout_driver.proc_entry = 0;
-
-	if (tty_register_driver(&serial_driver))
-		panic("Couldn't register simserial driver\n");
-
-	if (tty_register_driver(&callout_driver))
-		panic("Couldn't register callout driver\n");
-
-	return 0;
-}
-
-#ifndef MODULE
-__initcall(simrs_init);
-#endif
diff -Nru a/arch/ia64/hp/zx1/Makefile b/arch/ia64/hp/zx1/Makefile
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/arch/ia64/hp/zx1/Makefile	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,26 @@
+#
+# ia64/platform/hp/zx1/Makefile
+#
+# Copyright (C) 2002 Hewlett Packard
+# Copyright (C) Alex Williamson (alex_williamson@hp.com)
+#
+
+O_TARGET := zx1.o
+
+obj-y := hpzx1_misc.o
+obj-$(CONFIG_IA64_GENERIC) += hpzx1_machvec.o
+
+include $(TOPDIR)/Rules.make
+#
+# ia64/platform/hp/zx1/Makefile
+#
+# Copyright (C) 2002 Hewlett Packard
+# Copyright (C) Alex Williamson (alex_williamson@hp.com)
+#
+
+O_TARGET := zx1.o
+
+obj-y := hpzx1_misc.o
+obj-$(CONFIG_IA64_GENERIC) += hpzx1_machvec.o
+
+include $(TOPDIR)/Rules.make
diff -Nru a/arch/ia64/hp/zx1/hpzx1_machvec.c b/arch/ia64/hp/zx1/hpzx1_machvec.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/arch/ia64/hp/zx1/hpzx1_machvec.c	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,4 @@
+#define MACHVEC_PLATFORM_NAME	hpzx1
+#include <asm/machvec_init.h>
+#define MACHVEC_PLATFORM_NAME	hpzx1
+#include <asm/machvec_init.h>
diff -Nru a/arch/ia64/hp/zx1/hpzx1_misc.c b/arch/ia64/hp/zx1/hpzx1_misc.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/arch/ia64/hp/zx1/hpzx1_misc.c	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,800 @@
+/*
+ * Misc. support for HP zx1 chipset support
+ *
+ * Copyright (C) 2002 Hewlett-Packard Co
+ * Copyright (C) 2002 Alex Williamson <alex_williamson@hp.com>
+ * Copyright (C) 2002 Bjorn Helgaas <bjorn_helgaas@hp.com>
+ */
+
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/acpi.h>
+#include <asm/iosapic.h>
+#include <asm/efi.h>
+
+#include "../drivers/acpi/include/platform/acgcc.h"
+#include "../drivers/acpi/include/actypes.h"
+#include "../drivers/acpi/include/acexcep.h"
+#include "../drivers/acpi/include/acpixf.h"
+#include "../drivers/acpi/include/actbl.h"
+#include "../drivers/acpi/include/acconfig.h"
+#include "../drivers/acpi/include/acmacros.h"
+#include "../drivers/acpi/include/aclocal.h"
+#include "../drivers/acpi/include/acobject.h"
+#include "../drivers/acpi/include/acstruct.h"
+#include "../drivers/acpi/include/acnamesp.h"
+#include "../drivers/acpi/include/acutils.h"
+
+#define PFX "hpzx1: "
+
+struct fake_pci_dev {
+	struct fake_pci_dev *next;
+	unsigned char bus;
+	unsigned int devfn;
+	int sizing;		// in middle of BAR sizing operation?
+	unsigned long csr_base;
+	unsigned int csr_size;
+	unsigned long mapped_csrs;	// ioremapped
+};
+
+static struct fake_pci_dev *fake_pci_head, **fake_pci_tail = &fake_pci_head;
+
+static struct pci_ops orig_pci_ops;
+
+static inline struct fake_pci_dev *
+fake_pci_find_slot(unsigned char bus, unsigned int devfn)
+{
+	struct fake_pci_dev *dev;
+
+	for (dev = fake_pci_head; dev; dev = dev->next)
+		if (dev->bus == bus && dev->devfn == devfn)
+			return dev;
+	return NULL;
+}
+
+static struct fake_pci_dev *
+alloc_fake_pci_dev(void)
+{
+        struct fake_pci_dev *dev;
+
+        dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return NULL;
+
+	memset(dev, 0, sizeof(*dev));
+
+        *fake_pci_tail = dev;
+        fake_pci_tail = &dev->next;
+
+        return dev;
+}
+
+#define HP_CFG_RD(sz, bits, name) \
+static int hp_cfg_read##sz (struct pci_dev *dev, int where, u##bits *value) \
+{ \
+	struct fake_pci_dev *fake_dev; \
+	if (!(fake_dev = fake_pci_find_slot(dev->bus->number, dev->devfn))) \
+		return orig_pci_ops.name(dev, where, value); \
+	\
+	switch (where) { \
+	case PCI_COMMAND: \
+		*value = read##sz(fake_dev->mapped_csrs + where); \
+		*value |= PCI_COMMAND_MEMORY; /* SBA omits this */ \
+		break; \
+	case PCI_BASE_ADDRESS_0: \
+		if (fake_dev->sizing) \
+			*value = ~(fake_dev->csr_size - 1); \
+		else \
+			*value = (fake_dev->csr_base & \
+				    PCI_BASE_ADDRESS_MEM_MASK) | \
+				PCI_BASE_ADDRESS_SPACE_MEMORY; \
+		fake_dev->sizing = 0; \
+		break; \
+	default: \
+		*value = read##sz(fake_dev->mapped_csrs + where); \
+		break; \
+	} \
+	return PCIBIOS_SUCCESSFUL; \
+}
+
+#define HP_CFG_WR(sz, bits, name) \
+static int hp_cfg_write##sz (struct pci_dev *dev, int where, u##bits value) \
+{ \
+	struct fake_pci_dev *fake_dev; \
+	if (!(fake_dev = fake_pci_find_slot(dev->bus->number, dev->devfn))) \
+		return orig_pci_ops.name(dev, where, value); \
+	\
+	switch (where) { \
+	case PCI_BASE_ADDRESS_0: \
+		if (value == ~0) \
+			fake_dev->sizing = 1; \
+		break; \
+	default: \
+		write##sz(value, fake_dev->mapped_csrs + where); \
+		break; \
+	} \
+	return PCIBIOS_SUCCESSFUL; \
+}
+
+HP_CFG_RD(b,  8, read_byte)
+HP_CFG_RD(w, 16, read_word)
+HP_CFG_RD(l, 32, read_dword)
+HP_CFG_WR(b,  8, write_byte)
+HP_CFG_WR(w, 16, write_word)
+HP_CFG_WR(l, 32, write_dword)
+
+static struct pci_ops hp_pci_conf = {
+	hp_cfg_readb,
+	hp_cfg_readw,
+	hp_cfg_readl,
+	hp_cfg_writeb,
+	hp_cfg_writew,
+	hp_cfg_writel,
+};
+
+/*
+ * Assume we'll never have a physical slot higher than 0x10, so we can
+ * use slots above that for "fake" PCI devices to represent things
+ * that only show up in the ACPI namespace.
+ */
+#define HP_MAX_SLOT	0x10
+
+static struct fake_pci_dev *
+hpzx1_fake_pci_dev(unsigned long addr, unsigned int bus, unsigned int size)
+{
+	struct fake_pci_dev *dev;
+	int slot;
+
+	// Note: lspci thinks 0x1f is invalid
+	for (slot = 0x1e; slot > HP_MAX_SLOT; slot--) {
+		if (!fake_pci_find_slot(bus, PCI_DEVFN(slot, 0)))
+			break;
+	}
+	if (slot == HP_MAX_SLOT) {
+		printk(KERN_ERR PFX
+			"no slot space for device (0x%p) on bus 0x%02x\n",
+			(void *) addr, bus);
+		return NULL;
+	}
+
+	dev = alloc_fake_pci_dev();
+	if (!dev) {
+		printk(KERN_ERR PFX
+			"no memory for device (0x%p) on bus 0x%02x\n",
+			(void *) addr, bus);
+		return NULL;
+	}
+
+	dev->bus = bus;
+	dev->devfn = PCI_DEVFN(slot, 0);
+	dev->csr_base = addr;
+	dev->csr_size = size;
+
+	/*
+	 * Drivers should ioremap what they need, but we have to do
+	 * it here, too, so PCI config accesses work.
+	 */
+	dev->mapped_csrs = ioremap(dev->csr_base, dev->csr_size);
+
+	return dev;
+}
+
+typedef struct {
+	u8	guid_id;
+	u8	guid[16];
+	u8	csr_base[8];
+	u8	csr_length[8];
+} acpi_hp_vendor_long;
+
+#define HP_CCSR_LENGTH 0x21
+#define HP_CCSR_TYPE 0x2
+#define HP_CCSR_GUID EFI_GUID(0x69e9adf9, 0x924f, 0xab5f,			\
+			      0xf6, 0x4a, 0x24, 0xd2, 0x01, 0x37, 0x0e, 0xad)
+
+extern acpi_status acpi_get_crs(acpi_handle, acpi_buffer *);
+extern acpi_resource *acpi_get_crs_next(acpi_buffer *, int *);
+extern acpi_resource_data *acpi_get_crs_type(acpi_buffer *, int *, int);
+extern void acpi_dispose_crs(acpi_buffer *);
+extern acpi_status acpi_cf_evaluate_method(acpi_handle, UINT8 *, NATIVE_UINT *);
+
+static acpi_status
+hp_csr_space(acpi_handle obj, u64 *csr_base, u64 *csr_length)
+{
+	int i, offset = 0;
+	acpi_status status;
+	acpi_buffer buf;
+	acpi_resource_vendor *res;
+	acpi_hp_vendor_long *hp_res;
+	efi_guid_t vendor_guid;
+
+	*csr_base = 0;
+	*csr_length = 0;
+
+	status = acpi_get_crs(obj, &buf);
+	if (status != AE_OK) {
+		printk(KERN_ERR PFX "Unable to get _CRS data on object\n");
+		return status;
+	}
+
+	res = (acpi_resource_vendor *)acpi_get_crs_type(&buf, &offset, ACPI_RSTYPE_VENDOR);
+	if (!res) {
+		printk(KERN_ERR PFX "Failed to find config space for device\n");
+		acpi_dispose_crs(&buf);
+		return AE_NOT_FOUND;
+	}
+
+	hp_res = (acpi_hp_vendor_long *)(res->reserved);
+
+	if (res->length != HP_CCSR_LENGTH || hp_res->guid_id != HP_CCSR_TYPE) {
+		printk(KERN_ERR PFX "Unknown Vendor data\n");
+		acpi_dispose_crs(&buf);
+		return AE_TYPE; /* Revisit error? */
+	}
+
+	memcpy(&vendor_guid, hp_res->guid, sizeof(efi_guid_t));
+	if (efi_guidcmp(vendor_guid, HP_CCSR_GUID) != 0) {
+		printk(KERN_ERR PFX "Vendor GUID does not match\n");
+		acpi_dispose_crs(&buf);
+		return AE_TYPE; /* Revisit error? */
+	}
+
+	for (i = 0 ; i < 8 ; i++) {
+		*csr_base |= ((u64)(hp_res->csr_base[i]) << (i * 8));
+		*csr_length |= ((u64)(hp_res->csr_length[i]) << (i * 8));
+	}
+
+	acpi_dispose_crs(&buf);
+
+	return AE_OK;
+}
+
+static acpi_status
+hpzx1_sba_probe(acpi_handle obj, u32 depth, void *context, void **ret)
+{
+	u64 csr_base = 0, csr_length = 0;
+	char *name = context;
+	struct fake_pci_dev *dev;
+	acpi_status status;
+
+	status = hp_csr_space(obj, &csr_base, &csr_length);
+
+	if (status != AE_OK)
+		return status;
+
+	/*
+	 * Only SBA shows up in ACPI namespace, so its CSR space
+	 * includes both SBA and IOC.  Make SBA and IOC show up
+	 * separately in PCI space.
+	 */
+	if ((dev = hpzx1_fake_pci_dev(csr_base, 0, 0x1000)))
+		printk(KERN_INFO PFX "%s SBA at 0x%lx; pci dev %02x:%02x.%d\n",
+			name, csr_base, dev->bus,
+			PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
+	if ((dev = hpzx1_fake_pci_dev(csr_base + 0x1000, 0, 0x1000)))
+		printk(KERN_INFO PFX "%s IOC at 0x%lx; pci dev %02x:%02x.%d\n",
+			name, csr_base + 0x1000, dev->bus,
+			PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
+
+	return AE_OK;
+}
+
+static acpi_status
+hpzx1_lba_probe(acpi_handle obj, u32 depth, void *context, void **ret)
+{
+	acpi_status status;
+	u64 csr_base = 0, csr_length = 0;
+	char *name = context;
+	NATIVE_UINT busnum = 0;
+	struct fake_pci_dev *dev;
+
+	status = hp_csr_space(obj, &csr_base, &csr_length);
+
+	if (status != AE_OK)
+		return status;
+
+	status = acpi_cf_evaluate_method(obj, METHOD_NAME__BBN, &busnum);
+	if (ACPI_FAILURE(status)) {
+		printk(KERN_ERR PFX "evaluate _BBN fail=0x%x\n", status);
+		busnum = 0;	// no _BBN; stick it on bus 0
+	}
+
+	if ((dev = hpzx1_fake_pci_dev(csr_base, busnum, csr_length)))
+		printk(KERN_INFO PFX "%s LBA at 0x%lx, _BBN 0x%02x; "
+			"pci dev %02x:%02x.%d\n",
+			name, csr_base, busnum, dev->bus,
+			PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
+
+	return AE_OK;
+}
+
+static void
+hpzx1_acpi_dev_init(void)
+{
+	extern struct pci_ops pci_conf;
+
+	/*
+	 * Make fake PCI devices for the following hardware in the
+	 * ACPI namespace.  This makes it more convenient for drivers
+	 * because they can claim these devices based on PCI
+	 * information, rather than needing to know about ACPI.  The
+	 * 64-bit "HPA" space for this hardware is available as BAR
+	 * 0/1.
+	 *
+	 * HWP0001: Single IOC SBA w/o IOC in namespace
+	 * HWP0002: LBA device
+	 * HWP0003: AGP LBA device
+	 */
+	acpi_get_devices("HWP0001", hpzx1_sba_probe, "HWP0001", NULL);
+#ifdef CONFIG_IA64_HP_PROTO
+	if (fake_pci_tail != &fake_pci_head) {
+#endif
+	acpi_get_devices("HWP0002", hpzx1_lba_probe, "HWP0002", NULL);
+	acpi_get_devices("HWP0003", hpzx1_lba_probe, "HWP0003", NULL);
+
+#ifdef CONFIG_IA64_HP_PROTO
+	}
+
+#define ZX1_FUNC_ID_VALUE    (PCI_DEVICE_ID_HP_ZX1_SBA << 16) | PCI_VENDOR_ID_HP
+	/*
+	 * Early protos don't have bridges in the ACPI namespace, so
+	 * if we didn't find anything, add the things we know are
+	 * there.
+	 */
+	if (fake_pci_tail == &fake_pci_head) {
+		u64 hpa, csr_base;
+		struct fake_pci_dev *dev;
+
+		csr_base = 0xfed00000UL;
+		hpa = (u64) ioremap(csr_base, 0x1000);
+		if (__raw_readl(hpa) == ZX1_FUNC_ID_VALUE) {
+			if ((dev = hpzx1_fake_pci_dev(csr_base, 0, 0x1000)))
+				printk(KERN_INFO PFX "HWP0001 SBA at 0x%lx; "
+					"pci dev %02x:%02x.%d\n", csr_base,
+					dev->bus, PCI_SLOT(dev->devfn),
+					PCI_FUNC(dev->devfn));
+			if ((dev = hpzx1_fake_pci_dev(csr_base + 0x1000, 0,
+					0x1000)))
+				printk(KERN_INFO PFX "HWP0001 IOC at 0x%lx; "
+					"pci dev %02x:%02x.%d\n",
+					csr_base + 0x1000,
+					dev->bus, PCI_SLOT(dev->devfn),
+					PCI_FUNC(dev->devfn));
+
+			csr_base = 0xfed24000UL;
+			iounmap(hpa);
+			hpa = (u64) ioremap(csr_base, 0x1000);
+			if ((dev = hpzx1_fake_pci_dev(csr_base, 0x40, 0x1000)))
+				printk(KERN_INFO PFX "HWP0003 AGP LBA at "
+					"0x%lx; pci dev %02x:%02x.%d\n",
+					csr_base,
+					dev->bus, PCI_SLOT(dev->devfn),
+					PCI_FUNC(dev->devfn));
+		}
+		iounmap(hpa);
+	}
+#endif
+
+	if (fake_pci_tail == &fake_pci_head)
+		return;
+
+	/*
+	 * Replace PCI ops, but only if we made fake devices.
+	 */
+	orig_pci_ops = pci_conf;
+	pci_conf = hp_pci_conf;
+}
+
+extern void sba_init(void);
+	
+void
+hpzx1_pci_fixup (int phase)
+{
+	if (phase == 0)
+		hpzx1_acpi_dev_init();
+	iosapic_pci_fixup(phase);
+        if (phase == 1)
+		sba_init();
+}
+/*
+ * Misc. support for HP zx1 chipset support
+ *
+ * Copyright (C) 2002 Hewlett-Packard Co
+ * Copyright (C) 2002 Alex Williamson <alex_williamson@hp.com>
+ * Copyright (C) 2002 Bjorn Helgaas <bjorn_helgaas@hp.com>
+ */
+
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/acpi.h>
+#include <asm/iosapic.h>
+#include <asm/efi.h>
+
+#include "../drivers/acpi/include/platform/acgcc.h"
+#include "../drivers/acpi/include/actypes.h"
+#include "../drivers/acpi/include/acexcep.h"
+#include "../drivers/acpi/include/acpixf.h"
+#include "../drivers/acpi/include/actbl.h"
+#include "../drivers/acpi/include/acconfig.h"
+#include "../drivers/acpi/include/acmacros.h"
+#include "../drivers/acpi/include/aclocal.h"
+#include "../drivers/acpi/include/acobject.h"
+#include "../drivers/acpi/include/acstruct.h"
+#include "../drivers/acpi/include/acnamesp.h"
+#include "../drivers/acpi/include/acutils.h"
+
+#define PFX "hpzx1: "
+
+struct fake_pci_dev {
+	struct fake_pci_dev *next;
+	unsigned char bus;
+	unsigned int devfn;
+	int sizing;		// in middle of BAR sizing operation?
+	unsigned long csr_base;
+	unsigned int csr_size;
+	unsigned long mapped_csrs;	// ioremapped
+};
+
+static struct fake_pci_dev *fake_pci_head, **fake_pci_tail = &fake_pci_head;
+
+static struct pci_ops orig_pci_ops;
+
+static inline struct fake_pci_dev *
+fake_pci_find_slot(unsigned char bus, unsigned int devfn)
+{
+	struct fake_pci_dev *dev;
+
+	for (dev = fake_pci_head; dev; dev = dev->next)
+		if (dev->bus == bus && dev->devfn == devfn)
+			return dev;
+	return NULL;
+}
+
+static struct fake_pci_dev *
+alloc_fake_pci_dev(void)
+{
+        struct fake_pci_dev *dev;
+
+        dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return NULL;
+
+	memset(dev, 0, sizeof(*dev));
+
+        *fake_pci_tail = dev;
+        fake_pci_tail = &dev->next;
+
+        return dev;
+}
+
+#define HP_CFG_RD(sz, bits, name) \
+static int hp_cfg_read##sz (struct pci_dev *dev, int where, u##bits *value) \
+{ \
+	struct fake_pci_dev *fake_dev; \
+	if (!(fake_dev = fake_pci_find_slot(dev->bus->number, dev->devfn))) \
+		return orig_pci_ops.name(dev, where, value); \
+	\
+	switch (where) { \
+	case PCI_COMMAND: \
+		*value = read##sz(fake_dev->mapped_csrs + where); \
+		*value |= PCI_COMMAND_MEMORY; /* SBA omits this */ \
+		break; \
+	case PCI_BASE_ADDRESS_0: \
+		if (fake_dev->sizing) \
+			*value = ~(fake_dev->csr_size - 1); \
+		else \
+			*value = (fake_dev->csr_base & \
+				    PCI_BASE_ADDRESS_MEM_MASK) | \
+				PCI_BASE_ADDRESS_SPACE_MEMORY; \
+		fake_dev->sizing = 0; \
+		break; \
+	default: \
+		*value = read##sz(fake_dev->mapped_csrs + where); \
+		break; \
+	} \
+	return PCIBIOS_SUCCESSFUL; \
+}
+
+#define HP_CFG_WR(sz, bits, name) \
+static int hp_cfg_write##sz (struct pci_dev *dev, int where, u##bits value) \
+{ \
+	struct fake_pci_dev *fake_dev; \
+	if (!(fake_dev = fake_pci_find_slot(dev->bus->number, dev->devfn))) \
+		return orig_pci_ops.name(dev, where, value); \
+	\
+	switch (where) { \
+	case PCI_BASE_ADDRESS_0: \
+		if (value == ~0) \
+			fake_dev->sizing = 1; \
+		break; \
+	default: \
+		write##sz(value, fake_dev->mapped_csrs + where); \
+		break; \
+	} \
+	return PCIBIOS_SUCCESSFUL; \
+}
+
+HP_CFG_RD(b,  8, read_byte)
+HP_CFG_RD(w, 16, read_word)
+HP_CFG_RD(l, 32, read_dword)
+HP_CFG_WR(b,  8, write_byte)
+HP_CFG_WR(w, 16, write_word)
+HP_CFG_WR(l, 32, write_dword)
+
+static struct pci_ops hp_pci_conf = {
+	hp_cfg_readb,
+	hp_cfg_readw,
+	hp_cfg_readl,
+	hp_cfg_writeb,
+	hp_cfg_writew,
+	hp_cfg_writel,
+};
+
+/*
+ * Assume we'll never have a physical slot higher than 0x10, so we can
+ * use slots above that for "fake" PCI devices to represent things
+ * that only show up in the ACPI namespace.
+ */
+#define HP_MAX_SLOT	0x10
+
+static struct fake_pci_dev *
+hpzx1_fake_pci_dev(unsigned long addr, unsigned int bus, unsigned int size)
+{
+	struct fake_pci_dev *dev;
+	int slot;
+
+	// Note: lspci thinks 0x1f is invalid
+	for (slot = 0x1e; slot > HP_MAX_SLOT; slot--) {
+		if (!fake_pci_find_slot(bus, PCI_DEVFN(slot, 0)))
+			break;
+	}
+	if (slot == HP_MAX_SLOT) {
+		printk(KERN_ERR PFX
+			"no slot space for device (0x%p) on bus 0x%02x\n",
+			(void *) addr, bus);
+		return NULL;
+	}
+
+	dev = alloc_fake_pci_dev();
+	if (!dev) {
+		printk(KERN_ERR PFX
+			"no memory for device (0x%p) on bus 0x%02x\n",
+			(void *) addr, bus);
+		return NULL;
+	}
+
+	dev->bus = bus;
+	dev->devfn = PCI_DEVFN(slot, 0);
+	dev->csr_base = addr;
+	dev->csr_size = size;
+
+	/*
+	 * Drivers should ioremap what they need, but we have to do
+	 * it here, too, so PCI config accesses work.
+	 */
+	dev->mapped_csrs = (unsigned long) ioremap(dev->csr_base, dev->csr_size);
+
+	return dev;
+}
+
+typedef struct {
+	u8	guid_id;
+	u8	guid[16];
+	u8	csr_base[8];
+	u8	csr_length[8];
+} acpi_hp_vendor_long;
+
+#define HP_CCSR_LENGTH 0x21
+#define HP_CCSR_TYPE 0x2
+#define HP_CCSR_GUID \
+    ((efi_guid_t) { 0x69e9adf9, 0x924f, 0xab5f, { 0xf6, 0x4a, 0x24, 0xd2, 0x01, 0x37, 0x0e, 0xad }})
+
+extern acpi_status acpi_get_crs(acpi_handle, acpi_buffer *);
+extern acpi_resource *acpi_get_crs_next(acpi_buffer *, int *);
+extern acpi_resource_data *acpi_get_crs_type(acpi_buffer *, int *, int);
+extern void acpi_dispose_crs(acpi_buffer *);
+extern acpi_status acpi_cf_evaluate_method(acpi_handle, UINT8 *, NATIVE_UINT *);
+
+static acpi_status
+hp_csr_space(acpi_handle obj, u64 *csr_base, u64 *csr_length)
+{
+	int i, offset = 0;
+	acpi_status status;
+	acpi_buffer buf;
+	acpi_resource_vendor *res;
+	acpi_hp_vendor_long *hp_res;
+	efi_guid_t vendor_guid;
+
+	*csr_base = 0;
+	*csr_length = 0;
+
+	status = acpi_get_crs(obj, &buf);
+	if (status != AE_OK) {
+		printk(KERN_ERR PFX "Unable to get _CRS data on object\n");
+		return status;
+	}
+
+	res = (acpi_resource_vendor *)acpi_get_crs_type(&buf, &offset, ACPI_RSTYPE_VENDOR);
+	if (!res) {
+		printk(KERN_ERR PFX "Failed to find config space for device\n");
+		acpi_dispose_crs(&buf);
+		return AE_NOT_FOUND;
+	}
+
+	hp_res = (acpi_hp_vendor_long *)(res->reserved);
+
+	if (res->length != HP_CCSR_LENGTH || hp_res->guid_id != HP_CCSR_TYPE) {
+		printk(KERN_ERR PFX "Unknown Vendor data\n");
+		acpi_dispose_crs(&buf);
+		return AE_TYPE; /* Revisit error? */
+	}
+
+	memcpy(&vendor_guid, hp_res->guid, sizeof(efi_guid_t));
+	if (efi_guidcmp(vendor_guid, HP_CCSR_GUID) != 0) {
+		printk(KERN_ERR PFX "Vendor GUID does not match\n");
+		acpi_dispose_crs(&buf);
+		return AE_TYPE; /* Revisit error? */
+	}
+
+	for (i = 0 ; i < 8 ; i++) {
+		*csr_base |= ((u64)(hp_res->csr_base[i]) << (i * 8));
+		*csr_length |= ((u64)(hp_res->csr_length[i]) << (i * 8));
+	}
+
+	acpi_dispose_crs(&buf);
+
+	return AE_OK;
+}
+
+static acpi_status
+hpzx1_sba_probe(acpi_handle obj, u32 depth, void *context, void **ret)
+{
+	u64 csr_base = 0, csr_length = 0;
+	char *name = context;
+	struct fake_pci_dev *dev;
+	acpi_status status;
+
+	status = hp_csr_space(obj, &csr_base, &csr_length);
+
+	if (status != AE_OK)
+		return status;
+
+	/*
+	 * Only SBA shows up in ACPI namespace, so its CSR space
+	 * includes both SBA and IOC.  Make SBA and IOC show up
+	 * separately in PCI space.
+	 */
+	if ((dev = hpzx1_fake_pci_dev(csr_base, 0, 0x1000)))
+		printk(KERN_INFO PFX "%s SBA at 0x%lx; pci dev %02x:%02x.%d\n",
+			name, csr_base, dev->bus,
+			PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
+	if ((dev = hpzx1_fake_pci_dev(csr_base + 0x1000, 0, 0x1000)))
+		printk(KERN_INFO PFX "%s IOC at 0x%lx; pci dev %02x:%02x.%d\n",
+			name, csr_base + 0x1000, dev->bus,
+			PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
+
+	return AE_OK;
+}
+
+static acpi_status
+hpzx1_lba_probe(acpi_handle obj, u32 depth, void *context, void **ret)
+{
+	acpi_status status;
+	u64 csr_base = 0, csr_length = 0;
+	char *name = context;
+	NATIVE_UINT busnum = 0;
+	struct fake_pci_dev *dev;
+
+	status = hp_csr_space(obj, &csr_base, &csr_length);
+
+	if (status != AE_OK)
+		return status;
+
+	status = acpi_cf_evaluate_method(obj, METHOD_NAME__BBN, &busnum);
+	if (ACPI_FAILURE(status)) {
+		printk(KERN_ERR PFX "evaluate _BBN fail=0x%x\n", status);
+		busnum = 0;	// no _BBN; stick it on bus 0
+	}
+
+	if ((dev = hpzx1_fake_pci_dev(csr_base, busnum, csr_length)))
+		printk(KERN_INFO PFX "%s LBA at 0x%lx, _BBN 0x%02x; "
+			"pci dev %02x:%02x.%d\n",
+			name, csr_base, busnum, dev->bus,
+			PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
+
+	return AE_OK;
+}
+
+static void
+hpzx1_acpi_dev_init(void)
+{
+	extern struct pci_ops pci_conf;
+
+	/*
+	 * Make fake PCI devices for the following hardware in the
+	 * ACPI namespace.  This makes it more convenient for drivers
+	 * because they can claim these devices based on PCI
+	 * information, rather than needing to know about ACPI.  The
+	 * 64-bit "HPA" space for this hardware is available as BAR
+	 * 0/1.
+	 *
+	 * HWP0001: Single IOC SBA w/o IOC in namespace
+	 * HWP0002: LBA device
+	 * HWP0003: AGP LBA device
+	 */
+	acpi_get_devices("HWP0001", hpzx1_sba_probe, "HWP0001", NULL);
+#ifdef CONFIG_IA64_HP_PROTO
+	if (fake_pci_tail != &fake_pci_head) {
+#endif
+	acpi_get_devices("HWP0002", hpzx1_lba_probe, "HWP0002", NULL);
+	acpi_get_devices("HWP0003", hpzx1_lba_probe, "HWP0003", NULL);
+
+#ifdef CONFIG_IA64_HP_PROTO
+	}
+
+#define ZX1_FUNC_ID_VALUE    (PCI_DEVICE_ID_HP_ZX1_SBA << 16) | PCI_VENDOR_ID_HP
+	/*
+	 * Early protos don't have bridges in the ACPI namespace, so
+	 * if we didn't find anything, add the things we know are
+	 * there.
+	 */
+	if (fake_pci_tail == &fake_pci_head) {
+		u64 hpa, csr_base;
+		struct fake_pci_dev *dev;
+
+		csr_base = 0xfed00000UL;
+		hpa = (u64) ioremap(csr_base, 0x1000);
+		if (__raw_readl(hpa) == ZX1_FUNC_ID_VALUE) {
+			if ((dev = hpzx1_fake_pci_dev(csr_base, 0, 0x1000)))
+				printk(KERN_INFO PFX "HWP0001 SBA at 0x%lx; "
+					"pci dev %02x:%02x.%d\n", csr_base,
+					dev->bus, PCI_SLOT(dev->devfn),
+					PCI_FUNC(dev->devfn));
+			if ((dev = hpzx1_fake_pci_dev(csr_base + 0x1000, 0,
+					0x1000)))
+				printk(KERN_INFO PFX "HWP0001 IOC at 0x%lx; "
+					"pci dev %02x:%02x.%d\n",
+					csr_base + 0x1000,
+					dev->bus, PCI_SLOT(dev->devfn),
+					PCI_FUNC(dev->devfn));
+
+			csr_base = 0xfed24000UL;
+			iounmap(hpa);
+			hpa = (u64) ioremap(csr_base, 0x1000);
+			if ((dev = hpzx1_fake_pci_dev(csr_base, 0x40, 0x1000)))
+				printk(KERN_INFO PFX "HWP0003 AGP LBA at "
+					"0x%lx; pci dev %02x:%02x.%d\n",
+					csr_base,
+					dev->bus, PCI_SLOT(dev->devfn),
+					PCI_FUNC(dev->devfn));
+		}
+		iounmap(hpa);
+	}
+#endif
+
+	if (fake_pci_tail == &fake_pci_head)
+		return;
+
+	/*
+	 * Replace PCI ops, but only if we made fake devices.
+	 */
+	orig_pci_ops = pci_conf;
+	pci_conf = hp_pci_conf;
+}
+
+extern void sba_init(void);
+	
+void
+hpzx1_pci_fixup (int phase)
+{
+	if (phase == 0)
+		hpzx1_acpi_dev_init();
+	iosapic_pci_fixup(phase);
+        if (phase == 1)
+		sba_init();
+}
diff -Nru a/arch/ia64/ia32/binfmt_elf32.c b/arch/ia64/ia32/binfmt_elf32.c
--- a/arch/ia64/ia32/binfmt_elf32.c	Mon Apr 22 15:32:19 2002
+++ b/arch/ia64/ia32/binfmt_elf32.c	Mon Apr 22 15:32:19 2002
@@ -47,6 +47,8 @@
 #define ELF_PLAT_INIT(_r)		ia64_elf32_init(_r)
 #define setup_arg_pages(bprm)		ia32_setup_arg_pages(bprm)
 #define elf_map				elf32_map
+
+#undef SET_PERSONALITY
 #define SET_PERSONALITY(ex, ibcs2)	elf32_set_personality()
 
 /* Ugly but avoids duplication */
diff -Nru a/arch/ia64/ia32/sys_ia32.c b/arch/ia64/ia32/sys_ia32.c
--- a/arch/ia64/ia32/sys_ia32.c	Mon Apr 22 15:32:18 2002
+++ b/arch/ia64/ia32/sys_ia32.c	Mon Apr 22 15:32:18 2002
@@ -3003,7 +3003,7 @@
 		if (request != PTRACE_KILL)
 			goto out;
 	}
-	if (child->p_pptr != current)
+	if (child->parent != current)
 		goto out;
 
 	switch (request) {
diff -Nru a/arch/ia64/kernel/Makefile b/arch/ia64/kernel/Makefile
--- a/arch/ia64/kernel/Makefile	Mon Apr 22 15:32:19 2002
+++ b/arch/ia64/kernel/Makefile	Mon Apr 22 15:32:19 2002
@@ -17,6 +17,7 @@
 	 machvec.o pal.o process.o perfmon.o ptrace.o sal.o semaphore.o setup.o	\
 	 signal.o sys_ia64.o traps.o time.o unaligned.o unwind.o
 obj-$(CONFIG_IA64_GENERIC) += iosapic.o
+obj-$(CONFIG_IA64_HP_ZX1) += iosapic.o
 obj-$(CONFIG_IA64_DIG) += iosapic.o
 obj-$(CONFIG_IA64_PALINFO) += palinfo.o
 obj-$(CONFIG_EFI_VARS) += efivars.o
diff -Nru a/arch/ia64/kernel/acpi.c b/arch/ia64/kernel/acpi.c
--- a/arch/ia64/kernel/acpi.c	Mon Apr 22 15:32:19 2002
+++ b/arch/ia64/kernel/acpi.c	Mon Apr 22 15:32:19 2002
@@ -1,21 +1,34 @@
 /*
- * Advanced Configuration and Power Interface
+ *  acpi.c - Architecture-Specific Low-Level ACPI Support
  *
- * Based on 'ACPI Specification 1.0b' February 2, 1999 and
- * 'IA-64 Extensions to ACPI Specification' Revision 0.6
+ *  Copyright (C) 1999 VA Linux Systems
+ *  Copyright (C) 1999,2000 Walt Drummond <drummond@valinux.com>
+ *  Copyright (C) 2000 Hewlett-Packard Co.
+ *  Copyright (C) 2000 David Mosberger-Tang <davidm@hpl.hp.com>
+ *  Copyright (C) 2000 Intel Corp.
+ *  Copyright (C) 2000,2001 J.I. Lee <jung-ik.lee@intel.com>
+ *  Copyright (C) 2001 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
  *
- * Copyright (C) 1999 VA Linux Systems
- * Copyright (C) 1999, 2000 Walt Drummond <drummond@valinux.com>
- * Copyright (C) 2000, 2002 Hewlett-Packard Co.
- *	David Mosberger-Tang <davidm@hpl.hp.com>
- * Copyright (C) 2000 Intel Corp.
- * Copyright (C) 2000, 2001 J.I. Lee <jung-ik.lee@intel.com>
- *      ACPI based kernel configuration manager.
- *      ACPI 2.0 & IA64 ext 0.71
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  */
 
 #include <linux/config.h>
-
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
@@ -23,31 +36,16 @@
 #include <linux/string.h>
 #include <linux/types.h>
 #include <linux/irq.h>
-#ifdef CONFIG_SERIAL_ACPI
-#include <linux/acpi_serial.h>
-#endif
-
-#include <asm/acpi-ext.h>
-#include <asm/acpikcfg.h>
+#include <linux/acpi.h>
 #include <asm/efi.h>
 #include <asm/io.h>
 #include <asm/iosapic.h>
 #include <asm/machvec.h>
 #include <asm/page.h>
+#include <asm/system.h>
 
-#undef ACPI_DEBUG		/* Guess what this does? */
-
-/* global array to record platform interrupt vectors for generic int routing */
-int platform_irq_list[ACPI_MAX_PLATFORM_IRQS];
 
-/* These are ugly but will be reclaimed by the kernel */
-int __initdata available_cpus;
-int __initdata total_cpus;
-
-int __initdata pcat_compat;
-
-void (*pm_idle) (void);
-void (*pm_power_off) (void);
+#define PREFIX			"ACPI: "
 
 asm (".weak iosapic_register_irq");
 asm (".weak iosapic_register_legacy_irq");
@@ -55,10 +53,16 @@
 asm (".weak iosapic_init");
 asm (".weak iosapic_version");
 
+void (*pm_idle) (void);
+void (*pm_power_off) (void);
+
+
+/*
+ * TBD: Should go away once we have an ACPI parser.
+ */
 const char *
 acpi_get_sysname (void)
 {
-	/* the following should go away once we have an ACPI parser: */
 #ifdef CONFIG_IA64_GENERIC
 	return "hpsim";
 #else
@@ -74,16 +78,21 @@
 #	error Unknown platform.  Fix acpi.c.
 # endif
 #endif
-
 }
 
+#ifdef CONFIG_ACPI_BOOT
+
+#define ACPI_MAX_PLATFORM_IRQS	256
+
+/* Array to record platform interrupt vectors for generic interrupt routing. */
+int platform_irq_list[ACPI_MAX_PLATFORM_IRQS];
+
 /*
- * Interrupt routing API for device drivers.
- * Provides the interrupt vector for a generic platform event
- * (currently only CPEI implemented)
+ * Interrupt routing API for device drivers.  Provides interrupt vector for
+ * a generic platform event.  Currently only CPEI is implemented.
  */
 int
-acpi_request_vector(u32 int_type)
+acpi_request_vector (u32 int_type)
 {
 	int vector = -1;
 
@@ -96,586 +105,494 @@
 	return vector;
 }
 
-/*
- * Configure legacy IRQ information.
- */
-static void __init
-acpi_legacy_irq (char *p)
+
+/* --------------------------------------------------------------------------
+                            Boot-time Table Parsing
+   -------------------------------------------------------------------------- */
+
+static int			total_cpus __initdata;
+static int			available_cpus __initdata;
+struct acpi_table_madt *	acpi_madt __initdata;
+
+
+static int __init
+acpi_parse_lapic_addr_ovr (acpi_table_entry_header *header)
 {
-	acpi_entry_int_override_t *legacy = (acpi_entry_int_override_t *) p;
-	unsigned long polarity = 0, edge_triggered = 0;
+	struct acpi_table_lapic_addr_ovr *lapic = NULL;
 
-	/*
-	 * If the platform we're running doesn't define
-	 * iosapic_register_legacy_irq(), we ignore this info...
-	 */
-	if (!iosapic_register_legacy_irq)
-		return;
+	lapic = (struct acpi_table_lapic_addr_ovr *) header;
+	if (!lapic)
+		return -EINVAL;
+
+	acpi_table_print_madt_entry(header);
 
-	switch (legacy->flags) {
-	      case 0x5:	polarity = 1; edge_triggered = 1; break;
-	      case 0x7: polarity = 0; edge_triggered = 1; break;
-	      case 0xd: polarity = 1; edge_triggered = 0; break;
-	      case 0xf: polarity = 0; edge_triggered = 0; break;
-	      default:
-		printk("    ACPI Legacy IRQ 0x%02x: Unknown flags 0x%x\n", legacy->isa_irq,
-		       legacy->flags);
-		break;
+	if (lapic->address) {
+		iounmap((void *) ipi_base_addr);
+		ipi_base_addr = (unsigned long) ioremap(lapic->address, 0);
 	}
-	iosapic_register_legacy_irq(legacy->isa_irq, legacy->pin, polarity, edge_triggered);
+
+	return 0;
 }
 
-/*
- * ACPI 2.0 tables parsing functions
- */
 
-static unsigned long
-readl_unaligned(void *p)
+static int __init
+acpi_parse_lsapic (acpi_table_entry_header *header)
 {
-	unsigned long ret;
+	struct acpi_table_lsapic *lsapic = NULL;
 
-	memcpy(&ret, p, sizeof(long));
-	return ret;
-}
+	lsapic = (struct acpi_table_lsapic *) header;
+	if (!lsapic)
+		return -EINVAL;
 
-/*
- * Identify usable CPU's and remember them for SMP bringup later.
- */
-static void __init
-acpi20_lsapic (char *p)
-{
-	int add = 1;
+	acpi_table_print_madt_entry(header);
 
-	acpi20_entry_lsapic_t *lsapic = (acpi20_entry_lsapic_t *) p;
-	printk("      CPU %.04x:%.04x: ", lsapic->eid, lsapic->id);
+	printk("CPU %d (0x%04x)", total_cpus, (lsapic->id << 8) | lsapic->eid);
 
-	if ((lsapic->flags & LSAPIC_ENABLED) == 0) {
-		printk("disabled.\n");
-		add = 0;
-	}
-
-#ifdef CONFIG_SMP
-	smp_boot_data.cpu_phys_id[total_cpus] = -1;
-#endif
-	if (add) {
+	if (lsapic->flags.enabled) {
 		available_cpus++;
-		printk("available");
+		printk(" enabled");
 #ifdef CONFIG_SMP
 		smp_boot_data.cpu_phys_id[total_cpus] = (lsapic->id << 8) | lsapic->eid;
 		if (hard_smp_processor_id() == smp_boot_data.cpu_phys_id[total_cpus])
 			printk(" (BSP)");
 #endif
-		printk(".\n");
 	}
+	else {
+		printk(" disabled");
+#ifdef CONFIG_SMP
+		smp_boot_data.cpu_phys_id[total_cpus] = -1;
+#endif
+	}
+
+	printk("\n");
+
 	total_cpus++;
+	return 0;
 }
 
-/*
- * Extract iosapic info from madt (again) to determine which iosapic
- * this platform interrupt resides in
- */
+
+static int __init
+acpi_parse_lapic_nmi (acpi_table_entry_header *header)
+{
+	struct acpi_table_lapic_nmi *lacpi_nmi = NULL;
+
+	lacpi_nmi = (struct acpi_table_lapic_nmi*) header;
+	if (!lacpi_nmi)
+		return -EINVAL;
+
+	acpi_table_print_madt_entry(header);
+
+	/* TBD: Support lapic_nmi entries */
+
+	return 0;
+}
+
+
 static int __init
-acpi20_which_iosapic (int global_vector, acpi_madt_t *madt, u32 *irq_base, char **iosapic_address)
+acpi_find_iosapic (int global_vector, u32 *irq_base, char **iosapic_address)
 {
-	acpi_entry_iosapic_t *iosapic;
-	char *p, *end;
-	int ver, max_pin;
+	struct acpi_table_iosapic *iosapic = NULL;
+	int ver = 0;
+	int max_pin = 0;
+	char *p = 0;
+	char *end = 0;
 
-	p = (char *) (madt + 1);
-	end = p + (madt->header.length - sizeof(acpi_madt_t));
+	if (!irq_base || !iosapic_address)
+		return -ENODEV;
+
+	p = (char *) (acpi_madt + 1);
+	end = p + (acpi_madt->header.length - sizeof(struct acpi_table_madt));
 
 	while (p < end) {
-		switch (*p) {
-		      case ACPI20_ENTRY_IO_SAPIC:
-			/* collect IOSAPIC info for platform int use later */
-			iosapic = (acpi_entry_iosapic_t *)p;
-			*irq_base = iosapic->irq_base;
+		if (*p == ACPI_MADT_IOSAPIC) {
+			iosapic = (struct acpi_table_iosapic *) p;
+
+			*irq_base = iosapic->global_irq_base;
 			*iosapic_address = ioremap(iosapic->address, 0);
-			/* is this the iosapic we're looking for? */
+
 			ver = iosapic_version(*iosapic_address);
 			max_pin = (ver >> 16) & 0xff;
+
 			if ((global_vector - *irq_base) <= max_pin)
-				return 0;		/* found it! */
-			break;
-		      default:
-			break;
+				return 0;	/* Found it! */
 		}
 		p += p[1];
 	}
-	return 1;
+	return -ENODEV;
 }
 
-/*
- * Info on platform interrupt sources: NMI, PMI, INIT, etc.
- */
-static void __init
-acpi20_platform (char *p, acpi_madt_t *madt)
+
+static int __init
+acpi_parse_iosapic (acpi_table_entry_header *header)
 {
-	int vector;
-	u32 irq_base;
-	char *iosapic_address;
-	unsigned long polarity = 0, trigger = 0;
-	acpi20_entry_platform_src_t *plat = (acpi20_entry_platform_src_t *) p;
+	struct acpi_table_iosapic *iosapic;
 
-	printk("PLATFORM: IOSAPIC %x -> Vector %x on CPU %.04u:%.04u\n",
-	       plat->iosapic_vector, plat->global_vector, plat->eid, plat->id);
+	iosapic = (struct acpi_table_iosapic *) header;
+	if (!iosapic)
+		return -EINVAL;
 
-	/* record platform interrupt vectors for generic int routing code */
+	acpi_table_print_madt_entry(header);
+
+	if (iosapic_init) {
+#ifndef CONFIG_ITANIUM
+		/* PCAT_COMPAT flag indicates dual-8259 setup */
+		iosapic_init(iosapic->address, iosapic->global_irq_base,
+			     acpi_madt->flags.pcat_compat);
+#else
+		/* Firmware on old Itanium systems is broken */
+		iosapic_init(iosapic->address, iosapic->global_irq_base, 1);
+#endif
+	}
+	return 0;
+}
+
+
+static int __init
+acpi_parse_plat_int_src (acpi_table_entry_header *header)
+{
+	struct acpi_table_plat_int_src *plintsrc = NULL;
+	int vector = 0;
+	u32 irq_base = 0;
+	char *iosapic_address = NULL;
+
+	plintsrc = (struct acpi_table_plat_int_src *) header;
+	if (!plintsrc)
+		return -EINVAL;
+
+	acpi_table_print_madt_entry(header);
 
 	if (!iosapic_register_platform_irq) {
-		printk("acpi20_platform(): no ACPI platform IRQ support\n");
-		return;
+		printk(KERN_WARNING PREFIX "No ACPI platform IRQ support\n");
+		return -ENODEV;
 	}
 
-	/* extract polarity and trigger info from flags */
-	switch (plat->flags) {
-	      case 0x5: polarity = 1; trigger = 1; break;
-	      case 0x7: polarity = 0; trigger = 1; break;
-	      case 0xd: polarity = 1; trigger = 0; break;
-	      case 0xf: polarity = 0; trigger = 0; break;
-	      default:
-		printk("acpi20_platform(): unknown flags 0x%x\n", plat->flags);
-		break;
-	}
-
-	/* which iosapic does this IRQ belong to? */
-	if (acpi20_which_iosapic(plat->global_vector, madt, &irq_base, &iosapic_address)) {
-		printk("acpi20_platform(): I/O SAPIC not found!\n");
-		return;
+	if (0 != acpi_find_iosapic(plintsrc->global_irq, &irq_base, &iosapic_address)) {
+		printk(KERN_WARNING PREFIX "IOSAPIC not found\n");
+		return -ENODEV;
 	}
 
 	/*
-	 * get vector assignment for this IRQ, set attributes, and program the IOSAPIC
-	 * routing table
+	 * Get vector assignment for this IRQ, set attributes, and program the
+	 * IOSAPIC routing table.
 	 */
-	vector = iosapic_register_platform_irq(plat->int_type,
-					       plat->global_vector,
-					       plat->iosapic_vector,
-					       plat->eid,
-					       plat->id,
-					       polarity,
-					       trigger,
-					       irq_base,
-					       iosapic_address);
-	platform_irq_list[plat->int_type] = vector;
+	vector = iosapic_register_platform_irq (plintsrc->type,
+						plintsrc->global_irq,
+						plintsrc->iosapic_vector,
+						plintsrc->eid,
+						plintsrc->id,
+						(plintsrc->flags.polarity == 1) ? 1 : 0,
+						(plintsrc->flags.trigger == 1) ? 1 : 0,
+						irq_base,
+						iosapic_address);
+
+	platform_irq_list[plintsrc->type] = vector;
+	return 0;
 }
 
-/*
- * Override the physical address of the local APIC in the MADT stable header.
- */
-static void __init
-acpi20_lapic_addr_override (char *p)
+
+static int __init
+acpi_parse_int_src_ovr (acpi_table_entry_header *header)
 {
-	acpi20_entry_lapic_addr_override_t * lapic = (acpi20_entry_lapic_addr_override_t *) p;
+	struct acpi_table_int_src_ovr *p = NULL;
 
-	if (lapic->lapic_address) {
-		iounmap((void *)ipi_base_addr);
-		ipi_base_addr = (unsigned long) ioremap(lapic->lapic_address, 0);
+	p = (struct acpi_table_int_src_ovr *) header;
+	if (!p)
+		return -EINVAL;
 
-		printk("LOCAL ACPI override to 0x%lx(p=0x%lx)\n",
-		       ipi_base_addr, lapic->lapic_address);
-	}
+	acpi_table_print_madt_entry(header);
+
+	/* Ignore if the platform doesn't support overrides */
+	if (!iosapic_register_legacy_irq)
+		return 0;
+
+	iosapic_register_legacy_irq(p->bus_irq, p->global_irq,
+		(p->flags.polarity == 1) ? 1 : 0,
+		(p->flags.trigger == 1) ? 1 : 0);
+
+	return 0;
 }
 
-/*
- * Parse the ACPI Multiple APIC Description Table
- */
-static void __init
-acpi20_parse_madt (acpi_madt_t *madt)
+
+static int __init
+acpi_parse_nmi_src (acpi_table_entry_header *header)
 {
-	acpi_entry_iosapic_t *iosapic = NULL;
-	acpi20_entry_lsapic_t *lsapic = NULL;
-	char *p, *end;
-	int i;
-
-	/* Base address of IPI Message Block */
-	if (madt->lapic_address) {
-		ipi_base_addr = (unsigned long) ioremap(madt->lapic_address, 0);
-		printk("Lapic address set to 0x%lx\n", ipi_base_addr);
-	} else
-		printk("Lapic address set to default 0x%lx\n", ipi_base_addr);
+	struct acpi_table_nmi_src *nmi_src = NULL;
 
-	/*
-	 * The PCAT_COMPAT flag indicates that the system has a dual-8259 compatible
-	 * setup.
-	 */
-#ifdef CONFIG_ITANIUM
-	pcat_compat = 1; /* fw on some Itanium systems is broken... */
-#else
-	pcat_compat = (madt->flags & MADT_PCAT_COMPAT);
-#endif
+	nmi_src = (struct acpi_table_nmi_src*) header;
+	if (!nmi_src)
+		return -EINVAL;
 
-	p = (char *) (madt + 1);
-	end = p + (madt->header.length - sizeof(acpi_madt_t));
+	acpi_table_print_madt_entry(header);
 
-	/* Initialize platform interrupt vector array */
-	for (i = 0; i < ACPI_MAX_PLATFORM_IRQS; i++)
-		platform_irq_list[i] = -1;
+	/* TBD: Support nimsrc entries */
 
-	/*
-	 * Split-up entry parsing to ensure ordering.
-	 */
-	while (p < end) {
-		switch (*p) {
-		      case ACPI20_ENTRY_LOCAL_APIC_ADDR_OVERRIDE:
-			printk("ACPI 2.0 MADT: LOCAL APIC Override\n");
-			acpi20_lapic_addr_override(p);
-			break;
-
-		      case ACPI20_ENTRY_LOCAL_SAPIC:
-			printk("ACPI 2.0 MADT: LOCAL SAPIC\n");
-			lsapic = (acpi20_entry_lsapic_t *) p;
-			acpi20_lsapic(p);
-			break;
-
-		      case ACPI20_ENTRY_IO_SAPIC:
-			iosapic = (acpi_entry_iosapic_t *) p;
-			if (iosapic_init)
-				iosapic_init(iosapic->address, iosapic->irq_base, pcat_compat);
-			break;
-
-		      case ACPI20_ENTRY_PLATFORM_INT_SOURCE:
-			printk("ACPI 2.0 MADT: PLATFORM INT SOURCE\n");
-			acpi20_platform(p, madt);
-			break;
-
-		      case ACPI20_ENTRY_LOCAL_APIC:
-			printk("ACPI 2.0 MADT: LOCAL APIC entry\n"); break;
-		      case ACPI20_ENTRY_IO_APIC:
-			printk("ACPI 2.0 MADT: IO APIC entry\n"); break;
-		      case ACPI20_ENTRY_NMI_SOURCE:
-			printk("ACPI 2.0 MADT: NMI SOURCE entry\n"); break;
-		      case ACPI20_ENTRY_LOCAL_APIC_NMI:
-			printk("ACPI 2.0 MADT: LOCAL APIC NMI entry\n"); break;
-		      case ACPI20_ENTRY_INT_SRC_OVERRIDE:
-			break;
-		      default:
-			printk("ACPI 2.0 MADT: unknown entry skip\n"); break;
-			break;
-		}
-		p += p[1];
-	}
+	return 0;
+}
 
-	p = (char *) (madt + 1);
-	end = p + (madt->header.length - sizeof(acpi_madt_t));
 
-	while (p < end) {
-		switch (*p) {
-		      case ACPI20_ENTRY_LOCAL_APIC:
-			if (lsapic) break;
-			printk("ACPI 2.0 MADT: LOCAL APIC entry\n");
-			/* parse local apic if there's no local Sapic */
-			break;
-		      case ACPI20_ENTRY_IO_APIC:
-			if (iosapic) break;
-			printk("ACPI 2.0 MADT: IO APIC entry\n");
-			/* parse ioapic if there's no ioSapic */
-			break;
-		      default:
-			break;
-		}
-		p += p[1];
-	}
+static int __init
+acpi_parse_madt (unsigned long phys_addr, unsigned long size)
+{
+	int i = 0;
 
-	p = (char *) (madt + 1);
-	end = p + (madt->header.length - sizeof(acpi_madt_t));
+	if (!phys_addr || !size)
+		return -EINVAL;
 
-	while (p < end) {
-		switch (*p) {
-		      case ACPI20_ENTRY_INT_SRC_OVERRIDE:
-			printk("ACPI 2.0 MADT: INT SOURCE Override\n");
-			acpi_legacy_irq(p);
-			break;
-		      default:
-			break;
-		}
-		p += p[1];
+	acpi_madt = (struct acpi_table_madt *) __va(phys_addr);
+	if (!acpi_madt) {
+		printk(KERN_WARNING PREFIX "Unable to map MADT\n");
+		return -ENODEV;
 	}
 
-	/* Make bootup pretty */
-	printk("      %d CPUs available, %d CPUs total\n",
-		available_cpus, total_cpus);
+	/* Initialize platform interrupt vector array */
+
+	for (i = 0; i < ACPI_MAX_PLATFORM_IRQS; i++)
+		platform_irq_list[i] = -1;
+
+	/* Get base address of IPI Message Block */
+
+	if (acpi_madt->lapic_address)
+		ipi_base_addr = (unsigned long)
+			ioremap(acpi_madt->lapic_address, 0);
+
+	printk(KERN_INFO PREFIX "Local APIC address 0x%lx\n", ipi_base_addr);
+
+	return 0;
 }
 
+
 int __init
-acpi20_parse (acpi20_rsdp_t *rsdp20)
+acpi_find_rsdp (unsigned long *rsdp_phys)
 {
-# ifdef CONFIG_ACPI
-	acpi_xsdt_t *xsdt;
-	acpi_desc_table_hdr_t *hdrp;
-	acpi_madt_t *madt = NULL;
-	int tables, i;
+	if (!rsdp_phys)
+		return -EINVAL;
 
-	if (strncmp(rsdp20->signature, ACPI_RSDP_SIG, ACPI_RSDP_SIG_LEN)) {
-		printk("ACPI 2.0 RSDP signature incorrect!\n");
+	if (efi.acpi20) {
+		(*rsdp_phys) = __pa(efi.acpi20);
 		return 0;
-	} else {
-		printk("ACPI 2.0 Root System Description Ptr at 0x%lx\n",
-			(unsigned long)rsdp20);
 	}
-
-	xsdt = __va(rsdp20->xsdt);
-	hdrp = &xsdt->header;
-	if (strncmp(hdrp->signature,
-		ACPI_XSDT_SIG, ACPI_XSDT_SIG_LEN)) {
-		printk("ACPI 2.0 XSDT signature incorrect. Trying RSDT\n");
-		/* RSDT parsing here */
-		return 0;
-	} else {
-		printk("ACPI 2.0 XSDT at 0x%lx (p=0x%lx)\n",
-		(unsigned long)xsdt, (unsigned long)rsdp20->xsdt);
+	else if (efi.acpi) {
+		printk(KERN_WARNING PREFIX "v1.0/r0.71 tables no longer supported\n");
 	}
 
-	printk("ACPI 2.0: %.6s %.8s %d.%d\n",
-		hdrp->oem_id,
-		hdrp->oem_table_id,
-		hdrp->oem_revision >> 16,
-		hdrp->oem_revision & 0xffff);
+	return -ENODEV;
+}
+
 
-	acpi_cf_init((void *)rsdp20);
+#ifdef CONFIG_SERIAL_ACPI
 
-	tables =(hdrp->length -sizeof(acpi_desc_table_hdr_t))>>3;
+#include <linux/acpi_serial.h>
 
-	for (i = 0; i < tables; i++) {
-		hdrp = (acpi_desc_table_hdr_t *) __va(readl_unaligned(&xsdt->entry_ptrs[i]));
-		printk("        :table %4.4s found\n", hdrp->signature);
+static int __init
+acpi_parse_spcr (unsigned long phys_addr, unsigned long size)
+{
+	acpi_ser_t *spcr = NULL;
+	unsigned long global_int = 0;
 
-		/* Only interested int the MADT table for now ... */
-		if (strncmp(hdrp->signature,
-			ACPI_MADT_SIG, ACPI_MADT_SIG_LEN) != 0)
-			continue;
+	if (!phys_addr || !size)
+		return -EINVAL;
 
-		/* Save MADT pointer for later */
-		madt = (acpi_madt_t *) hdrp;
-		acpi20_parse_madt(madt);
-	}
+	if (!iosapic_register_irq)
+		return -ENODEV;
 
-#ifdef CONFIG_SERIAL_ACPI
 	/*
-	 * Now we're interested in other tables.  We want the iosapics already
-	 * initialized, so we do it in a separate loop.
+	 * ACPI is able to describe serial ports that live at non-standard
+	 * memory addresses and use non-standard interrupts, either via
+	 * direct SAPIC mappings or via PCI interrupts.  We handle interrupt
+	 * routing for SAPIC-based (non-PCI) devices here.  Interrupt routing
+	 * for PCI devices will be handled when processing the PCI Interrupt
+	 * Routing Table (PRT).
 	 */
-	for (i = 0; i < tables; i++) {
-		hdrp = (acpi_desc_table_hdr_t *) __va(readl_unaligned(&xsdt->entry_ptrs[i]));
-		/*
-		 * search for SPCR and DBGP table entries so we can enable
-		 * non-pci interrupts to IO-SAPICs.
-		 */
-		if (!strncmp(hdrp->signature, ACPI_SPCRT_SIG, ACPI_SPCRT_SIG_LEN) ||
-		    !strncmp(hdrp->signature, ACPI_DBGPT_SIG, ACPI_DBGPT_SIG_LEN))
-		{
-			acpi_ser_t *spcr = (void *)hdrp;
-			unsigned long global_int;
-
-			setup_serial_acpi(hdrp);
-
-			/*
-			 * ACPI is able to describe serial ports that live at non-standard
-			 * memory space addresses and use SAPIC interrupts.  If not also
-			 * PCI devices, there would be no interrupt vector information for
-			 * them.  This checks for and fixes that situation.
-			 */
-			if (spcr->length < sizeof(acpi_ser_t))
-				/* table is not long enough for full info, thus no int */
-				break;
-
-			/*
-			 * If the device is not in PCI space, but uses a SAPIC interrupt,
-			 * we need to program the SAPIC so that serial can autoprobe for
-			 * the IA64 interrupt vector later on.  If the device is in PCI
-			 * space, it should already be setup via the PCI vectors
-			 */
-			if (spcr->base_addr.space_id != ACPI_SERIAL_PCICONF_SPACE &&
-			    spcr->int_type == ACPI_SERIAL_INT_SAPIC)
-			{
-				u32 irq_base;
-				char *iosapic_address;
-				int vector;
-
-				/* We have a UART in memory space with a SAPIC interrupt */
-				global_int = (  (spcr->global_int[3] << 24)
-					      | (spcr->global_int[2] << 16)
-					      | (spcr->global_int[1] << 8)
-					      | spcr->global_int[0]);
-
-				if (!iosapic_register_irq)
-					continue;
-
-				/* which iosapic does this IRQ belong to? */
-				if (acpi20_which_iosapic(global_int, madt, &irq_base,
-							 &iosapic_address) == 0)
-				{
-					vector = iosapic_register_irq(global_int,
-					                              1, /* active high polarity */
-					                              1, /* edge triggered */
-					                              irq_base,
-					                              iosapic_address);
-				}
-			}
-		}
+
+	spcr = (acpi_ser_t *) __va(phys_addr);
+	if (!spcr) {
+		printk(KERN_WARNING PREFIX "Unable to map SPCR\n");
+		return -ENODEV;
 	}
-#endif
-	acpi_cf_terminate();
 
-#  ifdef CONFIG_SMP
-	if (available_cpus == 0) {
-		printk("ACPI: Found 0 CPUS; assuming 1\n");
-		available_cpus = 1; /* We've got at least one of these, no? */
+	setup_serial_acpi(spcr);
+
+	if (spcr->length < sizeof(acpi_ser_t))
+		/* Table not long enough for full info, thus no interrupt */
+		return -ENODEV;
+
+	if ((spcr->base_addr.space_id != ACPI_SERIAL_PCICONF_SPACE) &&
+	    (spcr->int_type == ACPI_SERIAL_INT_SAPIC))
+	{
+		u32 irq_base = 0;
+		char *iosapic_address = NULL;
+		int vector = 0;
+
+		/* We have a UART in memory space with an SAPIC interrupt */
+
+		global_int = (  (spcr->global_int[3] << 24) |
+				(spcr->global_int[2] << 16) |
+				(spcr->global_int[1] << 8)  |
+				(spcr->global_int[0])  );
+
+		/* Which iosapic does this IRQ belong to? */
+
+		if (0 == acpi_find_iosapic(global_int, &irq_base, &iosapic_address)) {
+			vector = iosapic_register_irq (global_int, 1, 1,
+						       irq_base, iosapic_address);
+		}
 	}
-	smp_boot_data.cpu_count = total_cpus;
-#  endif
-# endif /* CONFIG_ACPI */
-	return 1;
+	return 0;
 }
-/*
- * ACPI 1.0b with 0.71 IA64 extensions functions; should be removed once all
- * platforms start supporting ACPI 2.0
- */
 
-/*
- * Identify usable CPU's and remember them for SMP bringup later.
- */
-static void __init
-acpi_lsapic (char *p)
+#endif /*CONFIG_SERIAL_ACPI*/
+
+
+int __init
+acpi_boot_init (char *cmdline)
 {
-	int add = 1;
+	int result = 0;
+
+	/* Initialize the ACPI boot-time table parser */
+	result = acpi_table_init(cmdline);
+	if (0 != result)
+		return result;
 
-	acpi_entry_lsapic_t *lsapic = (acpi_entry_lsapic_t *) p;
+	/*
+	 * MADT
+	 * ----
+	 * Parse the Multiple APIC Description Table (MADT), if exists.
+	 * Note that this table provides platform SMP configuration
+	 * information -- the successor to MPS tables.
+	 */
 
-	if ((lsapic->flags & LSAPIC_PRESENT) == 0)
-		return;
+	result = acpi_table_parse(ACPI_APIC, acpi_parse_madt);
+	if (1 > result)
+		return result;
 
-	printk("      CPU %d (%.04x:%.04x): ", total_cpus, lsapic->eid, lsapic->id);
+	/* Local APIC */
 
-	if ((lsapic->flags & LSAPIC_ENABLED) == 0) {
-		printk("Disabled.\n");
-		add = 0;
-	} else if (lsapic->flags & LSAPIC_PERFORMANCE_RESTRICTED) {
-		printk("Performance Restricted; ignoring.\n");
-		add = 0;
+	result = acpi_table_parse_madt(ACPI_MADT_LAPIC_ADDR_OVR, acpi_parse_lapic_addr_ovr);
+	if (0 > result) {
+		printk(KERN_ERR PREFIX "Error parsing LAPIC address override entry\n");
+		return result;
 	}
 
-#ifdef CONFIG_SMP
-	smp_boot_data.cpu_phys_id[total_cpus] = -1;
-#endif
-	if (add) {
-		printk("Available.\n");
-		available_cpus++;
-#ifdef CONFIG_SMP
-		smp_boot_data.cpu_phys_id[total_cpus] = (lsapic->id << 8) | lsapic->eid;
-#endif /* CONFIG_SMP */
+	result = acpi_table_parse_madt(ACPI_MADT_LSAPIC, acpi_parse_lsapic);
+	if (1 > result) {
+		printk(KERN_ERR PREFIX "Error parsing MADT - no LAPIC entries!\n");
+		return -ENODEV;
 	}
-	total_cpus++;
-}
 
-/*
- * Info on platform interrupt sources: NMI. PMI, INIT, etc.
- */
-static void __init
-acpi_platform (char *p)
-{
-	acpi_entry_platform_src_t *plat = (acpi_entry_platform_src_t *) p;
+	result = acpi_table_parse_madt(ACPI_MADT_LAPIC_NMI, acpi_parse_lapic_nmi);
+	if (0 > result) {
+		printk(KERN_ERR PREFIX "Error parsing LAPIC NMI entry\n");
+		return result;
+	}
 
-	printk("PLATFORM: IOSAPIC %x -> Vector %x on CPU %.04u:%.04u\n",
-	       plat->iosapic_vector, plat->global_vector, plat->eid, plat->id);
-}
+	/* I/O APIC */
 
-/*
- * Parse the ACPI Multiple SAPIC Table
- */
-static void __init
-acpi_parse_msapic (acpi_sapic_t *msapic)
-{
-	acpi_entry_iosapic_t *iosapic;
-	char *p, *end;
+	result = acpi_table_parse_madt(ACPI_MADT_IOSAPIC, acpi_parse_iosapic);
+	if (1 > result) {
+		printk(KERN_ERR PREFIX "Error parsing MADT - no IOAPIC entries!\n");
+		return ((result == 0) ? -ENODEV : result);
+	}
 
-	/* Base address of IPI Message Block */
-	ipi_base_addr = (unsigned long) ioremap(msapic->interrupt_block, 0);
+	/* System-Level Interrupt Routing */
 
-	p = (char *) (msapic + 1);
-	end = p + (msapic->header.length - sizeof(acpi_sapic_t));
+	result = acpi_table_parse_madt(ACPI_MADT_PLAT_INT_SRC, acpi_parse_plat_int_src);
+	if (0 > result) {
+		printk(KERN_ERR PREFIX "Error parsing platform interrupt source entry\n");
+		return result;
+	}
 
-	while (p < end) {
-		switch (*p) {
-		      case ACPI_ENTRY_LOCAL_SAPIC:
-			acpi_lsapic(p);
-			break;
-
-		      case ACPI_ENTRY_IO_SAPIC:
-			iosapic = (acpi_entry_iosapic_t *) p;
-			if (iosapic_init)
-				/*
-				 * The ACPI I/O SAPIC table doesn't have a PCAT_COMPAT
-				 * flag like the MADT table, but we can safely assume that
-				 * ACPI 1.0b systems have a dual-8259 setup.
-				 */
-				iosapic_init(iosapic->address, iosapic->irq_base, 1);
-			break;
-
-		      case ACPI_ENTRY_INT_SRC_OVERRIDE:
-			acpi_legacy_irq(p);
-			break;
-
-		      case ACPI_ENTRY_PLATFORM_INT_SOURCE:
-			acpi_platform(p);
-			break;
+	result = acpi_table_parse_madt(ACPI_MADT_INT_SRC_OVR, acpi_parse_int_src_ovr);
+	if (0 > result) {
+		printk(KERN_ERR PREFIX "Error parsing interrupt source overrides entry\n");
+		return result;
+	}
 
-		      default:
-			break;
-		}
+	result = acpi_table_parse_madt(ACPI_MADT_NMI_SRC, acpi_parse_nmi_src);
+	if (0 > result) {
+		printk(KERN_ERR PREFIX "Error parsing NMI SRC entry\n");
+		return result;
+	}
 
-		/* Move to next table entry. */
-		p += p[1];
+#ifdef CONFIG_SERIAL_ACPI
+	/*
+	 * TBD: Need phased approach to table parsing (only do those absolutely
+	 *      required during boot-up).  Recommend expanding concept of fix-
+	 *      feature devices (LDM) to include table-based devices such as
+	 *      serial ports, EC, SMBus, etc.
+	 */
+	acpi_table_parse(ACPI_SPCR, acpi_parse_spcr);
+#endif /*CONFIG_SERIAL_ACPI*/
+
+#ifdef CONFIG_SMP
+	if (available_cpus == 0) {
+		printk("ACPI: Found 0 CPUS; assuming 1\n");
+		available_cpus = 1; /* We've got at least one of these, no? */
 	}
+	smp_boot_data.cpu_count = total_cpus;
+#endif
+	/* Make boot-up look pretty */
+	printk("%d CPUs available, %d CPUs total\n", available_cpus, total_cpus);
 
-	/* Make bootup pretty */
-	printk("      %d CPUs available, %d CPUs total\n", available_cpus, total_cpus);
+	return 0;
 }
 
+
+/* --------------------------------------------------------------------------
+                             PCI Interrupt Routing
+   -------------------------------------------------------------------------- */
+
 int __init
-acpi_parse (acpi_rsdp_t *rsdp)
+acpi_get_prt (struct pci_vector_struct **vectors, int *count)
 {
-# ifdef CONFIG_ACPI
-	acpi_rsdt_t *rsdt;
-	acpi_desc_table_hdr_t *hdrp;
-	long tables, i;
+	struct pci_vector_struct *vector = NULL;
+	struct list_head *node = NULL;
+	struct acpi_prt_entry *entry = NULL;
+	int i = 0;
 
-	if (strncmp(rsdp->signature, ACPI_RSDP_SIG, ACPI_RSDP_SIG_LEN)) {
-		printk("Uh-oh, ACPI RSDP signature incorrect!\n");
-		return 0;
-	}
+	if (!vectors || !count)
+		return -EINVAL;
 
-	rsdt = __va(rsdp->rsdt);
-	if (strncmp(rsdt->header.signature, ACPI_RSDT_SIG, ACPI_RSDT_SIG_LEN)) {
-		printk("Uh-oh, ACPI RDST signature incorrect!\n");
-		return 0;
+	*vectors = NULL;
+	*count = 0;
+
+	if (acpi_prts.count < 0) {
+		printk(KERN_ERR PREFIX "No PCI IRQ routing entries\n");
+		return -ENODEV;
 	}
 
-	printk("ACPI: %.6s %.8s %d.%d\n", rsdt->header.oem_id, rsdt->header.oem_table_id,
-	       rsdt->header.oem_revision >> 16, rsdt->header.oem_revision & 0xffff);
+	/* Allocate vectors */
 
-	acpi_cf_init(rsdp);
+	*vectors = kmalloc(sizeof(struct pci_vector_struct) * acpi_prts.count, GFP_KERNEL);
+	if (!(*vectors))
+		return -ENOMEM;
 
-	tables = (rsdt->header.length - sizeof(acpi_desc_table_hdr_t)) / 8;
-	for (i = 0; i < tables; i++) {
-		hdrp = (acpi_desc_table_hdr_t *) __va(rsdt->entry_ptrs[i]);
+	/* Convert PRT entries to IOSAPIC PCI vectors */
 
-		/* Only interested int the MSAPIC table for now ... */
-		if (strncmp(hdrp->signature, ACPI_SAPIC_SIG, ACPI_SAPIC_SIG_LEN) != 0)
-			continue;
+	vector = *vectors;
 
-		acpi_parse_msapic((acpi_sapic_t *) hdrp);
+	list_for_each(node, &acpi_prts.entries) {
+		entry = (struct acpi_prt_entry *)node;
+		vector[i].bus    = (u16) entry->id.bus;
+		vector[i].pci_id = (u32) entry->id.dev << 16 | 0xffff;
+		vector[i].pin    = (u8)  entry->id.pin;
+		vector[i].irq    = (u8)  entry->source.index;
+		i++;
 	}
+	*count = acpi_prts.count;
+	return 0;
+}
 
-	acpi_cf_terminate();
+/* Assume IA64 always use I/O SAPIC */
 
-#  ifdef CONFIG_SMP
-	if (available_cpus == 0) {
-		printk("ACPI: Found 0 CPUS; assuming 1\n");
-		available_cpus = 1; /* We've got at least one of these, no? */
-	}
-	smp_boot_data.cpu_count = total_cpus;
-#  endif
-# endif /* CONFIG_ACPI */
-	return 1;
+int __init
+acpi_get_interrupt_model (int *type)
+{
+        if (!type)
+                return -EINVAL;
+
+	*type = ACPI_INT_MODEL_IOSAPIC;
+
+        return 0;
 }
+
+#endif /* CONFIG_ACPI_BOOT */
diff -Nru a/arch/ia64/kernel/brl_emu.c b/arch/ia64/kernel/brl_emu.c
--- a/arch/ia64/kernel/brl_emu.c	Mon Apr 22 15:32:19 2002
+++ b/arch/ia64/kernel/brl_emu.c	Mon Apr 22 15:32:19 2002
@@ -55,7 +55,7 @@
 ia64_emulate_brl (struct pt_regs *regs, unsigned long ar_ec)
 {
 	unsigned long bundle[2];
-	unsigned long opcode, btype, qp, offset;
+	unsigned long opcode, btype, qp, offset, cpl;
 	unsigned long next_ip;
 	struct siginfo siginfo;
 	struct illegal_op_return rv;
@@ -158,9 +158,9 @@
 			 *  AR[PFS].pec = AR[EC]
 			 *  AR[PFS].ppl = PSR.cpl
 			 */
+			cpl = ia64_psr(regs)->cpl;
 			regs->ar_pfs = ((regs->cr_ifs & 0x3fffffffff)
-					| (ar_ec << 52)
-					| ((unsigned long) ia64_psr(regs)->cpl << 62));
+					| (ar_ec << 52) | (cpl << 62));
 
 			/*
 			 *  CFM.sof -= CFM.sol
diff -Nru a/arch/ia64/kernel/efi.c b/arch/ia64/kernel/efi.c
--- a/arch/ia64/kernel/efi.c	Mon Apr 22 15:32:18 2002
+++ b/arch/ia64/kernel/efi.c	Mon Apr 22 15:32:18 2002
@@ -155,10 +155,10 @@
 		      case EFI_CONVENTIONAL_MEMORY:
 			if (!(md->attribute & EFI_MEMORY_WB))
 				continue;
-			if (md->phys_addr + (md->num_pages << 12) > mem_limit) {
+			if (md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) > mem_limit) {
 				if (md->phys_addr > mem_limit)
 					continue;
-				md->num_pages = (mem_limit - md->phys_addr) >> 12;
+				md->num_pages = (mem_limit - md->phys_addr) >> EFI_PAGE_SHIFT;
 			}
 			if (md->num_pages == 0) {
 				printk("efi_memmap_walk: ignoring empty region at 0x%lx",
@@ -167,7 +167,7 @@
 			}
 
 			curr.start = PAGE_OFFSET + md->phys_addr;
-			curr.end   = curr.start + (md->num_pages << 12);
+			curr.end   = curr.start + (md->num_pages << EFI_PAGE_SHIFT);
 
 			if (!prev_valid) {
 				prev = curr;
@@ -250,16 +250,17 @@
 		 * dedicated ITR for the PAL code.
 		 */
 		if ((vaddr & mask) == (KERNEL_START & mask)) {
-			printk(__FUNCTION__ ": no need to install ITR for PAL code\n");
+			printk("%s: no need to install ITR for PAL code\n", __FUNCTION__);
 			continue;
 		}
 
-		if (md->num_pages << 12 > IA64_GRANULE_SIZE)
+		if (md->num_pages << EFI_PAGE_SHIFT > IA64_GRANULE_SIZE)
 			panic("Woah!  PAL code size bigger than a granule!");
 
 		mask  = ~((1 << IA64_GRANULE_SHIFT) - 1);
 		printk("CPU %d: mapping PAL code [0x%lx-0x%lx) into [0x%lx-0x%lx)\n",
-		       smp_processor_id(), md->phys_addr, md->phys_addr + (md->num_pages << 12),
+		       smp_processor_id(), md->phys_addr,
+		       md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT),
 		       vaddr & mask, (vaddr & mask) + IA64_GRANULE_SIZE);
 
 		/*
@@ -375,7 +376,8 @@
 			md = p;
 			printk("mem%02u: type=%u, attr=0x%lx, range=[0x%016lx-0x%016lx) (%luMB)\n",
 			       i, md->type, md->attribute, md->phys_addr,
-			       md->phys_addr + (md->num_pages<<12) - 1, md->num_pages >> 8);
+			       md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1,
+			       md->num_pages >> (20 - EFI_PAGE_SHIFT));
 		}
 	}
 #endif
@@ -482,8 +484,50 @@
 	return 0;
 }
 
+u32
+efi_mem_type (u64 phys_addr)
+{
+	void *efi_map_start, *efi_map_end, *p;
+	efi_memory_desc_t *md;
+	u64 efi_desc_size;
+
+	efi_map_start = __va(ia64_boot_param->efi_memmap);
+	efi_map_end   = efi_map_start + ia64_boot_param->efi_memmap_size;
+	efi_desc_size = ia64_boot_param->efi_memdesc_size;
+
+	for (p = efi_map_start; p < efi_map_end; p += efi_desc_size) {
+		md = p;
+
+		if ((md->phys_addr <= phys_addr) && (phys_addr <=
+		    (md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1)))
+			 return md->type;
+	}
+	return 0;
+}
+
+u64
+efi_mem_attributes (u64 phys_addr)
+{
+	void *efi_map_start, *efi_map_end, *p;
+	efi_memory_desc_t *md;
+	u64 efi_desc_size;
+
+	efi_map_start = __va(ia64_boot_param->efi_memmap);
+	efi_map_end   = efi_map_start + ia64_boot_param->efi_memmap_size;
+	efi_desc_size = ia64_boot_param->efi_memdesc_size;
+
+	for (p = efi_map_start; p < efi_map_end; p += efi_desc_size) {
+		md = p;
+
+		if ((md->phys_addr <= phys_addr) && (phys_addr <=
+		    (md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1)))
+			return md->attribute;
+	}
+	return 0;
+}
+
 static void __exit
-efivars_exit(void)
+efivars_exit (void)
 {
 #ifdef CONFIG_PROC_FS
  	remove_proc_entry(efi_dir->name, NULL);
diff -Nru a/arch/ia64/kernel/entry.S b/arch/ia64/kernel/entry.S
--- a/arch/ia64/kernel/entry.S	Mon Apr 22 15:32:19 2002
+++ b/arch/ia64/kernel/entry.S	Mon Apr 22 15:32:19 2002
@@ -36,6 +36,7 @@
 #include <asm/kregs.h>
 #include <asm/offsets.h>
 #include <asm/pgtable.h>
+#include <asm/percpu.h>
 #include <asm/processor.h>
 #include <asm/thread_info.h>
 #include <asm/unistd.h>
@@ -214,62 +215,80 @@
 	.save @priunat,r17
 	mov r17=ar.unat		// preserve caller's
 	.body
-	adds r3=80,sp
+#ifdef CONFIG_ITANIUM
+	adds r2=16+128,sp
+	adds r3=16+64,sp
+	adds r14=SW(R4)+16,sp
 	;;
+	st8.spill [r14]=r4,16		// spill r4
 	lfetch.fault.excl.nt1 [r3],128
-	mov ar.rsc=0		// put RSE in mode: enforced lazy, little endian, pl 0
-	adds r2=16+128,sp
 	;;
 	lfetch.fault.excl.nt1 [r2],128
 	lfetch.fault.excl.nt1 [r3],128
-	adds r14=SW(R4)+16,sp
 	;;
 	lfetch.fault.excl [r2]
 	lfetch.fault.excl [r3]
 	adds r15=SW(R5)+16,sp
+#else
+	add r2=16+3*128,sp
+	add r3=16,sp
+	add r14=SW(R4)+16,sp
+	;;
+	st8.spill [r14]=r4,SW(R6)-SW(R4)	// spill r4 and prefetch offset 0x1c0
+	lfetch.fault.excl.nt1 [r3],128	//		prefetch offset 0x010
+	;;
+	lfetch.fault.excl.nt1 [r3],128	//		prefetch offset 0x090
+	lfetch.fault.excl.nt1 [r2],128	//		prefetch offset 0x190
+	;;
+	lfetch.fault.excl.nt1 [r3]	//		prefetch offset 0x110
+	lfetch.fault.excl.nt1 [r2]	//		prefetch offset 0x210
+	adds r15=SW(R5)+16,sp
+#endif
 	;;
-	mov r18=ar.fpsr		// preserve fpsr
-	mov r19=ar.rnat
-	add r2=SW(F2)+16,sp	// r2 = &sw->f2
-.mem.offset 0,0; st8.spill [r14]=r4,16		// spill r4
-.mem.offset 8,0; st8.spill [r15]=r5,16		// spill r5
-	add r3=SW(F3)+16,sp	// r3 = &sw->f3
+	st8.spill [r15]=r5,SW(R7)-SW(R5)	// spill r5
+	mov.m ar.rsc=0			// put RSE in mode: enforced lazy, little endian, pl 0
+	add r2=SW(F2)+16,sp		// r2 = &sw->f2
+	;;
+	st8.spill [r14]=r6,SW(B0)-SW(R6)	// spill r6
+	mov.m r18=ar.fpsr		// preserve fpsr
+	add r3=SW(F3)+16,sp		// r3 = &sw->f3
 	;;
 	stf.spill [r2]=f2,32
-	stf.spill [r3]=f3,32
+	mov.m r19=ar.rnat
 	mov r21=b0
-.mem.offset 0,0; st8.spill [r14]=r6,16		// spill r6
-.mem.offset 8,0; st8.spill [r15]=r7,16		// spill r7
+
+	stf.spill [r3]=f3,32
+	st8.spill [r15]=r7,SW(B2)-SW(R7)	// spill r7
 	mov r22=b1
 	;;
 	// since we're done with the spills, read and save ar.unat:
-	mov r29=ar.unat		// M-unit
-	mov r20=ar.bspstore	// M-unit
+	mov.m r29=ar.unat
+	mov.m r20=ar.bspstore
 	mov r23=b2
 	stf.spill [r2]=f4,32
 	stf.spill [r3]=f5,32
 	mov r24=b3
 	;;
-	st8 [r14]=r21,16	// save b0
-	st8 [r15]=r22,16	// save b1
+	st8 [r14]=r21,SW(B1)-SW(B0)		// save b0
+	st8 [r15]=r23,SW(B3)-SW(B2)		// save b2
 	mov r25=b4
 	stf.spill [r2]=f10,32
 	stf.spill [r3]=f11,32
 	mov r26=b5
 	;;
-	st8 [r14]=r23,16	// save b2
-	st8 [r15]=r24,16	// save b3
+	st8 [r14]=r22,SW(B4)-SW(B1)		// save b1
+	st8 [r15]=r24,SW(AR_PFS)-SW(B3)		// save b3
 	mov r21=ar.lc		// I-unit
 	stf.spill [r2]=f12,32
 	stf.spill [r3]=f13,32
 	;;
-	st8 [r14]=r25,16	// save b4
-	st8 [r15]=r26,16	// save b5
+	st8 [r14]=r25,SW(B5)-SW(B4)		// save b4
+	st8 [r15]=r16,SW(AR_LC)-SW(AR_PFS)	// save ar.pfs
 	stf.spill [r2]=f14,32
 	stf.spill [r3]=f15,32
 	;;
-	st8 [r14]=r16		// save ar.pfs
-	st8 [r15]=r21		// save ar.lc
+	st8 [r14]=r26				// save b5
+	st8 [r15]=r21				// save ar.lc
 	stf.spill [r2]=f16,32
 	stf.spill [r3]=f17,32
 	;;
@@ -284,26 +303,26 @@
 	;;
 	stf.spill [r2]=f24,32
 	stf.spill [r3]=f25,32
-	add r14=SW(CALLER_UNAT)+16,sp
 	;;
 	stf.spill [r2]=f26,32
 	stf.spill [r3]=f27,32
-	add r15=SW(AR_FPSR)+16,sp
 	;;
 	stf.spill [r2]=f28,32
 	stf.spill [r3]=f29,32
-	st8 [r14]=r17		// save caller_unat
-	st8 [r15]=r18		// save fpsr
-	mov r21=pr
 	;;
-	stf.spill [r2]=f30,(SW(AR_UNAT)-SW(F30))
-	stf.spill [r3]=f31,(SW(AR_RNAT)-SW(F31))
+	stf.spill [r2]=f30,SW(AR_UNAT)-SW(F30)
+	stf.spill [r3]=f31,SW(PR)-SW(F31)
+	add r14=SW(CALLER_UNAT)+16,sp
+	;;
+	st8 [r2]=r29,SW(AR_RNAT)-SW(AR_UNAT)	// save ar.unat
+	st8 [r14]=r17,SW(AR_FPSR)-SW(CALLER_UNAT) // save caller_unat
+	mov r21=pr
 	;;
-	st8 [r2]=r29,16		// save ar.unat
-	st8 [r3]=r19,16		// save ar.rnat
+	st8 [r2]=r19,SW(AR_BSPSTORE)-SW(AR_RNAT) // save ar.rnat
+	st8 [r3]=r21				// save predicate registers
 	;;
-	st8 [r2]=r20		// save ar.bspstore
-	st8 [r3]=r21		// save predicate registers
+	st8 [r2]=r20				// save ar.bspstore
+	st8 [r14]=r18				// save fpsr
 	mov ar.rsc=3		// put RSE back into eager mode, pl 0
 	br.cond.sptk.many b7
 END(save_switch_stack)
@@ -647,23 +666,38 @@
 	/*
 	 * To prevent leaking bits between the kernel and user-space,
 	 * we must clear the stacked registers in the "invalid" partition here.
-	 * Not pretty, but at least it's fast (3.34 registers/cycle).
-	 * Architecturally, this loop could go at 4.67 registers/cycle, but that would
-	 * oversubscribe Itanium.
+	 * Not pretty, but at least it's fast (3.34 registers/cycle on Itanium,
+	 * 5 registers/cycle on McKinley).
 	 */
 #	define pRecurse	p6
 #	define pReturn	p7
+#ifdef CONFIG_ITANIUM
 #	define Nregs	10
+#else
+#	define Nregs	14
+#endif
 	alloc loc0=ar.pfs,2,Nregs-2,2,0
 	shr.u loc1=r18,9		// RNaTslots <= dirtySize / (64*8) + 1
 	sub r17=r17,r18			// r17 = (physStackedSize + 8) - dirtySize
 	;;
+#if 1
+	.align 32		// see comment below about gas bug...
+#endif
 	mov ar.rsc=r19			// load ar.rsc to be used for "loadrs"
 	shladd in0=loc1,3,r17
 	mov in1=0
+#if 0
+	// gas-2.12.90 is unable to generate a stop bit after .align, which is bad,
+	// because alloc must be at the beginning of an insn-group.
+	.align 32
+#else
+	nop 0
+	nop 0
+	nop 0
+#endif
 	;;
-//	.align 32	// gas-2.11.90 is unable to generate a stop bit after .align
 rse_clear_invalid:
+#ifdef CONFIG_ITANIUM
 	// cycle 0
  { .mii
 	alloc loc0=ar.pfs,2,Nregs-2,2,0
@@ -692,9 +726,31 @@
 	mov loc7=0
 (pReturn) br.ret.sptk.many b6
 }
+#else /* !CONFIG_ITANIUM */
+	alloc loc0=ar.pfs,2,Nregs-2,2,0
+	cmp.lt pRecurse,p0=Nregs*8,in0	// if more than Nregs regs left to clear, (re)curse
+	add out0=-Nregs*8,in0
+	add out1=1,in1			// increment recursion count
+	mov loc1=0
+	mov loc2=0
+	;;
+	mov loc3=0
+	mov loc4=0
+	mov loc9=0
+	mov loc5=0
+	mov loc6=0
+(pRecurse) br.call.sptk.many b6=rse_clear_invalid
+	;;
+	mov loc7=0
+	mov loc8=0
+	cmp.ne pReturn,p0=r0,in1	// if recursion count != 0, we need to do a br.ret
+	mov loc10=0
+	mov loc11=0
+(pReturn) br.ret.sptk.many b6
+#endif /* !CONFIG_ITANIUM */
 #	undef pRecurse
 #	undef pReturn
-
+	;;
 	alloc r17=ar.pfs,0,0,0,0	// drop current register frame
 	;;
 	loadrs
@@ -1087,7 +1143,11 @@
 	data8 sys_sched_get_priority_min
 	data8 sys_sched_rr_get_interval
 	data8 sys_nanosleep
+#if defined(CONFIG_NFSD) || defined(CONFIG_NFSD_MODULE)
 	data8 sys_nfsservctl
+#else
+	data8 sys_ni_syscall
+#endif
 	data8 sys_prctl				// 1170
 	data8 sys_getpagesize
 	data8 sys_mmap2
@@ -1147,14 +1207,10 @@
 	data8 sys_removexattr
 	data8 sys_lremovexattr
 	data8 sys_fremovexattr
-#if 0
 	data8 sys_tkill
-#else
-	data8 ia64_ni_syscall
-#endif
-	data8 ia64_ni_syscall			// 1230
-	data8 ia64_ni_syscall
-	data8 ia64_ni_syscall
+	data8 sys_futex				// 1230
+	data8 sys_sched_setaffinity
+	data8 sys_sched_getaffinity
 	data8 ia64_ni_syscall
 	data8 ia64_ni_syscall
 	data8 ia64_ni_syscall			// 1235
diff -Nru a/arch/ia64/kernel/head.S b/arch/ia64/kernel/head.S
--- a/arch/ia64/kernel/head.S	Mon Apr 22 15:32:19 2002
+++ b/arch/ia64/kernel/head.S	Mon Apr 22 15:32:19 2002
@@ -560,137 +560,114 @@
 END(__ia64_load_fpu)
 
 GLOBAL_ENTRY(__ia64_init_fpu)
-	alloc r2=ar.pfs,0,0,0,0
-	stf.spill [sp]=f0
-	mov      f32=f0
-	;;
-	ldf.fill f33=[sp]
-	ldf.fill f34=[sp]
-	mov      f35=f0
-	;;
-	ldf.fill f36=[sp]
-	ldf.fill f37=[sp]
-	mov      f38=f0
-	;;
-	ldf.fill f39=[sp]
-	ldf.fill f40=[sp]
-	mov      f41=f0
-	;;
-	ldf.fill f42=[sp]
-	ldf.fill f43=[sp]
-	mov      f44=f0
-	;;
-	ldf.fill f45=[sp]
-	ldf.fill f46=[sp]
-	mov      f47=f0
-	;;
-	ldf.fill f48=[sp]
-	ldf.fill f49=[sp]
-	mov      f50=f0
-	;;
-	ldf.fill f51=[sp]
-	ldf.fill f52=[sp]
-	mov      f53=f0
-	;;
-	ldf.fill f54=[sp]
-	ldf.fill f55=[sp]
-	mov      f56=f0
-	;;
-	ldf.fill f57=[sp]
-	ldf.fill f58=[sp]
-	mov      f59=f0
-	;;
-	ldf.fill f60=[sp]
-	ldf.fill f61=[sp]
-	mov      f62=f0
-	;;
-	ldf.fill f63=[sp]
-	ldf.fill f64=[sp]
-	mov      f65=f0
-	;;
-	ldf.fill f66=[sp]
-	ldf.fill f67=[sp]
-	mov      f68=f0
-	;;
-	ldf.fill f69=[sp]
-	ldf.fill f70=[sp]
-	mov      f71=f0
-	;;
-	ldf.fill f72=[sp]
-	ldf.fill f73=[sp]
-	mov      f74=f0
-	;;
-	ldf.fill f75=[sp]
-	ldf.fill f76=[sp]
-	mov      f77=f0
-	;;
-	ldf.fill f78=[sp]
-	ldf.fill f79=[sp]
-	mov      f80=f0
-	;;
-	ldf.fill f81=[sp]
-	ldf.fill f82=[sp]
-	mov      f83=f0
-	;;
-	ldf.fill f84=[sp]
-	ldf.fill f85=[sp]
-	mov      f86=f0
-	;;
-	ldf.fill f87=[sp]
-	ldf.fill f88=[sp]
-	mov      f89=f0
-	;;
-	ldf.fill f90=[sp]
-	ldf.fill f91=[sp]
-	mov      f92=f0
-	;;
-	ldf.fill f93=[sp]
-	ldf.fill f94=[sp]
-	mov      f95=f0
-	;;
-	ldf.fill f96=[sp]
-	ldf.fill f97=[sp]
-	mov      f98=f0
-	;;
-	ldf.fill f99=[sp]
-	ldf.fill f100=[sp]
-	mov      f101=f0
-	;;
-	ldf.fill f102=[sp]
-	ldf.fill f103=[sp]
-	mov      f104=f0
-	;;
-	ldf.fill f105=[sp]
-	ldf.fill f106=[sp]
-	mov      f107=f0
-	;;
-	ldf.fill f108=[sp]
-	ldf.fill f109=[sp]
-	mov      f110=f0
-	;;
-	ldf.fill f111=[sp]
-	ldf.fill f112=[sp]
-	mov      f113=f0
-	;;
-	ldf.fill f114=[sp]
-	ldf.fill f115=[sp]
-	mov      f116=f0
-	;;
-	ldf.fill f117=[sp]
-	ldf.fill f118=[sp]
-	mov      f119=f0
-	;;
-	ldf.fill f120=[sp]
-	ldf.fill f121=[sp]
-	mov      f122=f0
-	;;
-	ldf.fill f123=[sp]
-	ldf.fill f124=[sp]
-	mov      f125=f0
-	;;
-	ldf.fill f126=[sp]
-	mov      f127=f0
-	br.ret.sptk.many rp
+	stf.spill [sp]=f0		// M3
+	mov	 f32=f0			// F
+	nop.b	 0
+
+	ldfps	 f33,f34=[sp]		// M0
+	ldfps	 f35,f36=[sp]		// M1
+	mov      f37=f0			// F
+	;;
+
+	setf.s	 f38=r0			// M2
+	setf.s	 f39=r0			// M3
+	mov      f40=f0			// F
+
+	ldfps	 f41,f42=[sp]		// M0
+	ldfps	 f43,f44=[sp]		// M1
+	mov      f45=f0			// F
+
+	setf.s	 f46=r0			// M2
+	setf.s	 f47=r0			// M3
+	mov      f48=f0			// F
+
+	ldfps	 f49,f50=[sp]		// M0
+	ldfps	 f51,f52=[sp]		// M1
+	mov      f53=f0			// F
+
+	setf.s	 f54=r0			// M2
+	setf.s	 f55=r0			// M3
+	mov      f56=f0			// F
+
+	ldfps	 f57,f58=[sp]		// M0
+	ldfps	 f59,f60=[sp]		// M1
+	mov      f61=f0			// F
+
+	setf.s	 f62=r0			// M2
+	setf.s	 f63=r0			// M3
+	mov      f64=f0			// F
+
+	ldfps	 f65,f66=[sp]		// M0
+	ldfps	 f67,f68=[sp]		// M1
+	mov      f69=f0			// F
+
+	setf.s	 f70=r0			// M2
+	setf.s	 f71=r0			// M3
+	mov      f72=f0			// F
+
+	ldfps	 f73,f74=[sp]		// M0
+	ldfps	 f75,f76=[sp]		// M1
+	mov      f77=f0			// F
+
+	setf.s	 f78=r0			// M2
+	setf.s	 f79=r0			// M3
+	mov      f80=f0			// F
+
+	ldfps	 f81,f82=[sp]		// M0
+	ldfps	 f83,f84=[sp]		// M1
+	mov      f85=f0			// F
+
+	setf.s	 f86=r0			// M2
+	setf.s	 f87=r0			// M3
+	mov      f88=f0			// F
+
+	/*
+	 * When the instructions are cached, it would be faster to initialize
+	 * the remaining registers with simply mov instructions (F-unit).
+	 * This gets the time down to ~29 cycles.  However, this would use up
+	 * 33 bundles, whereas continuing with the above pattern yields
+	 * 10 bundles and ~30 cycles.
+	 */
+
+	ldfps	 f89,f90=[sp]		// M0
+	ldfps	 f91,f92=[sp]		// M1
+	mov      f93=f0			// F
+
+	setf.s	 f94=r0			// M2
+	setf.s	 f95=r0			// M3
+	mov      f96=f0			// F
+
+	ldfps	 f97,f98=[sp]		// M0
+	ldfps	 f99,f100=[sp]		// M1
+	mov      f101=f0		// F
+
+	setf.s	 f102=r0		// M2
+	setf.s	 f103=r0		// M3
+	mov      f104=f0		// F
+
+	ldfps	 f105,f106=[sp]		// M0
+	ldfps	 f107,f108=[sp]		// M1
+	mov      f109=f0		// F
+
+	setf.s	 f110=r0		// M2
+	setf.s	 f111=r0		// M3
+	mov      f112=f0		// F
+
+	ldfps	 f113,f114=[sp]		// M0
+	ldfps	 f115,f116=[sp]		// M1
+	mov      f117=f0		// F
+
+	setf.s	 f118=r0		// M2
+	setf.s	 f119=r0		// M3
+	mov      f120=f0		// F
+
+	ldfps	 f121,f122=[sp]		// M0
+	ldfps	 f123,f124=[sp]		// M1
+	mov      f125=f0		// F
+
+	setf.s	 f126=r0		// M2
+	setf.s	 f127=r0		// M3
+	br.ret.sptk.many rp		// F
 END(__ia64_init_fpu)
 
 /*
diff -Nru a/arch/ia64/kernel/ia64_ksyms.c b/arch/ia64/kernel/ia64_ksyms.c
--- a/arch/ia64/kernel/ia64_ksyms.c	Mon Apr 22 15:32:19 2002
+++ b/arch/ia64/kernel/ia64_ksyms.c	Mon Apr 22 15:32:19 2002
@@ -6,7 +6,11 @@
 #include <linux/module.h>
 
 #include <linux/string.h>
-EXPORT_SYMBOL_NOVERS(memset);
+#undef memset
+extern void *memset (void *, int, size_t);
+EXPORT_SYMBOL_NOVERS(memset);			/* gcc generates direct calls to memset()... */
+EXPORT_SYMBOL_NOVERS(__memset_generic);
+EXPORT_SYMBOL_NOVERS(__bzero);
 EXPORT_SYMBOL(memchr);
 EXPORT_SYMBOL(memcmp);
 EXPORT_SYMBOL_NOVERS(memcpy);
@@ -67,7 +71,7 @@
 EXPORT_SYMBOL(last_cli_ip);
 #endif
 
-#include <asm/pgalloc.h>
+#include <asm/tlbflush.h>
 
 EXPORT_SYMBOL(flush_tlb_range);
 
@@ -147,3 +151,10 @@
 #include <linux/proc_fs.h>
 extern struct proc_dir_entry *efi_dir;
 EXPORT_SYMBOL(efi_dir);
+
+#include <asm/machvec.h>
+#ifdef CONFIG_IA64_GENERIC
+EXPORT_SYMBOL(ia64_mv);
+#endif
+EXPORT_SYMBOL(machvec_noop);
+
diff -Nru a/arch/ia64/kernel/iosapic.c b/arch/ia64/kernel/iosapic.c
--- a/arch/ia64/kernel/iosapic.c	Mon Apr 22 15:32:19 2002
+++ b/arch/ia64/kernel/iosapic.c	Mon Apr 22 15:32:19 2002
@@ -22,6 +22,7 @@
  * 02/01/07     E. Focht        <efocht@ess.nec.de> Redirectable interrupt vectors in
  *                              iosapic_set_affinity(), initializations for
  *                              /proc/irq/#/smp_affinity
+ * 02/04/02	P. Diefenbaugh	Cleaned up ACPI PCI IRQ routing.
  */
 /*
  * Here is what the interrupt logic between a PCI device and the CPU looks like:
@@ -56,9 +57,8 @@
 #include <linux/smp_lock.h>
 #include <linux/string.h>
 #include <linux/irq.h>
+#include <linux/acpi.h>
 
-#include <asm/acpi-ext.h>
-#include <asm/acpikcfg.h>
 #include <asm/delay.h>
 #include <asm/hw_irq.h>
 #include <asm/io.h>
@@ -92,11 +92,37 @@
 	unsigned char trigger	: 1;	/* trigger mode (see iosapic.h) */
 } iosapic_irq[IA64_NUM_VECTORS];
 
+static struct iosapic {
+	char *addr;			/* base address of IOSAPIC */
+	unsigned char	pcat_compat;	/* 8259 compatibility flag */
+	unsigned char 	base_irq;	/* first irq assigned to this IOSAPIC */
+	unsigned short 	max_pin;	/* max input pin supported in this IOSAPIC */
+} iosapic_lists[256] __initdata;
+
+static int num_iosapic = 0;
+
+
+/*
+ * Find an IOSAPIC associated with an IRQ
+ */
+static inline int __init
+find_iosapic (unsigned int irq)
+{
+	int i;
+
+	for (i = 0; i < num_iosapic; i++) {
+		if ((irq - iosapic_lists[i].base_irq) < iosapic_lists[i].max_pin)
+			return i;
+	}
+
+	return -1;
+}
+
 /*
  * Translate IOSAPIC irq number to the corresponding IA-64 interrupt vector.  If no
  * entry exists, return -1.
  */
-int
+static int
 iosapic_irq_to_vector (int irq)
 {
 	int vector;
@@ -479,7 +505,7 @@
 	int vector;
 
 	switch (int_type) {
-	case ACPI20_ENTRY_PIS_PMI:
+	      case ACPI_INTERRUPT_PMI:
 		vector = iosapic_vector;
 		/*
 		 * since PMI vector is alloc'd by FW(ACPI) not by kernel,
@@ -488,15 +514,15 @@
 		iosapic_reassign_vector(vector);
 		delivery = IOSAPIC_PMI;
 		break;
-	case ACPI20_ENTRY_PIS_CPEI:
-		vector = IA64_PCE_VECTOR;
-		delivery = IOSAPIC_LOWEST_PRIORITY;
-		break;
-	case ACPI20_ENTRY_PIS_INIT:
+	      case ACPI_INTERRUPT_INIT:
 		vector = ia64_alloc_irq();
 		delivery = IOSAPIC_INIT;
 		break;
-	default:
+	      case ACPI_INTERRUPT_CPEI:
+		vector = IA64_PCE_VECTOR;
+		delivery = IOSAPIC_LOWEST_PRIORITY;
+		break;
+	      default:
 		printk("iosapic_register_platform_irq(): invalid int type\n");
 		return -1;
 	}
@@ -542,31 +568,41 @@
 void __init
 iosapic_init (unsigned long phys_addr, unsigned int base_irq, int pcat_compat)
 {
-	int i, irq, max_pin, vector, pin;
+	int irq, max_pin, vector, pin;
 	unsigned int ver;
 	char *addr;
 	static int first_time = 1;
 
 	if (first_time) {
 		first_time = 0;
-
 		for (vector = 0; vector < IA64_NUM_VECTORS; ++vector)
 			iosapic_irq[vector].pin = -1;	/* mark as unused */
+	}
 
+	if (pcat_compat) {
 		/*
-		 * Fetch the PCI interrupt routing table:
+		 * Disable the compatibility mode interrupts (8259 style), needs IN/OUT support
+		 * enabled.
 		 */
-		acpi_cf_get_pci_vectors(&pci_irq.route, &pci_irq.num_routes);
+		printk("%s: Disabling PC-AT compatible 8259 interrupts\n", __FUNCTION__);
+		outb(0xff, 0xA1);
+		outb(0xff, 0x21);
 	}
 
 	addr = ioremap(phys_addr, 0);
 	ver = iosapic_version(addr);
 	max_pin = (ver >> 16) & 0xff;
 
+	iosapic_lists[num_iosapic].addr = addr;
+	iosapic_lists[num_iosapic].pcat_compat = pcat_compat;
+	iosapic_lists[num_iosapic].base_irq = base_irq;
+	iosapic_lists[num_iosapic].max_pin = max_pin;
+	num_iosapic++;
+
 	printk("IOSAPIC: version %x.%x, address 0x%lx, IRQs 0x%02x-0x%02x\n",
 	       (ver & 0xf0) >> 4, (ver & 0x0f), phys_addr, base_irq, base_irq + max_pin);
 
-	if ((base_irq == 0) && pcat_compat)
+	if ((base_irq == 0) && pcat_compat) {
 		/*
 		 * Map the legacy ISA devices into the IOSAPIC data.  Some of these may
 		 * get reprogrammed later on with data from the ACPI Interrupt Source
@@ -590,11 +626,37 @@
 			/* program the IOSAPIC routing table: */
 			set_rte(vector, (ia64_get_lid() >> 16) & 0xffff);
 		}
+	}
+}
+
+void __init
+iosapic_init_pci_irq (void)
+{
+	int i, index, vector, pin;
+	int base_irq, max_pin, pcat_compat;
+	unsigned int irq;
+	char *addr;
+
+	if (0 != acpi_get_prt(&pci_irq.route, &pci_irq.num_routes))
+		return;
 
 	for (i = 0; i < pci_irq.num_routes; i++) {
+
 		irq = pci_irq.route[i].irq;
 
-		if ((irq < (int)base_irq) || (irq > (int)(base_irq + max_pin)))
+		index = find_iosapic(irq);
+		if (index < 0) {
+			printk("PCI: IRQ %u has no IOSAPIC mapping\n", irq);
+			continue;
+		}
+
+		addr = iosapic_lists[index].addr;
+		base_irq = iosapic_lists[index].base_irq;
+		max_pin = iosapic_lists[index].max_pin;
+		pcat_compat = iosapic_lists[index].pcat_compat;
+		pin = irq - base_irq;
+
+		if ((unsigned) pin > max_pin)
 			/* the interrupt route is for another controller... */
 			continue;
 
@@ -607,18 +669,13 @@
 				vector = ia64_alloc_irq();
 		}
 
-		register_irq(irq, vector, irq - base_irq,
-			     /* IOSAPIC_POL_LOW, IOSAPIC_LEVEL */
-			     IOSAPIC_LOWEST_PRIORITY, 0, 0, base_irq, addr);
+		register_irq(irq, vector, pin, IOSAPIC_LOWEST_PRIORITY, 0, 0, base_irq, addr);
 
-# ifdef DEBUG_IRQ_ROUTING
+#ifdef DEBUG_IRQ_ROUTING
 		printk("PCI: (B%d,I%d,P%d) -> IOSAPIC irq 0x%02x -> vector 0x%02x\n",
 		       pci_irq.route[i].bus, pci_irq.route[i].pci_id>>16, pci_irq.route[i].pin,
 		       iosapic_irq[vector].base_irq + iosapic_irq[vector].pin, vector);
-# endif
-
-			/* program the IOSAPIC routing table: */
-			set_rte(vector, (ia64_get_lid() >> 16) & 0xffff);
+#endif
 	}
 }
 
@@ -631,6 +688,11 @@
 	struct hw_interrupt_type *irq_type;
 	irq_desc_t *idesc;
 
+	if (phase == 0) {
+		iosapic_init_pci_irq();
+		return;
+	}
+
 	if (phase != 1)
 		return;
 
@@ -670,10 +732,10 @@
 
 				irq_type = &irq_type_iosapic_level;
 				idesc = irq_desc(vector);
-				if (idesc->handler != irq_type){
+				if (idesc->handler != irq_type) {
 					if (idesc->handler != &no_irq_type)
-						printk("iosapic_pci_fixup: changing vector 0x%02x from "
-						       "%s to %s\n", vector,
+						printk("iosapic_pci_fixup: changing vector 0x%02x "
+						       "from %s to %s\n", vector,
 						       idesc->handler->typename,
 						       irq_type->typename);
 					idesc->handler = irq_type;
diff -Nru a/arch/ia64/kernel/irq.c b/arch/ia64/kernel/irq.c
--- a/arch/ia64/kernel/irq.c	Mon Apr 22 15:32:19 2002
+++ b/arch/ia64/kernel/irq.c	Mon Apr 22 15:32:19 2002
@@ -68,6 +68,23 @@
 irq_desc_t _irq_desc[NR_IRQS] __cacheline_aligned =
 	{ [0 ... NR_IRQS-1] = { IRQ_DISABLED, &no_irq_type, NULL, 0, SPIN_LOCK_UNLOCKED}};
 
+#ifdef CONFIG_IA64_GENERIC
+struct irq_desc * __ia64_irq_desc (unsigned int irq)
+{
+	return _irq_desc + irq;
+}
+
+ia64_vector __ia64_irq_to_vector (unsigned int irq)
+{
+	return (ia64_vector) irq;
+}
+
+unsigned int __ia64_local_vector_to_irq (ia64_vector vec)
+{
+	return (unsigned int) vec;
+}
+#endif
+
 static void register_irq_proc (unsigned int irq);
 
 /*
diff -Nru a/arch/ia64/kernel/ivt.S b/arch/ia64/kernel/ivt.S
--- a/arch/ia64/kernel/ivt.S	Mon Apr 22 15:32:18 2002
+++ b/arch/ia64/kernel/ivt.S	Mon Apr 22 15:32:18 2002
@@ -127,7 +127,7 @@
 	;;
 (p7)	dep r17=r17,r19,(PAGE_SHIFT-3),3	// put region number bits in place
 	srlz.d					// ensure "rsm psr.dt" has taken effect
-(p6)	movl r19=__pa(SWAPPER_PGD_ADDR)		// region 5 is rooted at swapper_pg_dir
+(p6)	movl r19=__pa(swapper_pg_dir)		// region 5 is rooted at swapper_pg_dir
 (p6)	shr.u r21=r21,PGDIR_SHIFT+PAGE_SHIFT
 (p7)	shr.u r21=r21,PGDIR_SHIFT+PAGE_SHIFT-3
 	;;
@@ -413,7 +413,7 @@
 	;;
 (p7)	dep r17=r17,r19,(PAGE_SHIFT-3),3	// put region number bits in place
 	srlz.d
-(p6)	movl r19=__pa(SWAPPER_PGD_ADDR)		// region 5 is rooted at swapper_pg_dir
+(p6)	movl r19=__pa(swapper_pg_dir)		// region 5 is rooted at swapper_pg_dir
 (p6)	shr.u r21=r21,PGDIR_SHIFT+PAGE_SHIFT
 (p7)	shr.u r21=r21,PGDIR_SHIFT+PAGE_SHIFT-3
 	;;
diff -Nru a/arch/ia64/kernel/mca.c b/arch/ia64/kernel/mca.c
--- a/arch/ia64/kernel/mca.c	Mon Apr 22 15:32:19 2002
+++ b/arch/ia64/kernel/mca.c	Mon Apr 22 15:32:19 2002
@@ -41,6 +41,7 @@
 #include <linux/irq.h>
 #include <linux/smp_lock.h>
 #include <linux/bootmem.h>
+#include <linux/acpi.h>
 
 #include <asm/machvec.h>
 #include <asm/page.h>
@@ -51,7 +52,6 @@
 
 #include <asm/irq.h>
 #include <asm/hw_irq.h>
-#include <asm/acpi-ext.h>
 
 #undef MCA_PRT_XTRA_DATA
 
@@ -353,7 +353,9 @@
 verify_guid (efi_guid_t *test, efi_guid_t *target)
 {
 	int     rc;
+#ifdef IA64_MCA_DEBUG_INFO
 	char out[40];
+#endif
 
 	if ((rc = efi_guidcmp(*test, *target))) {
 		IA64_MCA_DEBUG(KERN_DEBUG
@@ -497,7 +499,7 @@
 	{
 		irq_desc_t *desc;
 		unsigned int irq;
-		int cpev = acpi_request_vector(ACPI20_ENTRY_PIS_CPEI);
+		int cpev = acpi_request_vector(ACPI_INTERRUPT_CPEI);
 
 		if (cpev >= 0) {
 			for (irq = 0; irq < NR_IRQS; ++irq)
diff -Nru a/arch/ia64/kernel/minstate.h b/arch/ia64/kernel/minstate.h
--- a/arch/ia64/kernel/minstate.h	Mon Apr 22 15:32:19 2002
+++ b/arch/ia64/kernel/minstate.h	Mon Apr 22 15:32:19 2002
@@ -1,5 +1,7 @@
 #include <linux/config.h>
 
+#include <asm/cache.h>
+
 #include "entry.h"
 
 /*
@@ -28,18 +30,19 @@
  * on interrupts.
  */
 #define MINSTATE_START_SAVE_MIN_VIRT								\
-	dep r1=-1,r1,61,3;				/* r1 = current (virtual) */		\
 (pUser)	mov ar.rsc=0;		/* set enforced lazy mode, pl 0, little-endian, loadrs=0 */	\
+	dep r1=-1,r1,61,3;				/* r1 = current (virtual) */		\
 	;;											\
+(pUser)	mov.m rARRNAT=ar.rnat;									\
 (pUser)	addl rKRBS=IA64_RBS_OFFSET,r1;			/* compute base of RBS */		\
-(pUser)	mov rARRNAT=ar.rnat;									\
 (pKern) mov r1=sp;					/* get sp  */				\
 	;;											\
-(pUser)	addl r1=IA64_STK_OFFSET-IA64_PT_REGS_SIZE,r1;	/* compute base of memory stack */	\
+(pUser) lfetch.fault.excl.nt1 [rKRBS];								\
 (pUser)	mov rARBSPSTORE=ar.bspstore;			/* save ar.bspstore */			\
+(pUser)	addl r1=IA64_STK_OFFSET-IA64_PT_REGS_SIZE,r1;	/* compute base of memory stack */	\
 	;;											\
-(pKern) addl r1=-IA64_PT_REGS_SIZE,r1;			/* if in kernel mode, use sp (r12) */	\
 (pUser)	mov ar.bspstore=rKRBS;				/* switch to kernel RBS */		\
+(pKern) addl r1=-IA64_PT_REGS_SIZE,r1;			/* if in kernel mode, use sp (r12) */	\
 	;;											\
 (pUser)	mov r18=ar.bsp;										\
 (pUser)	mov ar.rsc=0x3;		/* set eager mode, pl 0, little-endian, loadrs=0 */		\
@@ -125,52 +128,58 @@
 	;;											  \
 	SAVE_IFS;										  \
 	MINSTATE_START_SAVE_MIN									  \
+	add r17=L1_CACHE_BYTES,r1			/* really: biggest cache-line size */	  \
 	;;											  \
-	mov r16=r1;					/* initialize first base pointer */	  \
-	adds r17=8,r1;					/* initialize second base pointer */	  \
+	st8 [r1]=rCRIPSR;	/* save cr.ipsr */						  \
+	lfetch.fault.excl.nt1 [r17],L1_CACHE_BYTES;						  \
+	add r16=16,r1;					/* initialize first base pointer */	  \
 	;;											  \
-	st8 [r16]=rCRIPSR,16;	/* save cr.ipsr */						  \
-	st8 [r17]=rCRIIP,16;	/* save cr.iip */						  \
+	lfetch.fault.excl.nt1 [r17],L1_CACHE_BYTES;						  \
+	;;											  \
+	lfetch.fault.excl.nt1 [r17];								  \
+	adds r17=8,r1;					/* initialize second base pointer */	  \
 (pKern)	mov r18=r0;		/* make sure r18 isn't NaT */					  \
 	;;											  \
+	st8 [r17]=rCRIIP,16;	/* save cr.iip */						  \
 	st8 [r16]=rCRIFS,16;	/* save cr.ifs */						  \
-	st8 [r17]=rARUNAT,16;	/* save ar.unat */						  \
 (pUser)	sub r18=r18,rKRBS;	/* r18=RSE.ndirty*8 */						  \
 	;;											  \
+	st8 [r17]=rARUNAT,16;	/* save ar.unat */						  \
 	st8 [r16]=rARPFS,16;	/* save ar.pfs */						  \
+	shl r18=r18,16;		/* compute ar.rsc to be used for "loadrs" */			  \
+	;;											  \
 	st8 [r17]=rARRSC,16;	/* save ar.rsc */						  \
-	tbit.nz p15,p0=rCRIPSR,IA64_PSR_I_BIT							  \
-	;;			/* avoid RAW on r16 & r17 */					  \
-(pKern)	adds r16=16,r16;	/* skip over ar_rnat field */					  \
-(pKern)	adds r17=16,r17;	/* skip over ar_bspstore field */				  \
 (pUser)	st8 [r16]=rARRNAT,16;	/* save ar.rnat */						  \
+(pKern)	adds r16=16,r16;	/* skip over ar_rnat field */					  \
+	;;			/* avoid RAW on r16 & r17 */					  \
 (pUser)	st8 [r17]=rARBSPSTORE,16;	/* save ar.bspstore */					  \
-	;;											  \
 	st8 [r16]=rARPR,16;	/* save predicates */						  \
-	st8 [r17]=rB6,16;	/* save b6 */							  \
-	shl r18=r18,16;		/* compute ar.rsc to be used for "loadrs" */			  \
+(pKern)	adds r17=16,r17;	/* skip over ar_bspstore field */				  \
 	;;											  \
+	st8 [r17]=rB6,16;	/* save b6 */							  \
 	st8 [r16]=r18,16;	/* save ar.rsc value for "loadrs" */				  \
-	st8.spill [r17]=rR1,16;	/* save original r1 */						  \
+	tbit.nz p15,p0=rCRIPSR,IA64_PSR_I_BIT							  \
 	;;											  \
+.mem.offset 8,0;	st8.spill [r17]=rR1,16;	/* save original r1 */				  \
 .mem.offset 0,0;	st8.spill [r16]=r2,16;							  \
+	;;											  \
 .mem.offset 8,0;	st8.spill [r17]=r3,16;							  \
+.mem.offset 0,0;	st8.spill [r16]=r12,16;							  \
 	adds r2=IA64_PT_REGS_R16_OFFSET,r1;							  \
 	;;											  \
-.mem.offset 0,0;		st8.spill [r16]=r12,16;						  \
-.mem.offset 8,0;		st8.spill [r17]=r13,16;						  \
+.mem.offset 8,0;	st8.spill [r17]=r13,16;							  \
+.mem.offset 0,0;	st8.spill [r16]=r14,16;							  \
 	cmp.eq pNonSys,pSys=r0,r0	/* initialize pSys=0, pNonSys=1 */			  \
 	;;											  \
-.mem.offset 0,0;		st8.spill [r16]=r14,16;						  \
-.mem.offset 8,0;		st8.spill [r17]=r15,16;						  \
+.mem.offset 8,0;	st8.spill [r17]=r15,16;							  \
+.mem.offset 0,0;	st8.spill [r16]=r8,16;							  \
 	dep r14=-1,r0,61,3;									  \
 	;;											  \
-.mem.offset 0,0;		st8.spill [r16]=r8,16;						  \
-.mem.offset 8,0;		st8.spill [r17]=r9,16;						  \
+.mem.offset 8,0;	st8.spill [r17]=r9,16;							  \
+.mem.offset 0,0;	st8.spill [r16]=r10,16;							  \
 	adds r12=-16,r1;	/* switch to kernel memory stack (with 16 bytes of scratch) */	  \
 	;;											  \
-.mem.offset 0,0;		st8.spill [r16]=r10,16;						  \
-.mem.offset 8,0;		st8.spill [r17]=r11,16;						  \
+.mem.offset 8,0;	st8.spill [r17]=r11,16;							  \
 	mov r13=IA64_KR(CURRENT);	/* establish `current' */				  \
 	;;											  \
 	EXTRA;											  \
@@ -190,10 +199,12 @@
  */
 #define SAVE_REST				\
 .mem.offset 0,0;	st8.spill [r2]=r16,16;	\
-.mem.offset 8,0;	st8.spill [r3]=r17,16;	\
 	;;					\
+.mem.offset 8,0;	st8.spill [r3]=r17,16;	\
 .mem.offset 0,0;	st8.spill [r2]=r18,16;	\
+	;;					\
 .mem.offset 8,0;	st8.spill [r3]=r19,16;	\
+.mem.offset 0,0;	st8.spill [r2]=r20,16;	\
 	;;					\
 	mov r16=ar.ccv;		/* M-unit */	\
 	movl r18=FPSR_DEFAULT	/* L-unit */	\
@@ -201,30 +212,29 @@
 	mov r17=ar.fpsr;	/* M-unit */	\
 	mov ar.fpsr=r18;	/* M-unit */	\
 	;;					\
-.mem.offset 0,0;	st8.spill [r2]=r20,16;	\
 .mem.offset 8,0;	st8.spill [r3]=r21,16;	\
+.mem.offset 0,0;	st8.spill [r2]=r22,16;	\
 	mov r18=b0;				\
 	;;					\
-.mem.offset 0,0;	st8.spill [r2]=r22,16;	\
 .mem.offset 8,0;	st8.spill [r3]=r23,16;	\
+.mem.offset 0,0;	st8.spill [r2]=r24,16;	\
 	mov r19=b7;				\
 	;;					\
-.mem.offset 0,0;	st8.spill [r2]=r24,16;	\
 .mem.offset 8,0;	st8.spill [r3]=r25,16;	\
-	;;					\
 .mem.offset 0,0;	st8.spill [r2]=r26,16;	\
-.mem.offset 8,0;	st8.spill [r3]=r27,16;	\
 	;;					\
+.mem.offset 8,0;	st8.spill [r3]=r27,16;	\
 .mem.offset 0,0;	st8.spill [r2]=r28,16;	\
-.mem.offset 8,0;	st8.spill [r3]=r29,16;	\
 	;;					\
+.mem.offset 8,0;	st8.spill [r3]=r29,16;	\
 .mem.offset 0,0;	st8.spill [r2]=r30,16;	\
-.mem.offset 8,0;	st8.spill [r3]=r31,16;	\
 	;;					\
+.mem.offset 8,0;	st8.spill [r3]=r31,16;	\
 	st8 [r2]=r16,16;	/* ar.ccv */	\
-	st8 [r3]=r17,16;	/* ar.fpsr */	\
 	;;					\
+	st8 [r3]=r17,16;	/* ar.fpsr */	\
 	st8 [r2]=r18,16;	/* b0 */	\
+	;;					\
 	st8 [r3]=r19,16+8;	/* b7 */	\
 	;;					\
 	stf.spill [r2]=f6,32;			\
diff -Nru a/arch/ia64/kernel/pci.c b/arch/ia64/kernel/pci.c
--- a/arch/ia64/kernel/pci.c	Mon Apr 22 15:32:19 2002
+++ b/arch/ia64/kernel/pci.c	Mon Apr 22 15:32:19 2002
@@ -42,101 +42,183 @@
 extern void ia64_mca_check_errors( void );
 #endif
 
+struct pci_fixup pcibios_fixups[1];
+
+struct pci_ops *pci_root_ops;
+
+int (*pci_config_read)(int seg, int bus, int dev, int fn, int reg, int len, u32 *value);
+int (*pci_config_write)(int seg, int bus, int dev, int fn, int reg, int len, u32 value);
+
+
 /*
- * This interrupt-safe spinlock protects all accesses to PCI
- * configuration space.
+ * Low-level SAL-based PCI configuration access functions. Note that SAL
+ * calls are already serialized (via sal_lock), so we don't need another
+ * synchronization mechanism here.  Not using segment number (yet).
  */
-static spinlock_t pci_lock = SPIN_LOCK_UNLOCKED;
 
-struct pci_fixup pcibios_fixups[] = {
-	{ 0 }
-};
+#define PCI_SAL_ADDRESS(bus, dev, fn, reg) \
+	((u64)(bus << 16) | (u64)(dev << 11) | (u64)(fn << 8) | (u64)(reg))
 
-/* Macro to build a PCI configuration address to be passed as a parameter to SAL. */
+static int
+pci_sal_read (int seg, int bus, int dev, int fn, int reg, int len, u32 *value)
+{
+	int result = 0;
+	u64 data = 0;
 
-#define PCI_CONFIG_ADDRESS(dev, where) \
-	(((u64) dev->bus->number << 16) | ((u64) (dev->devfn & 0xff) << 8) | (where & 0xff))
+	if (!value || (bus > 255) || (dev > 31) || (fn > 7) || (reg > 255))
+		return -EINVAL;
+
+	result = ia64_sal_pci_config_read(PCI_SAL_ADDRESS(bus, dev, fn, reg), len, &data);
+
+	*value = (u32) data;
+
+	return result;
+}
 
 static int
-pci_conf_read_config_byte(struct pci_dev *dev, int where, u8 *value)
+pci_sal_write (int seg, int bus, int dev, int fn, int reg, int len, u32 value)
 {
-	s64 status;
-	u64 lval;
+	if ((bus > 255) || (dev > 31) || (fn > 7) || (reg > 255))
+		return -EINVAL;
 
-	status = ia64_sal_pci_config_read(PCI_CONFIG_ADDRESS(dev, where), 1, &lval);
-	*value = lval;
-	return status;
+	return ia64_sal_pci_config_write(PCI_SAL_ADDRESS(bus, dev, fn, reg), len, value);
 }
 
+
 static int
-pci_conf_read_config_word(struct pci_dev *dev, int where, u16 *value)
+pci_sal_read_config_byte (struct pci_dev *dev, int where, u8 *value)
 {
-	s64 status;
-	u64 lval;
+	int result = 0;
+	u32 data = 0;
+
+	if (!value)
+		return -EINVAL;
 
-	status = ia64_sal_pci_config_read(PCI_CONFIG_ADDRESS(dev, where), 2, &lval);
-	*value = lval;
-	return status;
+	result = pci_sal_read(0, dev->bus->number, PCI_SLOT(dev->devfn),
+			      PCI_FUNC(dev->devfn), where, 1, &data);
+
+	*value = (u8) data;
+
+	return result;
 }
 
 static int
-pci_conf_read_config_dword(struct pci_dev *dev, int where, u32 *value)
+pci_sal_read_config_word (struct pci_dev *dev, int where, u16 *value)
 {
-	s64 status;
-	u64 lval;
+	int result = 0;
+	u32 data = 0;
+
+	if (!value)
+		return -EINVAL;
+
+	result = pci_sal_read(0, dev->bus->number, PCI_SLOT(dev->devfn),
+			      PCI_FUNC(dev->devfn), where, 2, &data);
+
+	*value = (u16) data;
 
-	status = ia64_sal_pci_config_read(PCI_CONFIG_ADDRESS(dev, where), 4, &lval);
-	*value = lval;
-	return status;
+	return result;
 }
 
 static int
-pci_conf_write_config_byte (struct pci_dev *dev, int where, u8 value)
+pci_sal_read_config_dword (struct pci_dev *dev, int where, u32 *value)
 {
-	return ia64_sal_pci_config_write(PCI_CONFIG_ADDRESS(dev, where), 1, value);
+	if (!value)
+		return -EINVAL;
+
+	return pci_sal_read(0, dev->bus->number, PCI_SLOT(dev->devfn),
+			    PCI_FUNC(dev->devfn), where, 4, value);
 }
 
 static int
-pci_conf_write_config_word (struct pci_dev *dev, int where, u16 value)
+pci_sal_write_config_byte (struct pci_dev *dev, int where, u8 value)
 {
-	return ia64_sal_pci_config_write(PCI_CONFIG_ADDRESS(dev, where), 2, value);
+	return pci_sal_write(0, dev->bus->number, PCI_SLOT(dev->devfn),
+			     PCI_FUNC(dev->devfn), where, 1, value);
 }
 
 static int
-pci_conf_write_config_dword (struct pci_dev *dev, int where, u32 value)
+pci_sal_write_config_word (struct pci_dev *dev, int where, u16 value)
 {
-	return ia64_sal_pci_config_write(PCI_CONFIG_ADDRESS(dev, where), 4, value);
+	return pci_sal_write(0, dev->bus->number, PCI_SLOT(dev->devfn),
+			     PCI_FUNC(dev->devfn), where, 2, value);
 }
 
-struct pci_ops pci_conf = {
-      pci_conf_read_config_byte,
-      pci_conf_read_config_word,
-      pci_conf_read_config_dword,
-      pci_conf_write_config_byte,
-      pci_conf_write_config_word,
-      pci_conf_write_config_dword
+static int
+pci_sal_write_config_dword (struct pci_dev *dev, int where, u32 value)
+{
+	return pci_sal_write(0, dev->bus->number, PCI_SLOT(dev->devfn),
+			     PCI_FUNC(dev->devfn), where, 4, value);
+}
+
+struct pci_ops pci_sal_ops = {
+	pci_sal_read_config_byte,
+	pci_sal_read_config_word,
+	pci_sal_read_config_dword,
+	pci_sal_write_config_byte,
+	pci_sal_write_config_word,
+	pci_sal_write_config_dword
 };
 
+
 /*
  * Initialization. Uses the SAL interface
  */
+
+struct pci_bus *
+pcibios_scan_root(int seg, int bus)
+{
+	struct list_head *list = NULL;
+	struct pci_bus *pci_bus = NULL;
+
+	list_for_each(list, &pci_root_buses) {
+		pci_bus = pci_bus_b(list);
+		if (pci_bus->number == bus) {
+			/* Already scanned */
+			printk("PCI: Bus (%02x:%02x) already probed\n", seg, bus);
+			return pci_bus;
+		}
+	}
+
+	printk("PCI: Probing PCI hardware on bus (%02x:%02x)\n", seg, bus);
+
+	return pci_scan_bus(bus, pci_root_ops, NULL);
+}
+
+void __init
+pcibios_config_init (void)
+{
+	if (pci_root_ops)
+		return;
+
+	printk("PCI: Using SAL to access configuration space\n");
+
+	pci_root_ops = &pci_sal_ops;
+	pci_config_read = pci_sal_read;
+	pci_config_write = pci_sal_write;
+
+	return;
+}
+
 void __init
 pcibios_init (void)
 {
 #	define PCI_BUSES_TO_SCAN 255
-	int i;
+	int i = 0;
 
 #ifdef CONFIG_IA64_MCA
 	ia64_mca_check_errors();    /* For post-failure MCA error logging */
 #endif
 
-	platform_pci_fixup(0);	/* phase 0 initialization (before PCI bus has been scanned) */
+	pcibios_config_init();
+
+	platform_pci_fixup(0);	/* phase 0 fixups (before buses scanned) */
 
 	printk("PCI: Probing PCI hardware\n");
 	for (i = 0; i < PCI_BUSES_TO_SCAN; i++)
-		pci_scan_bus(i, &pci_conf, NULL);
+		pci_scan_bus(i, pci_root_ops, NULL);
+
+	platform_pci_fixup(1);	/* phase 1 fixups (after buses scanned) */
 
-	platform_pci_fixup(1);	/* phase 1 initialization (after PCI bus has been scanned) */
 	return;
 }
 
@@ -186,7 +268,12 @@
 int
 pcibios_enable_device (struct pci_dev *dev)
 {
+	if (!dev)
+		return -EINVAL;
+
 	/* Not needed, since we enable all devices at startup.  */
+
+	printk(KERN_INFO "PCI: Found IRQ %d for device %s\n", dev->irq, dev->slot_name);
 	return 0;
 }
 
@@ -233,8 +320,7 @@
 		vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
 
 	if (remap_page_range(vma, vma->vm_start, vma->vm_pgoff << PAGE_SHIFT,
-			     vma->vm_end - vma->vm_start,
-			     vma->vm_page_prot))
+			     vma->vm_end - vma->vm_start, vma->vm_page_prot))
 		return -EAGAIN;
 
 	return 0;
diff -Nru a/arch/ia64/kernel/process.c b/arch/ia64/kernel/process.c
--- a/arch/ia64/kernel/process.c	Mon Apr 22 15:32:19 2002
+++ b/arch/ia64/kernel/process.c	Mon Apr 22 15:32:19 2002
@@ -24,7 +24,7 @@
 #include <asm/efi.h>
 #include <asm/elf.h>
 #include <asm/perfmon.h>
-#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
 #include <asm/processor.h>
 #include <asm/sal.h>
 #include <asm/uaccess.h>
@@ -145,7 +145,7 @@
 /*
  * We use this if we don't have any better idle routine..
  */
-static void
+void
 default_idle (void)
 {
 	/* may want to do PAL_LIGHT_HALT here... */
@@ -660,7 +660,7 @@
 {
 	struct task_struct *tsk;
 
-	tsk = __get_free_pages(GFP_KERNEL, KERNEL_STACK_SIZE_ORDER);
+	tsk = (void *) __get_free_pages(GFP_KERNEL, KERNEL_STACK_SIZE_ORDER);
 	if (!tsk)
 		return NULL;
 
diff -Nru a/arch/ia64/kernel/semaphore.c b/arch/ia64/kernel/semaphore.c
--- a/arch/ia64/kernel/semaphore.c	Mon Apr 22 15:32:19 2002
+++ b/arch/ia64/kernel/semaphore.c	Mon Apr 22 15:32:19 2002
@@ -1,8 +1,8 @@
 /*
  * IA-64 semaphore implementation (derived from x86 version).
  *
- * Copyright (C) 1999-2000 Hewlett-Packard Co
- * Copyright (C) 1999-2000 David Mosberger-Tang <davidm@hpl.hp.com>
+ * Copyright (C) 1999-2000, 2002 Hewlett-Packard Co
+ *	David Mosberger-Tang <davidm@hpl.hp.com>
  */
 
 /*
@@ -25,6 +25,7 @@
  */
 #include <linux/sched.h>
 
+#include <asm/errno.h>
 #include <asm/semaphore.h>
 
 /*
diff -Nru a/arch/ia64/kernel/setup.c b/arch/ia64/kernel/setup.c
--- a/arch/ia64/kernel/setup.c	Mon Apr 22 15:32:18 2002
+++ b/arch/ia64/kernel/setup.c	Mon Apr 22 15:32:18 2002
@@ -19,6 +19,7 @@
 #include <linux/config.h>
 #include <linux/init.h>
 
+#include <linux/acpi.h>
 #include <linux/bootmem.h>
 #include <linux/console.h>
 #include <linux/delay.h>
@@ -30,7 +31,6 @@
 #include <linux/threads.h>
 #include <linux/tty.h>
 
-#include <asm/acpi-ext.h>
 #include <asm/ia32.h>
 #include <asm/page.h>
 #include <asm/machvec.h>
@@ -54,7 +54,10 @@
 
 extern char _end;
 
+#ifdef CONFIG_SMP
 unsigned long __per_cpu_offset[NR_CPUS];
+#endif
+
 struct cpuinfo_ia64 cpu_info __per_cpu_data;
 
 unsigned long ia64_phys_stacked_size_p8;
@@ -64,6 +67,8 @@
 
 unsigned long ia64_iobase;	/* virtual address for I/O accesses */
 
+unsigned char aux_device_present = 0xaa;        /* XXX remove this when legacy I/O is gone */
+
 #define COMMAND_LINE_SIZE	512
 
 char saved_command_line[COMMAND_LINE_SIZE]; /* used in proc filesystem */
@@ -282,6 +287,7 @@
 setup_arch (char **cmdline_p)
 {
 	extern unsigned long ia64_iobase;
+	unsigned long phys_iobase;
 
 	unw_init();
 
@@ -314,24 +320,23 @@
 #endif
 
 	/*
-	 *  Set `iobase' to the appropriate address in region 6
-	 *    (uncached access range)
+	 *  Set `iobase' to the appropriate address in region 6 (uncached access range).
 	 *
-	 *  The EFI memory map is the "prefered" location to get the I/O port
-	 *  space base, rather the relying on AR.KR0. This should become more
-	 *  clear in future SAL specs. We'll fall back to getting it out of
-	 *  AR.KR0 if no appropriate entry is found in the memory map.
+	 *  The EFI memory map is the "preferred" location to get the I/O port space base,
+	 *  rather the relying on AR.KR0. This should become more clear in future SAL
+	 *  specs. We'll fall back to getting it out of AR.KR0 if no appropriate entry is
+	 *  found in the memory map.
 	 */
-	ia64_iobase = efi_get_iobase();
-	if (ia64_iobase)
+	phys_iobase = efi_get_iobase();
+	if (phys_iobase)
 		/* set AR.KR0 since this is all we use it for anyway */
-		ia64_set_kr(IA64_KR_IO_BASE, ia64_iobase);
+		ia64_set_kr(IA64_KR_IO_BASE, phys_iobase);
 	else {
-		ia64_iobase = ia64_get_kr(IA64_KR_IO_BASE);
+		phys_iobase = ia64_get_kr(IA64_KR_IO_BASE);
 		printk("No I/O port range found in EFI memory map, falling back to AR.KR0\n");
-		printk("I/O port base = 0x%lx\n", ia64_iobase);
+		printk("I/O port base = 0x%lx\n", phys_iobase);
 	}
-	ia64_iobase = __IA64_UNCACHED_OFFSET | (ia64_iobase & ~PAGE_OFFSET);
+	ia64_iobase = (unsigned long) ioremap(phys_iobase, 0);
 
 #ifdef CONFIG_SMP
 	cpu_physical_id(0) = hard_smp_processor_id();
@@ -339,20 +344,23 @@
 
 	cpu_init();	/* initialize the bootstrap CPU */
 
-	if (efi.acpi20) {
-		/* Parse the ACPI 2.0 tables */
-		acpi20_parse(efi.acpi20);
-	} else if (efi.acpi) {
-		/* Parse the ACPI tables */
-		acpi_parse(efi.acpi);
-	}
-
+#ifdef CONFIG_ACPI_BOOT
+	acpi_boot_init(*cmdline_p);
+#endif
 #ifdef CONFIG_VT
-# if defined(CONFIG_VGA_CONSOLE)
-	conswitchp = &vga_con;
-# elif defined(CONFIG_DUMMY_CONSOLE)
+# if defined(CONFIG_DUMMY_CONSOLE)
 	conswitchp = &dummy_con;
 # endif
+# if defined(CONFIG_VGA_CONSOLE)
+	/*
+	 * Non-legacy systems may route legacy VGA MMIO range to system
+	 * memory.  vga_con probes the MMIO hole, so memory looks like
+	 * a VGA device to it.  The EFI memory map can tell us if it's
+	 * memory so we can avoid this problem.
+	 */
+	if (efi_mem_type(0xA0000) != EFI_CONVENTIONAL_MEMORY)
+		conswitchp = &vga_con;
+# endif
 #endif
 
 #ifdef CONFIG_IA64_MCA
@@ -522,20 +530,25 @@
 void
 cpu_init (void)
 {
-	extern char __per_cpu_start[], __phys_per_cpu_start[], __per_cpu_end[];
+	extern char __per_cpu_start[], __phys_per_cpu_start[];
 	extern void __init ia64_mmu_init (void *);
 	unsigned long num_phys_stacked;
 	pal_vm_info_2_u_t vmi;
 	unsigned int max_ctx;
 	struct cpuinfo_ia64 *my_cpu_info;
 	void *my_cpu_data;
+
+#ifdef CONFIG_SMP
+	extern char __per_cpu_end[];
 	int cpu = smp_processor_id();
 
 	my_cpu_data = alloc_bootmem_pages(__per_cpu_end - __per_cpu_start);
 	memcpy(my_cpu_data, __phys_per_cpu_start, __per_cpu_end - __per_cpu_start);
-
 	__per_cpu_offset[cpu] = (char *) my_cpu_data - __per_cpu_start;
-
+	my_cpu_info = my_cpu_data + ((char *) &cpu_info - __per_cpu_start);
+#else
+	my_cpu_data = __phys_per_cpu_start;
+#endif
 	my_cpu_info = my_cpu_data + ((char *) &cpu_info - __per_cpu_start);
 
 	/*
diff -Nru a/arch/ia64/kernel/signal.c b/arch/ia64/kernel/signal.c
--- a/arch/ia64/kernel/signal.c	Mon Apr 22 15:32:18 2002
+++ b/arch/ia64/kernel/signal.c	Mon Apr 22 15:32:18 2002
@@ -530,8 +530,8 @@
 				info.si_signo = signr;
 				info.si_errno = 0;
 				info.si_code = SI_USER;
-				info.si_pid = current->p_pptr->pid;
-				info.si_uid = current->p_pptr->uid;
+				info.si_pid = current->parent->pid;
+				info.si_uid = current->parent->uid;
 			}
 
 			/* If the (new) signal is now blocked, requeue it.  */
@@ -570,7 +570,7 @@
 			      case SIGSTOP:
 				current->state = TASK_STOPPED;
 				current->exit_code = signr;
-				sig = current->p_pptr->sig;
+				sig = current->parent->sig;
 				if (sig && !(sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP))
 					notify_parent(current, SIGCHLD);
 				schedule();
diff -Nru a/arch/ia64/kernel/smp.c b/arch/ia64/kernel/smp.c
--- a/arch/ia64/kernel/smp.c	Mon Apr 22 15:32:19 2002
+++ b/arch/ia64/kernel/smp.c	Mon Apr 22 15:32:19 2002
@@ -48,6 +48,7 @@
 #include <asm/ptrace.h>
 #include <asm/sal.h>
 #include <asm/system.h>
+#include <asm/tlbflush.h>
 #include <asm/unistd.h>
 #include <asm/mca.h>
 
@@ -236,7 +237,7 @@
 	int cpus = 1;
 
 	if (cpuid == smp_processor_id()) {
-		printk(__FUNCTION__" trying to call self\n");
+		printk("%s: trying to call self\n", __FUNCTION__);
 		return -EBUSY;
 	}
 
diff -Nru a/arch/ia64/kernel/smpboot.c b/arch/ia64/kernel/smpboot.c
--- a/arch/ia64/kernel/smpboot.c	Mon Apr 22 15:32:19 2002
+++ b/arch/ia64/kernel/smpboot.c	Mon Apr 22 15:32:19 2002
@@ -68,6 +68,7 @@
 
 extern void __init calibrate_delay(void);
 extern void start_ap(void);
+extern unsigned long ia64_iobase;
 
 int cpucount;
 task_t *task_for_booting_cpu;
@@ -344,6 +345,11 @@
 	 * Get our bogomips.
 	 */
 	ia64_init_itm();
+
+	/*
+	 * Set I/O port base per CPU
+	 */
+	ia64_set_kr(IA64_KR_IO_BASE, __pa(ia64_iobase));
 
 #ifdef CONFIG_IA64_MCA
 	ia64_mca_cmc_vector_setup();	/* Setup vector on AP & enable */
diff -Nru a/arch/ia64/kernel/sys_ia64.c b/arch/ia64/kernel/sys_ia64.c
--- a/arch/ia64/kernel/sys_ia64.c	Mon Apr 22 15:32:19 2002
+++ b/arch/ia64/kernel/sys_ia64.c	Mon Apr 22 15:32:19 2002
@@ -47,7 +47,7 @@
 		/* At this point:  (!vmm || addr < vmm->vm_end). */
 		if (TASK_SIZE - len < addr)
 			return -ENOMEM;
-		if (rgn_offset(addr) + len > RGN_MAP_LIMIT)	/* no risk of overflow here... */
+		if (REGION_OFFSET(addr) + len > RGN_MAP_LIMIT)	/* no risk of overflow here... */
 			return -ENOMEM;
 		if (!vmm || addr + len <= vmm->vm_start)
 			return addr;
@@ -126,7 +126,7 @@
 	}
 
 	/* Check against unimplemented/unmapped addresses: */
-	if ((newbrk - oldbrk) > RGN_MAP_LIMIT || rgn_offset(newbrk) > RGN_MAP_LIMIT)
+	if ((newbrk - oldbrk) > RGN_MAP_LIMIT || REGION_OFFSET(newbrk) > RGN_MAP_LIMIT)
 		goto out;
 
 	/* Check against rlimit.. */
@@ -206,7 +206,7 @@
 	 * or across a region boundary.  Note: RGN_MAP_LIMIT is equal to 2^n-PAGE_SIZE
 	 * (for some integer n <= 61) and len > 0.
 	 */
-	roff = rgn_offset(addr);
+	roff = REGION_OFFSET(addr);
 	if ((len > RGN_MAP_LIMIT) || (roff > (RGN_MAP_LIMIT - len))) {
 		addr = -EINVAL;
 		goto out;
diff -Nru a/arch/ia64/kernel/traps.c b/arch/ia64/kernel/traps.c
--- a/arch/ia64/kernel/traps.c	Mon Apr 22 15:32:19 2002
+++ b/arch/ia64/kernel/traps.c	Mon Apr 22 15:32:19 2002
@@ -214,7 +214,8 @@
 {
 	struct pt_regs *regs = (struct pt_regs *) &stack;
 
-	printk("<sc%ld(%lx,%lx,%lx,%lx)>\n", regs->r15, arg0, arg1, arg2, arg3);
+	printk("%s(%d): <sc%ld(%lx,%lx,%lx,%lx)>\n", current->comm, current->pid,
+	       regs->r15, arg0, arg1, arg2, arg3);
 	return -ENOSYS;
 }
 
diff -Nru a/arch/ia64/kernel/unwind_i.h b/arch/ia64/kernel/unwind_i.h
--- a/arch/ia64/kernel/unwind_i.h	Mon Apr 22 15:32:19 2002
+++ b/arch/ia64/kernel/unwind_i.h	Mon Apr 22 15:32:19 2002
@@ -103,7 +103,7 @@
 	unsigned int in_body : 1;	/* are we inside a body (as opposed to a prologue)? */
 	unsigned long flags;		/* see UNW_FLAG_* in unwind.h */
 
-	u8 *imask;			/* imask of of spill_mask record or NULL */
+	u8 *imask;			/* imask of spill_mask record or NULL */
 	unsigned long pr_val;		/* predicate values */
 	unsigned long pr_mask;		/* predicate mask */
 	long spill_offset;		/* psp-relative offset for spill base */
diff -Nru a/arch/ia64/lib/Makefile b/arch/ia64/lib/Makefile
--- a/arch/ia64/lib/Makefile	Mon Apr 22 15:32:19 2002
+++ b/arch/ia64/lib/Makefile	Mon Apr 22 15:32:19 2002
@@ -16,6 +16,9 @@
 	flush.o io.o do_csum.o								\
 	memcpy.o memset.o strlen.o swiotlb.o
 
+obj-$(CONFIG_ITANIUM) += copy_page.o
+obj-$(CONFIG_MCKINLEY) += copy_page_mck.o
+
 IGNORE_FLAGS_OBJS =	__divsi3.o __udivsi3.o __modsi3.o __umodsi3.o \
 			__divdi3.o __udivdi3.o __moddi3.o __umoddi3.o
 
diff -Nru a/arch/ia64/lib/copy_page_mck.S b/arch/ia64/lib/copy_page_mck.S
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/arch/ia64/lib/copy_page_mck.S	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,185 @@
+/*
+ * McKinley-optimized version of copy_page().
+ *
+ * Copyright (C) 2002 Hewlett-Packard Co
+ *	David Mosberger <davidm@hpl.hp.com>
+ *
+ * Inputs:
+ *	in0:	address of target page
+ *	in1:	address of source page
+ * Output:
+ *	no return value
+ *
+ * General idea:
+ *	- use regular loads and stores to prefetch data to avoid consuming M-slot just for
+ *	  lfetches => good for in-cache performance
+ *	- avoid l2 bank-conflicts by not storing into the same 16-byte bank within a single
+ *	  cycle
+ *
+ * Principle of operation:
+ *	First, note that L1 has a line-size of 64 bytes and L2 a line-size of 128 bytes.
+ *	To avoid secondary misses in L2, we prefetch both source and destination with a line-size
+ *	of 128 bytes.  When both of these lines are in the L2 and the first half of the
+ *	source line is in L1, we start copying the remaining words.  The second half of the
+ *	source line is prefetched in an earlier iteration, so that by the time we start
+ *	accessing it, it's also present in the L1.
+ *
+ *	We use a software-pipelined loop to control the overall operation.  The pipeline
+ *	has 2*PREFETCH_DIST+K stages.  The first PREFETCH_DIST stages are used for prefetching
+ *	source cache-lines.  The second PREFETCH_DIST stages are used for prefetching destination
+ *	cache-lines, the last K stages are used to copy the cache-line words not copied by
+ *	the prefetches.  The four relevant points in the pipelined are called A, B, C, D:
+ *	p[A] is TRUE if a source-line should be prefetched, p[B] is TRUE if a destination-line
+ *	should be prefetched, p[C] is TRUE if the second half of an L2 line should be brought
+ *	into L1D and p[D] is TRUE if a cacheline needs to be copied.
+ *
+ *	This all sounds very complicated, but thanks to the modulo-scheduled loop support,
+ *	the resulting code is very regular and quite easy to follow (once you get the idea).
+ *
+ *	As a secondary optimization, the first 2*PREFETCH_DIST iterations are implemented
+ *	as the separate .prefetch_loop.  Logically, this loop performs exactly like the
+ *	main-loop (.line_copy), but has all known-to-be-predicated-off instructions removed,
+ *	so that each loop iteration is faster (again, good for cached case).
+ *
+ *	When reading the code, it helps to keep the following picture in mind:
+ *
+ *	       word 0 word 1
+ *            +------+------+---
+ *	      |	v[x] | 	t1  | ^
+ *	      |	t2   |	t3  | |
+ *	      |	t4   |	t5  | |
+ *	      |	t6   |	t7  | | 128 bytes
+ *     	      |	n[y] | 	t9  | |	(L2 cache line)
+ *	      |	t10  | 	t11 | |
+ *	      |	t12  | 	t13 | |
+ *	      |	t14  | 	t15 | v
+ *	      +------+------+---
+ *
+ *	Here, v[x] is copied by the (memory) prefetch.  n[y] is loaded at p[C]
+ *	to fetch the second-half of the L2 cache line into L1, and the tX words are copied in
+ *	an order that avoids bank conflicts.
+ */
+#include <asm/asmmacro.h>
+#include <asm/page.h>
+
+#define PREFETCH_DIST	8		// McKinley sustains 16 outstanding L2 misses (8 ld, 8 st)
+
+#define src0		r2
+#define src1		r3
+#define dst0		r9
+#define dst1		r10
+#define src_pre_mem	r11
+#define dst_pre_mem	r14
+#define src_pre_l2	r15
+#define dst_pre_l2	r16
+#define t1		r17
+#define t2		r18
+#define t3		r19
+#define t4		r20
+#define t5		t1	// alias!
+#define t6		t2	// alias!
+#define t7		t3	// alias!
+#define t9		t5	// alias!
+#define t10		t4	// alias!
+#define t11		t7	// alias!
+#define t12		t6	// alias!
+#define t14		t10	// alias!
+#define t13		r21
+#define t15		r22
+
+#define saved_lc	r23
+#define saved_pr	r24
+
+#define	A	0
+#define B	(PREFETCH_DIST)
+#define C	(B + PREFETCH_DIST)
+#define D	(C + 3)
+#define N	(D + 1)
+#define Nrot	((N + 7) & ~7)
+
+GLOBAL_ENTRY(copy_page)
+	.prologue
+	alloc r8 = ar.pfs, 2, Nrot-2, 0, Nrot
+
+	.rotr v[2*PREFETCH_DIST], n[D-C+1]
+	.rotp p[N]
+
+	.save ar.lc, saved_lc
+	mov saved_lc = ar.lc
+	.save pr, saved_pr
+	mov saved_pr = pr
+	.body
+
+	mov src_pre_mem = in1
+	mov pr.rot = 0x10000
+	mov ar.ec = 1				// special unrolled loop
+
+	mov dst_pre_mem = in0
+	mov ar.lc = 2*PREFETCH_DIST - 1
+
+	add src_pre_l2 = 8*8, in1
+	add dst_pre_l2 = 8*8, in0
+	add src0 = 8, in1			// first t1 src
+	add src1 = 3*8, in1			// first t3 src
+	add dst0 = 8, in0			// first t1 dst
+	add dst1 = 3*8, in0			// first t3 dst
+	mov t1 = (PAGE_SIZE/128) - (2*PREFETCH_DIST) - 1
+	nop.m 0
+	nop.i 0
+	;;
+	// same as .line_copy loop, but with all predicated-off instructions removed:
+.prefetch_loop:
+(p[A])	ld8 v[A] = [src_pre_mem], 128		// M0
+(p[B])	st8 [dst_pre_mem] = v[B], 128		// M2
+	br.ctop.sptk .prefetch_loop
+	;;
+	cmp.eq p16, p0 = r0, r0			// reset p16 to 1 (br.ctop cleared it to zero)
+	mov ar.lc = t1				// with 64KB pages, t1 is too big to fit in 8 bits!
+	mov ar.ec = N				// # of stages in pipeline
+	;;
+.line_copy:
+(p[D])	ld8 t2 = [src0], 3*8			// M0
+(p[D])	ld8 t4 = [src1], 3*8			// M1
+(p[B])	st8 [dst_pre_mem] = v[B], 128		// M2 prefetch dst from memory
+(p[D])	st8 [dst_pre_l2] = n[D-C], 128		// M3 prefetch dst from L2
+	;;
+(p[A])	ld8 v[A] = [src_pre_mem], 128		// M0 prefetch src from memory
+(p[C])	ld8 n[0] = [src_pre_l2], 128		// M1 prefetch src from L2
+(p[D])	st8 [dst0] =  t1, 8			// M2
+(p[D])	st8 [dst1] =  t3, 8			// M3
+	;;
+(p[D])	ld8  t5 = [src0], 8
+(p[D])	ld8  t7 = [src1], 3*8
+(p[D])	st8 [dst0] =  t2, 3*8
+(p[D])	st8 [dst1] =  t4, 3*8
+	;;
+(p[D])	ld8  t6 = [src0], 3*8
+(p[D])	ld8 t10 = [src1], 8
+(p[D])	st8 [dst0] =  t5, 8
+(p[D])	st8 [dst1] =  t7, 3*8
+	;;
+(p[D])	ld8  t9 = [src0], 3*8
+(p[D])	ld8 t11 = [src1], 3*8
+(p[D])	st8 [dst0] =  t6, 3*8
+(p[D])	st8 [dst1] = t10, 8
+	;;
+(p[D])	ld8 t12 = [src0], 8
+(p[D])	ld8 t14 = [src1], 8
+(p[D])	st8 [dst0] =  t9, 3*8
+(p[D])	st8 [dst1] = t11, 3*8
+	;;
+(p[D])	ld8 t13 = [src0], 4*8
+(p[D])	ld8 t15 = [src1], 4*8
+(p[D])	st8 [dst0] = t12, 8
+(p[D])	st8 [dst1] = t14, 8
+	;;
+(p[D-1])ld8  t1 = [src0], 8
+(p[D-1])ld8  t3 = [src1], 8
+(p[D])	st8 [dst0] = t13, 4*8
+(p[D])	st8 [dst1] = t15, 4*8
+	br.ctop.sptk .line_copy
+	;;
+	mov ar.lc = saved_lc
+	mov pr = saved_pr, -1
+	br.ret.sptk.many rp
+END(copy_page)
diff -Nru a/arch/ia64/lib/do_csum.S b/arch/ia64/lib/do_csum.S
--- a/arch/ia64/lib/do_csum.S	Mon Apr 22 15:32:19 2002
+++ b/arch/ia64/lib/do_csum.S	Mon Apr 22 15:32:19 2002
@@ -8,9 +8,11 @@
  *	in0: address of buffer to checksum (char *)
  *	in1: length of the buffer (int)
  *
- * Copyright (C) 1999, 2001 Hewlett-Packard Co
- * Copyright (C) 1999 Stephane Eranian <eranian@hpl.hp.com>
+ * Copyright (C) 1999, 2001-2002 Hewlett-Packard Co
+ *	Stephane Eranian <eranian@hpl.hp.com>
  *
+ * 02/04/08	David Mosberger <davidm@hpl.hp.com>
+ *		More cleanup and tuning.
  * 01/04/18	Jun Nakajima <jun.nakajima@intel.com>
  *		Clean up and optimize and the software pipeline, loading two
  *		back-to-back 8-byte words per loop. Clean up the initialization
@@ -71,8 +73,6 @@
 //	calculating the Internet checksum.
 //
 // NOT YET DONE:
-//	- use the lfetch instruction to augment the chances of the data being in
-//	  the cache when we need it.
 //	- Maybe another algorithm which would take care of the folding at the
 //	  end in a different manner
 //	- Work with people more knowledgeable than me on the network stack
@@ -102,10 +102,6 @@
 #define buf		in0
 #define len		in1
 
-#ifndef CONFIG_IA64_LOAD_LATENCY
-#define CONFIG_IA64_LOAD_LATENCY	2
-#endif
-
 #define LOAD_LATENCY	2	// XXX fix me
 
 #if (LOAD_LATENCY != 1) && (LOAD_LATENCY != 2)
@@ -122,45 +118,46 @@
 	.prologue
 	.save ar.pfs, saved_pfs
 	alloc saved_pfs=ar.pfs,2,16,1,16
-	.rotr word1[4], word2[4],result1[4],result2[4]
-	.rotp p[PIPE_DEPTH]
+	.rotr word1[4], word2[4],result1[LOAD_LATENCY+2],result2[LOAD_LATENCY+2]
+	.rotp p[PIPE_DEPTH], pC1[2], pC2[2]
 	mov ret0=r0		// in case we have zero length
 	cmp.lt p0,p6=r0,len	// check for zero length or negative (32bit len)
-	;;			// avoid WAW on CFM
-	mov tmp3=0x7		// a temporary mask/value
+	;;
 	add tmp1=buf,len	// last byte's address
-(p6)	br.ret.spnt.many rp	// return if true (hope we can avoid that)
+	.save pr, saved_pr
+	mov saved_pr=pr		// preserve predicates (rotation)
+(p6)	br.ret.spnt.many rp	// return if zero or negative length
 
-	and firstoff=7,buf	// how many bytes off for first1 element
-	tbit.nz p15,p0=buf,0	// is buf an odd address ?
 	mov hmask=-1		// intialize head mask
-	;;
-	andcm first1=buf,tmp3	// 8byte aligned down address of first1 element
+	tbit.nz p15,p0=buf,0	// is buf an odd address?
+	and first1=-8,buf	// 8-byte align down address of first1 element
+
+	and firstoff=7,buf	// how many bytes off for first1 element
 	mov tmask=-1		// initialize tail mask
-	adds tmp2=-1,tmp1	// last-1
+
 	;;
+	adds tmp2=-1,tmp1	// last-1
 	and lastoff=7,tmp1	// how many bytes off for last element
-	andcm last=tmp2,tmp3	// address of word containing last byte
-	.save pr, saved_pr
-	mov saved_pr=pr		// preserve predicates (rotation)
+	;;
+	sub tmp1=8,lastoff	// complement to lastoff
+	and last=-8,tmp2	// address of word containing last byte
 	;;
 	sub tmp3=last,first1	// tmp3=distance from first1 to last
+	.save ar.lc, saved_lc
+	mov saved_lc=ar.lc	// save lc
 	cmp.eq p8,p9=last,first1	// everything fits in one word ?
-	sub tmp1=8,lastoff	// complement to lastoff
-	ld8 firstval=[first1],8	// load,ahead of time, "first1" word
+
+	ld8 firstval=[first1],8	// load, ahead of time, "first1" word
+	and tmp1=7, tmp1	// make sure that if tmp1==8 -> tmp1=0
 	shl tmp2=firstoff,3	// number of bits
 	;;
-	and tmp1=7, tmp1	// make sure that if tmp1==8 -> tmp1=0
-(p9)	ld8 lastval=[last]	// load,ahead of time, "last" word, if needed
+(p9)	ld8 lastval=[last]	// load, ahead of time, "last" word, if needed
+	shl tmp1=tmp1,3		// number of bits
 (p9)	adds tmp3=-8,tmp3	// effectively loaded
 	;;
 (p8)	mov lastval=r0		// we don't need lastval if first1==last
-	shl tmp1=tmp1,3		// number of bits
 	shl hmask=hmask,tmp2	// build head mask, mask off [0,first1off[
-	;;
 	shr.u tmask=tmask,tmp1	// build tail mask, mask off ]8,lastoff]
-	.save ar.lc, saved_lc
-	mov saved_lc=ar.lc	// save lc
 	;;
 	.body
 #define count tmp3
@@ -171,8 +168,8 @@
 	;;
 	// If count is odd, finish this 8-byte word so that we can
 	// load two back-to-back 8-byte words per loop thereafter.
-	tbit.nz p10,p11=count,0		// if (count is odd)
 	and word1[0]=firstval,hmask	// and mask it as appropriate
+	tbit.nz p10,p11=count,0		// if (count is odd)
 	;;
 (p8)	mov result1[0]=word1[0]
 (p9)	add result1[0]=word1[0],word2[0]
@@ -181,9 +178,8 @@
 	;;
 (p6)	adds result1[0]=1,result1[0]
 (p8)	br.cond.dptk .do_csum_exit	// if (within an 8-byte word)
-	;;
 (p11)	br.cond.dptk .do_csum16		// if (count is even)
-	;;
+
 	// Here count is odd.
 	ld8 word1[1]=[first1],8		// load an 8-byte word
 	cmp.eq p9,p10=1,count		// if (count == 1)
@@ -194,11 +190,9 @@
 	cmp.ltu p6,p0=result1[0],word1[1]
 	;;
 (p6)	adds result1[0]=1,result1[0]
-	;;
 (p9)	br.cond.sptk .do_csum_exit	// if (count == 1) exit
 	// Fall through to caluculate the checksum, feeding result1[0] as
 	// the initial value in result1[0].
-	;;
 	//
 	// Calculate the checksum loading two 8-byte words per loop.
 	//
@@ -207,45 +201,36 @@
 	shr.u count=count,1	// we do 16 bytes per loop
 	;;
 	cmp.eq p9,p10=r0,count	// if (count == 0)
+	adds count=-1,count
 	brp.loop.imp 1f,2f
 	;;
-	adds count=-1,count
 	mov ar.ec=PIPE_DEPTH
-	;;
 	mov ar.lc=count	// set lc
-	;;
+
 	// result1[0] must be initialized in advance.
 	mov result2[0]=r0
-	;;
 	mov pr.rot=1<<16
-	;;
 	mov carry1=r0
 	mov carry2=r0
-	;;
 	add first2=8,first1
-	;;
 (p9)	br.cond.sptk .do_csum_exit
 	;;
-	nop.m	0
-	nop.i	0
-	;;
 	.align 32
 1:
-(ELD_1)	cmp.ltu p31,p0=result1[LOAD_LATENCY],word1[LOAD_LATENCY+1]
-(p32)	adds carry1=1,carry1
-(ELD_1)	cmp.ltu p47,p0=result2[LOAD_LATENCY],word2[LOAD_LATENCY+1]
-(p48)	adds carry2=1,carry2
+(ELD_1)	cmp.ltu pC1[0],p0=result1[LOAD_LATENCY],word1[LOAD_LATENCY+1]
+(pC1[1])adds carry1=1,carry1
+(ELD_1)	cmp.ltu pC2[0],p0=result2[LOAD_LATENCY],word2[LOAD_LATENCY+1]
+(pC2[1])adds carry2=1,carry2
 (ELD)	add result1[LOAD_LATENCY-1]=result1[LOAD_LATENCY],word1[LOAD_LATENCY]
 (ELD)	add result2[LOAD_LATENCY-1]=result2[LOAD_LATENCY],word2[LOAD_LATENCY]
-2:
-(p16)	ld8 word1[0]=[first1],16
-(p16)	ld8 word2[0]=[first2],16
+[2:]
+(p[0])	ld8 word1[0]=[first1],16
+(p[0])	ld8 word2[0]=[first2],16
 	br.ctop.sptk 1b
 	;;
-	// Since len is a 32-bit value, carry cannot be larger than
-	// a 64-bit value.
-(p32)	adds carry1=1,carry1	// since we miss the last one
-(p48)	adds carry2=1,carry2
+	// Since len is a 32-bit value, carry cannot be larger than a 64-bit value.
+(pC1[1])adds carry1=1,carry1	// since we miss the last one
+(pC2[1])adds carry2=1,carry2
 	;;
 	add result1[LOAD_LATENCY+1]=result1[LOAD_LATENCY+1],carry1
 	add result2[LOAD_LATENCY+1]=result2[LOAD_LATENCY+1],carry2
@@ -263,18 +248,15 @@
 (p6)	adds result1[0]=1,result1[0]
 	;;
 .do_csum_exit:
-	movl tmp3=0xffffffff
-	;;
-	// XXX Fixme
 	//
 	// now fold 64 into 16 bits taking care of carry
 	// that's not very good because it has lots of sequentiality
 	//
-	and tmp1=result1[0],tmp3
+	mov tmp3=0xffff
+	zxt4 tmp1=result1[0]
 	shr.u tmp2=result1[0],32
 	;;
 	add result1[0]=tmp1,tmp2
-	shr.u tmp3=tmp3,16
 	;;
 	and tmp1=result1[0],tmp3
 	shr.u tmp2=result1[0],16
diff -Nru a/arch/ia64/lib/memset.S b/arch/ia64/lib/memset.S
--- a/arch/ia64/lib/memset.S	Mon Apr 22 15:32:19 2002
+++ b/arch/ia64/lib/memset.S	Mon Apr 22 15:32:19 2002
@@ -9,8 +9,8 @@
  * 	in1:	byte value to use for storing
  *	in2:	length of the buffer
  *
- * Copyright (C) 1999, 2001 Hewlett-Packard Co
- * Copyright (C) 1999 Stephane Eranian <eranian@hpl.hp.com>
+ * Copyright (C) 1999, 2001, 2002 Hewlett-Packard Co
+ *	Stephane Eranian <eranian@hpl.hp.com>
  */
 
 #include <asm/asmmacro.h>
@@ -30,7 +30,19 @@
 #define saved_lc	r20
 #define tmp		r21
 
-GLOBAL_ENTRY(memset)
+GLOBAL_ENTRY(__bzero)
+	.prologue
+	.save ar.pfs, saved_pfs
+	alloc saved_pfs=ar.pfs,0,0,3,0
+	mov out2=out1
+	mov out1=0
+	/* FALL THROUGH (explicit NOPs so that next alloc is preceded by stop bit!) */
+	nop.m 0
+	nop.f 0
+	nop.i 0
+	;;
+END(__bzero)
+GLOBAL_ENTRY(__memset_generic)
 	.prologue
 	.save ar.pfs, saved_pfs
  	alloc saved_pfs=ar.pfs,3,0,0,0	// cnt is sink here
@@ -105,4 +117,7 @@
 	;;
 (p6)	st1 [buf]=val		// only 1 byte left
 	br.ret.sptk.many rp
-END(memset)
+END(__memset_generic)
+
+	.global memset
+memset = __memset_generic		// alias needed for gcc
diff -Nru a/arch/ia64/lib/swiotlb.c b/arch/ia64/lib/swiotlb.c
--- a/arch/ia64/lib/swiotlb.c	Mon Apr 22 15:32:19 2002
+++ b/arch/ia64/lib/swiotlb.c	Mon Apr 22 15:32:19 2002
@@ -10,6 +10,7 @@
  *			unnecessary i-cache flushing.
  */
 
+#include <linux/cache.h>
 #include <linux/mm.h>
 #include <linux/module.h>
 #include <linux/pci.h>
@@ -24,9 +25,6 @@
 #include <linux/init.h>
 #include <linux/bootmem.h>
 
-#define ALIGN(val, align) ((unsigned long)	\
-	(((unsigned long) (val) + ((align) - 1)) & ~((align) - 1)))
-
 #define OFFSET(val,align) ((unsigned long)	\
 	                   ( (val) & ( (align) - 1)))
 
@@ -276,8 +274,11 @@
 	int gfp = GFP_ATOMIC;
 	void *ret;
 
-	if (!hwdev || hwdev->dma_mask <= 0xffffffff)
-		gfp |= GFP_DMA; /* XXX fix me: should change this to GFP_32BIT or ZONE_32BIT */
+	/*
+	 * Alloc_consistent() is defined to return memory < 4GB, no matter what the DMA
+	 * mask says.
+	 */
+	gfp |= GFP_DMA; /* XXX fix me: should change this to GFP_32BIT or ZONE_32BIT */
 	ret = (void *)__get_free_pages(gfp, get_order(size));
 	if (!ret)
 		return NULL;
diff -Nru a/arch/ia64/mm/fault.c b/arch/ia64/mm/fault.c
--- a/arch/ia64/mm/fault.c	Mon Apr 22 15:32:19 2002
+++ b/arch/ia64/mm/fault.c	Mon Apr 22 15:32:19 2002
@@ -120,15 +120,15 @@
 	if (!(prev_vma && (prev_vma->vm_flags & VM_GROWSUP) && (address == prev_vma->vm_end))) {
 		if (!(vma->vm_flags & VM_GROWSDOWN))
 			goto bad_area;
-		if (rgn_index(address) != rgn_index(vma->vm_start)
-		    || rgn_offset(address) >= RGN_MAP_LIMIT)
+		if (REGION_NUMBER(address) != REGION_NUMBER(vma->vm_start)
+		    || REGION_OFFSET(address) >= RGN_MAP_LIMIT)
 			goto bad_area;
 		if (expand_stack(vma, address))
 			goto bad_area;
 	} else {
 		vma = prev_vma;
-		if (rgn_index(address) != rgn_index(vma->vm_start)
-		    || rgn_offset(address) >= RGN_MAP_LIMIT)
+		if (REGION_NUMBER(address) != REGION_NUMBER(vma->vm_start)
+		    || REGION_OFFSET(address) >= RGN_MAP_LIMIT)
 			goto bad_area;
 		if (expand_backing_store(vma, address))
 			goto bad_area;
diff -Nru a/arch/ia64/mm/init.c b/arch/ia64/mm/init.c
--- a/arch/ia64/mm/init.c	Mon Apr 22 15:32:19 2002
+++ b/arch/ia64/mm/init.c	Mon Apr 22 15:32:19 2002
@@ -40,10 +40,10 @@
 
 static int pgt_cache_water[2] = { 25, 50 };
 
-int
+void
 check_pgt_cache (void)
 {
-	int low, high, freed = 0;
+	int low, high;
 
 	low = pgt_cache_water[0];
 	high = pgt_cache_water[1];
@@ -51,12 +51,11 @@
 	if (pgtable_cache_size > high) {
 		do {
 			if (pgd_quicklist)
-				free_page((unsigned long)pgd_alloc_one_fast(0)), ++freed;
+				free_page((unsigned long)pgd_alloc_one_fast(0));
 			if (pmd_quicklist)
-				free_page((unsigned long)pmd_alloc_one_fast(0, 0)), ++freed;
+				free_page((unsigned long)pmd_alloc_one_fast(0, 0));
 		} while (pgtable_cache_size > low);
 	}
-	return freed;
 }
 
 /*
@@ -347,8 +346,6 @@
 paging_init (void)
 {
 	unsigned long max_dma, zones_size[MAX_NR_ZONES];
-
-	clear_page((void *) ZERO_PAGE_ADDR);
 
 	/* initialize mem_map[] */
 
diff -Nru a/arch/ia64/mm/tlb.c b/arch/ia64/mm/tlb.c
--- a/arch/ia64/mm/tlb.c	Mon Apr 22 15:32:19 2002
+++ b/arch/ia64/mm/tlb.c	Mon Apr 22 15:32:19 2002
@@ -16,10 +16,11 @@
 #include <linux/smp.h>
 #include <linux/mm.h>
 
+#include <asm/delay.h>
 #include <asm/mmu_context.h>
 #include <asm/pgalloc.h>
 #include <asm/pal.h>
-#include <asm/delay.h>
+#include <asm/tlbflush.h>
 
 #define SUPPORTED_PGBITS (			\
 		1 << _PAGE_SIZE_256M |		\
@@ -79,7 +80,7 @@
 	flush_tlb_all();
 }
 
-static inline void
+void
 ia64_global_tlb_purge (unsigned long start, unsigned long end, unsigned long nbits)
 {
 	static spinlock_t ptcg_lock = SPIN_LOCK_UNLOCKED;
diff -Nru a/arch/ia64/vmlinux.lds.S b/arch/ia64/vmlinux.lds.S
--- a/arch/ia64/vmlinux.lds.S	Mon Apr 22 15:32:19 2002
+++ b/arch/ia64/vmlinux.lds.S	Mon Apr 22 15:32:19 2002
@@ -28,11 +28,6 @@
   .text : AT(ADDR(.text) - PAGE_OFFSET)
     {
 	*(.text.ivt)
-	/* these are not really text pages, but they need to be page aligned: */
-	*(__special_page_section)
-	__start_gate_section = .;
-	*(.text.gate)
-	__stop_gate_section = .;
 	*(.text)
     }
   .text2 : AT(ADDR(.text2) - PAGE_OFFSET)
@@ -119,6 +114,13 @@
   /* The initial task and kernel stack */
   .data.init_task : AT(ADDR(.data.init_task) - PAGE_OFFSET)
 	{ *(.data.init_task) }
+
+  .data.page_aligned : AT(ADDR(.data.page_aligned) - PAGE_OFFSET)
+        { *(__special_page_section)
+	  __start_gate_section = .;
+	  *(.text.gate)
+	  __stop_gate_section = .;
+	}
 
   . = ALIGN(SMP_CACHE_BYTES);
   .data.cacheline_aligned : AT(ADDR(.data.cacheline_aligned) - PAGE_OFFSET)
diff -Nru a/arch/mips/config.in b/arch/mips/config.in
--- a/arch/mips/config.in	Mon Apr 22 15:32:19 2002
+++ b/arch/mips/config.in	Mon Apr 22 15:32:19 2002
@@ -388,15 +388,7 @@
 
 source net/irda/Config.in
 
-mainmenu_option next_comment
-comment 'ISDN subsystem'
-if [ "$CONFIG_NET" != "n" ]; then
-   tristate 'ISDN support' CONFIG_ISDN
-   if [ "$CONFIG_ISDN" != "n" ]; then
-      source drivers/isdn/Config.in
-   fi
-fi
-endmenu
+source drivers/isdn/Config.in
 
 mainmenu_option next_comment
 comment 'Old CD-ROM drivers (not SCSI, not IDE)'
diff -Nru a/arch/mips64/config.in b/arch/mips64/config.in
--- a/arch/mips64/config.in	Mon Apr 22 15:32:18 2002
+++ b/arch/mips64/config.in	Mon Apr 22 15:32:18 2002
@@ -181,16 +181,7 @@
 
 source net/irda/Config.in
 
-mainmenu_option next_comment
-comment 'ISDN subsystem'
-
-if [ "$CONFIG_NET" != "n" ]; then
-   tristate 'ISDN support' CONFIG_ISDN
-   if [ "$CONFIG_ISDN" != "n" ]; then
-      source drivers/isdn/Config.in
-   fi
-fi
-endmenu
+source drivers/isdn/Config.in
 
 mainmenu_option next_comment
 comment 'Old CD-ROM drivers (not SCSI, not IDE)'
diff -Nru a/arch/ppc/config.in b/arch/ppc/config.in
--- a/arch/ppc/config.in	Mon Apr 22 15:32:18 2002
+++ b/arch/ppc/config.in	Mon Apr 22 15:32:18 2002
@@ -457,14 +457,7 @@
 
 source net/irda/Config.in
 
-mainmenu_option next_comment
-comment 'ISDN subsystem'
-
-tristate 'ISDN support' CONFIG_ISDN
-if [ "$CONFIG_ISDN" != "n" ]; then
-   source drivers/isdn/Config.in
-fi
-endmenu
+source drivers/isdn/Config.in
 
 mainmenu_option next_comment
 comment 'Old CD-ROM drivers (not SCSI, not IDE)'
diff -Nru a/arch/ppc64/config.in b/arch/ppc64/config.in
--- a/arch/ppc64/config.in	Mon Apr 22 15:32:18 2002
+++ b/arch/ppc64/config.in	Mon Apr 22 15:32:18 2002
@@ -124,14 +124,7 @@
 
 source net/irda/Config.in
 
-mainmenu_option next_comment
-comment 'ISDN subsystem'
-
-tristate 'ISDN support' CONFIG_ISDN
-if [ "$CONFIG_ISDN" != "n" ]; then
-   source drivers/isdn/Config.in
-fi
-endmenu
+source drivers/isdn/Config.in
 
 mainmenu_option next_comment
 comment 'Old CD-ROM drivers (not SCSI, not IDE)'
diff -Nru a/arch/sparc/config.in b/arch/sparc/config.in
--- a/arch/sparc/config.in	Mon Apr 22 15:32:18 2002
+++ b/arch/sparc/config.in	Mon Apr 22 15:32:18 2002
@@ -118,14 +118,7 @@
   define_bool CONFIG_BLK_DEV_HD n
 fi
 
-mainmenu_option next_comment
-comment 'ISDN subsystem'
-
-tristate 'ISDN support' CONFIG_ISDN
-if [ "$CONFIG_ISDN" != "n" ]; then
-   source drivers/isdn/Config.in
-fi
-endmenu
+source drivers/isdn/Config.in
 
 mainmenu_option next_comment
 comment 'SCSI support'
diff -Nru a/arch/sparc64/solaris/Makefile b/arch/sparc64/solaris/Makefile
--- a/arch/sparc64/solaris/Makefile	Mon Apr 22 15:32:18 2002
+++ b/arch/sparc64/solaris/Makefile	Mon Apr 22 15:32:18 2002
@@ -1,11 +1,6 @@
 #
 # Makefile for the Solaris binary emulation.
 #
-# Note! Dependencies are done automagically by 'make dep', which also
-# removes any old dependencies. DON'T put your own dependencies here
-# unless it's something special (ie not a .c file).
-#
-# Note 2! The CFLAGS definition is now in the main makefile...
 
 ifeq ($(CONFIG_SOLARIS_EMUL),m)
 CPPFLAGS = $(MODFLAGS)
@@ -17,18 +12,9 @@
 .S.o:
 	$(CC) $(AFLAGS) $(CPPFLAGS) -ansi -c $< -o $*.o
 
-list-multi := solaris.o
-
 solaris-objs := entry64.o fs.o misc.o signal.o systbl.o socket.o \
 		ioctl.o ipc.o socksys.o timod.o
 
 obj-$(CONFIG_SOLARIS_EMUL) += solaris.o
-
-ifneq ($(CONFIG_SOLARIS_EMUL),y)
-do_it_all:
-endif
-
-solaris.o: $(solaris-objs)
-	$(LD) -r -o $@ $(solaris-objs)
 
 include $(TOPDIR)/Rules.make
diff -Nru a/arch/x86_64/Config.help b/arch/x86_64/Config.help
--- a/arch/x86_64/Config.help	Mon Apr 22 15:32:18 2002
+++ b/arch/x86_64/Config.help	Mon Apr 22 15:32:18 2002
@@ -396,33 +396,31 @@
   sending the processor to sleep and saving power.
 
 CONFIG_ACPI
-  ACPI/OSPM support for Linux is currently under development. As such,
-  this support is preliminary and EXPERIMENTAL.  Configuring ACPI
-  support enables kernel interfaces that allow higher level software
-  (OSPM) to manipulate ACPI defined hardware and software interfaces,
-  including the evaluation of ACPI control methods.  If unsure, choose
-  N here.  Note, this option will enlarge your kernel by about 120K.
-
-  This support requires an ACPI compliant platform (hardware/firmware).
-  If both ACPI and Advanced Power Management (APM) support are
-  configured, whichever is loaded first shall be used.
-
-  This code DOES NOT currently provide a complete OSPM implementation
-  -- it has not yet reached APM's level of functionality.  When fully
-  implemented, Linux ACPI/OSPM will provide a more robust functional
-  replacement for legacy configuration and power management
-  interfaces, including the Plug-and-Play BIOS specification (PnP
-  BIOS), the Multi-Processor Specification (MPS), and the Advanced
-  Power Management specification (APM).
-
-  Linux support for ACPI/OSPM is based on Intel Corporation's ACPI
-  Component Architecture (ACPI CA). The latest ACPI CA source code,
-  documentation, debug builds, and implementation status information
-  can be downloaded from:
-  <http://developer.intel.com/technology/iapc/acpi/downloads.htm>.
-
-  The ACPI Sourceforge project may also be of interest:
-  <http://sf.net/projects/acpi/>
+  Advanced Configuration and Power Interface (ACPI) support for 
+  Linux requires an ACPI compliant platform (hardware/firmware),
+  and assumes the presence of OS-directed configuration and power
+  management (OSPM) software. 
+
+  Linux ACPI provides a robust functional replacement for several 
+  legacy configuration and power management intefaces, including 
+  the Plug-and-Play BIOS specification (PnP BIOS), the 
+  MultiProcessor Specification (MPS), and the Advanced Power 
+  Management (APM) specification.  If both ACPI and APM support 
+  are configured, whichever is loaded first shall be used.
+  
+  The ACPI SourceForge project contains the latest source code, 
+  documentation, tools, mailing list subscription, and other 
+  information.  This project is available at:
+    <http://sourceforge.net/projects/acpi>
+
+  Linux support for ACPI is based on Intel Corporation's ACPI
+  Component Architecture (ACPI CA).  For more information see:
+    <http://developer.intel.com/technology/iapc/acpi>
+
+  ACPI is an open industry specification co-developed by Compaq, 
+  Intel, Microsoft, Phoenix, and Toshiba.  The specification is 
+  available at:
+    <http://www.acpi.info>
 
 CONFIG_X86_MSR
   This device gives privileged processes access to the x86
@@ -437,34 +435,6 @@
   with major 203 and minors 0 to 31 for /dev/cpu/0/cpuid to
   /dev/cpu/31/cpuid.
 
-CONFIG_SOUND
-  If you have a sound card in your computer, i.e. if it can say more
-  than an occasional beep, say Y.  Be sure to have all the information
-  about your sound card and its configuration down (I/O port,
-  interrupt and DMA channel), because you will be asked for it.
-
-  You want to read the Sound-HOWTO, available from
-  <http://www.linuxdoc.org/docs.html#howto>. General information about
-  the modular sound system is contained in the files
-  <file:Documentation/sound/Introduction>.  The file
-  <file:Documentation/sound/README.OSS> contains some slightly
-  outdated but still useful information as well.
-
-  If you have a PnP sound card and you want to configure it at boot
-  time using the ISA PnP tools (read
-  <http://www.roestock.demon.co.uk/isapnptools/>), then you need to
-  compile the sound card support as a module ( = code which can be
-  inserted in and removed from the running kernel whenever you want)
-  and load that module after the PnP configuration is finished.  To do
-  this, say M here and read <file:Documentation/modules.txt> as well
-  as <file:Documentation/sound/README.modules>; the module will be
-  called soundcore.o.
-
-  I'm told that even without a sound card, you can make your computer
-  say more than an occasional beep, by programming the PC speaker.
-  Kernel patches and supporting utilities to do that are in the pcsp
-  package, available at <ftp://ftp.infradead.org/pub/pcsp/>.
-
 CONFIG_PREEMPT
   This option reduces the latency of the kernel when reacting to
   real-time or interactive events by allowing a low priority process to
@@ -529,3 +499,13 @@
   klogd/syslogd or the X server.You should normally N here, unless
   you want to debug such a crash.
   
+CONFIG_X86_MCE_NONFATAL
+  Enabling this feature starts a timer that triggers every 5 seconds which
+  will look at the machine check registers to see if anything happened.
+  Non-fatal problems automatically get corrected (but still logged).
+  Disable this if you don't want to see these messages.
+  Seeing the messages this option prints out may be indicative of dying hardware,
+  or out-of-spec (ie, overclocked) hardware.
+  This option only does something on hardware with Intel P6 style MCE.
+  (Pentium Pro and above, AMD Athlon/Duron)
+
diff -Nru a/arch/x86_64/Makefile b/arch/x86_64/Makefile
--- a/arch/x86_64/Makefile	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/Makefile	Mon Apr 22 15:32:19 2002
@@ -17,20 +17,19 @@
 # 20000913  Pavel Machek <pavel@suse.cz>
 #	    Converted for x86_64 architecture
 # 20010105  Andi Kleen, add IA32 compiler.
+#           ....and later removed it again....
 #
-# $Id: Makefile,v 1.28 2001/06/29 17:47:43 aj Exp $
-
+# $Id: Makefile,v 1.31 2002/03/22 15:56:07 ak Exp $
 
 #
-# boot system currently needs IA32 tools to link (to be fixed) 
+# early bootup linking needs 32bit. You can either use real 32bit tools
+# here or 64bit tools switch to 32bit mode.
 #
-# Change this to your i386 compiler/binutils
-IA32_PREFIX := /usr/bin/
-IA32_CC := $(IA32_PREFIX)gcc -O2 -fomit-frame-pointer -nostdinc -I $(HPATH)
-IA32_LD := $(IA32_PREFIX)ld
-IA32_AS := $(IA32_PREFIX)gcc -D__ASSEMBLY__ -traditional -c -nostdinc -I $(HPATH) 
-IA32_OBJCOPY := $(IA32_PREFIX)objcopy
-IA32_CPP := $(IA32_PREFIX)gcc -E
+IA32_CC := $(CROSS_COMPILE)gcc -m32 -O2 -fomit-frame-pointer -nostdinc -I $(HPATH)
+IA32_LD := $(CROSS_COMPILE)ld -m elf_i386
+IA32_AS := $(CROSS_COMPILE)gcc -m32 -Wa,--32 -D__ASSEMBLY__ -traditional -c -nostdinc -I $(HPATH) 
+IA32_OBJCOPY := $(CROSS_COMPILE)objcopy
+IA32_CPP := $(CROSS_COMPILE)gcc -m32 -E
 export IA32_CC IA32_LD IA32_AS IA32_OBJCOPY IA32_CPP
 
 
@@ -39,16 +38,14 @@
 LDFLAGS=-e stext
 LINKFLAGS =-T $(TOPDIR)/arch/x86_64/vmlinux.lds $(LDFLAGS)
 
-CFLAGS += $(shell if $(CC) -mno-red-zone -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-mno-red-zone"; fi ) 
+CFLAGS += -mno-red-zone
 CFLAGS += -mcmodel=kernel
 CFLAGS += -pipe
-# generates worse code, but makes the assembly much more readable:
 CFLAGS += -fno-reorder-blocks	
-# work around early gcc 3.1 bugs. Later snapshots should this already fixed.
+# needed for later gcc 3.1
+CFLAGS += -finline-limit=2000
+# needed for earlier gcc 3.1
 CFLAGS += -fno-strength-reduce
-# make sure all inline functions are inlined
-CFLAGS += -finline-limit=3000
-
 #CFLAGS += -g
 
 # prevent gcc from keeping the stack 16 byte aligned (FIXME)
@@ -61,8 +58,6 @@
 CORE_FILES +=  arch/x86_64/mm/mm.o
 LIBS := $(TOPDIR)/arch/x86_64/lib/lib.a $(LIBS)
 
-CLEAN_FILES += include/asm-x86_64/offset.h
-
 ifdef CONFIG_IA32_EMULATION
 SUBDIRS += arch/x86_64/ia32
 CORE_FILES += arch/x86_64/ia32/ia32.o
@@ -88,14 +83,14 @@
 
 vmlinux: arch/x86_64/vmlinux.lds
 
-checkoffset: FORCE
-	make -C arch/$(ARCH)/tools $(TOPDIR)/include/asm-x86_64/offset.h
-
 FORCE: ;
 
 .PHONY: zImage bzImage compressed zlilo bzlilo zdisk bzdisk install \
 		clean archclean archmrproper archdep checkoffset
 
+checkoffset: FORCE
+	make -C arch/$(ARCH)/tools $(TOPDIR)/include/asm-x86_64/offset.h
+
 bzImage: checkoffset vmlinux
 	@$(MAKEBOOT) bzImage
 
@@ -104,12 +99,10 @@
 
 tmp:
 	@$(MAKEBOOT) BOOTIMAGE=bzImage zlilo
+
 bzlilo: checkoffset vmlinux
 	@$(MAKEBOOT) BOOTIMAGE=bzImage zlilo
 
-zdisk: checkoffset vmlinux
-	@$(MAKEBOOT) BOOTIMAGE=zImage zdisk
-
 bzdisk: checkoffset vmlinux
 	@$(MAKEBOOT) BOOTIMAGE=bzImage zdisk
 
@@ -118,7 +111,7 @@
 
 archclean:
 	@$(MAKEBOOT) clean
-	$(MAKE) -C $(TOPDIR)/arch/x86_64/tools clean
+	@$(MAKE) -C $(TOPDIR)/arch/x86_64/tools clean
 
 archmrproper:
 	rm -f $(TOPDIR)/arch/x86_64/tools/offset.h
diff -Nru a/arch/x86_64/boot/setup.S b/arch/x86_64/boot/setup.S
--- a/arch/x86_64/boot/setup.S	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/boot/setup.S	Mon Apr 22 15:32:19 2002
@@ -435,36 +435,7 @@
 	rep
 	stosb
 is_disk1:
-# check for Micro Channel (MCA) bus
-	movw	%cs, %ax			# aka SETUPSEG
-	subw	$DELTA_INITSEG, %ax		# aka INITSEG
-	movw	%ax, %ds
-	xorw	%ax, %ax
-	movw	%ax, (0xa0)			# set table length to 0
-	movb	$0xc0, %ah
-	stc
-	int	$0x15				# moves feature table to es:bx
-	jc	no_mca
-
-	pushw	%ds
-	movw	%es, %ax
-	movw	%ax, %ds
-	movw	%cs, %ax			# aka SETUPSEG
-	subw	$DELTA_INITSEG, %ax		# aka INITSEG
-	movw	%ax, %es
-	movw	%bx, %si
-	movw	$0xa0, %di
-	movw	(%si), %cx
-	addw	$2, %cx				# table length is a short
-	cmpw	$0x10, %cx
-	jc	sysdesc_ok
 
-	movw	$0x10, %cx			# we keep only first 16 bytes
-sysdesc_ok:
-	rep
-	movsb
-	popw	%ds
-no_mca:
 # Check for PS/2 pointing device
 	movw	%cs, %ax			# aka SETUPSEG
 	subw	$DELTA_INITSEG, %ax		# aka INITSEG
@@ -477,71 +448,11 @@
 	movw	$0xAA, (0x1ff)			# device present
 no_psmouse:
 
-#if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE)
-# Then check for an APM BIOS...
-						# %ds points to the bootsector
-	movw	$0, 0x40			# version = 0 means no APM BIOS
-	movw	$0x05300, %ax			# APM BIOS installation check
-	xorw	%bx, %bx
-	int	$0x15
-	jc	done_apm_bios			# Nope, no APM BIOS
-	
-	cmpw	$0x0504d, %bx			# Check for "PM" signature
-	jne	done_apm_bios			# No signature, no APM BIOS
-
-	andw	$0x02, %cx			# Is 32 bit supported?
-	je	done_apm_bios			# No 32-bit, no (good) APM BIOS
-
-	movw	$0x05304, %ax			# Disconnect first just in case
-	xorw	%bx, %bx
-	int	$0x15				# ignore return code
-	movw	$0x05303, %ax			# 32 bit connect
-	xorl	%ebx, %ebx
-	xorw	%cx, %cx			# paranoia :-)
-	xorw	%dx, %dx			#   ...
-	xorl	%esi, %esi			#   ...
-	xorw	%di, %di			#   ...
-	int	$0x15
-	jc	no_32_apm_bios			# Ack, error. 
-
-	movw	%ax,  (66)			# BIOS code segment
-	movl	%ebx, (68)			# BIOS entry point offset
-	movw	%cx,  (72)			# BIOS 16 bit code segment
-	movw	%dx,  (74)			# BIOS data segment
-	movl	%esi, (78)			# BIOS code segment lengths
-	movw	%di,  (82)			# BIOS data segment length
-# Redo the installation check as the 32 bit connect
-# modifies the flags returned on some BIOSs
-	movw	$0x05300, %ax			# APM BIOS installation check
-	xorw	%bx, %bx
-	xorw	%cx, %cx			# paranoia
-	int	$0x15
-	jc	apm_disconnect			# error -> shouldn't happen
-
-	cmpw	$0x0504d, %bx			# check for "PM" signature
-	jne	apm_disconnect			# no sig -> shouldn't happen
-
-	movw	%ax, (64)			# record the APM BIOS version
-	movw	%cx, (76)			# and flags
-	jmp	done_apm_bios
-
-apm_disconnect:					# Tidy up
-	movw	$0x05304, %ax			# Disconnect
-	xorw	%bx, %bx
-	int	$0x15				# ignore return code
-
-	jmp	done_apm_bios
-
-no_32_apm_bios:
-	andw	$0xfffd, (76)			# remove 32 bit support bit
-done_apm_bios:
-#endif
-
 # Now we want to move to protected mode ...
 	cmpw	$0, %cs:realmode_swtch
 	jz	rmodeswtch_normal
 
-	lcall	*%cs:realmode_swtch
+	lcall	%cs:realmode_swtch
 
 	jmp	rmodeswtch_end
 
diff -Nru a/arch/x86_64/boot/video.S b/arch/x86_64/boot/video.S
--- a/arch/x86_64/boot/video.S	Mon Apr 22 15:32:18 2002
+++ b/arch/x86_64/boot/video.S	Mon Apr 22 15:32:18 2002
@@ -66,7 +66,6 @@
 #define VIDEO_80x30 0x0f05
 #define VIDEO_80x34 0x0f06
 #define VIDEO_80x60 0x0f07
-#define VIDEO_GFX_HACK 0x0f08
 #define VIDEO_LAST_SPECIAL 0x0f09
 
 /* Video modes given by resolution */
@@ -741,15 +740,7 @@
 	movw	$0x503c, force_size
 	jmp	setvde
 
-# Special hack for ThinkPad graphics
 set_gfx:
-#ifdef CONFIG_VIDEO_GFX_HACK
-	movw	$VIDEO_GFX_BIOS_AX, %ax
-	movw	$VIDEO_GFX_BIOS_BX, %bx
-	int	$0x10
-	movw	$VIDEO_GFX_DUMMY_RESOLUTION, force_size
-	stc
-#endif
 	ret
 
 #ifdef CONFIG_VIDEO_RETAIN
@@ -974,10 +965,6 @@
 	.word	0x5022				# 80x34
 	.word	VIDEO_80x60
 	.word	0x503c				# 80x60
-#ifdef CONFIG_VIDEO_GFX_HACK
-	.word	VIDEO_GFX_HACK
-	.word	VIDEO_GFX_DUMMY_RESOLUTION
-#endif
 
 vga_modes_end:
 # Detect VESA modes.
diff -Nru a/arch/x86_64/config.in b/arch/x86_64/config.in
--- a/arch/x86_64/config.in	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/config.in	Mon Apr 22 15:32:19 2002
@@ -11,16 +11,16 @@
 define_bool CONFIG_SBUS n
 
 define_bool CONFIG_UID16 y
-define_bool CONFIG_RWSEM_GENERIC_SPINLOCK n
-define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM y
-
+define_bool CONFIG_RWSEM_GENERIC_SPINLOCK y
+define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM n
+define_bool CONFIG_X86_CMPXCHG y
 
 source init/Config.in
 
 mainmenu_option next_comment
 comment 'Processor type and features'
 choice 'Processor family' \
-	"Clawhammer			CONFIG_MK8" Clawhammer
+	"AMD-Hammer			CONFIG_MK8" CONFIG_MK8
 	
 #
 # Define implied options from the CPU selection here
@@ -29,7 +29,6 @@
 define_int CONFIG_X86_L1_CACHE_SHIFT 6
 define_bool CONFIG_X86_TSC y
 define_bool CONFIG_X86_GOOD_APIC y
-define_bool CONFIG_X86_CMPXCHG y
 
 tristate '/dev/cpu/*/msr - Model-specific register support' CONFIG_X86_MSR
 tristate '/dev/cpu/*/cpuid - CPU information support' CONFIG_X86_CPUID
@@ -37,32 +36,31 @@
 define_bool CONFIG_MATH_EMULATION n
 define_bool CONFIG_MCA n
 define_bool CONFIG_EISA n
+define_bool CONFIG_X86_IO_APIC y
+define_bool CONFIG_X86_LOCAL_APIC y
 
-bool 'MTRR (Memory Type Range Register) support' CONFIG_MTRR
+#currently broken:
+#bool 'MTRR (Memory Type Range Register) support' CONFIG_MTRR
 bool 'Symmetric multi-processing support' CONFIG_SMP
 bool 'Preemptible Kernel' CONFIG_PREEMPT
-# currently doesn't boot without hacks. probably simulator bug.
-#if [ "$CONFIG_SMP" != "y" ]; then
-#   bool 'APIC and IO-APIC support on uniprocessors' CONFIG_X86_UP_IOAPIC
-#    if [ "$CONFIG_X86_UP_IOAPIC" = "y" ]; then
-#       define_bool CONFIG_X86_IO_APIC y
-#       define_bool CONFIG_X86_LOCAL_APIC y
-#    fi
-#fi
 if [ "$CONFIG_SMP" = "y" -a "$CONFIG_X86_CMPXCHG" = "y" ]; then
     define_bool CONFIG_HAVE_DEC_LOCK y
 fi
+
+define_bool CONFIG_X86_MCE y
+bool 'Check for non-fatal machine check errors' CONFIG_X86_MCE_NONFATAL $CONFIG_X86_MCE
+
 endmenu
 
 mainmenu_option next_comment
+
 comment 'General options'
 
-if [ "$CONFIG_SMP" = "y" ]; then
-   define_bool CONFIG_X86_IO_APIC y
-   define_bool CONFIG_X86_LOCAL_APIC y
-fi
+source drivers/acpi/Config.in
+
 bool 'PCI support' CONFIG_PCI
 if [ "$CONFIG_PCI" = "y" ]; then
+# x86-64 doesn't support PCI BIOS access from long mode so always go direct. 
    define_bool CONFIG_PCI_DIRECT y 
 fi
 
@@ -80,22 +78,14 @@
 if [ "$CONFIG_PROC_FS" = "y" ]; then
    define_bool CONFIG_KCORE_ELF y
 fi
-# We probably are not going to support a.out, are we? Or should we support a.out in i386 compatibility mode?
-		#tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT
-		tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
+#tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT
+tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
 tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC
 
 bool 'Power Management support' CONFIG_PM
 
 bool 'IA32 Emulation' CONFIG_IA32_EMULATION
 
-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-   dep_bool '  ACPI support' CONFIG_ACPI $CONFIG_PM
-   if [ "$CONFIG_ACPI" != "n" ]; then
-      source drivers/acpi/Config.in
-   fi
-fi
-
 endmenu
 
 source drivers/mtd/Config.in
@@ -161,15 +151,7 @@
 
 source net/irda/Config.in
 
-mainmenu_option next_comment
-comment 'ISDN subsystem'
-if [ "$CONFIG_NET" != "n" ]; then
-   tristate 'ISDN support' CONFIG_ISDN
-   if [ "$CONFIG_ISDN" != "n" ]; then
-      source drivers/isdn/Config.in
-   fi
-fi
-endmenu
+source drivers/isdn/Config.in
 
 mainmenu_option next_comment
 comment 'Old CD-ROM drivers (not SCSI, not IDE)'
@@ -186,10 +168,6 @@
 source drivers/input/Config.in
 source drivers/char/Config.in
 
-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-   source net/bluetooth/Config.in
-fi
-
 source drivers/misc/Config.in
 
 source drivers/media/Config.in
@@ -219,6 +197,10 @@
 
 source drivers/usb/Config.in
 
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+   source net/bluetooth/Config.in
+fi
+
 mainmenu_option next_comment
 comment 'Kernel hacking'
 
@@ -228,13 +210,13 @@
 #   bool '  Memory mapped I/O debugging' CONFIG_DEBUG_IOVIRT
    bool '  Magic SysRq key' CONFIG_MAGIC_SYSRQ
    bool '  Spinlock debugging' CONFIG_DEBUG_SPINLOCK
-#   bool '  Early printk' CONFIG_EARLY_PRINTK
+   bool '  Early printk' CONFIG_EARLY_PRINTK
    bool '  Additional run-time checks' CONFIG_CHECKING
-fi
-bool 'Simnow environment (disables time-consuming things)' CONFIG_SIMNOW
+   bool '  Debug __init statements' CONFIG_INIT_DEBUG
 #if [ "$CONFIG_SERIAL_CONSOLE" = "y" ]; then
 #  bool 'Early serial console (ttyS0)' CONFIG_EARLY_SERIAL_CONSOLE
 #fi
+fi
 endmenu
 
 source lib/Config.in
diff -Nru a/arch/x86_64/defconfig b/arch/x86_64/defconfig
--- a/arch/x86_64/defconfig	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/defconfig	Mon Apr 22 15:32:19 2002
@@ -6,8 +6,9 @@
 CONFIG_ISA=y
 # CONFIG_SBUS is not set
 CONFIG_UID16=y
-# CONFIG_RWSEM_GENERIC_SPINLOCK is not set
-CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set
+CONFIG_X86_CMPXCHG=y
 
 #
 # Code maturity level options
@@ -37,19 +38,27 @@
 CONFIG_X86_L1_CACHE_SHIFT=6
 CONFIG_X86_TSC=y
 CONFIG_X86_GOOD_APIC=y
-CONFIG_X86_CMPXCHG=y
 CONFIG_X86_MSR=y
 CONFIG_X86_CPUID=y
 # CONFIG_MATH_EMULATION is not set
 # CONFIG_MCA is not set
 # CONFIG_EISA is not set
-CONFIG_MTRR=y
-# CONFIG_SMP is not set
+CONFIG_X86_IO_APIC=y
+CONFIG_X86_LOCAL_APIC=y
+CONFIG_SMP=y
 # CONFIG_PREEMPT is not set
+CONFIG_HAVE_DEC_LOCK=y
+CONFIG_X86_MCE=y
+# CONFIG_X86_MCE_NONFATAL is not set
 
 #
 # General options
 #
+
+#
+# ACPI Support
+#
+# CONFIG_ACPI is not set
 CONFIG_PCI=y
 CONFIG_PCI_DIRECT=y
 # CONFIG_PCI_NAMES is not set
@@ -60,7 +69,6 @@
 # CONFIG_BINFMT_MISC is not set
 CONFIG_PM=y
 CONFIG_IA32_EMULATION=y
-# CONFIG_ACPI is not set
 
 #
 # Memory Technology Devices (MTD)
@@ -162,7 +170,7 @@
 CONFIG_IDE=y
 
 #
-# IDE, ATA and ATAPI Block devices
+# ATA and ATAPI Block devices
 #
 CONFIG_BLK_DEV_IDE=y
 
@@ -172,7 +180,7 @@
 # CONFIG_BLK_DEV_HD_IDE is not set
 # CONFIG_BLK_DEV_HD is not set
 CONFIG_BLK_DEV_IDEDISK=y
-# CONFIG_IDEDISK_MULTI_MODE is not set
+CONFIG_IDEDISK_MULTI_MODE=y
 # CONFIG_IDEDISK_STROKE is not set
 # CONFIG_BLK_DEV_IDEDISK_VENDOR is not set
 # CONFIG_BLK_DEV_IDEDISK_FUJITSU is not set
@@ -184,23 +192,55 @@
 # CONFIG_BLK_DEV_COMMERIAL is not set
 # CONFIG_BLK_DEV_TIVO is not set
 # CONFIG_BLK_DEV_IDECS is not set
-# CONFIG_BLK_DEV_IDECD is not set
+CONFIG_BLK_DEV_IDECD=y
 # CONFIG_BLK_DEV_IDETAPE is not set
 # CONFIG_BLK_DEV_IDEFLOPPY is not set
 # CONFIG_BLK_DEV_IDESCSI is not set
 
 #
-# IDE chipset support/bugfixes
+# IDE chipset support
 #
 # CONFIG_BLK_DEV_CMD640 is not set
 # CONFIG_BLK_DEV_CMD640_ENHANCED is not set
 # CONFIG_BLK_DEV_ISAPNP is not set
 # CONFIG_BLK_DEV_RZ1000 is not set
-# CONFIG_BLK_DEV_IDEPCI is not set
+CONFIG_BLK_DEV_IDEPCI=y
+# CONFIG_BLK_DEV_OFFBOARD is not set
+# CONFIG_IDEPCI_SHARE_IRQ is not set
+CONFIG_BLK_DEV_IDEDMA_PCI=y
+CONFIG_IDEDMA_PCI_AUTO=y
+# CONFIG_IDEDMA_ONLYDISK is not set
+CONFIG_BLK_DEV_IDEDMA=y
+# CONFIG_BLK_DEV_IDE_TCQ is not set
+# CONFIG_BLK_DEV_IDE_TCQ_DEFAULT is not set
+# CONFIG_IDEDMA_PCI_WIP is not set
+# CONFIG_IDEDMA_NEW_DRIVE_LISTINGS is not set
+# CONFIG_BLK_DEV_AEC62XX is not set
+# CONFIG_AEC62XX_TUNING is not set
+# CONFIG_BLK_DEV_ALI15X3 is not set
+# CONFIG_WDC_ALI15X3 is not set
+# CONFIG_BLK_DEV_AMD74XX is not set
+# CONFIG_BLK_DEV_CMD64X is not set
+# CONFIG_BLK_DEV_CY82C693 is not set
+# CONFIG_BLK_DEV_CS5530 is not set
+# CONFIG_BLK_DEV_HPT34X is not set
+# CONFIG_HPT34X_AUTODMA is not set
+# CONFIG_BLK_DEV_HPT366 is not set
+# CONFIG_BLK_DEV_PIIX is not set
+# CONFIG_BLK_DEV_NS87415 is not set
+# CONFIG_BLK_DEV_OPTI621 is not set
+# CONFIG_BLK_DEV_PDC_ADMA is not set
+# CONFIG_BLK_DEV_PDC202XX is not set
+# CONFIG_PDC202XX_BURST is not set
+# CONFIG_PDC202XX_FORCE is not set
+# CONFIG_BLK_DEV_SVWKS is not set
+# CONFIG_BLK_DEV_SIS5513 is not set
+# CONFIG_BLK_DEV_TRM290 is not set
+# CONFIG_BLK_DEV_VIA82CXXX is not set
 # CONFIG_IDE_CHIPSETS is not set
-# CONFIG_IDEDMA_AUTO is not set
+# CONFIG_IDEDMA_IVB is not set
+CONFIG_IDEDMA_AUTO=y
 # CONFIG_DMA_NONPCI is not set
-# CONFIG_BLK_DEV_IDE_MODES is not set
 # CONFIG_BLK_DEV_ATARAID is not set
 # CONFIG_BLK_DEV_ATARAID_PDC is not set
 # CONFIG_BLK_DEV_ATARAID_HPT is not set
@@ -316,11 +356,6 @@
 # CONFIG_MWAVE is not set
 
 #
-# Bluetooth support
-#
-# CONFIG_BLUEZ is not set
-
-#
 # Misc devices
 #
 
@@ -359,6 +394,9 @@
 # CONFIG_ISO9660_FS is not set
 # CONFIG_JOLIET is not set
 # CONFIG_ZISOFS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_JFS_DEBUG is not set
+# CONFIG_JFS_STATISTICS is not set
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
@@ -389,6 +427,7 @@
 # CONFIG_ROOT_NFS is not set
 # CONFIG_NFSD is not set
 # CONFIG_NFSD_V3 is not set
+# CONFIG_NFSD_TCP is not set
 # CONFIG_SUNRPC is not set
 # CONFIG_LOCKD is not set
 # CONFIG_SMB_FS is not set
@@ -434,110 +473,9 @@
 # CONFIG_USB is not set
 
 #
-# USB Host Controller Drivers
-#
-# CONFIG_USB_EHCI_HCD is not set
-# CONFIG_USB_OHCI_HCD is not set
-# CONFIG_USB_UHCI is not set
-# CONFIG_USB_UHCI_ALT is not set
-# CONFIG_USB_OHCI is not set
-
-#
-# USB Device Class drivers
-#
-# CONFIG_USB_AUDIO is not set
-# CONFIG_USB_BLUETOOTH is not set
-
-#
-#   SCSI support is needed for USB Storage
-#
-# CONFIG_USB_STORAGE is not set
-# CONFIG_USB_STORAGE_DEBUG is not set
-# CONFIG_USB_STORAGE_DATAFAB is not set
-# CONFIG_USB_STORAGE_FREECOM is not set
-# CONFIG_USB_STORAGE_ISD200 is not set
-# CONFIG_USB_STORAGE_DPCM is not set
-# CONFIG_USB_STORAGE_HP8200e is not set
-# CONFIG_USB_STORAGE_SDDR09 is not set
-# CONFIG_USB_STORAGE_JUMPSHOT is not set
-# CONFIG_USB_ACM is not set
-# CONFIG_USB_PRINTER is not set
-
-#
-# USB Human Interface Devices (HID)
-#
-
-#
-#   Input core support is needed for USB HID
-#
-
-#
-# USB Imaging devices
-#
-# CONFIG_USB_DC2XX is not set
-# CONFIG_USB_MDC800 is not set
-# CONFIG_USB_SCANNER is not set
-# CONFIG_USB_MICROTEK is not set
-# CONFIG_USB_HPUSBSCSI is not set
-
-#
-# USB Multimedia devices
-#
-
-#
-#   Video4Linux support is needed for USB Multimedia device support
-#
-
-#
-# USB Network adaptors
-#
-# CONFIG_USB_PEGASUS is not set
-# CONFIG_USB_KAWETH is not set
-# CONFIG_USB_CATC is not set
-# CONFIG_USB_CDCETHER is not set
-# CONFIG_USB_USBNET is not set
-
-#
-# USB port drivers
-#
-# CONFIG_USB_USS720 is not set
-
-#
-# USB Serial Converter support
-#
-# CONFIG_USB_SERIAL is not set
-# CONFIG_USB_SERIAL_GENERIC is not set
-# CONFIG_USB_SERIAL_BELKIN is not set
-# CONFIG_USB_SERIAL_WHITEHEAT is not set
-# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set
-# CONFIG_USB_SERIAL_EMPEG is not set
-# CONFIG_USB_SERIAL_FTDI_SIO is not set
-# CONFIG_USB_SERIAL_VISOR is not set
-# CONFIG_USB_SERIAL_IPAQ is not set
-# CONFIG_USB_SERIAL_IR is not set
-# CONFIG_USB_SERIAL_EDGEPORT is not set
-# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set
-# CONFIG_USB_SERIAL_KEYSPAN is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set
-# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set
-# CONFIG_USB_SERIAL_MCT_U232 is not set
-# CONFIG_USB_SERIAL_KLSI is not set
-# CONFIG_USB_SERIAL_PL2303 is not set
-# CONFIG_USB_SERIAL_CYBERJACK is not set
-# CONFIG_USB_SERIAL_XIRCOM is not set
-# CONFIG_USB_SERIAL_OMNINET is not set
-
-#
-# USB Miscellaneous drivers
+# Bluetooth support
 #
-# CONFIG_USB_RIO500 is not set
-# CONFIG_USB_AUERSWALD is not set
+# CONFIG_BLUEZ is not set
 
 #
 # Kernel hacking
@@ -546,8 +484,9 @@
 # CONFIG_DEBUG_SLAB is not set
 # CONFIG_MAGIC_SYSRQ is not set
 # CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_EARLY_PRINTK is not set
 # CONFIG_CHECKING is not set
-CONFIG_SIMNOW=y
+# CONFIG_INIT_DEBUG is not set
 
 #
 # Library routines
diff -Nru a/arch/x86_64/ia32/Makefile b/arch/x86_64/ia32/Makefile
--- a/arch/x86_64/ia32/Makefile	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/ia32/Makefile	Mon Apr 22 15:32:19 2002
@@ -7,11 +7,13 @@
 .S.o:
 	$(CC) $(AFLAGS) -c -o $*.o $<
 
+export-objs := ia32_ioctl.o	
+
 all: ia32.o
 
 O_TARGET := ia32.o
-obj-$(CONFIG_IA32_EMULATION)	 := ia32entry.o sys_ia32.o ia32_ioctl.o ia32_signal.o ia32_binfmt.o \
-	socket32.o ptrace32.o
+obj-$(CONFIG_IA32_EMULATION) := ia32entry.o sys_ia32.o ia32_ioctl.o ia32_signal.o \
+	ia32_binfmt.o fpu32.o socket32.o ptrace32.o
 
 clean::
 
diff -Nru a/arch/x86_64/ia32/fpu32.c b/arch/x86_64/ia32/fpu32.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/arch/x86_64/ia32/fpu32.c	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,172 @@
+/* 
+ * Copyright 2002 Andi Kleen, SuSE Labs.
+ * FXSAVE<->i387 conversion support. Based on code by Gareth Hughes.
+ * This is used for ptrace, signals and coredumps in 32bit emulation.
+ * $Id: fpu32.c,v 1.1 2002/03/21 14:16:32 ak Exp $
+ */ 
+
+#include <linux/sched.h>
+#include <asm/sigcontext32.h>
+#include <asm/processor.h>
+#include <asm/uaccess.h>
+#include <asm/i387.h>
+
+static inline unsigned short twd_i387_to_fxsr(unsigned short twd)
+{
+	unsigned int tmp; /* to avoid 16 bit prefixes in the code */
+ 
+	/* Transform each pair of bits into 01 (valid) or 00 (empty) */
+        tmp = ~twd;
+        tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */
+        /* and move the valid bits to the lower byte. */
+        tmp = (tmp | (tmp >> 1)) & 0x3333; /* 00VV00VV00VV00VV */
+        tmp = (tmp | (tmp >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */
+        tmp = (tmp | (tmp >> 4)) & 0x00ff; /* 00000000VVVVVVVV */
+        return tmp;
+}
+
+static inline unsigned long twd_fxsr_to_i387(struct i387_fxsave_struct *fxsave)
+{
+	struct _fpxreg *st = NULL;
+	unsigned long twd = (unsigned long) fxsave->twd;
+	unsigned long tag;
+	unsigned long ret = 0xffff0000;
+	int i;
+
+#define FPREG_ADDR(f, n)	((char *)&(f)->st_space + (n) * 16);
+
+	for (i = 0 ; i < 8 ; i++) {
+		if (twd & 0x1) {
+			st = (struct _fpxreg *) FPREG_ADDR( fxsave, i );
+
+			switch (st->exponent & 0x7fff) {
+			case 0x7fff:
+				tag = 2;		/* Special */
+				break;
+			case 0x0000:
+				if ( !st->significand[0] &&
+				     !st->significand[1] &&
+				     !st->significand[2] &&
+				     !st->significand[3] ) {
+					tag = 1;	/* Zero */
+				} else {
+					tag = 2;	/* Special */
+				}
+				break;
+			default:
+				if (st->significand[3] & 0x8000) {
+					tag = 0;	/* Valid */
+				} else {
+					tag = 2;	/* Special */
+				}
+				break;
+			}
+		} else {
+			tag = 3;			/* Empty */
+		}
+		ret |= (tag << (2 * i));
+		twd = twd >> 1;
+	}
+	return ret;
+}
+
+
+static inline int convert_fxsr_from_user(struct i387_fxsave_struct *fxsave,
+					 struct _fpstate_ia32 *buf)
+{
+	struct _fpxreg *to;
+	struct _fpreg *from;
+	int i;
+	int err; 
+	__u32 v;
+
+	err = __get_user(fxsave->cwd, (u16 *)&buf->cw); 
+	err |= __get_user(fxsave->swd, (u16 *)&buf->sw);
+	err |= __get_user(fxsave->twd, (u16 *)&buf->tag);
+	fxsave->twd = twd_i387_to_fxsr(fxsave->twd);
+	err |= __get_user(fxsave->rip, &buf->ipoff); 
+	err |= __get_user(fxsave->rdp, &buf->dataoff); 
+	err |= __get_user(v, &buf->cssel); 
+	fxsave->fop = v >> 16;
+	if (err) 
+		return -1; 
+
+	to = (struct _fpxreg *)&fxsave->st_space[0];
+	from = &buf->_st[0];
+	for (i = 0 ; i < 8 ; i++, to++, from++) {
+		if (__copy_from_user(to, from, sizeof(*from)))
+			return -1;
+	}
+	return 0;
+}
+
+
+static inline int convert_fxsr_to_user(struct _fpstate_ia32 *buf,
+				       struct i387_fxsave_struct *fxsave,
+				       struct pt_regs *regs,
+				       struct task_struct *tsk)
+{
+	struct _fpreg *to;
+	struct _fpxreg *from;
+	int i;
+	u32 ds; 
+	int err; 
+
+	err = __put_user((unsigned long)fxsave->cwd | 0xffff0000, &buf->cw);
+	err |= __put_user((unsigned long)fxsave->swd | 0xffff0000, &buf->sw);
+	err |= __put_user((u32)fxsave->rip, &buf->ipoff); 
+	err |= __put_user((u32)(regs->cs | ((u32)fxsave->fop << 16)), 
+			  &buf->cssel); 
+	err |= __put_user((u32)twd_fxsr_to_i387(fxsave), &buf->tag); 
+	err |= __put_user((u32)fxsave->rdp, &buf->dataoff); 
+	if (tsk == current) 
+		asm("movl %%ds,%0 " : "=r" (ds)); 
+	else /* ptrace. task has stopped. */
+		ds = tsk->thread.ds;
+	err |= __put_user(ds, &buf->datasel); 
+	if (err) 
+		return -1; 
+
+	to = &buf->_st[0];
+	from = (struct _fpxreg *) &fxsave->st_space[0];
+	for ( i = 0 ; i < 8 ; i++, to++, from++ ) {
+		if (__copy_to_user(to, from, sizeof(*to)))
+			return -1;
+	}
+	return 0;
+}
+
+int restore_i387_ia32(struct task_struct *tsk, struct _fpstate_ia32 *buf, int fsave) 
+{ 
+	clear_fpu(tsk);
+	if (!fsave) { 
+		if (__copy_from_user(&tsk->thread.i387.fxsave, 
+				     &buf->_fxsr_env[0],
+				     sizeof(struct i387_fxsave_struct)))
+			return -1;
+	} 
+	tsk->thread.i387.fxsave.mxcsr &= 0xffbf;
+	return convert_fxsr_from_user(&tsk->thread.i387.fxsave, buf);
+}  
+
+int save_i387_ia32(struct task_struct *tsk, 
+		   struct _fpstate_ia32 *buf, 
+		   struct pt_regs *regs,
+		   int fsave)
+{
+	int err = 0;
+
+	if (!tsk->used_math) 
+		return 0;
+	tsk->used_math = 0; 
+	unlazy_fpu(tsk);
+	if (convert_fxsr_to_user(buf, &tsk->thread.i387.fxsave, regs, tsk))
+		return -1;
+	err |= __put_user(tsk->thread.i387.fxsave.swd, &buf->status);
+	if (fsave) 
+		return err ? -1 : 1; 	
+	err |= __put_user(X86_FXSR_MAGIC, &buf->magic);
+	err |= __copy_to_user(&buf->_fxsr_env[0], &tsk->thread.i387.fxsave,
+			      sizeof(struct i387_fxsave_struct));
+	return err ? -1 : 1;
+}
diff -Nru a/arch/x86_64/ia32/ia32_binfmt.c b/arch/x86_64/ia32/ia32_binfmt.c
--- a/arch/x86_64/ia32/ia32_binfmt.c	Mon Apr 22 15:32:18 2002
+++ b/arch/x86_64/ia32/ia32_binfmt.c	Mon Apr 22 15:32:18 2002
@@ -1,16 +1,24 @@
 /* 
- * Written 2000 by Andi Kleen. 
+ * Written 2000,2002 by Andi Kleen. 
  * 
  * Losely based on the sparc64 and IA64 32bit emulation loaders.
+ * This tricks binfmt_elf.c into loading 32bit binaries using lots 
+ * of ugly preprocessor tricks. Talk about very very poor man's inheritance.
  */ 
 #include <linux/types.h>
 #include <linux/config.h> 
 #include <linux/stddef.h>
 #include <linux/module.h>
 #include <linux/rwsem.h>
+#include <linux/sched.h>
+#include <linux/string.h>
 #include <asm/segment.h> 
 #include <asm/ptrace.h>
 #include <asm/processor.h>
+#include <asm/user32.h>
+#include <asm/sigcontext32.h>
+#include <asm/fpu32.h>
+#include <asm/i387.h>
 
 struct file;
 struct elf_phdr; 
@@ -28,19 +36,99 @@
 #define ELF_CLASS ELFCLASS32
 
 #define ELF_DATA	ELFDATA2LSB
-//#define USE_ELF_CORE_DUMP
+
+#define USE_ELF_CORE_DUMP 1
+
+/* Overwrite elfcore.h */ 
+#define _LINUX_ELFCORE_H 1
+typedef unsigned int elf_greg_t;
+
+#define ELF_NGREG (sizeof (struct user_regs_struct32) / sizeof(elf_greg_t))
+typedef elf_greg_t elf_gregset_t[ELF_NGREG];
+
+struct elf_siginfo
+{
+	int	si_signo;			/* signal number */
+	int	si_code;			/* extra code */
+	int	si_errno;			/* errno */
+};
+
+struct timeval32
+{
+    int tv_sec, tv_usec;
+};
+
+struct elf_prstatus
+{
+	struct elf_siginfo pr_info;	/* Info associated with signal */
+	short	pr_cursig;		/* Current signal */
+	unsigned int pr_sigpend;	/* Set of pending signals */
+	unsigned int pr_sighold;	/* Set of held signals */
+	pid_t	pr_pid;
+	pid_t	pr_ppid;
+	pid_t	pr_pgrp;
+	pid_t	pr_sid;
+	struct timeval32 pr_utime;	/* User time */
+	struct timeval32 pr_stime;	/* System time */
+	struct timeval32 pr_cutime;	/* Cumulative user time */
+	struct timeval32 pr_cstime;	/* Cumulative system time */
+	elf_gregset_t pr_reg;	/* GP registers */
+	int pr_fpvalid;		/* True if math co-processor being used.  */
+};
+
+#define ELF_PRARGSZ	(80)	/* Number of chars for args */
+
+struct elf_prpsinfo
+{
+	char	pr_state;	/* numeric process state */
+	char	pr_sname;	/* char for pr_state */
+	char	pr_zomb;	/* zombie */
+	char	pr_nice;	/* nice val */
+	unsigned int pr_flag;	/* flags */
+	__u16	pr_uid;
+	__u16	pr_gid;
+	pid_t	pr_pid, pr_ppid, pr_pgrp, pr_sid;
+	/* Lots missing */
+	char	pr_fname[16];	/* filename of executable */
+	char	pr_psargs[ELF_PRARGSZ];	/* initial part of arg list */
+};
+
+#define __STR(x) #x
+#define STR(x) __STR(x)
+
+#define _GET_SEG(x) \
+	({ __u32 seg; asm("movl %%" STR(x) ",%0" : "=r"(seg)); seg; })
+
+/* Assumes current==process to be dumped */
+#define ELF_CORE_COPY_REGS(pr_reg, regs)       		\
+	pr_reg[0] = regs->rbx;				\
+	pr_reg[1] = regs->rcx;				\
+	pr_reg[2] = regs->rdx;				\
+	pr_reg[3] = regs->rsi;				\
+	pr_reg[4] = regs->rdi;				\
+	pr_reg[5] = regs->rbp;				\
+	pr_reg[6] = regs->rax;				\
+	pr_reg[7] = _GET_SEG(ds);   			\
+	pr_reg[8] = _GET_SEG(es);			\
+	pr_reg[9] = _GET_SEG(fs);			\
+	pr_reg[10] = _GET_SEG(gs);			\
+	pr_reg[11] = regs->orig_rax;			\
+	pr_reg[12] = regs->rip;				\
+	pr_reg[13] = regs->cs;				\
+	pr_reg[14] = regs->eflags;			\
+	pr_reg[15] = regs->rsp;				\
+	pr_reg[16] = regs->ss;
+
+#define user user32
+
+#define dump_fpu dump_fpu_ia32
 
 #define __ASM_X86_64_ELF_H 1
 #include <asm/ia32.h>
 #include <linux/elf.h>
 
-typedef __u32  elf_greg_t;
-
-typedef elf_greg_t elf_gregset_t[8];
-
-/* FIXME -- wrong */
 typedef struct user_i387_ia32_struct elf_fpregset_t;
-typedef struct user_i387_struct elf_fpxregset_t;
+typedef struct user32_fxsr_struct elf_fpxregset_t;
 
 #undef elf_check_arch
 #define elf_check_arch(x) \
@@ -177,11 +265,35 @@
 	unsigned long map_addr;
 	struct task_struct *me = current; 
 
+	if (prot & PROT_READ) 
+		prot |= PROT_EXEC; 
+
 	down_write(&me->mm->mmap_sem);
 	map_addr = do_mmap(filep, ELF_PAGESTART(addr),
-			   eppnt->p_filesz + ELF_PAGEOFFSET(eppnt->p_vaddr), prot, type|MAP_32BIT,
+			   eppnt->p_filesz + ELF_PAGEOFFSET(eppnt->p_vaddr), prot, 
+			   type|MAP_32BIT,
 			   eppnt->p_offset - ELF_PAGEOFFSET(eppnt->p_vaddr));
 	up_write(&me->mm->mmap_sem);
 	return(map_addr);
 }
 
+int dump_fpu_ia32(struct pt_regs *regs, elf_fpregset_t *fp)
+{
+	struct _fpstate_ia32 *fpu = (void*)fp; 
+	struct task_struct *tsk = current;
+	mm_segment_t oldfs = get_fs();
+	int ret;
+
+	if (!tsk->used_math) 
+		return 0;
+	if (!(test_thread_flag(TIF_IA32)))
+		BUG(); 
+	unlazy_fpu(tsk);
+	set_fs(KERNEL_DS); 
+	ret = save_i387_ia32(current, fpu, regs, 1);
+	/* Correct for i386 bug. It puts the fop into the upper 16bits of 
+	   the tag word (like FXSAVE), not into the fcs*/ 
+	fpu->cssel |= fpu->tag & 0xffff0000; 
+	set_fs(oldfs); 
+	return ret; 
+}
diff -Nru a/arch/x86_64/ia32/ia32_ioctl.c b/arch/x86_64/ia32/ia32_ioctl.c
--- a/arch/x86_64/ia32/ia32_ioctl.c	Mon Apr 22 15:32:18 2002
+++ b/arch/x86_64/ia32/ia32_ioctl.c	Mon Apr 22 15:32:18 2002
@@ -1,9 +1,9 @@
-/* $Id: ia32_ioctl.c,v 1.2 2001/07/05 06:28:42 ak Exp $
+/* $Id: ia32_ioctl.c,v 1.11 2002/04/18 14:36:37 ak Exp $
  * ioctl32.c: Conversion between 32bit and 64bit native ioctls.
  *
  * Copyright (C) 1997-2000  Jakub Jelinek  (jakub@redhat.com)
  * Copyright (C) 1998  Eddie C. Dost  (ecd@skynet.be)
- * Copyright (C) 2001  Andi Kleen, SuSE Labs 
+ * Copyright (C) 2001,2002  Andi Kleen, SuSE Labs 
  *
  * These routines maintain argument size conversion between 32bit and 64bit
  * ioctls.
@@ -51,6 +51,10 @@
 #include <linux/elevator.h>
 #include <linux/rtc.h>
 #include <linux/pci.h>
+#include <linux/rtc.h>
+#include <linux/module.h>
+#include <linux/serial.h>
+#include <linux/reiserfs_fs.h>
 #if defined(CONFIG_BLK_DEV_LVM) || defined(CONFIG_BLK_DEV_LVM_MODULE)
 /* Ugh. This header really is not clean */
 #define min min
@@ -2972,12 +2976,139 @@
 	return rw_long(fd, AUTOFS_IOC_SETTIMEOUT, arg);
 }
 
+/* SuSE extension */ 
+#ifndef TIOCGDEV
+#define TIOCGDEV       _IOR('T',0x32, unsigned int)
+#endif
+static int tiocgdev(unsigned fd, unsigned cmd,  unsigned int *ptr) 
+{ 
+
+	struct file *file = fget(fd);
+	struct tty_struct *real_tty;
+
+	if (!fd)
+		return -EBADF;
+	if (file->f_op->ioctl != tty_ioctl)
+		return -EINVAL; 
+	real_tty = (struct tty_struct *)file->private_data;
+	if (!real_tty) 	
+		return -EINVAL; 
+	return put_user(kdev_t_to_nr(real_tty->device), ptr); 
+} 
+
+
+struct raw32_config_request 
+{
+	int	raw_minor;
+	__u64	block_major;
+	__u64	block_minor;
+} __attribute__((packed));
+
+static int raw_ioctl(unsigned fd, unsigned cmd,  void *ptr) 
+{ 
+	int ret;
+	switch (cmd) { 
+	case RAW_SETBIND:
+	case RAW_GETBIND: {
+		struct raw_config_request req; 
+		struct raw32_config_request *user_req = ptr;
+		mm_segment_t oldfs = get_fs(); 
+
+		if (get_user(req.raw_minor, &user_req->raw_minor) ||
+		    get_user(req.block_major, &user_req->block_major) ||
+		    get_user(req.block_minor, &user_req->block_minor))
+			return -EFAULT;
+		set_fs(KERNEL_DS); 
+		ret = sys_ioctl(fd,cmd,(unsigned long)&req); 
+		set_fs(oldfs); 
+		break;
+	}
+	default:
+		ret = sys_ioctl(fd,cmd,(unsigned long)ptr);
+		break;
+	} 
+	return ret; 		
+} 
+
+struct serial_struct32 {
+	int	type;
+	int	line;
+	unsigned int	port;
+	int	irq;
+	int	flags;
+	int	xmit_fifo_size;
+	int	custom_divisor;
+	int	baud_base;
+	unsigned short	close_delay;
+	char	io_type;
+	char	reserved_char[1];
+	int	hub6;
+	unsigned short	closing_wait; /* time to wait before closing */
+	unsigned short	closing_wait2; /* no longer used... */
+	__u32 iomem_base;
+	unsigned short	iomem_reg_shift;
+	unsigned int	port_high;
+	int	reserved[1];
+};
+
+static int serial_struct_ioctl(unsigned fd, unsigned cmd,  void *ptr) 
+{
+	typedef struct serial_struct SS;
+	struct serial_struct32 *ss32 = ptr; 
+	int err = 0;
+	struct serial_struct ss; 
+	mm_segment_t oldseg = get_fs(); 
+	set_fs(KERNEL_DS);
+	if (cmd == TIOCSSERIAL) { 
+		err = -EFAULT;
+		if (copy_from_user(&ss, ss32, sizeof(struct serial_struct32)))
+			goto out;
+		memmove(&ss.iomem_reg_shift, ((char*)&ss.iomem_base)+4, 
+			sizeof(SS)-offsetof(SS,iomem_reg_shift)); 
+		ss.iomem_base = (void *)((unsigned long)ss.iomem_base & 0xffffffff);
+	}
+	if (!err)
+		err = sys_ioctl(fd,cmd,(unsigned long)(&ss)); 
+	if (cmd == TIOCGSERIAL && err >= 0) { 
+		__u32 base;
+		if (__copy_to_user(ss32,&ss,offsetof(SS,iomem_base)) ||
+		    __copy_to_user(&ss32->iomem_reg_shift,
+				   &ss.iomem_reg_shift,
+				   sizeof(SS) - offsetof(SS, iomem_reg_shift)))
+			err = -EFAULT;
+		if (ss.iomem_base > (unsigned char *)0xffffffff)
+			base = -1; 
+		else
+			base = (unsigned long)ss.iomem_base;
+		err |= __put_user(base, &ss32->iomem_base); 		
+	} 
+ out:
+	set_fs(oldseg);
+	return err;	
+}
+
 struct ioctl_trans {
 	unsigned long cmd;
-	unsigned long handler;
+	int (*handler)(unsigned int, unsigned int, unsigned long, struct file * filp);
 	struct ioctl_trans *next;
 };
 
+/* generic function to change a single long put_user to arg to 32bit */
+static int arg2long(unsigned int fd, unsigned int cmd, unsigned long arg)
+{
+	int ret; 
+	unsigned long val = 0; 
+	mm_segment_t oldseg = get_fs();
+	set_fs(KERNEL_DS);
+	ret = sys_ioctl(fd, cmd, (unsigned long)&val);  
+	set_fs(oldseg); 
+	if (!ret || val) {
+		if (put_user((int)val, (unsigned int *)arg)) 
+			return -EFAULT; 
+	}
+	return ret; 
+} 
+
 #define REF_SYMBOL(handler) if (0) (void)handler;
 #define HANDLE_IOCTL2(cmd,handler) REF_SYMBOL(handler);  asm volatile(".quad %c0, " #handler ",0"::"i" (cmd)); 
 #define HANDLE_IOCTL(cmd,handler) HANDLE_IOCTL2(cmd,handler)
@@ -2986,7 +3117,7 @@
 #define IOCTL_TABLE_END  asm volatile("\nioctl_end:"); }
 
 IOCTL_TABLE_START
-/* List here exlicitly which ioctl's are known to have
+/* List here explicitly which ioctl's are known to have
  * compatable types passed or none at all...
  */
 /* Big T */
@@ -3002,6 +3133,7 @@
 COMPATIBLE_IOCTL(TCSETSW)
 COMPATIBLE_IOCTL(TCSETSF)
 COMPATIBLE_IOCTL(TIOCLINUX)
+HANDLE_IOCTL(TIOCGDEV, tiocgdev)
 /* Little t */
 COMPATIBLE_IOCTL(TIOCGETD)
 COMPATIBLE_IOCTL(TIOCSETD)
@@ -3025,8 +3157,6 @@
 COMPATIBLE_IOCTL(TIOCSCTTY)
 COMPATIBLE_IOCTL(TIOCGPTN)
 COMPATIBLE_IOCTL(TIOCSPTLCK)
-COMPATIBLE_IOCTL(TIOCGSERIAL)
-COMPATIBLE_IOCTL(TIOCSSERIAL)
 COMPATIBLE_IOCTL(TIOCSERGETLSR)
 COMPATIBLE_IOCTL(FBIOGET_VSCREENINFO)
 COMPATIBLE_IOCTL(FBIOPUT_VSCREENINFO)
@@ -3082,6 +3212,8 @@
 COMPATIBLE_IOCTL(BLKROGET)
 COMPATIBLE_IOCTL(BLKRRPART)
 COMPATIBLE_IOCTL(BLKFLSBUF)
+COMPATIBLE_IOCTL(BLKRASET)
+COMPATIBLE_IOCTL(BLKFRASET)
 COMPATIBLE_IOCTL(BLKSECTSET)
 COMPATIBLE_IOCTL(BLKSSZGET)
 
@@ -3203,7 +3335,7 @@
 COMPATIBLE_IOCTL(RTC_SET_TIME)
 COMPATIBLE_IOCTL(RTC_WKALM_SET)
 COMPATIBLE_IOCTL(RTC_WKALM_RD)
-COMPATIBLE_IOCTL(RTC_IRQP_READ)
+HANDLE_IOCTL(RTC_IRQP_READ,arg2long)
 COMPATIBLE_IOCTL(RTC_IRQP_SET)
 COMPATIBLE_IOCTL(RTC_EPOCH_READ)
 COMPATIBLE_IOCTL(RTC_EPOCH_SET)
@@ -3479,9 +3611,6 @@
 COMPATIBLE_IOCTL(DEVFSDIOC_SET_EVENT_MASK)
 COMPATIBLE_IOCTL(DEVFSDIOC_RELEASE_EVENT_QUEUE)
 COMPATIBLE_IOCTL(DEVFSDIOC_SET_DEBUG_MASK)
-/* Raw devices */
-COMPATIBLE_IOCTL(RAW_SETBIND)
-COMPATIBLE_IOCTL(RAW_GETBIND)
 /* SMB ioctls which do not need any translations */
 COMPATIBLE_IOCTL(SMB_IOC_NEWCONN)
 /* Little a */
@@ -3543,6 +3672,18 @@
 COMPATIBLE_IOCTL(DRM_IOCTL_UNLOCK)
 COMPATIBLE_IOCTL(DRM_IOCTL_FINISH)
 #endif /* DRM */
+#ifdef CONFIG_AUTOFS_FS
+COMPATIBLE_IOCTL(AUTOFS_IOC_READY);
+COMPATIBLE_IOCTL(AUTOFS_IOC_FAIL);
+COMPATIBLE_IOCTL(AUTOFS_IOC_CATATONIC);
+COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER);
+COMPATIBLE_IOCTL(AUTOFS_IOC_SETTIMEOUT);
+COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE);
+#endif
+COMPATIBLE_IOCTL(REISERFS_IOC_UNPACK);
+/* serial driver */ 
+HANDLE_IOCTL(TIOCGSERIAL, serial_struct_ioctl);
+HANDLE_IOCTL(TIOCSSERIAL, serial_struct_ioctl);
 /* elevator */
 COMPATIBLE_IOCTL(BLKELVGET)
 COMPATIBLE_IOCTL(BLKELVSET)
@@ -3589,6 +3730,8 @@
 HANDLE_IOCTL(SIOCETHTOOL, ethtool_ioctl)
 HANDLE_IOCTL(SIOCADDRT, routing_ioctl)
 HANDLE_IOCTL(SIOCDELRT, routing_ioctl)
+/* Raw devices */
+HANDLE_IOCTL(RAW_SETBIND, raw_ioctl)
 /* Note SIOCRTMSG is no longer, so this is safe and * the user would have seen just an -EINVAL anyways. */
 HANDLE_IOCTL(SIOCRTMSG, ret_einval)
 HANDLE_IOCTL(SIOCGSTAMP, do_siocgstamp)
@@ -3751,14 +3894,14 @@
 
 /* Always call these with kernel lock held! */
 
+
 int register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int, unsigned int, unsigned long, struct file *))
 {
 	int i;
 	if (!additional_ioctls) {
-		additional_ioctls = module_map(PAGE_SIZE);
+		additional_ioctls = (struct ioctl_trans *)get_zeroed_page(GFP_KERNEL);
 		if (!additional_ioctls)
 			return -ENOMEM;
-		memset(additional_ioctls, 0, PAGE_SIZE);
 	}
 	for (i = 0; i < PAGE_SIZE/sizeof(struct ioctl_trans); i++)
 		if (!additional_ioctls[i].cmd)
@@ -3767,13 +3910,15 @@
 		return -ENOMEM;
 	additional_ioctls[i].cmd = cmd;
 	if (!handler)
-		additional_ioctls[i].handler = (u32)(long)sys_ioctl;
+		additional_ioctls[i].handler = 
+	    (int (*)(unsigned,unsigned,unsigned long, struct file *))sys_ioctl;
 	else
-		additional_ioctls[i].handler = (u32)(long)handler;
+		additional_ioctls[i].handler = handler;
 	ioctl32_insert_translation(&additional_ioctls[i]);
 	return 0;
 }
 
+
 int unregister_ioctl32_conversion(unsigned int cmd)
 {
 	unsigned long hash = ioctl32_hash(cmd);
@@ -3799,6 +3944,9 @@
 	return -EINVAL;
 }
 
+EXPORT_SYMBOL(register_ioctl32_conversion); 
+EXPORT_SYMBOL(unregister_ioctl32_conversion); 
+
 asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
 {
 	struct file * filp;
@@ -3820,7 +3968,7 @@
 	while (t && t->cmd != cmd)
 		t = (struct ioctl_trans *)(long)t->next;
 	if (t) {
-		handler = (void *)(long)t->handler;
+		handler = t->handler;
 		error = handler(fd, cmd, arg, filp);
 	} else {
 		static int count = 0;
diff -Nru a/arch/x86_64/ia32/ia32_signal.c b/arch/x86_64/ia32/ia32_signal.c
--- a/arch/x86_64/ia32/ia32_signal.c	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/ia32/ia32_signal.c	Mon Apr 22 15:32:19 2002
@@ -7,7 +7,7 @@
  *  2000-06-20  Pentium III FXSR, SSE support by Gareth Hughes
  *  2000-12-*   x86-64 compatibility mode signal handling by Andi Kleen
  * 
- *  $Id: ia32_signal.c,v 1.15 2001/10/16 23:41:42 ak Exp $
+ *  $Id: ia32_signal.c,v 1.17 2002/03/21 14:16:32 ak Exp $
  */
 
 #include <linux/sched.h>
@@ -29,6 +29,8 @@
 #include <asm/ptrace.h>
 #include <asm/ia32_unistd.h>
 #include <asm/user32.h>
+#include <asm/sigcontext32.h>
+#include <asm/fpu32.h>
 
 #define ptr_to_u32(x) ((u32)(u64)(x))	/* avoid gcc warning */ 
 
@@ -147,7 +149,7 @@
 };
 
 static int
-restore_sigcontext(struct pt_regs *regs, struct sigcontext_ia32 *sc, unsigned int *peax)
+ia32_restore_sigcontext(struct pt_regs *regs, struct sigcontext_ia32 *sc, unsigned int *peax)
 {
 	unsigned int err = 0;
 	
@@ -171,7 +173,13 @@
 	/* Reload fs and gs if they have changed in the signal handler.
 	   This does not handle long fs/gs base changes in the handler, but does not clobber 
 	   them at least in the normal case. */ 
-	RELOAD_SEG(gs);
+	
+	
+	{
+		unsigned short gs; 
+		err |= __get_user(gs, &sc->gs);
+		load_gs_index(gs); 
+	} 
 	RELOAD_SEG(fs);
 
 	COPY(di); COPY(si); COPY(bp); COPY(sp); COPY(bx);
@@ -187,13 +195,13 @@
 
 	{
 		u32 tmp;
-		struct _fpstate * buf;
+		struct _fpstate_ia32 * buf;
 		err |= __get_user(tmp, &sc->fpstate);
-		buf = (struct _fpstate *) (u64)tmp;
+		buf = (struct _fpstate_ia32 *) (u64)tmp;
 		if (buf) {
 			if (verify_area(VERIFY_READ, buf, sizeof(*buf)))
 				goto badframe;
-			err |= restore_i387(buf);
+			err |= restore_i387_ia32(current, buf, 0);
 		}
 	}
 
@@ -228,7 +236,7 @@
 	recalc_sigpending();
 	spin_unlock_irq(&current->sigmask_lock);
 	
-	if (restore_sigcontext(&regs, &frame->sc, &eax))
+	if (ia32_restore_sigcontext(&regs, &frame->sc, &eax))
 		goto badframe;
 	return eax;
 
@@ -255,7 +263,7 @@
 	recalc_sigpending();
 	spin_unlock_irq(&current->sigmask_lock);
 	
-	if (restore_sigcontext(&regs, &frame->uc.uc_mcontext, &eax))
+	if (ia32_restore_sigcontext(&regs, &frame->uc.uc_mcontext, &eax))
 		goto badframe;
 
 	if (__copy_from_user(&st, &frame->uc.uc_stack, sizeof(st)))
@@ -281,7 +289,7 @@
  */
 
 static int
-setup_sigcontext(struct sigcontext_ia32 *sc, struct _fpstate_ia32 *fpstate,
+ia32_setup_sigcontext(struct sigcontext_ia32 *sc, struct _fpstate_ia32 *fpstate,
 		 struct pt_regs *regs, unsigned int mask)
 {
 	int tmp, err = 0;
@@ -306,7 +314,7 @@
 	err |= __put_user((u32)regs->eflags, &sc->eflags);
 	err |= __put_user((u32)regs->rsp, &sc->esp_at_signal);
 
-	tmp = save_i387(fpstate);
+	tmp = save_i387_ia32(current, fpstate, regs, 0);
 	if (tmp < 0)
 	  err = -EFAULT;
 	else
@@ -351,23 +359,24 @@
 {
 	struct sigframe *frame;
 	int err = 0;
-	struct exec_domain *exec_domain = current_thread_info()->exec_domain; 
 
 	frame = get_sigframe(ka, regs, sizeof(*frame));
 
 	if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
 		goto give_sigsegv;
 
-	err |= __put_user((exec_domain
-		           && exec_domain->signal_invmap
-		           && sig < 32
-		           ? exec_domain->signal_invmap[sig]
+	{
+		struct exec_domain *ed = current_thread_info()->exec_domain;
+		
+		err |= __put_user((ed && ed->signal_invmap && sig < 32
+		           ? ed->signal_invmap[sig]
 		           : sig),
 		          &frame->sig);
+	}
 	if (err)
 		goto give_sigsegv;
 
-	err |= setup_sigcontext(&frame->sc, &frame->fpstate, regs, set->sig[0]);
+	err |= ia32_setup_sigcontext(&frame->sc, &frame->fpstate, regs, set->sig[0]);
 	if (err)
 		goto give_sigsegv;
 
@@ -419,19 +428,19 @@
 {
 	struct rt_sigframe *frame;
 	int err = 0;
-	struct exec_domain *exec_domain = current_thread_info()->exec_domain; 
 
 	frame = get_sigframe(ka, regs, sizeof(*frame));
 
 	if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
 		goto give_sigsegv;
 
-	err |= __put_user((exec_domain
-		    	   && exec_domain->signal_invmap
-		    	   && sig < 32
-		    	   ? exec_domain->signal_invmap[sig]
+	{
+		struct exec_domain *ed = current_thread_info()->exec_domain;
+		err |= __put_user((ed && ed->signal_invmap && sig < 32
+		    	   ? ed->signal_invmap[sig]
 			   : sig),
 			  &frame->sig);
+	}
 	err |= __put_user((u32)(u64)&frame->info, &frame->pinfo);
 	err |= __put_user((u32)(u64)&frame->uc, &frame->puc);
 	err |= ia32_copy_siginfo_to_user(&frame->info, info);
@@ -445,7 +454,7 @@
 	err |= __put_user(sas_ss_flags(regs->rsp),
 			  &frame->uc.uc_stack.ss_flags);
 	err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size);
-	err |= setup_sigcontext(&frame->uc.uc_mcontext, &frame->fpstate,
+	err |= ia32_setup_sigcontext(&frame->uc.uc_mcontext, &frame->fpstate,
 			        regs, set->sig[0]);
 	err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
 	if (err)
diff -Nru a/arch/x86_64/ia32/ia32entry.S b/arch/x86_64/ia32/ia32entry.S
--- a/arch/x86_64/ia32/ia32entry.S	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/ia32/ia32entry.S	Mon Apr 22 15:32:19 2002
@@ -1,17 +1,18 @@
 /*
  * Compatibility mode system call entry point for x86-64. 
  * 		
- * Copyright 2000,2001,2002 Andi Kleen, SuSE Labs.
+ * Copyright 2000,2001 Andi Kleen, SuSE Labs.
  * 
- * $Id: ia32entry.S,v 1.24 2001/11/11 17:47:47 ak Exp $ 		
+ * $Id: ia32entry.S,v 1.31 2002/03/24 13:01:45 ak Exp $ 		
  */		 
 
 #include <asm/calling.h>
 #include <asm/offset.h>
-#include <asm/thread_info.h>
+#include <asm/current.h>
 #include <linux/linkage.h>
 #include <asm/errno.h>
 #include <asm/ia32_unistd.h>	
+#include <asm/thread_info.h>	
 
 	.macro IA32_ARG_FIXUP
 	movl	%edi,%r8d
@@ -29,7 +30,7 @@
  */ 	
 ENTRY(ia32_cstar_target)
 	movq $-ENOSYS,%rax
-	SYSRET32
+	sysret
 	
 /* 
  * Emulated IA32 system calls via int 0x80. 
@@ -65,7 +66,6 @@
 	cmpl $(IA32_NR_syscalls),%eax
 	jae  ia32_badsys
 	IA32_ARG_FIXUP
-	movl $1,%r10d
 	call *ia32_sys_call_table(,%rax,8) # xxx: rip relative
 	movq %rax,RAX-ARGOFFSET(%rsp)
 	jmp int_ret_from_sys_call 
@@ -106,7 +106,7 @@
 	PTREGSCALL stub32_rt_sigsuspend, sys_rt_sigsuspend
 
 ENTRY(ia32_ptregs_common)
-	popq %r11	/* save return address outside the stack frame. */
+	popq %r11
 	SAVE_REST
 	movq %r11, %r15
 	call *%rax
@@ -241,14 +241,14 @@
 	.quad stub32_clone		/* 120 */
 	.quad sys_setdomainname
 	.quad sys_newuname
-	.quad ni_syscall	/* modify_ldt */
+	.quad sys_modify_ldt
 	.quad sys32_adjtimex
-	.quad sys_mprotect		/* 125 */
+	.quad sys32_mprotect		/* 125 */
 	.quad sys32_sigprocmask
-	.quad ni_syscall	/* query_module */
-	.quad ni_syscall	/* init_module */
-	.quad ni_syscall	/* delete module */
-	.quad ni_syscall	/* 130  get_kernel_syms */
+	.quad sys32_module_warning	/* create_module */
+	.quad sys32_module_warning	/* init_module */
+	.quad sys32_module_warning	/* delete module */
+	.quad sys32_module_warning	/* 130  get_kernel_syms */
 	.quad ni_syscall	/* quotactl */ 
 	.quad sys_getpgid
 	.quad sys_fchdir
@@ -287,7 +287,7 @@
 	.quad ni_syscall	/* vm86 */ 
 	.quad ni_syscall	/* query_module */
 	.quad sys_poll
-	.quad ni_syscall	/* nfsserverctl */ 
+	.quad sys32_nfsservctl
 	.quad sys_setresgid16	/* 170 */
 	.quad sys_getresgid16
 	.quad sys_prctl
@@ -302,8 +302,8 @@
 	.quad sys32_pwrite
 	.quad sys_chown16
 	.quad sys_getcwd
-	.quad ni_syscall	/* capget */
-	.quad ni_syscall	/* capset */
+	.quad sys_capget
+	.quad sys_capset
 	.quad stub32_sigaltstack
 	.quad sys32_sendfile
 	.quad ni_syscall		/* streams1 */
@@ -338,7 +338,7 @@
 	.quad sys_pivot_root
 	.quad sys_mincore
 	.quad sys_madvise
-	.quad sys_getdents64	/* 220 */ 
+	.quad sys_getdents64	/* 220 getdents64 */ 
 	.quad sys32_fcntl64	
 	.quad sys_ni_syscall	/* tux */
 	.quad sys_ni_syscall    /* security */
@@ -357,6 +357,10 @@
 	.quad sys_lremovexattr
 	.quad sys_fremovexattr
 	.quad sys_tkill		/* 238 */ 
+	.quad sys_sendfile64 
+	.quad sys_futex
+        .quad sys32_sched_setaffinity
+        .quad sys32_sched_getaffinity
 ia32_syscall_end:		
 	.rept IA32_NR_syscalls-(ia32_syscall_end-ia32_sys_call_table)/8
 		.quad ni_syscall
diff -Nru a/arch/x86_64/ia32/ptrace32.c b/arch/x86_64/ia32/ptrace32.c
--- a/arch/x86_64/ia32/ptrace32.c	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/ia32/ptrace32.c	Mon Apr 22 15:32:19 2002
@@ -1,27 +1,29 @@
 /* 
  * 32bit ptrace for x86-64.
  *
- * Copyright 2001 Andi Kleen, SuSE Labs.
- * Some parts copied from arch/i386/kernel/ptrace.c. See that file for 
- * earlier copyright.
+ * Copyright 2001,2002 Andi Kleen, SuSE Labs.
+ * Some parts copied from arch/i386/kernel/ptrace.c. See that file for earlier 
+ * copyright.
  * 
- * This allows to access 64bit processes too but there is no way to see 
- * the extended register contents.
+ * This allows to access 64bit processes too; but there is no way to see the extended 
+ * register contents.
  *
- * $Id: ptrace32.c,v 1.2 2001/08/15 06:41:13 ak Exp $
+ * $Id: ptrace32.c,v 1.12 2002/03/24 13:02:02 ak Exp $
  */ 
 
 #include <linux/kernel.h>
 #include <linux/stddef.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
-#include <linux/mm.h>
-#include <linux/ptrace.h>
 #include <asm/ptrace.h>
 #include <asm/uaccess.h>
 #include <asm/user32.h>
+#include <asm/user.h>
 #include <asm/errno.h>
 #include <asm/debugreg.h>
+#include <asm/i387.h>
+#include <asm/fpu32.h>
+#include <linux/mm.h>
 
 #define R32(l,q) \
 	case offsetof(struct user32, regs.l): stack[offsetof(struct pt_regs, q)/8] = val; break
@@ -139,7 +141,6 @@
 
 #undef R32
 
-
 static struct task_struct *find_target(int request, int pid, int *err)
 { 
 	struct task_struct *child;
@@ -154,23 +155,11 @@
 	if (child)
 		get_task_struct(child);
 	read_unlock(&tasklist_lock);
-	if (child) { 
-		*err = -ESRCH;
-		if (!(child->ptrace & PT_PTRACED))
-			goto out;
-		if (child->state != TASK_STOPPED) {
-			if (request != PTRACE_KILL)
-				goto out;
-		}
-		if (child->p_pptr != current)
-			goto out;
-
+	*err = ptrace_check_attach(child,0);
+	if (*err == 0)
 		return child; 
-	} 
- out:
 	put_task_struct(child);
 	return NULL; 
-	
 } 
 
 extern asmlinkage long sys_ptrace(long request, long pid, unsigned long addr, unsigned long data);
@@ -178,6 +167,7 @@
 asmlinkage long sys32_ptrace(long request, u32 pid, u32 addr, u32 data)
 {
 	struct task_struct *child;
+	struct pt_regs *childregs; 
 	int ret;
 	__u32 val;
 
@@ -203,6 +193,8 @@
 	case PTRACE_SETREGS:
 	case PTRACE_SETFPREGS:
 	case PTRACE_GETFPREGS:
+	case PTRACE_SETFPXREGS:
+	case PTRACE_GETFPXREGS:
 		break;
 		
 	default:
@@ -213,11 +205,13 @@
 	if (!child)
 		return ret;
 
+	childregs = (struct pt_regs *)(child->thread.rsp0 - sizeof(struct pt_regs)); 
+
 	switch (request) {
 	case PTRACE_PEEKDATA:
 	case PTRACE_PEEKTEXT:
 		ret = 0;
-		if (access_process_vm(child, addr, &val, sizeof(u32), 0) != sizeof(u32))
+		if (access_process_vm(child, addr, &val, sizeof(u32), 0)!=sizeof(u32))
 			ret = -EIO;
 		else
 			ret = put_user(val, (unsigned int *)(u64)data); 
@@ -226,13 +220,13 @@
 	case PTRACE_POKEDATA:
 	case PTRACE_POKETEXT:
 		ret = 0;
-		if (access_process_vm(child, addr, &data, sizeof(u32), 1) != sizeof(u32))
+		if (access_process_vm(child, addr, &data, sizeof(u32), 1)!=sizeof(u32))
 			ret = -EIO; 
 		break;
 
 	case PTRACE_PEEKUSR:
 		ret = getreg32(child, addr, &val);
-		if (ret >= 0) 
+		if (ret == 0)
 			ret = put_user(val, (__u32 *)(unsigned long) data);
 		break;
 
@@ -242,7 +236,7 @@
 
 	case PTRACE_GETREGS: { /* Get all gp regs from the child. */
 		int i;
-	  	if (!access_ok(VERIFY_WRITE, (unsigned *)(unsigned long)data, FRAME_SIZE)) {
+	  	if (!access_ok(VERIFY_WRITE, (unsigned *)(unsigned long)data, 16*4)) {
 			ret = -EIO;
 			break;
 		}
@@ -258,10 +252,11 @@
 	case PTRACE_SETREGS: { /* Set all gp regs in the child. */
 		unsigned long tmp;
 		int i;
-	  	if (!access_ok(VERIFY_READ, (unsigned *)(unsigned long)data, FRAME_SIZE)) {
+	  	if (!access_ok(VERIFY_READ, (unsigned *)(unsigned long)data, 16*4)) {
 			ret = -EIO;
 			break;
 		}
+		empty_fpu(child); 
 		ret = 0; 
 		for ( i = 0; i <= 16*4; i += sizeof(u32) ) {
 			ret |= __get_user(tmp, (u32 *) (unsigned long) data);
@@ -271,35 +266,37 @@
 		break;
 	}
 
-#if 0 /* to be done. */
-	case PTRACE_GETFPREGS: { /* Get the child extended FPU state. */
-		if (!access_ok(VERIFY_WRITE, (unsigned *)data,
-			       sizeof(struct user_i387_struct))) {
-			ret = -EIO;
+	case PTRACE_SETFPREGS:
+		empty_fpu(child); 
+		save_i387_ia32(child, (void *)(u64)data, childregs, 1);
+		ret = 0; 
 			break;
-		}
-		if ( !child->used_math ) {
-			/* Simulate an empty FPU. */
-			set_fpu_cwd(child, 0x037f);
-			set_fpu_swd(child, 0x0000);
-			set_fpu_twd(child, 0xffff);
-			set_fpu_mxcsr(child, 0x1f80);
-		}
-		ret = get_fpregs((struct user_i387_struct *)data, child);
+
+	case PTRACE_GETFPREGS:
+		empty_fpu(child); 
+		restore_i387_ia32(child, (void *)(u64)data, 1);
+		ret = 0;
 		break;
-	}
 
-	case PTRACE_SETFPREGS: { /* Set the child extended FPU state. */
-		if (!access_ok(VERIFY_READ, (unsigned *)data,
-			       sizeof(struct user_i387_struct))) {
-			ret = -EIO;
+	case PTRACE_GETFPXREGS: { 
+		struct user32_fxsr_struct *u = (void *)(u64)data; 
+		empty_fpu(child); 
+		ret = copy_to_user(u, &child->thread.i387.fxsave, sizeof(*u));
+		ret |= __put_user(childregs->cs, &u->fcs);
+		ret |= __put_user(child->thread.ds, &u->fos); 
+		if (ret) 
+			ret = -EFAULT;
+		break; 
+	} 
+	case PTRACE_SETFPXREGS: { 
+		struct user32_fxsr_struct *u = (void *)(u64)data; 
+		empty_fpu(child); 
+		/* no error checking to be bug to bug compatible with i386 */ 
+		copy_from_user(&child->thread.i387.fxsave, u, sizeof(*u));
+	        child->thread.i387.fxsave.mxcsr &= 0xffbf;
+		ret = 0; 
 			break;
 		}
-		child->used_math = 1;
-		ret = set_fpregs(child, (struct user_i387_struct *)data);
-		break;
-
-#endif
 
 	default:
 		ret = -EINVAL;
diff -Nru a/arch/x86_64/ia32/sys_ia32.c b/arch/x86_64/ia32/sys_ia32.c
--- a/arch/x86_64/ia32/sys_ia32.c	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/ia32/sys_ia32.c	Mon Apr 22 15:32:19 2002
@@ -9,12 +9,15 @@
  * Copyright (C) 1997 		David S. Miller (davem@caip.rutgers.edu)
  * Copyright (C) 2000		Hewlett-Packard Co.
  * Copyright (C) 2000		David Mosberger-Tang <davidm@hpl.hp.com>
- * Copyright (C) 2000,2001	Andi Kleen, SuSE Labs (x86-64 port) 
+ * Copyright (C) 2000,2001,2002	Andi Kleen, SuSE Labs (x86-64 port) 
  *
  * These routines maintain argument size conversion between 32bit and 64bit
  * environment. In 2.5 most of this should be moved to a generic directory. 
  *
  * This file assumes that there is a hole at the end of user address space.
+ * 
+ * Some of the functions are LE specific currently. These are hopefully all marked.
+ * This should be fixed.
  */
 
 #include <linux/config.h>
@@ -32,6 +35,7 @@
 #include <linux/smp_lock.h>
 #include <linux/sem.h>
 #include <linux/msg.h>
+#include <linux/binfmts.h>
 #include <linux/mm.h>
 #include <linux/shm.h>
 #include <linux/slab.h>
@@ -52,6 +56,7 @@
 #include <linux/stat.h>
 #include <linux/ipc.h>
 #include <linux/rwsem.h>
+#include <linux/init.h>
 #include <asm/mman.h>
 #include <asm/types.h>
 #include <asm/uaccess.h>
@@ -68,6 +73,18 @@
 #define ROUND_UP(x,a)	((__typeof__(x))(((unsigned long)(x) + ((a) - 1)) & ~((a) - 1)))
 #define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de)))
 
+#undef high2lowuid
+#undef high2lowgid
+#undef low2highuid
+#undef low2highgid
+
+#define high2lowuid(uid) ((uid) > 65535) ? (u16)overflowuid : (u16)(uid)
+#define high2lowgid(gid) ((gid) > 65535) ? (u16)overflowgid : (u16)(gid)
+#define low2highuid(uid) ((uid) == (u16)-1) ? (uid_t)-1 : (uid_t)(uid)
+#define low2highgid(gid) ((gid) == (u16)-1) ? (gid_t)-1 : (gid_t)(gid)
+extern int overflowuid,overflowgid; 
+
+
 static int
 putstat(struct stat32 *ubuf, struct stat *kbuf)
 {
@@ -246,6 +263,10 @@
 		if (!file)
 			return -EBADF;
 	}
+	if (a.prot & PROT_READ) 
+		a.prot |= PROT_EXEC; 
+
+	a.flags |= MAP_32BIT;
 
 	mm = current->mm; 
 	down_write(&mm->mmap_sem); 
@@ -253,15 +274,23 @@
 	if (file)
 		fput(file);
 
-	if (retval >= 0xFFFFFFFF) { 
+	/* Should not happen */
+	if (retval >= 0xFFFFFFFF && (long)retval > 0) { 
 		do_munmap(mm, retval, a.len); 
 		retval = -ENOMEM; 
 	} 
 	up_write(&mm->mmap_sem); 
 
+	return retval;
+}
 
+extern asmlinkage long sys_mprotect(unsigned long start,size_t len,unsigned long prot);
 
-	return retval;
+asmlinkage int sys32_mprotect(unsigned long start, size_t len, unsigned long prot)
+{
+	if (prot & PROT_READ) 
+		prot |= PROT_EXEC; 
+	return sys_mprotect(start,len,prot); 
 }
 
 asmlinkage long
@@ -479,40 +508,49 @@
 static inline long
 get_tv32(struct timeval *o, struct timeval32 *i)
 {
-	return (!access_ok(VERIFY_READ, i, sizeof(*i)) ||
-		__get_user(o->tv_sec, &i->tv_sec) ||
-		__get_user(o->tv_usec, &i->tv_usec));
-	return ENOSYS;
+	int err = -EFAULT; 
+	if (access_ok(VERIFY_READ, i, sizeof(*i))) { 
+		err = __get_user(o->tv_sec, &i->tv_sec);
+		err |= __get_user(o->tv_usec, &i->tv_usec);
+	}
+	return err; 
 }
 
 static inline long
 put_tv32(struct timeval32 *o, struct timeval *i)
 {
-	return (!access_ok(VERIFY_WRITE, o, sizeof(*o)) ||
-		__put_user(i->tv_sec, &o->tv_sec) ||
-		__put_user(i->tv_usec, &o->tv_usec));
+	int err = -EFAULT;
+	if (access_ok(VERIFY_WRITE, o, sizeof(*o))) { 
+		err = __put_user(i->tv_sec, &o->tv_sec);
+		err |= __put_user(i->tv_usec, &o->tv_usec);
+	} 
+	return err; 
 }
 
 static inline long
 get_it32(struct itimerval *o, struct itimerval32 *i)
 {
-	return (!access_ok(VERIFY_READ, i, sizeof(*i)) ||
-		__get_user(o->it_interval.tv_sec, &i->it_interval.tv_sec) ||
-		__get_user(o->it_interval.tv_usec, &i->it_interval.tv_usec) ||
-		__get_user(o->it_value.tv_sec, &i->it_value.tv_sec) ||
-		__get_user(o->it_value.tv_usec, &i->it_value.tv_usec));
-	return ENOSYS;
+	int err = -EFAULT; 
+	if (access_ok(VERIFY_READ, i, sizeof(*i))) { 
+		err = __get_user(o->it_interval.tv_sec, &i->it_interval.tv_sec);
+		err |= __get_user(o->it_interval.tv_usec, &i->it_interval.tv_usec);
+		err |= __get_user(o->it_value.tv_sec, &i->it_value.tv_sec);
+		err |= __get_user(o->it_value.tv_usec, &i->it_value.tv_usec);
+	}
+	return err;
 }
 
 static inline long
 put_it32(struct itimerval32 *o, struct itimerval *i)
 {
-	return (!access_ok(VERIFY_WRITE, i, sizeof(*i)) ||
-		__put_user(i->it_interval.tv_sec, &o->it_interval.tv_sec) ||
-		__put_user(i->it_interval.tv_usec, &o->it_interval.tv_usec) ||
-		__put_user(i->it_value.tv_sec, &o->it_value.tv_sec) ||
-		__put_user(i->it_value.tv_usec, &o->it_value.tv_usec));
-	return ENOSYS;
+	int err = -EFAULT;
+	if (access_ok(VERIFY_WRITE, o, sizeof(*o))) {
+		err = __put_user(i->it_interval.tv_sec, &o->it_interval.tv_sec);
+		err |= __put_user(i->it_interval.tv_usec, &o->it_interval.tv_usec);
+		err |= __put_user(i->it_value.tv_sec, &o->it_value.tv_sec);
+		err |= __put_user(i->it_value.tv_usec, &o->it_value.tv_usec); 
+	} 
+	return err;
 }
 
 extern int do_getitimer(int which, struct itimerval *value);
@@ -1159,6 +1197,7 @@
 		fourth.val = (int)pad;
 	else
 		fourth.__pad = (void *)A(pad);
+
 	switch (third) {
 
 	case IPC_INFO:
@@ -1615,7 +1654,7 @@
 	__kernel_clock_t32 tms_cstime;
 };
                                 
-extern asmlinkage long sys_times(struct tms * tbuf);
+extern int sys_times(struct tms *);
 
 asmlinkage long
 sys32_times(struct tms32 *tbuf)
@@ -1638,134 +1677,66 @@
 	return ret;
 }
 
-static inline int
-get_flock32(struct flock *kfl, struct flock32 *ufl)
+
+static inline int get_flock(struct flock *kfl, struct flock32 *ufl)
 {
-	if (verify_area(VERIFY_READ, ufl, sizeof(struct flock32)) ||
-	    __get_user(kfl->l_type, &ufl->l_type) ||
-	    __get_user(kfl->l_whence, &ufl->l_whence) ||
-	    __get_user(kfl->l_start, &ufl->l_start) ||
-	    __get_user(kfl->l_len, &ufl->l_len) ||
-	    __get_user(kfl->l_pid, &ufl->l_pid))
-		return -EFAULT;
-	return 0;
+	int err;
+	
+	err = get_user(kfl->l_type, &ufl->l_type);
+	err |= __get_user(kfl->l_whence, &ufl->l_whence);
+	err |= __get_user(kfl->l_start, &ufl->l_start);
+	err |= __get_user(kfl->l_len, &ufl->l_len);
+	err |= __get_user(kfl->l_pid, &ufl->l_pid);
+	return err;
 }
 
-static inline int
-put_flock32(struct flock *kfl, struct flock32 *ufl)
+static inline int put_flock(struct flock *kfl, struct flock32 *ufl)
 {
-	if (verify_area(VERIFY_WRITE, ufl, sizeof(struct flock32)) ||
-	    __put_user(kfl->l_type, &ufl->l_type) ||
-	    __put_user(kfl->l_whence, &ufl->l_whence) ||
-	    __put_user(kfl->l_start, &ufl->l_start) ||
-	    __put_user(kfl->l_len, &ufl->l_len) ||
-	    __put_user(kfl->l_pid, &ufl->l_pid))
-		return -EFAULT;
-	return 0;
+	int err;
+	
+	err = __put_user(kfl->l_type, &ufl->l_type);
+	err |= __put_user(kfl->l_whence, &ufl->l_whence);
+	err |= __put_user(kfl->l_start, &ufl->l_start);
+	err |= __put_user(kfl->l_len, &ufl->l_len);
+	err |= __put_user(kfl->l_pid, &ufl->l_pid);
+	return err;
 }
 
-extern asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd,
-				 unsigned long arg);
+extern asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg);
 
-asmlinkage long
-sys32_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg)
+asmlinkage long sys32_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg)
 {
-	struct flock f;
-	mm_segment_t old_fs;
-	long ret;
-
 	switch (cmd) {
 	case F_GETLK:
 	case F_SETLK:
 	case F_SETLKW:
-		if(cmd != F_GETLK && get_flock32(&f, (struct flock32 *)((long)arg)))
-			return -EFAULT;
-		old_fs = get_fs();
-		set_fs(KERNEL_DS);
-		ret = sys_fcntl(fd, cmd, (unsigned long)&f);
-		set_fs(old_fs);
-		if(cmd == F_GETLK && put_flock32(&f, (struct flock32 *)((long)arg)))
-			return -EFAULT;
-		return ret;
-	default:
-		/*
-		 *  `sys_fcntl' lies about arg, for the F_SETOWN
-		 *  sub-function arg can have a negative value.
-		 */
-		return sys_fcntl(fd, cmd, (unsigned long)((long)arg));
-	}
-}
-
-static inline int
-get_flock64(struct flock *kfl, struct ia32_flock64 *ufl)
-{
-	if (verify_area(VERIFY_READ, ufl, sizeof(struct ia32_flock64)) ||
-	    __get_user(kfl->l_type, &ufl->l_type) ||
-	    __get_user(kfl->l_whence, &ufl->l_whence) ||
-	    __copy_from_user(&kfl->l_start, &ufl->l_start, 8) ||
-	    __copy_from_user(&kfl->l_len, &ufl->l_len, 8) ||
-	    __get_user(kfl->l_pid, &ufl->l_pid))
-		return -EFAULT;
-	return 0;
-}
-
-static inline int
-put_flock64(struct flock *kfl, struct ia32_flock64 *ufl)
-{
-	if (verify_area(VERIFY_WRITE, ufl, sizeof(struct ia32_flock64)) ||
-	    __put_user(kfl->l_type, &ufl->l_type) ||
-	    __put_user(kfl->l_whence, &ufl->l_whence) ||
-	    __copy_to_user(&ufl->l_start,&kfl->l_start, 8) ||
-	    __copy_to_user(&ufl->l_len,&kfl->l_len, 8) ||
-	    __put_user(kfl->l_pid, &ufl->l_pid))
-		return -EFAULT;
-	return 0;
-}
-
-asmlinkage long
-sys32_fcntl64(unsigned int fd, unsigned int cmd, unsigned long arg)
-{
+		{
 	struct flock f;
 	mm_segment_t old_fs;
 	long ret;
 
-	/* sys_fcntl() is by default 64 bit and so don't know anything
-	 * about F_xxxx64 commands
-	 */ 
-	switch (cmd) {
-	case F_GETLK64:
-		cmd = F_GETLK;
-		break;
-	case F_SETLK64:
-		cmd = F_SETLK;
-		break;
-	case F_SETLKW64:
-		cmd = F_SETLKW;
-		break;
-	}
-	
-	switch (cmd) {
-	case F_SETLKW:
-	case F_SETLK:
-		if(get_flock64(&f, (struct ia32_flock64 *)arg))
+			if (get_flock(&f, (struct flock32 *)arg))
 			return -EFAULT;
-	case F_GETLK:
-		old_fs = get_fs();
-		set_fs(KERNEL_DS);
+			old_fs = get_fs(); set_fs (KERNEL_DS);
 		ret = sys_fcntl(fd, cmd, (unsigned long)&f);
-		set_fs(old_fs);
-		if(cmd == F_GETLK64 && put_flock64(&f, (struct ia32_flock64 *)((long)arg)))
+			set_fs (old_fs);
+			if (ret) return ret;
+			if (put_flock(&f, (struct flock32 *)arg))
 			return -EFAULT;
-		return ret;
+			return 0;
+		}
 	default:
-		/*
-		 *  `sys_fcntl' lies about arg, for the F_SETOWN
-		 *  sub-function arg can have a negative value.
-		 */
-		return sys_fcntl(fd, cmd, (unsigned long)((long)arg));
+		return sys_fcntl(fd, cmd, (unsigned long)arg);
 	}
 }
 
+asmlinkage long sys32_fcntl64(unsigned int fd, unsigned int cmd, unsigned long arg)
+{
+	if (cmd >= F_GETLK64 && cmd <= F_SETLKW64)
+		return sys_fcntl(fd, cmd + F_GETLK - F_GETLK64, arg);
+	return sys32_fcntl(fd, cmd, arg);
+}
+
 int sys32_ni_syscall(int call)
 { 
 	printk(KERN_INFO "IA32 syscall %d from %s not implemented\n", call,
@@ -1773,47 +1744,6 @@
 	return -ENOSYS;	       
 } 
 
-/* In order to reduce some races, while at the same time doing additional
- * checking and hopefully speeding things up, we copy filenames to the
- * kernel data space before using them..
- *
- * POSIX.1 2.4: an empty pathname is invalid (ENOENT).
- */
-static inline int
-do_getname32(const char *filename, char *page)
-{
-	int retval;
-
-	/* 32bit pointer will be always far below TASK_SIZE :)) */
-	retval = strncpy_from_user((char *)page, (char *)filename, PAGE_SIZE);
-	if (retval > 0) {
-		if (retval < PAGE_SIZE)
-			return 0;
-		return -ENAMETOOLONG;
-	} else if (!retval)
-		retval = -ENOENT;
-	return retval;
-}
-
-char *
-getname32(const char *filename)
-{
-	char *tmp, *result;
-
-	result = ERR_PTR(-ENOMEM);
-	tmp = (char *)__get_free_page(GFP_KERNEL);
-	if (tmp)  {
-		int retval = do_getname32(filename, tmp);
-
-		result = tmp;
-		if (retval < 0) {
-			putname(tmp);
-			result = ERR_PTR(retval);
-		}
-	}
-	return result;
-}
-
 /* 32-bit timeval and related flotsam.  */
 
 extern asmlinkage long sys_utime(char * filename, struct utimbuf * times);
@@ -1836,14 +1766,14 @@
 	    __get_user (t.actime, &times->actime) ||
 	    __get_user (t.modtime, &times->modtime))
 		return -EFAULT;
-	filenam = getname32 (filename);
+	filenam = getname (filename);
 	ret = PTR_ERR(filenam);
 	if (!IS_ERR(filenam)) {
 		old_fs = get_fs();
 		set_fs (KERNEL_DS); 
 		ret = sys_utime(filenam, &t);
 		set_fs (old_fs);
-		putname (filenam);
+		putname(filenam);
 	}
 	return ret;
 }
@@ -1903,8 +1833,8 @@
 static int checktype(char *user_type) 
 { 
 	int err = 0; 
-	char **s,*kernel_type = getname32(user_type); 
-	if (!kernel_type) 
+	char **s,*kernel_type = getname(user_type); 
+	if (!kernel_type || IS_ERR(kernel_type)) 
 		return -EFAULT; 
 	for (s = badfs; *s; ++s) 
 		if (!strcmp(kernel_type, *s)) { 
@@ -2332,7 +2262,7 @@
 	mm_segment_t old_fs;
 	int ret;
 
-	kfilename = getname32(filename);
+	kfilename = getname(filename);
 	ret = PTR_ERR(kfilename);
 	if (!IS_ERR(kfilename)) {
 		if (tvs) {
@@ -2442,9 +2372,10 @@
 
 typedef __kernel_ssize_t32 ssize_t32;
 
+/* warning. next two assume LE */
 asmlinkage ssize_t32
 sys32_pread(unsigned int fd, char *ubuf, __kernel_size_t32 count,
-	    u32 poshi, u32 poslo)
+	    u32 poslo, u32 poshi)
 {
 	return sys_pread(fd, ubuf, count,
 			 ((loff_t)AA(poshi) << 32) | AA(poslo));
@@ -2452,7 +2383,7 @@
 
 asmlinkage ssize_t32
 sys32_pwrite(unsigned int fd, char *ubuf, __kernel_size_t32 count,
-	     u32 poshi, u32 poslo)
+	     u32 poslo, u32 poshi)
 {
 	return sys_pwrite(fd, ubuf, count,
 			  ((loff_t)AA(poshi) << 32) | AA(poslo));
@@ -2682,14 +2613,15 @@
 	cnt = 0; 
 	do { 		
 		int ret = get_user(val, (__u32 *)(u64)src); 
-		if (ret)  {
+		if (ret)
 			return ret;
-		}	
 		if (dst)
 			dst[cnt] = (char *)(u64)val; 
 		cnt++;
 		src += 4; 	
-	} while(val && cnt < 1023);  // XXX: fix limit.
+		if (cnt >= (MAX_ARG_PAGES*PAGE_SIZE)/sizeof(void*))
+			return -E2BIG; 
+	} while(val); 
 	if (dst)
 		dst[cnt-1] = 0; 
 	return cnt; 
@@ -2701,6 +2633,7 @@
 	char **buf; 
 	int na,ne;
 	int ret;
+	unsigned sz; 
 
 	na = nargs(argv, NULL); 
 	if (na < 0) 
@@ -2709,7 +2642,11 @@
 	if (ne < 0) 
 		return -EFAULT; 
 
-	buf = kmalloc((na+ne)*sizeof(char*), GFP_KERNEL); 
+	sz = (na+ne)*sizeof(void *); 
+	if (sz > PAGE_SIZE) 
+		buf = vmalloc(sz); 
+	else
+		buf = kmalloc(sz, GFP_KERNEL); 
 	if (!buf)
 		return -ENOMEM; 
 	
@@ -2737,6 +2674,9 @@
 	putname(name);
  
 free:
+	if (sz > PAGE_SIZE)
+		vfree(buf); 
+	else
 	kfree(buf);
 	return ret; 
 } 
@@ -2786,3 +2726,396 @@
 	return sys_kill(pid, sig);
 }
  
+
+#if defined(CONFIG_NFSD) || defined(CONFIG_NFSD_MODULE)
+/* Stuff for NFS server syscalls... */
+struct nfsctl_svc32 {
+	u16			svc32_port;
+	s32			svc32_nthreads;
+};
+
+struct nfsctl_client32 {
+	s8			cl32_ident[NFSCLNT_IDMAX+1];
+	s32			cl32_naddr;
+	struct in_addr		cl32_addrlist[NFSCLNT_ADDRMAX];
+	s32			cl32_fhkeytype;
+	s32			cl32_fhkeylen;
+	u8			cl32_fhkey[NFSCLNT_KEYMAX];
+};
+
+struct nfsctl_export32 {
+	s8			ex32_client[NFSCLNT_IDMAX+1];
+	s8			ex32_path[NFS_MAXPATHLEN+1];
+	__kernel_dev_t32	ex32_dev;
+	__kernel_ino_t32	ex32_ino;
+	s32			ex32_flags;
+	__kernel_uid_t32	ex32_anon_uid;
+	__kernel_gid_t32	ex32_anon_gid;
+};
+
+struct nfsctl_uidmap32 {
+	u32			ug32_ident;   /* char * */
+	__kernel_uid_t32	ug32_uidbase;
+	s32			ug32_uidlen;
+	u32			ug32_udimap;  /* uid_t * */
+	__kernel_uid_t32	ug32_gidbase;
+	s32			ug32_gidlen;
+	u32			ug32_gdimap;  /* gid_t * */
+};
+
+struct nfsctl_fhparm32 {
+	struct sockaddr		gf32_addr;
+	__kernel_dev_t32	gf32_dev;
+	__kernel_ino_t32	gf32_ino;
+	s32			gf32_version;
+};
+
+struct nfsctl_fdparm32 {
+	struct sockaddr		gd32_addr;
+	s8			gd32_path[NFS_MAXPATHLEN+1];
+	s32			gd32_version;
+};
+
+struct nfsctl_fsparm32 {
+	struct sockaddr		gd32_addr;
+	s8			gd32_path[NFS_MAXPATHLEN+1];
+	s32			gd32_maxlen;
+};
+
+struct nfsctl_arg32 {
+	s32			ca32_version;	/* safeguard */
+	union {
+		struct nfsctl_svc32	u32_svc;
+		struct nfsctl_client32	u32_client;
+		struct nfsctl_export32	u32_export;
+		struct nfsctl_uidmap32	u32_umap;
+		struct nfsctl_fhparm32	u32_getfh;
+		struct nfsctl_fdparm32	u32_getfd;
+		struct nfsctl_fsparm32	u32_getfs;
+	} u;
+#define ca32_svc	u.u32_svc
+#define ca32_client	u.u32_client
+#define ca32_export	u.u32_export
+#define ca32_umap	u.u32_umap
+#define ca32_getfh	u.u32_getfh
+#define ca32_getfd	u.u32_getfd
+#define ca32_getfs	u.u32_getfs
+#define ca32_authd	u.u32_authd
+};
+
+union nfsctl_res32 {
+	__u8			cr32_getfh[NFS_FHSIZE];
+	struct knfsd_fh		cr32_getfs;
+};
+
+static int nfs_svc32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32)
+{
+	int err;
+	
+	err = get_user(karg->ca_version, &arg32->ca32_version);
+	err |= __get_user(karg->ca_svc.svc_port, &arg32->ca32_svc.svc32_port);
+	err |= __get_user(karg->ca_svc.svc_nthreads, &arg32->ca32_svc.svc32_nthreads);
+	return err;
+}
+
+static int nfs_clnt32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32)
+{
+	int err;
+	
+	err = get_user(karg->ca_version, &arg32->ca32_version);
+	err |= copy_from_user(&karg->ca_client.cl_ident[0],
+			  &arg32->ca32_client.cl32_ident[0],
+			  NFSCLNT_IDMAX);
+	err |= __get_user(karg->ca_client.cl_naddr, &arg32->ca32_client.cl32_naddr);
+	err |= copy_from_user(&karg->ca_client.cl_addrlist[0],
+			  &arg32->ca32_client.cl32_addrlist[0],
+			  (sizeof(struct in_addr) * NFSCLNT_ADDRMAX));
+	err |= __get_user(karg->ca_client.cl_fhkeytype,
+		      &arg32->ca32_client.cl32_fhkeytype);
+	err |= __get_user(karg->ca_client.cl_fhkeylen,
+		      &arg32->ca32_client.cl32_fhkeylen);
+	err |= copy_from_user(&karg->ca_client.cl_fhkey[0],
+			  &arg32->ca32_client.cl32_fhkey[0],
+			  NFSCLNT_KEYMAX);
+	return err;
+}
+
+static int nfs_exp32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32)
+{
+	int err;
+	
+	err = get_user(karg->ca_version, &arg32->ca32_version);
+	err |= copy_from_user(&karg->ca_export.ex_client[0],
+			  &arg32->ca32_export.ex32_client[0],
+			  NFSCLNT_IDMAX);
+	err |= copy_from_user(&karg->ca_export.ex_path[0],
+			  &arg32->ca32_export.ex32_path[0],
+			  NFS_MAXPATHLEN);
+	err |= __get_user(karg->ca_export.ex_dev,
+		      &arg32->ca32_export.ex32_dev);
+	err |= __get_user(karg->ca_export.ex_ino,
+		      &arg32->ca32_export.ex32_ino);
+	err |= __get_user(karg->ca_export.ex_flags,
+		      &arg32->ca32_export.ex32_flags);
+	err |= __get_user(karg->ca_export.ex_anon_uid,
+		      &arg32->ca32_export.ex32_anon_uid);
+	err |= __get_user(karg->ca_export.ex_anon_gid,
+		      &arg32->ca32_export.ex32_anon_gid);
+	karg->ca_export.ex_anon_uid = high2lowuid(karg->ca_export.ex_anon_uid);
+	karg->ca_export.ex_anon_gid = high2lowgid(karg->ca_export.ex_anon_gid);
+	return err;
+}
+
+static int nfs_uud32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32)
+{
+	u32 uaddr;
+	int i;
+	int err;
+
+	memset(karg, 0, sizeof(*karg));
+	if(get_user(karg->ca_version, &arg32->ca32_version))
+		return -EFAULT;
+	karg->ca_umap.ug_ident = (char *)get_free_page(GFP_USER);
+	if(!karg->ca_umap.ug_ident)
+		return -ENOMEM;
+	err = get_user(uaddr, &arg32->ca32_umap.ug32_ident);
+	if(strncpy_from_user(karg->ca_umap.ug_ident,
+			     (char *)A(uaddr), PAGE_SIZE) <= 0)
+		return -EFAULT;
+	err |= __get_user(karg->ca_umap.ug_uidbase,
+		      &arg32->ca32_umap.ug32_uidbase);
+	err |= __get_user(karg->ca_umap.ug_uidlen,
+		      &arg32->ca32_umap.ug32_uidlen);
+	err |= __get_user(uaddr, &arg32->ca32_umap.ug32_udimap);
+	if (err)
+		return -EFAULT;
+	karg->ca_umap.ug_udimap = kmalloc((sizeof(uid_t) * karg->ca_umap.ug_uidlen),
+					  GFP_USER);
+	if(!karg->ca_umap.ug_udimap)
+		return -ENOMEM;
+	for(i = 0; i < karg->ca_umap.ug_uidlen; i++)
+		err |= __get_user(karg->ca_umap.ug_udimap[i],
+			      &(((__kernel_uid_t32 *)A(uaddr))[i]));
+	err |= __get_user(karg->ca_umap.ug_gidbase,
+		      &arg32->ca32_umap.ug32_gidbase);
+	err |= __get_user(karg->ca_umap.ug_uidlen,
+		      &arg32->ca32_umap.ug32_gidlen);
+	err |= __get_user(uaddr, &arg32->ca32_umap.ug32_gdimap);
+	if (err)
+		return -EFAULT;
+	karg->ca_umap.ug_gdimap = kmalloc((sizeof(gid_t) * karg->ca_umap.ug_uidlen),
+					  GFP_USER);
+	if(!karg->ca_umap.ug_gdimap)
+		return -ENOMEM;
+	for(i = 0; i < karg->ca_umap.ug_gidlen; i++)
+		err |= __get_user(karg->ca_umap.ug_gdimap[i],
+			      &(((__kernel_gid_t32 *)A(uaddr))[i]));
+
+	return err;
+}
+
+static int nfs_getfh32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32)
+{
+	int err;
+	
+	err = get_user(karg->ca_version, &arg32->ca32_version);
+	err |= copy_from_user(&karg->ca_getfh.gf_addr,
+			  &arg32->ca32_getfh.gf32_addr,
+			  (sizeof(struct sockaddr)));
+	err |= __get_user(karg->ca_getfh.gf_dev,
+		      &arg32->ca32_getfh.gf32_dev);
+	err |= __get_user(karg->ca_getfh.gf_ino,
+		      &arg32->ca32_getfh.gf32_ino);
+	err |= __get_user(karg->ca_getfh.gf_version,
+		      &arg32->ca32_getfh.gf32_version);
+	return err;
+}
+
+static int nfs_getfd32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32)
+{
+	int err;
+	
+	err = get_user(karg->ca_version, &arg32->ca32_version);
+	err |= copy_from_user(&karg->ca_getfd.gd_addr,
+			  &arg32->ca32_getfd.gd32_addr,
+			  (sizeof(struct sockaddr)));
+	err |= copy_from_user(&karg->ca_getfd.gd_path,
+			  &arg32->ca32_getfd.gd32_path,
+			  (NFS_MAXPATHLEN+1));
+	err |= get_user(karg->ca_getfd.gd_version,
+		      &arg32->ca32_getfd.gd32_version);
+	return err;
+}
+
+static int nfs_getfs32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32)
+{
+	int err;
+	
+	err = get_user(karg->ca_version, &arg32->ca32_version);
+	err |= copy_from_user(&karg->ca_getfs.gd_addr,
+			  &arg32->ca32_getfs.gd32_addr,
+			  (sizeof(struct sockaddr)));
+	err |= copy_from_user(&karg->ca_getfs.gd_path,
+			  &arg32->ca32_getfs.gd32_path,
+			  (NFS_MAXPATHLEN+1));
+	err |= get_user(karg->ca_getfs.gd_maxlen,
+		      &arg32->ca32_getfs.gd32_maxlen);
+	return err;
+}
+
+/* This really doesn't need translations, we are only passing
+ * back a union which contains opaque nfs file handle data.
+ */
+static int nfs_getfh32_res_trans(union nfsctl_res *kres, union nfsctl_res32 *res32)
+{
+	return copy_to_user(res32, kres, sizeof(*res32));
+}
+
+int asmlinkage sys32_nfsservctl(int cmd, struct nfsctl_arg32 *arg32, union nfsctl_res32 *res32)
+{
+	struct nfsctl_arg *karg = NULL;
+	union nfsctl_res *kres = NULL;
+	mm_segment_t oldfs;
+	int err;
+
+	karg = kmalloc(sizeof(*karg), GFP_USER);
+	if(!karg)
+		return -ENOMEM;
+	if(res32) {
+		kres = kmalloc(sizeof(*kres), GFP_USER);
+		if(!kres) {
+			kfree(karg);
+			return -ENOMEM;
+		}
+	}
+	switch(cmd) {
+	case NFSCTL_SVC:
+		err = nfs_svc32_trans(karg, arg32);
+		break;
+	case NFSCTL_ADDCLIENT:
+		err = nfs_clnt32_trans(karg, arg32);
+		break;
+	case NFSCTL_DELCLIENT:
+		err = nfs_clnt32_trans(karg, arg32);
+		break;
+	case NFSCTL_EXPORT:
+	case NFSCTL_UNEXPORT:
+		err = nfs_exp32_trans(karg, arg32);
+		break;
+	/* This one is unimplemented, be we're ready for it. */
+	case NFSCTL_UGIDUPDATE:
+		err = nfs_uud32_trans(karg, arg32);
+		break;
+	case NFSCTL_GETFH:
+		err = nfs_getfh32_trans(karg, arg32);
+		break;
+	case NFSCTL_GETFD:
+		err = nfs_getfd32_trans(karg, arg32);
+		break;
+	case NFSCTL_GETFS:
+		err = nfs_getfs32_trans(karg, arg32);
+		break;
+	default:
+		err = -EINVAL;
+		break;
+	}
+	if(err)
+		goto done;
+	oldfs = get_fs();
+	set_fs(KERNEL_DS);
+	err = sys_nfsservctl(cmd, karg, kres);
+	set_fs(oldfs);
+
+	if (err)
+		goto done;
+
+	if((cmd == NFSCTL_GETFH) ||
+	   (cmd == NFSCTL_GETFD) ||
+	   (cmd == NFSCTL_GETFS))
+		err = nfs_getfh32_res_trans(kres, res32);
+
+done:
+	if(karg) {
+		if(cmd == NFSCTL_UGIDUPDATE) {
+			if(karg->ca_umap.ug_ident)
+				kfree(karg->ca_umap.ug_ident);
+			if(karg->ca_umap.ug_udimap)
+				kfree(karg->ca_umap.ug_udimap);
+			if(karg->ca_umap.ug_gdimap)
+				kfree(karg->ca_umap.ug_gdimap);
+		}
+		kfree(karg);
+	}
+	if(kres)
+		kfree(kres);
+	return err;
+}
+#else /* !NFSD */
+extern asmlinkage long sys_ni_syscall(void);
+int asmlinkage sys32_nfsservctl(int cmd, void *notused, void *notused2)
+{
+	return sys_ni_syscall();
+}
+#endif
+
+int sys32_module_warning(void)
+{ 
+	static long warn_time = -(60*HZ); 
+	if (time_before(warn_time + 60*HZ,jiffies) && strcmp(current->comm,"klogd")) { 
+		printk(KERN_INFO "%s: 32bit modutils not supported on 64bit kernel\n",
+		       current->comm);
+		warn_time = jiffies;
+	} 
+	return -ENOSYS ;
+} 
+
+int sys_sched_getaffinity(pid_t pid, unsigned int len, unsigned long *new_mask_ptr); 
+int sys_sched_setaffinity(pid_t pid, unsigned int len, unsigned long *new_mask_ptr); 
+
+/* only works on LE */
+int sys32_sched_setaffinity(pid_t pid, unsigned int len,
+			    unsigned int *new_mask_ptr)
+{
+	mm_segment_t oldfs = get_fs(); 
+	unsigned long mask; 
+	int err;
+	if (get_user(mask, new_mask_ptr)) 
+		return -EFAULT;	
+	set_fs(KERNEL_DS); 
+	err = sys_sched_setaffinity(pid,sizeof(mask),&mask); 	
+	set_fs(oldfs); 
+	return err;
+}
+
+/* only works on LE */ 
+int sys32_sched_getaffinity(pid_t pid, unsigned int len,
+			    unsigned int *new_mask_ptr)
+{
+	mm_segment_t oldfs = get_fs(); 
+	unsigned long mask; 
+	int err;
+	mask = 0; 
+	set_fs(KERNEL_DS); 
+	err = sys_sched_getaffinity(pid,sizeof(mask),&mask); 	
+	set_fs(oldfs); 
+	if (!err) 
+		err = put_user((u32)mask, new_mask_ptr); 
+	return err;
+}
+
+struct exec_domain ia32_exec_domain = { 
+	name: "linux/x86",
+	pers_low: PER_LINUX32,
+	pers_high: PER_LINUX32,
+};      
+
+static int __init ia32_init (void)
+{
+	printk("IA32 emulation $Id: sys_ia32.c,v 1.32 2002/03/24 13:02:28 ak Exp $\n");  
+	ia32_exec_domain.signal_map = default_exec_domain.signal_map;
+	ia32_exec_domain.signal_invmap = default_exec_domain.signal_invmap;
+	register_exec_domain(&ia32_exec_domain);
+	return 0;
+}
+
+__initcall(ia32_init);
diff -Nru a/arch/x86_64/kernel/Makefile b/arch/x86_64/kernel/Makefile
--- a/arch/x86_64/kernel/Makefile	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/kernel/Makefile	Mon Apr 22 15:32:19 2002
@@ -19,8 +19,8 @@
 
 obj-y	:= process.o semaphore.o signal.o entry.o traps.o irq.o \
 		ptrace.o i8259.o ioport.o ldt.o setup.o time.o sys_x86_64.o \
-		pci-dma.o x8664_ksyms.o i387.o syscall.o early_printk.o vsyscall.o \
-		setup64.o bluesmoke.o
+		pci-dma.o x8664_ksyms.o i387.o syscall.o vsyscall.o \
+		setup64.o bluesmoke.o bootflag.o
 
 ifdef CONFIG_PCI
 obj-y			+= pci-x86_64.o
@@ -33,6 +33,9 @@
 obj-$(CONFIG_SMP)	+= smp.o smpboot.o trampoline.o
 obj-$(CONFIG_X86_LOCAL_APIC)	+= apic.o  nmi.o
 obj-$(CONFIG_X86_IO_APIC)	+= io_apic.o mpparse.o
+#obj-$(CONFIG_ACPI)             += acpi.o
+#obj-$(CONFIG_ACPI_SLEEP)       += acpi_wakeup.o
+obj-$(CONFIG_EARLY_PRINTK)    += early_printk.o
 
 include $(TOPDIR)/Rules.make
 
diff -Nru a/arch/x86_64/kernel/apic.c b/arch/x86_64/kernel/apic.c
--- a/arch/x86_64/kernel/apic.c	Mon Apr 22 15:32:18 2002
+++ b/arch/x86_64/kernel/apic.c	Mon Apr 22 15:32:18 2002
@@ -33,6 +33,8 @@
 /* Using APIC to generate smp_local_timer_interrupt? */
 int using_apic_timer = 0;
 
+int dont_enable_local_apic __initdata = 0;
+
 int prof_multiplier[NR_CPUS] = { 1, };
 int prof_old_multiplier[NR_CPUS] = { 1, };
 int prof_counter[NR_CPUS] = { 1, };
@@ -51,7 +53,7 @@
 void clear_local_APIC(void)
 {
 	int maxlvt;
-	unsigned long v;
+	unsigned int v;
 
 	maxlvt = get_maxlvt();
 
@@ -90,7 +92,7 @@
 		apic_write_around(APIC_LVTPC, APIC_LVT_MASKED);
 	v = GET_APIC_VERSION(apic_read(APIC_LVR));
 	if (APIC_INTEGRATED(v)) {	/* !82489DX */
-		if (maxlvt > 3)
+		if (maxlvt > 3)		/* Due to Pentium errata 3AP and 11AP. */
 			apic_write(APIC_ESR, 0);
 		apic_read(APIC_ESR);
 	}
@@ -130,7 +132,7 @@
 
 void disable_local_APIC(void)
 {
-	unsigned long value;
+	unsigned int value;
 
 	clear_local_APIC();
 
@@ -223,7 +225,7 @@
  */
 void __init init_bsp_APIC(void)
 {
-	unsigned long value, ver;
+	unsigned int value, ver;
 
 	/*
 	 * Don't do the setup now if we have a SMP BIOS as the
@@ -262,7 +264,7 @@
 
 void __init setup_local_APIC (void)
 {
-	unsigned long value, ver, maxlvt;
+	unsigned int value, ver, maxlvt;
 
 	/* Pound the ESR really hard over the head with a big hammer - mbligh */
 	if (esr_disable) {
@@ -395,7 +397,7 @@
 		if (maxlvt > 3)		/* Due to the Pentium erratum 3AP. */
 			apic_write(APIC_ESR, 0);
 		value = apic_read(APIC_ESR);
-		printk("ESR value before enabling vector: %08lx\n", value);
+		printk("ESR value before enabling vector: %08x\n", value);
 
 		value = ERROR_APIC_VECTOR;      // enables sending errors
 		apic_write_around(APIC_LVTERR, value);
@@ -405,7 +407,7 @@
 		if (maxlvt > 3)
 			apic_write(APIC_ESR, 0);
 		value = apic_read(APIC_ESR);
-		printk("ESR value after enabling vector: %08lx\n", value);
+		printk("ESR value after enabling vector: %08x\n", value);
 	} else {
 		if (esr_disable)	
 			/* 
@@ -449,6 +451,7 @@
 	unsigned int apic_lvterr;
 	unsigned int apic_tmict;
 	unsigned int apic_tdcr;
+	unsigned int apic_thmr;
 } apic_pm_state;
 
 static void apic_pm_suspend(void *data)
@@ -470,6 +473,7 @@
 	apic_pm_state.apic_lvterr = apic_read(APIC_LVTERR);
 	apic_pm_state.apic_tmict = apic_read(APIC_TMICT);
 	apic_pm_state.apic_tdcr = apic_read(APIC_TDCR);
+	apic_pm_state.apic_thmr = apic_read(APIC_LVTTHMR);
 	__save_flags(flags);
 	__cli();
 	disable_local_APIC();
@@ -498,6 +502,7 @@
 	apic_write(APIC_SPIV, apic_pm_state.apic_spiv);
 	apic_write(APIC_LVT0, apic_pm_state.apic_lvt0);
 	apic_write(APIC_LVT1, apic_pm_state.apic_lvt1);
+	apic_write(APIC_LVTTHMR, apic_pm_state.apic_thmr);
 	apic_write(APIC_LVTPC, apic_pm_state.apic_lvtpc);
 	apic_write(APIC_LVTT, apic_pm_state.apic_lvtt);
 	apic_write(APIC_TDCR, apic_pm_state.apic_tdcr);
@@ -586,7 +591,6 @@
 static int __init detect_init_APIC (void)
 {
 	u32 h, l, features;
-	int needs_pm = 0;
 	extern void get_cpu_vendor(struct cpuinfo_x86*);
 
 	/* Workaround for us being called before identify_cpu(). */
@@ -619,7 +623,6 @@
 			l &= ~MSR_IA32_APICBASE_BASE;
 			l |= MSR_IA32_APICBASE_ENABLE | APIC_DEFAULT_PHYS_BASE;
 			wrmsr(MSR_IA32_APICBASE, l, h);
-			needs_pm = 1;
 		}
 	}
 	/*
@@ -631,17 +634,13 @@
 		printk("Could not enable APIC!\n");
 		return -1;
 	}
-	set_bit(X86_FEATURE_APIC, &boot_cpu_data.x86_capability);
+	set_bit(X86_FEATURE_APIC, boot_cpu_data.x86_capability);
 	mp_lapic_addr = APIC_DEFAULT_PHYS_BASE;
 	boot_cpu_id = 0;
 	if (nmi_watchdog != NMI_NONE)
 		nmi_watchdog = NMI_LOCAL_APIC;
 
 	printk("Found and enabled local APIC!\n");
-
-	if (needs_pm)
-		apic_pm_init1();
-
 	return 0;
 
 no_apic:
@@ -782,7 +781,7 @@
 
 void setup_APIC_timer(void * data)
 {
-	unsigned long clocks = (unsigned long) data, slice, t0, t1;
+	unsigned int clocks = (unsigned long) data, slice, t0, t1;
 	unsigned long flags;
 	int delta;
 
@@ -799,7 +798,8 @@
 	 */
 
 	slice = clocks / (smp_num_cpus+1);
-	printk("cpu: %d, clocks: %lu, slice: %lu\n", smp_processor_id(), clocks, slice);
+	printk("cpu: %d, clocks: %d, slice: %d\n",
+		smp_processor_id(), clocks, slice);
 
 	/*
 	 * Wait for IRQ0's slice:
@@ -822,7 +822,8 @@
 
 	__setup_APIC_LVTT(clocks);
 
-	printk("CPU%d<T0:%lu,T1:%lu,D:%d,S:%lu,C:%lu>\n", smp_processor_id(), t0, t1, delta, slice, clocks);
+	printk("CPU%d<T0:%u,T1:%u,D:%d,S:%u,C:%u>\n",
+			smp_processor_id(), t0, t1, delta, slice, clocks);
 
 	__restore_flags(flags);
 }
@@ -842,9 +843,9 @@
 
 int __init calibrate_APIC_clock(void)
 {
-	unsigned long long t1 = 0, t2 = 0;
-	long tt1, tt2;
-	long result;
+	unsigned long t1 = 0, t2 = 0;
+	int tt1, tt2;
+	int result;
 	int i;
 	const int LOOPS = HZ/10;
 
@@ -892,19 +893,23 @@
 
 	result = (tt1-tt2)*APIC_DIVISOR/LOOPS;
 
+	
+	printk("t1 = %ld t2 = %ld tt1 = %d tt2 = %d\n", t1, t2, tt1, tt2);  
+
+
 	if (cpu_has_tsc)
-		printk("..... CPU clock speed is %ld.%04ld MHz.\n",
-			((long)(t2-t1)/LOOPS)/(1000000/HZ),
-			((long)(t2-t1)/LOOPS)%(1000000/HZ));
+		printk("..... CPU clock speed is %d.%04d MHz.\n",
+			((int)(t2-t1)/LOOPS)/(1000000/HZ),
+			((int)(t2-t1)/LOOPS)%(1000000/HZ));
 
-	printk("..... host bus clock speed is %ld.%04ld MHz.\n",
+	printk("..... host bus clock speed is %d.%04d MHz.\n",
 		result/(1000000/HZ),
 		result%(1000000/HZ));
 
 	return result;
 }
 
-static unsigned long calibration_result;
+static unsigned int calibration_result;
 
 void __init setup_APIC_clocks (void)
 {
@@ -917,12 +922,12 @@
 	/*
 	 * Now set up the timer for real.
 	 */
-	setup_APIC_timer((void *)calibration_result);
+	setup_APIC_timer((void *)(u64)calibration_result);
 
 	__sti();
 
 	/* and update all other cpus */
-	smp_call_function(setup_APIC_timer, (void *)calibration_result, 1, 1);
+	smp_call_function(setup_APIC_timer, (void *)(u64)calibration_result, 1, 1);
 }
 
 void __init disable_APIC_timer(void)
@@ -985,7 +990,7 @@
  * value into /proc/profile.
  */
 
-inline void smp_local_timer_interrupt(struct pt_regs * regs)
+inline void smp_local_timer_interrupt(struct pt_regs *regs)
 {
 	int user = user_mode(regs);
 	int cpu = smp_processor_id();
@@ -1041,7 +1046,7 @@
  */
 unsigned int apic_timer_irqs [NR_CPUS];
 
-void smp_apic_timer_interrupt(struct pt_regs regs)
+void smp_apic_timer_interrupt(struct pt_regs *regs)
 {
 	int cpu = smp_processor_id();
 
@@ -1061,7 +1066,7 @@
 	 * interrupt lock, which is the WrongThing (tm) to do.
 	 */
 	irq_enter(cpu, 0);
-	smp_local_timer_interrupt(&regs);
+	smp_local_timer_interrupt(regs);
 	irq_exit(cpu, 0);
 
 	if (softirq_pending(cpu))
@@ -1073,7 +1078,9 @@
  */
 asmlinkage void smp_spurious_interrupt(void)
 {
-	unsigned long v;
+	unsigned int v;
+	static unsigned long last_warning; 
+	static unsigned long skipped; 
 
 	/*
 	 * Check if this really is a spurious interrupt and ACK it
@@ -1085,8 +1092,13 @@
 		ack_APIC_irq();
 
 	/* see sw-dev-man vol 3, chapter 7.4.13.5 */
-	printk(KERN_INFO "spurious APIC interrupt on CPU#%d, should never happen.\n",
-			smp_processor_id());
+	if (last_warning+30*HZ < jiffies) { 
+		printk(KERN_INFO "spurious APIC interrupt on CPU#%d, %ld skipped.\n",
+		       smp_processor_id(), skipped);
+		last_warning = jiffies; 
+	} else { 
+		skipped++; 
+	} 
 }
 
 /*
@@ -1095,7 +1107,7 @@
 
 asmlinkage void smp_error_interrupt(void)
 {
-	unsigned long v, v1;
+	unsigned int v, v1;
 
 	/* First tickle the hardware, only then report what went on. -- REW */
 	v = apic_read(APIC_ESR);
@@ -1114,7 +1126,7 @@
 	   6: Received illegal vector
 	   7: Illegal register address
 	*/
-	printk (KERN_ERR "APIC error on CPU%d: %02lx(%02lx)\n",
+	printk (KERN_ERR "APIC error on CPU%d: %02x(%02x)\n",
 	        smp_processor_id(), v , v1);
 }
 
diff -Nru a/arch/x86_64/kernel/bluesmoke.c b/arch/x86_64/kernel/bluesmoke.c
--- a/arch/x86_64/kernel/bluesmoke.c	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/kernel/bluesmoke.c	Mon Apr 22 15:32:19 2002
@@ -3,16 +3,127 @@
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/config.h>
+#include <linux/irq.h>
 #include <asm/processor.h> 
+#include <asm/system.h>
 #include <asm/msr.h>
+#include <asm/apic.h>
+#include <asm/pgtable.h>
+#include <asm/tlbflush.h>
 
 static int mce_disabled __initdata = 0;
 
+static int banks;
+
 /*
- *	Machine Check Handler For PII/PIII/K7
+ * If we get an MCE, we don't know what state the caches/TLB's are
+ * going to be in, so we throw them all away.
  */
+static void inline flush_all (void)
+{   
+	__asm__ __volatile__ ("invd": : );
+	__flush_tlb();
+}
 
-static int banks;
+/*
+ *	P4/Xeon Thermal transition interrupt handler
+ */
+
+static void intel_thermal_interrupt(struct pt_regs *regs)
+{
+#ifdef CONFIG_X86_LOCAL_APIC
+	u32 l, h;
+	unsigned int cpu = smp_processor_id();
+
+	ack_APIC_irq();
+
+	rdmsr(MSR_IA32_THERM_STATUS, l, h);
+	if (l & 1) {
+		printk(KERN_EMERG "CPU#%d: Temperature above threshold\n", cpu);
+		printk(KERN_EMERG "CPU#%d: Running in modulated clock mode\n", cpu);
+	} else {
+		printk(KERN_INFO "CPU#%d: Temperature/speed normal\n", cpu);
+	}
+#endif
+}
+
+static void unexpected_thermal_interrupt(struct pt_regs *regs)
+{	
+	printk(KERN_ERR "CPU#%d: Unexpected LVT TMR interrupt!\n", smp_processor_id());
+}
+
+/*
+ *	Thermal interrupt handler for this CPU setup
+ */
+
+static void (*vendor_thermal_interrupt)(struct pt_regs *regs) = unexpected_thermal_interrupt;
+
+asmlinkage void smp_thermal_interrupt(struct pt_regs regs)
+{
+	vendor_thermal_interrupt(&regs);
+}
+
+/* P4/Xeon Thermal regulation detect and init */
+
+static void __init intel_init_thermal(struct cpuinfo_x86 *c)
+{
+#ifdef CONFIG_X86_LOCAL_APIC
+	u32 l, h;
+	unsigned int cpu = smp_processor_id();
+
+	/* Thermal monitoring */
+	if (!test_bit(X86_FEATURE_ACPI, &c->x86_capability))
+		return;	/* -ENODEV */
+	
+	/* Clock modulation */
+	if (!test_bit(X86_FEATURE_ACC, &c->x86_capability))
+		return;	/* -ENODEV */
+
+	rdmsr(MSR_IA32_MISC_ENABLE, l, h);
+	/* first check if its enabled already, in which case there might
+	 * be some SMM goo which handles it, so we can't even put a handler
+	 * since it might be delivered via SMI already -zwanem.
+	 */
+
+	if (l & (1<<3)) {
+		printk(KERN_DEBUG "CPU#%d: Thermal monitoring already enabled\n", cpu);
+	} else {
+		wrmsr(MSR_IA32_MISC_ENABLE, l | (1<<3), h);
+		printk(KERN_INFO "CPU#%d: Thermal monitoring enabled\n", cpu);
+	}
+
+	/* check wether a vector already exists */	
+	l = apic_read(APIC_LVTTHMR);
+	if (l & 0xff) {
+		printk(KERN_DEBUG "CPU#%d: Thermal LVT already handled\n", cpu);
+		return; /* -EBUSY */
+	}
+	
+	wrmsr(MSR_IA32_MISC_ENABLE, l | (1<<3), h);
+	printk(KERN_INFO "CPU#%d: Thermal monitoring enabled\n", cpu);
+	
+	/* The temperature transition interrupt handler setup */
+	l = THERMAL_APIC_VECTOR;		/* our delivery vector */
+	l |= (APIC_DM_FIXED | APIC_LVT_MASKED);	/* we'll mask till we're ready */
+	apic_write_around(APIC_LVTTHMR, l);
+
+	rdmsr(MSR_IA32_THERM_INTERRUPT, l, h);
+	wrmsr(MSR_IA32_THERM_INTERRUPT, l | 0x3 , h);
+
+	/* ok we're good to go... */
+	vendor_thermal_interrupt = intel_thermal_interrupt;
+	l = apic_read(APIC_LVTTHMR);
+	apic_write_around(APIC_LVTTHMR, l & ~APIC_LVT_MASKED);
+
+	return;
+#endif
+}
+
+/*
+ *	Machine Check Handler For PII/PIII
+ */
 
 static void intel_machine_check(struct pt_regs * regs, long error_code)
 {
@@ -21,6 +132,8 @@
 	u32 mcgstl, mcgsth;
 	int i;
 	
+	flush_all();
+
 	rdmsr(MSR_IA32_MCG_STATUS, mcgstl, mcgsth);
 	if(mcgstl&(1<<0))	/* Recoverable ? */
 		recover=0;
@@ -41,13 +154,12 @@
 			if(high&(1<<27))
 			{
 				rdmsr(MSR_IA32_MC0_MISC+i*4, alow, ahigh);
-				printk("[%08x%08x]", alow, ahigh);
+				printk("[%08x%08x]", ahigh, alow);
 			}
 			if(high&(1<<26))
 			{
 				rdmsr(MSR_IA32_MC0_ADDR+i*4, alow, ahigh);
-				printk(" at %08x%08x", 
-					ahigh, alow);
+				printk(" at %08x%08x", ahigh, alow);
 			}
 			printk("\n");
 			/* Clear it */
@@ -66,9 +178,13 @@
 	wrmsr(MSR_IA32_MCG_STATUS,mcgstl, mcgsth);
 }
 
-static void unexpected_machine_check(struct pt_regs *regs, long error_code)
+/*
+ *	Handle unconfigured int18 (should never happen)
+ */
+
+static void unexpected_machine_check(struct pt_regs * regs, long error_code)
 { 
-	printk("unexpected machine check %lx\n", error_code); 
+	printk(KERN_ERR "CPU#%d: Unexpected int18 (Machine Check).\n", smp_processor_id());
 } 
 
 /*
@@ -77,13 +193,60 @@
  
 static void (*machine_check_vector)(struct pt_regs *, long error_code) = unexpected_machine_check;
 
-void do_machine_check(struct pt_regs * regs, long error_code)
+asmlinkage void do_machine_check(struct pt_regs * regs, long error_code)
 {
 	machine_check_vector(regs, error_code);
 }
 
+
+#ifdef CONFIG_X86_MCE_NONFATAL
+struct timer_list mce_timer;
+
+static void mce_checkregs (unsigned int cpu)
+{
+	u32 low, high;
+	int i;
+
+	if (cpu!=smp_processor_id())
+		BUG();
+
+	for (i=0; i<banks; i++) {
+		rdmsr(MSR_IA32_MC0_STATUS+i*4, low, high);
+
+		if ((low | high) != 0) {
+			flush_all();
+			printk (KERN_EMERG "MCE: The hardware reports a non fatal, correctable incident occured on CPU %d.\n", smp_processor_id());
+			printk (KERN_EMERG "Bank %d: %08x%08x\n", i, high, low);
+
+			/* Scrub the error so we don't pick it up in 5 seconds time. */
+			wrmsr(MSR_IA32_MC0_STATUS+i*4, 0UL, 0UL);
+
+			/* Serialize */
+			wmb();
+		}
+	}
+
+	/* Refresh the timer. */
+	mce_timer.expires = jiffies + 5 * HZ;
+	add_timer (&mce_timer);
+}
+
+static void mce_timerfunc (unsigned long data)
+{
+	int i;
+
+	for (i=0; i<smp_num_cpus; i++) {
+		if (i == smp_processor_id())
+			mce_checkregs(i);
+		else
+			smp_call_function (mce_checkregs, i, 1, 1);
+	}
+}
+#endif
+
+
 /*
- *	Set up machine check reporting for Intel processors
+ *	Set up machine check reporting for processors with Intel style MCE
  */
 
 static void __init intel_mcheck_init(struct cpuinfo_x86 *c)
@@ -96,14 +259,14 @@
 	 *	Check for MCE support
 	 */
 
-	if( !test_bit(X86_FEATURE_MCE, &c->x86_capability) )
+	if( !test_bit(X86_FEATURE_MCE, c->x86_capability) )
 		return;	
 	
 	/*
 	 *	Check for PPro style MCA
 	 */
 	 		
-	if( !test_bit(X86_FEATURE_MCA, &c->x86_capability) )
+	if( !test_bit(X86_FEATURE_MCA, c->x86_capability) )
 		return;
 		
 	/* Ok machine check is available */
@@ -114,19 +277,27 @@
 	if(done==0)
 		printk(KERN_INFO "Intel machine check architecture supported.\n");
 	rdmsr(MSR_IA32_MCG_CAP, l, h);
-	if(l&(1<<8))
+	if(l&(1<<8))	/* Control register present ? */
 		wrmsr(MSR_IA32_MCG_CTL, 0xffffffff, 0xffffffff);
 	banks = l&0xff;
-	for(i=1;i<banks;i++)
-	{
+
+	/* Don't enable bank 0 on intel P6 cores, it goes bang quickly. */
+	if (c->x86_vendor == X86_VENDOR_INTEL && c->x86 == 6) {
+		for(i=1; i<banks; i++)
+			wrmsr(MSR_IA32_MC0_CTL+4*i, 0xffffffff, 0xffffffff);
+	} else {
+		for(i=0; i<banks; i++)
 		wrmsr(MSR_IA32_MC0_CTL+4*i, 0xffffffff, 0xffffffff);
 	}
-	for(i=0;i<banks;i++)
-	{
+
+	for(i=0; i<banks; i++)
 		wrmsr(MSR_IA32_MC0_STATUS+4*i, 0x0, 0x0);
-	}
+
 	set_in_cr4(X86_CR4_MCE);
 	printk(KERN_INFO "Intel machine check reporting enabled on CPU#%d.\n", smp_processor_id());
+	
+	intel_init_thermal(c);
+
 	done=1;
 }
 
@@ -134,8 +305,6 @@
  *	This has to be run for each processor
  */
 
-
-
 void __init mcheck_init(struct cpuinfo_x86 *c)
 {
 	if(mce_disabled==1)
@@ -144,15 +313,23 @@
 	switch(c->x86_vendor)
 	{
 		case X86_VENDOR_AMD:
-			/*
-			 *	AMD K7 machine check is Intel like
-			 */
-			if(c->x86 == 6)
+			if(c->x86 == 6 || c->x86 == 15) {
 				intel_mcheck_init(c);
+#ifdef CONFIG_X86_MCE_NONFATAL
+				/* Set the timer to check for non-fatal errors every 5 seconds */
+				init_timer (&mce_timer);
+				mce_timer.expires = jiffies + 5 * HZ;
+				mce_timer.data = 0;
+				mce_timer.function = &mce_timerfunc;
+				add_timer (&mce_timer);
+#endif
+			}
 			break;
+
 		case X86_VENDOR_INTEL:
 			intel_mcheck_init(c);
 			break;
+
 		default:
 			break;
 	}
diff -Nru a/arch/x86_64/kernel/bootflag.c b/arch/x86_64/kernel/bootflag.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/arch/x86_64/kernel/bootflag.c	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,253 @@
+/*
+ *	Implement 'Simple Boot Flag Specification 1.0'
+ *
+ */
+
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+
+#include <linux/mc146818rtc.h>
+
+
+#define SBF_RESERVED (0x78)
+#define SBF_PNPOS    (1<<0)
+#define SBF_BOOTING  (1<<1)
+#define SBF_DIAG     (1<<2)
+#define SBF_PARITY   (1<<7)
+
+
+struct sbf_boot
+{
+	u8 sbf_signature[4];
+	u32 sbf_len;
+	u8 sbf_revision __attribute((packed));
+	u8 sbf_csum __attribute((packed));
+	u8 sbf_oemid[6] __attribute((packed));
+	u8 sbf_oemtable[8] __attribute((packed));
+	u8 sbf_revdata[4] __attribute((packed));
+	u8 sbf_creator[4] __attribute((packed));
+	u8 sbf_crearev[4] __attribute((packed));
+	u8 sbf_cmos __attribute((packed));
+	u8 sbf_spare[3] __attribute((packed));
+};
+
+
+static int sbf_port __initdata = -1;
+
+static int __init sbf_struct_valid(unsigned long tptr)
+{
+	u8 *ap;
+	u8 v;
+	unsigned int i;
+	struct sbf_boot sb;
+	
+	memcpy_fromio(&sb, tptr, sizeof(sb));
+
+	if(sb.sbf_len != 40 && sb.sbf_len != 39)
+		// 39 on IBM ThinkPad A21m, BIOS version 1.02b (KXET24WW; 2000-12-19).
+		return 0;
+
+	ap = (u8 *)&sb;
+	v= 0;
+	
+	for(i=0;i<sb.sbf_len;i++)
+		v+=*ap++;
+		
+	if(v)
+		return 0;
+
+	if(memcmp(sb.sbf_signature, "BOOT", 4))
+		return 0;
+
+	if (sb.sbf_len == 39)
+		printk (KERN_WARNING "SBF: ACPI BOOT descriptor is wrong length (%d)\n",
+			sb.sbf_len);
+
+	sbf_port = sb.sbf_cmos;	/* Save CMOS port */
+	return 1;
+}
+
+static int __init parity(u8 v)
+{
+	int x = 0;
+	int i;
+	
+	for(i=0;i<8;i++)
+	{
+		x^=(v&1);
+		v>>=1;
+	}
+	return x;
+}
+
+static void __init sbf_write(u8 v)
+{
+	unsigned long flags;
+	if(sbf_port != -1)
+	{
+		v &= ~SBF_PARITY;
+		if(!parity(v))
+			v|=SBF_PARITY;
+
+		printk(KERN_INFO "SBF: Setting boot flags 0x%x\n",v);
+
+		spin_lock_irqsave(&rtc_lock, flags);
+		CMOS_WRITE(v, sbf_port);
+		spin_unlock_irqrestore(&rtc_lock, flags);
+	}
+}
+
+static u8 __init sbf_read(void)
+{
+	u8 v;
+	unsigned long flags;
+	if(sbf_port == -1)
+		return 0;
+	spin_lock_irqsave(&rtc_lock, flags);
+	v = CMOS_READ(sbf_port);
+	spin_unlock_irqrestore(&rtc_lock, flags);
+	return v;
+}
+
+static int __init sbf_value_valid(u8 v)
+{
+	if(v&SBF_RESERVED)		/* Reserved bits */
+		return 0;
+	if(!parity(v))
+		return 0;
+	return 1;
+}
+
+
+static void __init sbf_bootup(void)
+{
+	u8 v;
+	if(sbf_port == -1)
+		return;
+	v = sbf_read();
+	if(!sbf_value_valid(v))
+		printk(KERN_WARNING "SBF: Simple boot flag value 0x%x read from CMOS RAM was invalid\n",v);
+	v &= ~SBF_RESERVED;
+	v &= ~SBF_BOOTING;
+	v &= ~SBF_DIAG;
+#if defined(CONFIG_ISAPNP)
+	v |= SBF_PNPOS;
+#endif
+	sbf_write(v);
+}
+
+static int __init sbf_init(void)
+{
+	unsigned int i;
+	void *rsdt;
+	u32 rsdtlen = 0;
+	u32 rsdtbase = 0;
+	u8 sum = 0;
+	int n;
+	
+	u8 *p;
+	
+	for(i=0xE0000; i <= 0xFFFE0; i+=16)
+	{
+		p = phys_to_virt(i);
+		
+		if(memcmp(p, "RSD PTR ", 8))
+			continue;
+		
+		sum = 0;
+		for(n=0; n<20; n++)
+			sum+=p[n];
+			
+		if(sum != 0)
+			continue;
+			
+		/* So it says RSD PTR and it checksums... */
+
+		/*
+		 *	Process the RDSP pointer
+		 */
+	 
+		rsdtbase = *(u32 *)(p+16);
+		
+		/*
+		 *	RSDT length is ACPI 2 only, for ACPI 1 we must map
+		 *	and remap.
+		 */
+		 
+		if(p[15]>1)
+			rsdtlen = *(u32 *)(p+20);
+		else
+			rsdtlen = 36;
+
+		if(rsdtlen < 36 || rsdtlen > 1024)
+			continue;
+		break;
+	}
+	if(i>0xFFFE0)
+		return 0;
+		
+		
+	rsdt = ioremap(rsdtbase, rsdtlen);
+	if(rsdt == 0)
+		return 0;
+		
+	i = readl(rsdt + 4);
+	
+	/*
+	 *	Remap if needed
+	 */
+	 
+	if(i > rsdtlen)
+	{
+		rsdtlen = i;
+		iounmap(rsdt);
+		rsdt = ioremap(rsdtbase, rsdtlen);
+		if(rsdt == 0)
+			return 0;
+	}
+	
+	for(n = 0; n < i; n++)
+		sum += readb(rsdt + n);
+		
+	if(sum)
+	{
+		iounmap(rsdt);
+		return 0;
+	}
+	
+	/* Ok the RSDT checksums too */
+	
+	for(n = 36; n+3 < i; n += 4)
+	{
+		unsigned long rp = readl(rsdt+n);
+		int len = 4096;
+
+		if(rp > 0xFFFFFFFFUL - len)
+			len = 0xFFFFFFFFUL - rp;
+			
+		/* Too close to the end!! */
+		if(len < 20)
+			continue;
+		rp = (unsigned long)ioremap(rp, 4096);
+		if(rp == 0)
+			continue;
+		if(sbf_struct_valid(rp))
+		{
+			/* Found the BOOT table and processed it */
+			printk(KERN_INFO "SBF: Simple Boot Flag extension found and enabled.\n");
+		}
+		iounmap((void *)rp);
+	}
+	iounmap(rsdt);
+	sbf_bootup();
+	return 0;
+}
+
+module_init(sbf_init);
diff -Nru a/arch/x86_64/kernel/cpuid.c b/arch/x86_64/kernel/cpuid.c
--- a/arch/x86_64/kernel/cpuid.c	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/kernel/cpuid.c	Mon Apr 22 15:32:19 2002
@@ -35,6 +35,7 @@
 #include <linux/poll.h>
 #include <linux/smp.h>
 #include <linux/major.h>
+#include <linux/fs.h>
 #include <linux/smp_lock.h>
 #include <linux/fs.h>
 
diff -Nru a/arch/x86_64/kernel/early_printk.c b/arch/x86_64/kernel/early_printk.c
--- a/arch/x86_64/kernel/early_printk.c	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/kernel/early_printk.c	Mon Apr 22 15:32:19 2002
@@ -1,9 +1,9 @@
+#define printk real_printk
 #include <asm/io.h>
+#undef printk
 
-/* This is "wrong" address to access it, we should access it using
-   0xffff8000000b8000ul; but 0xffff8000000b8000ul is not available
-   early at boot. */
-#define VGABASE		0xffffffff800b8000ul	
+
+#define VGABASE		0xffffffff800b8000ul	/* This is "wrong" address to access it, we should access it using 0xffff8000000b8000ul; but 0xffff8000000b8000ul is not available early at boot. */
 
 #define MAX_YPOS	25
 #define MAX_XPOS	80
@@ -61,6 +61,8 @@
 }
 
 static char buf[1024];
+
+int printk(const char *fmt, ...) __attribute__((alias("early_printk"))); 
 
 int early_printk(const char *fmt, ...)
 {
diff -Nru a/arch/x86_64/kernel/entry.S b/arch/x86_64/kernel/entry.S
--- a/arch/x86_64/kernel/entry.S	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/kernel/entry.S	Mon Apr 22 15:32:19 2002
@@ -5,7 +5,7 @@
  *  Copyright (C) 2000, 2001, 2002  Andi Kleen SuSE Labs
  *  Copyright (C) 2000  Pavel Machek <pavel@suse.cz>
  * 
- *  $Id: entry.S,v 1.66 2001/11/11 17:47:47 ak Exp $		
+ *  $Id$
  */
 
 /*
@@ -39,8 +39,7 @@
 #include <asm/msr.h>
 #include <asm/unistd.h>
 #include <asm/thread_info.h>
-	
-#define RIP_SYMBOL_NAME(x) x(%rip)
+#include <asm/hw_irq.h>
 
 	.code64
 
@@ -67,8 +66,7 @@
 	movq  	\tmp,RSP(%rsp)
 	movq    $__USER_DS,SS(%rsp)
 	movq    $__USER_CS,CS(%rsp)
-	movq	RCX(%rsp),\tmp  /* get return address */
-	movq	\tmp,RIP(%rsp)
+	movq 	$-1,RCX(%rsp)
 	movq	R11(%rsp),\tmp  /* get eflags */
 	movq	\tmp,EFLAGS(%rsp)
 	.endm
@@ -76,8 +74,6 @@
 	.macro RESTORE_TOP_OF_STACK tmp,offset=0
 	movq   RSP-\offset(%rsp),\tmp
 	movq   \tmp,PDAREF(pda_oldrsp)
-	movq   RIP-\offset(%rsp),\tmp
-	movq   \tmp,RCX-\offset(%rsp)
 	movq   EFLAGS-\offset(%rsp),\tmp
 	movq   \tmp,R11-\offset(%rsp)
 	.endm
@@ -97,17 +93,19 @@
 	addq $8*6, %rsp
 	.endm
 
-	
 /*
  * A newly forked process directly context switches into this.
  */ 	
 ENTRY(ret_from_fork)
+#if CONFIG_SMP || CONFIG_PREEMPT
+	call schedule_tail
+#endif		
 	GET_THREAD_INFO(%rcx)
 	bt $TIF_SYSCALL_TRACE,threadinfo_flags(%rcx)
 	jc rff_trace
 rff_action:	
 	RESTORE_REST
-	cmpq $__KERNEL_CS,CS-ARGOFFSET(%rsp)	# from kernel_thread?
+	testl $3,CS-ARGOFFSET(%rsp)	# from kernel_thread?
 	je   int_ret_from_sys_call
 	testl $_TIF_IA32,threadinfo_flags(%rcx)
 	jnz  int_ret_from_sys_call
@@ -132,25 +130,27 @@
  * rcx  return address for syscall/sysret, C arg3 
  * rsi  arg1
  * rdx  arg2	
- * r10  arg4 	(--> moved to rcx for C, serves as TOS flag afterwards) 
- * r8   arg5
- * r9   arg6
+ * r10  arg3 	(--> moved to rcx for C)
+ * r8   arg4
+ * r9   arg5
  * r11  eflags for syscall/sysret, temporary for C
  * r12-r15,rbp,rbx saved by C code, not touched. 		
  * 
  * Interrupts are off on entry.
  * Only called from user space.
  *
- * XXX need to add a flag for thread_saved_pc/KSTK_*. 			
+ * XXX	if we had a free scratch register we could save the RSP into the stack frame
+ *      and report it properly in ps. Unfortunately we haven't.
  */ 			 		
 
 ENTRY(system_call)
 	swapgs
 	movq	%rsp,PDAREF(pda_oldrsp) 
 	movq	PDAREF(pda_kernelstack),%rsp
-	pushq %rax
 	sti					
-	SAVE_ARGS
+	SAVE_ARGS 8,1
+	movq  %rax,ORIG_RAX-ARGOFFSET(%rsp) 
+	movq  %rcx,RIP-ARGOFFSET(%rsp)  
 	GET_THREAD_INFO(%rcx)
 	bt    $TIF_SYSCALL_TRACE,threadinfo_flags(%rcx) 
 	jc    tracesys
@@ -163,33 +163,49 @@
  * Syscall return path ending with SYSRET (fast path)
  * Has incomplete stack frame and undefined top of stack. 
  */		
-ENTRY(ret_from_sys_call)	
+	.globl ret_from_sys_call
+ret_from_sys_call:
+	movl $_TIF_WORK_MASK,%edi
+	/* edi:	flagmask */
+sysret_check:		
 	GET_THREAD_INFO(%rcx)
 	cli
 	movl threadinfo_flags(%rcx),%edx
-	andl $_TIF_WORK_MASK,%edx	# tracesys has been already checked.
+	andl %edi,%edx
 	jnz  sysret_careful 
-sysret_restore_args:
-	RESTORE_ARGS
+	movq RIP-ARGOFFSET(%rsp),%rcx
+	RESTORE_ARGS 0,-ARG_SKIP,1
 	movq	PDAREF(pda_oldrsp),%rsp
 	swapgs
-	SYSRET64
+	sysretq
 
+	/* Handle reschedules */
+	/* edx:	work, edi: workmask */	
 sysret_careful:
 	bt $TIF_NEED_RESCHED,%edx
-	jnc 1f
+	jnc sysret_signal
+	sti
+	pushq %rdi
 	call schedule
-	jmp ret_from_sys_call
-1:	sti
-	SAVE_REST
-	FIXUP_TOP_OF_STACK %rax
-	xorq %rsi,%rsi		# oldset
-	movq %rsp,%rdi		# &ptregs	
-	call do_notify_resume
-	RESTORE_TOP_OF_STACK %rax
-	RESTORE_REST
-	jmp ret_from_sys_call
+	popq  %rdi
+	jmp sysret_check
+
+	/* Handle a signal */ 
+sysret_signal:
+	sti
+	testl $(_TIF_SIGPENDING|_TIF_NOTIFY_RESUME),%edx
+	jz    1f
+
+	/* Really a signal */
+	/* edx:	work flags (arg3) */
+	leaq do_notify_resume(%rip),%rax
+	leaq -ARGOFFSET(%rsp),%rdi # &pt_regs -> arg1
+	xorl %esi,%esi # oldset -> arg2
+	call ptregscall_common
+1:	movl $_TIF_NEED_RESCHED,%edi
+	jmp sysret_check
 	
+	/* Do syscall tracing */
 tracesys:			 
 	SAVE_REST
 	movq $-ENOSYS,RAX(%rsp)
@@ -201,7 +217,6 @@
 	cmpq $__NR_syscall_max,%rax
 	ja  1f
 	movq %r10,%rcx	/* fixup for C */
-	movl $1,%r10d	/* set TOS flag */ 
 	call *sys_call_table(,%rax,8)
 	movq %rax,RAX-ARGOFFSET(%rsp)
 	SAVE_REST
@@ -220,40 +235,58 @@
  * Has correct top of stack, but partial stack frame.
  */ 	
 ENTRY(int_ret_from_sys_call)	
-	cmpq $__KERNEL_CS,CS-ARGOFFSET(%rsp)	# in kernel syscall?
+	testl $3,CS-ARGOFFSET(%rsp)	# kernel syscall?
 	je int_restore_args
-	movl $_TIF_ALLWORK_MASK,%esi
-int_with_reschedule:
+	movl $_TIF_ALLWORK_MASK,%edi
+	/* edi:	mask to check */
+int_with_check:
 	GET_THREAD_INFO(%rcx)
 	cli
 	movl threadinfo_flags(%rcx),%edx
-	andl %esi,%edx
+	andl %edi,%edx
 	jnz   int_careful
+int_restore_swapgs:	
 	swapgs
 int_restore_args:		
-	RESTORE_ARGS	
-	addq $8,%rsp	# Remove oldrax
+	RESTORE_ARGS 0,8,0
 	iretq
 
+	/* Either reschedule or signal or syscall exit tracking needed. */
+	/* First do a reschedule test. */
+	/* edx:	work, edi: workmask */
 int_careful:
-	sti
 	bt $TIF_NEED_RESCHED,%edx
 	jnc  int_very_careful
+	sti
+	pushq %rdi
 	call schedule
-	movl $_TIF_ALLWORK_MASK,%esi
-	jmp int_with_reschedule
+	popq %rdi
+	jmp int_with_check
+
+	/* handle signals and tracing -- both require a full stack frame */
 int_very_careful:
+	sti
 	SAVE_REST
-	leaq syscall_trace(%rip),%rbp
-	leaq do_notify_resume(%rip),%rbx
+	/* Check for syscall exit trace */	
 	bt $TIF_SYSCALL_TRACE,%edx
-	cmovcq %rbp,%rbx
-	xorq %rsi,%rsi		# oldset -> arg2 
+	jnc int_signal
 	movq %rsp,%rdi		# &ptregs -> arg1
-	call *%rbx
+	pushq %rdi
+	call syscall_trace
+	popq %rdi
+	btr  $TIF_SYSCALL_TRACE,%edi
+	jmp int_restore_rest
+	
+int_signal:
+	testl $(_TIF_NOTIFY_RESUME|_TIF_SIGPENDING),%edx
+	jz 1f
+	movq %rsp,%rdi		# &ptregs -> arg1
+	xorl %esi,%esi		# oldset -> arg2
+	call do_notify_resume
+1:	movl $_TIF_NEED_RESCHED,%edi	
+int_restore_rest:
 	RESTORE_REST
-	movl $_TIF_WORK_MASK,%esi
-	jmp int_with_reschedule	
+	jmp int_with_check
 		
 /* 
  * Certain special system calls that need to save a complete full stack frame.
@@ -292,8 +325,8 @@
 	FIXUP_TOP_OF_STACK %r11
 	call sys_execve
 	GET_THREAD_INFO(%rcx)
-	testl $_TIF_IA32,threadinfo_flags(%rcx)
-	jnz exec_32bit
+	bt $TIF_IA32,threadinfo_flags(%rcx)
+	jc exec_32bit
 	RESTORE_TOP_OF_STACK %r11
 	movq %r15, %r11
 	RESTORE_REST
@@ -327,24 +360,27 @@
  */ 
 
 /* 0(%rsp): interrupt number */ 
-ENTRY(common_interrupt)
-	cmpq $__KERNEL_CS,16(%rsp)
-	je   1f
-	swapgs
-1:	cld
+	.macro interrupt func
+	cld
 	SAVE_ARGS
 #ifdef CONFIG_PREEMPT
 	GET_THREAD_INFO(%rdx)
 	incl threadinfo_preempt_count(%rdx)
 #endif		
 	leaq -ARGOFFSET(%rsp),%rdi	# arg1 for handler
-	addl $1,PDAREF(pda_irqcount)	# XXX: should be merged with irq.c irqcount
+	testl $3,CS(%rdi)
+	je 1f
+	swapgs	
+1:	addl $1,PDAREF(pda_irqcount)	# XXX: should be merged with irq.c irqcount
 	movq PDAREF(pda_irqstackptr),%rax
 	cmoveq %rax,%rsp							
 	pushq %rdi			# save old stack	
-	call do_IRQ
+	call \func
+	.endm
+
+ENTRY(common_interrupt)
+	interrupt do_IRQ
 	/* 0(%rsp): oldrsp-ARGOFFSET */
-	.globl ret_from_intr
 ret_from_intr:		
 	popq  %rdi
 	cli	
@@ -355,35 +391,38 @@
 #ifdef CONFIG_PREEMPT	
 	decl threadinfo_preempt_count(%rcx)
 #endif
-	cmpq $__KERNEL_CS,CS-ARGOFFSET(%rsp)
+	testl $3,CS-ARGOFFSET(%rsp)
 	je retint_kernel
 	
 	/* Interrupt came from user space */
 	/*
-	 * Shared return path for exceptions and interrupts that came from user space.
 	 * Has a correct top of stack, but a partial stack frame
 	 * %rcx: thread info. Interrupts off.
 	 */		
 retint_with_reschedule:
-	testl $_TIF_WORK_MASK,threadinfo_flags(%rcx)
+	movl $_TIF_WORK_MASK,%edi
+retint_check:			
+	movl threadinfo_flags(%rcx),%edx
+	andl %edi,%edx
 	jnz  retint_careful
 retint_swapgs:	 	
 	swapgs 
 retint_restore_args:				
-	RESTORE_ARGS						
-	addq $8,%rsp
+	RESTORE_ARGS 0,8,0						
 	iretq
 
+	/* edi: workmask, edx: work */	
 retint_careful:
-	movl  threadinfo_flags(%rcx),%edx
 	bt    $TIF_NEED_RESCHED,%edx
 	jnc   retint_signal
 	sti
+	pushq %rdi
 	call  schedule
-retint_next_try:		
+	popq %rdi		
 	GET_THREAD_INFO(%rcx)
 	cli
-	jmp retint_with_reschedule
+	jmp retint_check
+	
 retint_signal:
 	testl $(_TIF_SIGPENDING|_TIF_NOTIFY_RESUME),%edx
 	jz    retint_swapgs
@@ -394,7 +433,10 @@
 	movq %rsp,%rdi		# &pt_regs
 	call do_notify_resume
 	RESTORE_REST
-	jmp retint_next_try
+	cli
+	movl $_TIF_NEED_RESCHED,%edi
+	GET_THREAD_INFO(%rcx)	
+	jmp retint_check
 
 #ifdef CONFIG_PREEMPT
 	/* Returning to kernel space. Check if we need preemption */
@@ -408,26 +450,59 @@
 	movl PDAREF(pda___local_bh_count),%eax
 	addl PDAREF(pda___local_irq_count),%eax
 	jnz  retint_restore_args
-	incl threadinfo_preempt_count(%rcx)
+	movl $PREEMPT_ACTIVE,threadinfo_preempt_count(%rcx)
 	sti
-	call preempt_schedule
+	call schedule
 	cli
+	GET_THREAD_INFO(%rcx)
+	movl $0,threadinfo_preempt_count(%rcx) 
 	jmp exit_intr
 #endif	
 	
 /*
+ * APIC interrupts.
+ */		
+	.macro apicinterrupt num,func
+	pushq $\num-256
+	interrupt \func
+	jmp ret_from_intr
+	.endm
+
+#ifdef CONFIG_SMP	
+ENTRY(reschedule_interrupt)
+	apicinterrupt RESCHEDULE_VECTOR,smp_reschedule_interrupt
+
+ENTRY(invalidate_interrupt)
+	apicinterrupt INVALIDATE_TLB_VECTOR,smp_invalidate_interrupt
+
+ENTRY(call_function_interrupt)
+	apicinterrupt CALL_FUNCTION_VECTOR,smp_call_function_interrupt
+#endif
+
+#ifdef CONFIG_X86_LOCAL_APIC	
+ENTRY(apic_timer_interrupt)
+	apicinterrupt LOCAL_TIMER_VECTOR,smp_apic_timer_interrupt
+
+ENTRY(error_interrupt)
+	apicinterrupt ERROR_APIC_VECTOR,smp_error_interrupt
+
+ENTRY(spurious_interrupt)
+	apicinterrupt SPURIOUS_APIC_VECTOR,smp_spurious_interrupt
+#endif
+				
+/*
  * Exception entry points.
  */ 		
 	.macro zeroentry sym
 	pushq $0	/* push error code/oldrax */ 
 	pushq %rax	/* push real oldrax to the rdi slot */ 
-	leaq  RIP_SYMBOL_NAME(\sym),%rax
+	leaq  \sym(%rip),%rax
 	jmp error_entry
 	.endm	
 
 	.macro errorentry sym
 	pushq %rax
-	leaq  RIP_SYMBOL_NAME(\sym),%rax
+	leaq  \sym(%rip),%rax
 	jmp error_entry
 	.endm
 
@@ -437,7 +512,7 @@
  */ 		  				
  	ALIGN
 error_entry:
-	cmpq $__KERNEL_CS,24(%rsp)	
+	testl $3,24(%rsp)	
 	je  error_kernelspace	
 	swapgs
 error_kernelspace:			
@@ -463,9 +538,15 @@
 	RESTORE_REST
 	cli
 	GET_THREAD_INFO(%rcx)	
-	cmpq $__KERNEL_CS,CS-ARGOFFSET(%rsp)
+	testl $3,CS-ARGOFFSET(%rsp)
 	je retint_kernel
-	jmp retint_with_reschedule
+	movl  threadinfo_flags(%rcx),%edx
+	movl  $_TIF_WORK_MASK,%edi
+	andl  %edi,%edx
+	jnz  retint_careful
+	swapgs 
+	RESTORE_ARGS 0,8,0						
+	iretq
 
 /*
  * Create a kernel thread.
@@ -544,6 +625,18 @@
 	ret
 
 ENTRY(page_fault)
+#ifdef CONFIG_KDB
+        pushq %rcx
+        pushq %rdx
+        pushq %rax
+        movl  $473,%ecx
+        rdmsr
+        andl  $0xfffffffe,%eax          /* Disable last branch recording */
+        wrmsr
+        popq  %rax
+        popq  %rdx
+        popq  %rcx
+#endif 
 	errorentry do_page_fault
 
 ENTRY(coprocessor_error)
@@ -552,12 +645,11 @@
 ENTRY(simd_coprocessor_error)
 	zeroentry do_simd_coprocessor_error	
 
-
 ENTRY(device_not_available)
-	cmpq $0,(%rsp)
-	jl  1f
+	testl $3,8(%rsp)
+	je 1f
 	swapgs
-1:	pushq $-1	
+1:	pushq $-1	#error code
 	SAVE_ALL
 	movq  %cr0,%rax
 	leaq  math_state_restore(%rip),%rcx
@@ -571,21 +663,27 @@
 ENTRY(debug)
 	zeroentry do_debug
 
-	/* XXX checkme */ 
 ENTRY(nmi)
-	cmpq $0,(%rsp)
-	jl 1f
-	swapgs
-1:	pushq $-1
+	pushq $-1
 	SAVE_ALL
-	movq %rsp,%rdi
+        /* NMI could happen inside the critical section of a swapgs,
+           so it is needed to use this expensive way to check. */
+        movl  $MSR_GS_BASE,%ecx
+        rdmsr
+        xorl  %ebx,%ebx
+        testl %edx,%edx
+        js    1f
+	swapgs
+	movl $1,%ebx
+1:	movq %rsp,%rdi # regs -> arg1
 	call do_nmi
-	RESTORE_ALL
-	addq $8,%rsp
-	cmpq $0,(%rsp)
-	jl 2f
+	/* XXX: should do preemption checks here */
+	cli
+	testl %ebx,%ebx
+	jz 2f
 	swapgs
-2:	iretq
+2:	RESTORE_ALL 8
+	iretq
 	
 ENTRY(int3)
 	zeroentry do_int3	
@@ -624,15 +722,14 @@
 	errorentry do_alignment_check
 
 ENTRY(divide_error)
-	errorentry do_divide_error
+	zeroentry do_divide_error
 
 ENTRY(spurious_interrupt_bug)
 	zeroentry do_spurious_interrupt_bug
 
-ENTRY(__bad_intr)
-	pushq $-1
-	SAVE_ALL
-	call  bad_intr
-	RESTORE_ALL
-	addq $8,%rsp
-	iretq
+ENTRY(machine_check)
+       zeroentry do_machine_check      
+
+ENTRY(call_debug)
+       zeroentry do_call_debug
+
diff -Nru a/arch/x86_64/kernel/head.S b/arch/x86_64/kernel/head.S
--- a/arch/x86_64/kernel/head.S	Mon Apr 22 15:32:18 2002
+++ b/arch/x86_64/kernel/head.S	Mon Apr 22 15:32:18 2002
@@ -4,29 +4,30 @@
  *  Copyright (C) 2000 Andrea Arcangeli <andrea@suse.de> SuSE
  *  Copyright (C) 2000 Pavel Machek <pavel@suse.cz>
  *  Copyright (C) 2000 Karsten Keil <kkeil@suse.de>
- *  Copyright (C) 2001 2002 Andi Kleen <ak@suse.de>
+ *  Copyright (C) 2001,2002 Andi Kleen <ak@suse.de>
  *
- *  $Id: head.S,v 1.41 2001/07/05 23:43:45 ak Exp $
+ *  $Id: head.S,v 1.49 2002/03/19 17:39:25 ak Exp $
  */
-.code64
-.text
+
 
 #include <linux/linkage.h>
 #include <linux/threads.h>
 #include <asm/desc.h>
 #include <asm/segment.h>
 #include <asm/page.h>
-#include <asm/offset.h>
+#include <asm/msr.h>
 	
-/* we don't able to switch in one step to final KERNEL ADDRESS SPACE
+/* we are not able to switch in one step to the final KERNEL ADRESS SPACE
  * because we need identity-mapped pages on setup so define __START_KERNEL to
  * 0x100000 for this stage
  * 
  */
 
-
+	.text
+	.code32
+/* %bx:	 1 if comming from smp trampoline on secondary cpu */ 
 startup_32:
-.code32
+	
 	/*
 	 * At this point the CPU runs in 32bit protected mode (CS.D = 1) with
 	 * paging disabled and the point of this file is to switch to 64bit
@@ -35,7 +36,9 @@
  	 * There is no stack until we set one up.
 	 */
 
-	/* As first check if extended functions are implemented */
+	movl %ebx,%ebp	/* Save trampoline flag */
+	
+	/* First check if extended functions are implemented */
 	movl	$0x80000000, %eax
 	cpuid
 	cmpl	$0x80000000, %eax
@@ -46,6 +49,8 @@
 	btl	$29, %edx
 	jnc	no_long_mode
 
+	movl	%edx,%edi
+	
 	/*
 	 * Prepare for entering 64bits mode
 	 */
@@ -61,14 +66,23 @@
 	movl	%eax, %cr3
 
 	/* Setup EFER (Extended Feature Enable Register) */
-	movl	$0xc0000080, %ecx
+	movl	$MSR_EFER, %ecx
 	rdmsr
 	/* Fool rdmsr and reset %eax to avoid dependences */
 	xorl	%eax, %eax
 	/* Enable Long Mode */
-	btsl	$8, %eax
+	btsl	$_EFER_LME, %eax
 	/* Enable System Call */
-	btsl	$0, %eax
+	btsl	$_EFER_SCE, %eax
+
+#if 0
+	/* No Execute supported? */	
+	btl	$20,%edi
+	jnc     1f
+	btsl	$_EFER_NX, %eax
+1:	
+#endif
+				
 	/* Make changes effective */
 	wrmsr
 
@@ -99,27 +113,37 @@
 	 * the new gdt/idt that has __KERNEL_CS with CS.L = 1.
 	 */
 
+	testw %bp,%bp	/* secondary CPU? */ 
+	jnz   second	
+	
 	/* Load new GDT with the 64bit segment using 32bit descriptor */
 	/* to avoid 32bit relocations we use fixed adresses here */
 	movl	$0x100F00, %eax
 	lgdt	(%eax)
+
 	movl    $0x100F10, %eax
 	/* Finally jump in 64bit mode */
 	ljmp	*(%eax)
 
-.code64
-reach_long64:
-	/*
-	 * Where we're running at 0x0000000000100000, and yes, finally
-	 * in 64bit mode.
-	 */
-	.globl	init_rsp
+second:
+	/* abuse syscall to get into 64bit mode. this way we don't need
+	   a working low identity mapping just for the short 32bit roundtrip. 
+	   XXX kludge. this should not be needed. */
+	movl  $MSR_STAR,%ecx
+	xorl  %eax,%eax
+	movl  $(__USER32_CS<<16)|__KERNEL_CS,%edx
+	wrmsr
 
-	/* Setup the first kernel stack (this instruction is modified by smpboot) */
-	.byte 0x48, 0xb8	/* movq *init_rsp,%rax */ 
-init_rsp:
-	.quad init_thread_union+THREAD_SIZE
-	movq	%rax, %rsp
+	movl  $MSR_CSTAR,%ecx
+	movl  $0xffffffff,%edx
+	movl  $0x80100100,%eax	# reach_long64 absolute
+	wrmsr
+	syscall
+
+	.code64
+	.org 0x100	
+reach_long64:
+	movq init_rsp(%rip),%rsp
 
 	/* zero EFLAGS after setting rsp */
 	pushq $0
@@ -133,15 +157,15 @@
 	 */
 	lgdt	pGDT64
 
-	/* esi is pointer to real mode structure with interesting info.
-	   pass it to C */
-	movl	%esi, %edi
-
 	movl $__KERNEL_DS,%eax
-	movl %eax,%ss
 	movl %eax,%ds	
+	movl %eax,%ss
 	movl %eax,%es
 			
+	/* esi is pointer to real mode structure with interesting info.
+	   pass it to C */
+	movl	%esi, %edi
+	
 	/* Finally jump to run C code and to be on real kernel address
 	 * Since we are running on identity-mapped space we have to jump
 	 * to the full 64bit address , this is only possible as indirect
@@ -150,11 +174,14 @@
 	movq	initial_code(%rip),%rax
 	jmp	*%rax
 
-
-	/* SMP bootup changes this */	
+	/* SMP bootup changes these two */	
 	.globl	initial_code
 initial_code:
 	.quad	x86_64_start_kernel
+	.globl init_rsp
+init_rsp:
+	.quad  init_thread_union+THREAD_SIZE-8
+
 
 .code32
 ENTRY(no_long_mode)
@@ -165,7 +192,7 @@
 .org 0xf00
 pGDT32:
 	.word	gdt32_end-gdt_table32
-	.quad	gdt_table32-__START_KERNEL+0x100000
+	.long	gdt_table32-__START_KERNEL+0x100000
 
 .org 0xf10	
 ljumpvector:
@@ -182,10 +209,9 @@
 	 * 2Mbyte large pages provided by PAE mode)
 	 */
 .org 0x1000
-ENTRY(level4_pgt)
+ENTRY(init_level4_pgt)
 	.quad	0x0000000000102007		/* -> level3_ident_pgt */
 	.fill	255,8,0
-	/* __PAGE_OFFSET 0xffff800000000000 */
 	.quad	0x000000000010a007
 	.fill	254,8,0
 	/* (2^48-(2*1024*1024*1024))/(2^39) = 511 */
@@ -193,7 +219,6 @@
 
 .org 0x2000
 /* Kernel does not "know" about 4-th level of page tables. */
-ENTRY(swapper_pg_dir)
 ENTRY(level3_ident_pgt)
 	.quad	0x0000000000104007
 	.fill	511,8,0
@@ -207,10 +232,8 @@
 
 .org 0x4000
 ENTRY(level2_ident_pgt)
-	/* 2 Mbytes are enough, this is necessary only for head.S */
+	/* 40MB for bootup. 	*/
 	.quad	0x0000000000000283
-	/* .fill	511,8,0 */
-	/* Jan needs more than 2Mbytes, so set a 40Mbyte mapping instead */
 	.quad	0x0000000000200183
 	.quad	0x0000000000400183
 	.quad	0x0000000000600183
@@ -230,10 +253,15 @@
 	.quad	0x0000000002200183
 	.quad	0x0000000002400183
 	.quad	0x0000000002600183
+	/* Temporary mappings for the super early allocator in arch/x86_64/mm/init.c */
+	.globl temp_boot_pmds
+temp_boot_pmds:
 	.fill	492,8,0
 	
 .org 0x5000
 ENTRY(level2_kernel_pgt)
+	/* 40MB kernel mapping. The kernel code cannot be bigger than that.
+	   When you change this change KERNEL_TEXT_SIZE in pgtable.h too. */
 	/* (2^48-(2*1024*1024*1024)-((2^39)*511)-((2^30)*510)) = 0 */
 	.quad	0x0000000000000183
 	.quad	0x0000000000200183
@@ -255,11 +283,7 @@
 	.quad	0x0000000002200183
 	.quad	0x0000000002400183
 	.quad	0x0000000002600183
-	/*
-	 * We could go ahead without any downside (except typing programmer
-	 * wise :) but 40Mbyte are just enough for the kernel statically
-	 * linked part (and extending it is trivial).
-	 */
+	/* Module mapping starts here */
 	.fill	492,8,0
 
 .org 0x6000
@@ -278,11 +302,7 @@
 ENTRY(level3_physmem_pgt)
 	.quad	0x0000000000105007		/* -> level2_kernel_pgt (so that __va works even before pagetable_init) */
 
-
-
 .org 0xb000
-
-
 .data
 
 .globl SYMBOL_NAME(gdt)
@@ -317,16 +337,17 @@
 	.quad	0x00cffe000000ffff	/* __USER32_CS */
 	.quad	0x00cff2000000ffff	/* __USER_DS, __USER32_DS  */		
 	.quad	0x00affa000000ffff	/* __USER_CS */
+	.word	0xFFFF				# 4Gb - (0x100000*0x1000 = 4Gb)
+	.word	0				# base address = 0
+	.word	0x9A00				# code read/exec
+	.word	0x00CF				# granularity = 4096, 386
+						#  (+5th nibble of limit)
+					/* __KERNEL32_CS */
 
 	.globl  tss_start
 tss_start:
 	.rept NR_CPUS	 	
-	.quad   0,0					/* TSS descriptors. filled in later */
-	.endr
-	.globl ldt_start
-ldt_start:	
-	.rept NR_CPUS
-	.quad   0,0					/* LDT descriptors. filled in later */ 
+	.quad   0,0,0,0,0,0,0,0		/* TSS/LDT/per cpu entries. filled in later */
 	.endr	
 gdt_end:	
 	.globl gdt_end
@@ -337,6 +358,3 @@
 	.quad   0
 	.quad 	0
 	.endr
-	
-.section .text.lock
-ENTRY(stext_lock)
diff -Nru a/arch/x86_64/kernel/head64.c b/arch/x86_64/kernel/head64.c
--- a/arch/x86_64/kernel/head64.c	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/kernel/head64.c	Mon Apr 22 15:32:19 2002
@@ -64,7 +64,7 @@
 	      (int *)&boot_cpu_data.x86_vendor_id[4]);
 
 	/* get cpu type */
-	cpuid(1, &eax, &dummy, &dummy, &boot_cpu_data.x86_capability[0]);
+	cpuid(1, &eax, &dummy, &dummy, (int *) &boot_cpu_data.x86_capability);
 	boot_cpu_data.x86 = (eax >> 8) & 0xf;
 	boot_cpu_data.x86_model = (eax >> 4) & 0xf;
 	boot_cpu_data.x86_mask = eax & 0xf;
diff -Nru a/arch/x86_64/kernel/i387.c b/arch/x86_64/kernel/i387.c
--- a/arch/x86_64/kernel/i387.c	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/kernel/i387.c	Mon Apr 22 15:32:19 2002
@@ -2,15 +2,21 @@
  *  linux/arch/x86_64/kernel/i387.c
  *
  *  Copyright (C) 1994 Linus Torvalds
+ *  Copyright (C) 2002 Andi Kleen, SuSE Labs
  *
  *  Pentium III FXSR, SSE support
  *  General FPU state handling cleanups
  *	Gareth Hughes <gareth@valinux.com>, May 2000
+ * 
+ *  x86-64 rework 2002 Andi Kleen. 
+ *  Does direct fxsave in and out of user space now for signal handlers.
+ *  All the FSAVE<->FXSAVE conversion code has been moved to the 32bit emulation,
+ *  the 64bit user space sees a FXSAVE frame directly. 
  */
 
 #include <linux/config.h>
 #include <linux/sched.h>
-#include <linux/spinlock.h>
+#include <linux/init.h>
 #include <asm/processor.h>
 #include <asm/i387.h>
 #include <asm/sigcontext.h>
@@ -18,422 +24,141 @@
 #include <asm/ptrace.h>
 #include <asm/uaccess.h>
 
-#define HAVE_HWFP 1
+static struct i387_fxsave_struct init_fpu_env; 
 
 /*
- * The _current_ task is using the FPU for the first time
- * so initialize it and set the mxcsr to its default
- * value at reset if we support XMM instructions and then
- * remeber the current task has used the FPU.
+ * Called at bootup to set up the initial FPU state that is later cloned
+ * into all processes.
  */
-void init_fpu(void)
+void __init fpu_init(void)
 {
-	__asm__("fninit");
-	if ( cpu_has_xmm )
-		load_mxcsr(0x1f80);
+	unsigned long oldcr0 = read_cr0();
+	extern void __bad_fxsave_alignment(void);
 		
-	current->used_math = 1;
-}
-
-/*
- * FPU lazy state save handling.
- */
-
-static inline void __save_init_fpu( struct task_struct *tsk )
-{
-	if ( cpu_has_fxsr ) {
-		asm volatile( "fxsave %0 ; fnclex"
-			      : "=m" (tsk->thread.i387.fxsave) );
-	} else {
-		asm volatile( "fnsave %0 ; fwait"
-			      : "=m" (tsk->thread.i387.fsave) );
-	}
-	clear_tsk_thread_flag(tsk, TIF_USEDFPU);
-}
+	if (offsetof(struct task_struct, thread.i387.fxsave) & 15)
+		__bad_fxsave_alignment();
+	set_in_cr4(X86_CR4_OSFXSR);
+	set_in_cr4(X86_CR4_OSXMMEXCPT);
+
+	write_cr0(oldcr0 & ~((1UL<<3)|(1UL<<2))); /* clear TS and EM */
+
+	asm("fninit"); 
+	load_mxcsr(0x1f80); 
+	/* initialize MMX state. normally this will be covered by fninit, but the 
+	   architecture doesn't guarantee it so do it explicitely. */ 
+	asm volatile("movq %0,%%mm0\n\t"
+	    "movq %%mm0,%%mm1\n\t"
+	    "movq %%mm0,%%mm2\n\t"
+	    "movq %%mm0,%%mm3\n\t"
+	    "movq %%mm0,%%mm4\n\t"
+	    "movq %%mm0,%%mm5\n\t"
+	    "movq %%mm0,%%mm6\n\t"
+	    "movq %%mm0,%%mm7\n\t" :: "m" (0ULL));
+	asm("emms");
+
+	/* initialize XMM state */ 
+	asm("xorpd %xmm0,%xmm0");
+	asm("xorpd %xmm1,%xmm1");
+	asm("xorpd %xmm2,%xmm2");
+	asm("xorpd %xmm3,%xmm3");
+	asm("xorpd %xmm4,%xmm4");
+	asm("xorpd %xmm5,%xmm5");
+	asm("xorpd %xmm6,%xmm6");
+	asm("xorpd %xmm7,%xmm7");
+	asm("xorpd %xmm8,%xmm8");
+	asm("xorpd %xmm9,%xmm9");
+	asm("xorpd %xmm10,%xmm10");
+	asm("xorpd %xmm11,%xmm11");
+	asm("xorpd %xmm12,%xmm12");
+	asm("xorpd %xmm13,%xmm13");
+	asm("xorpd %xmm14,%xmm14");
+	asm("xorpd %xmm15,%xmm15");
+	load_mxcsr(0x1f80);
+	asm volatile("fxsave %0" : "=m" (init_fpu_env));
 
-void save_init_fpu( struct task_struct *tsk )
-{
-	__save_init_fpu(tsk);
+	/* clean state in init */
 	stts();
-}
-
-void kernel_fpu_begin(void)
-{
-	preempt_disable();
-	if (test_thread_flag(TIF_USEDFPU)) {
-		__save_init_fpu(current);
-		return;
-	}
-	clts();
-}
-
-void restore_fpu( struct task_struct *tsk )
-{
-	if ( cpu_has_fxsr ) {
-		asm volatile( "fxrstor %0"
-			      : : "m" (tsk->thread.i387.fxsave) );
-	} else {
-		asm volatile( "frstor %0"
-			      : : "m" (tsk->thread.i387.fsave) );
-	}
-}
-
-/*
- * FPU tag word conversions.
- */
-
-static inline unsigned short twd_i387_to_fxsr( unsigned short twd )
-{
-	unsigned int tmp; /* to avoid 16 bit prefixes in the code */
- 
-	/* Transform each pair of bits into 01 (valid) or 00 (empty) */
-        tmp = ~twd;
-        tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */
-        /* and move the valid bits to the lower byte. */
-        tmp = (tmp | (tmp >> 1)) & 0x3333; /* 00VV00VV00VV00VV */
-        tmp = (tmp | (tmp >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */
-        tmp = (tmp | (tmp >> 4)) & 0x00ff; /* 00000000VVVVVVVV */
-        return tmp;
-}
-
-static inline u32 twd_fxsr_to_i387( struct i387_fxsave_struct *fxsave )
-{
-	struct _fpxreg *st = NULL;
-	u32 twd = (u32) fxsave->twd;
-	u32 tag;
-	u32 ret = 0xffff0000;
-	int i;
-
-#define FPREG_ADDR(f, n)	((char *)&(f)->st_space + (n) * 16);
-
-	for ( i = 0 ; i < 8 ; i++ ) {
-		if ( twd & 0x1 ) {
-			st = (struct _fpxreg *) FPREG_ADDR( fxsave, i );
-
-			switch ( st->exponent & 0x7fff ) {
-			case 0x7fff:
-				tag = 2;		/* Special */
-				break;
-			case 0x0000:
-				if ( !st->significand[0] &&
-				     !st->significand[1] &&
-				     !st->significand[2] &&
-				     !st->significand[3] ) {
-					tag = 1;	/* Zero */
-				} else {
-					tag = 2;	/* Special */
-				}
-				break;
-			default:
-				if ( st->significand[3] & 0x8000 ) {
-					tag = 0;	/* Valid */
-				} else {
-					tag = 2;	/* Special */
-				}
-				break;
-			}
-		} else {
-			tag = 3;			/* Empty */
-		}
-		ret |= (tag << (2 * i));
-		twd = twd >> 1;
-	}
-	return ret;
-}
-
-/*
- * FPU state interaction.
- */
-
-unsigned short get_fpu_cwd( struct task_struct *tsk )
-{
-	if ( cpu_has_fxsr ) {
-		return tsk->thread.i387.fxsave.cwd;
-	} else {
-		return (unsigned short)tsk->thread.i387.fsave.cwd;
-	}
-}
-
-unsigned short get_fpu_swd( struct task_struct *tsk )
-{
-	if ( cpu_has_fxsr ) {
-		return tsk->thread.i387.fxsave.swd;
-	} else {
-		return (unsigned short)tsk->thread.i387.fsave.swd;
-	}
-}
-
-unsigned short get_fpu_twd( struct task_struct *tsk )
-{
-	if ( cpu_has_fxsr ) {
-		return tsk->thread.i387.fxsave.twd;
-	} else {
-		return (unsigned short)tsk->thread.i387.fsave.twd;
-	}
-}
-
-unsigned short get_fpu_mxcsr( struct task_struct *tsk )
-{
-	if ( cpu_has_xmm ) {
-		return tsk->thread.i387.fxsave.mxcsr;
-	} else {
-		return 0x1f80;
-	}
-}
-
-void set_fpu_cwd( struct task_struct *tsk, unsigned short cwd )
-{
-	if ( cpu_has_fxsr ) {
-		tsk->thread.i387.fxsave.cwd = cwd;
-	} else {
-		tsk->thread.i387.fsave.cwd = ((u32)cwd | 0xffff0000);
-	}
-}
-
-void set_fpu_swd( struct task_struct *tsk, unsigned short swd )
-{
-	if ( cpu_has_fxsr ) {
-		tsk->thread.i387.fxsave.swd = swd;
-	} else {
-		tsk->thread.i387.fsave.swd = ((u32)swd | 0xffff0000);
-	}
-}
-
-void set_fpu_twd( struct task_struct *tsk, unsigned short twd )
-{
-	if ( cpu_has_fxsr ) {
-		tsk->thread.i387.fxsave.twd = twd_i387_to_fxsr(twd);
-	} else {
-		tsk->thread.i387.fsave.twd = ((u32)twd | 0xffff0000);
-	}
-}
-
-void set_fpu_mxcsr( struct task_struct *tsk, unsigned short mxcsr )
-{
-	if ( cpu_has_xmm ) {
-		tsk->thread.i387.fxsave.mxcsr = (mxcsr & 0xffbf);
-	}
+	clear_thread_flag(TIF_USEDFPU);
+	current->used_math = 0;
 }
 
 /*
- * FXSR floating point environment conversions.
+ * The _current_ task is using the FPU for the first time
+ * so initialize it and set the mxcsr to its default.
+ * remeber the current task has used the FPU.
  */
-
-static inline int convert_fxsr_to_user( struct _fpstate *buf,
-					struct i387_fxsave_struct *fxsave )
-{
-	u32 env[7];
-	struct _fpreg *to;
-	struct _fpxreg *from;
-	int i;
-
-	env[0] = (u32)fxsave->cwd | 0xffff0000;
-	env[1] = (u32)fxsave->swd | 0xffff0000;
-	env[2] = twd_fxsr_to_i387(fxsave);
-	env[3] = fxsave->fip;
-	env[4] = fxsave->fcs | ((u32)fxsave->fop << 16);
-	env[5] = fxsave->foo;
-	env[6] = fxsave->fos;
-
-	if ( __copy_to_user( buf, env, 7 * sizeof(u32) ) )
-		return 1;
-
-	to = &buf->_st[0];
-	from = (struct _fpxreg *) &fxsave->st_space[0];
-	for ( i = 0 ; i < 8 ; i++, to++, from++ ) {
-		if ( __copy_to_user( to, from, sizeof(*to) ) )
-			return 1;
-	}
-	return 0;
-}
-
-static inline int convert_fxsr_from_user( struct i387_fxsave_struct *fxsave,
-					  struct _fpstate *buf )
+void init_fpu(void)
 {
-	u32 env[7];
-	struct _fpxreg *to;
-	struct _fpreg *from;
-	int i;
-
-	if ( __copy_from_user( env, buf, 7 * sizeof(u32) ) )
-		return 1;
-
-	fxsave->cwd = (unsigned short)(env[0] & 0xffff);
-	fxsave->swd = (unsigned short)(env[1] & 0xffff);
-	fxsave->twd = twd_i387_to_fxsr((unsigned short)(env[2] & 0xffff));
-	fxsave->fip = env[3];
-	fxsave->fop = (unsigned short)((env[4] & 0xffff0000) >> 16);
-	fxsave->fcs = (env[4] & 0xffff);
-	fxsave->foo = env[5];
-	fxsave->fos = env[6];
-
-	to = (struct _fpxreg *) &fxsave->st_space[0];
-	from = &buf->_st[0];
-	for ( i = 0 ; i < 8 ; i++, to++, from++ ) {
-		if ( __copy_from_user( to, from, sizeof(*from) ) )
-			return 1;
-	}
-	return 0;
+#if 0
+	asm("fninit"); 
+	load_mxcsr(0x1f80);
+#else
+	asm volatile("fxrstor %0" :: "m" (init_fpu_env)); 
+#endif
+	current->used_math = 1;
 }
 
 /*
  * Signal frame handlers.
  */
 
-static inline int save_i387_fsave( struct _fpstate *buf )
-{
-	struct task_struct *tsk = current;
-
-	unlazy_fpu( tsk );
-	tsk->thread.i387.fsave.status = tsk->thread.i387.fsave.swd;
-	if ( __copy_to_user( buf, &tsk->thread.i387.fsave,
-			     sizeof(struct i387_fsave_struct) ) )
-		return -1;
-	return 1;
-}
-
-static inline int save_i387_fxsave( struct _fpstate *buf )
+int save_i387(struct _fpstate *buf)
 {
 	struct task_struct *tsk = current;
 	int err = 0;
 
-	unlazy_fpu( tsk );
-
-	if ( convert_fxsr_to_user( buf, &tsk->thread.i387.fxsave ) )
-		return -1;
-
-	err |= __put_user( tsk->thread.i387.fxsave.swd, &buf->status );
-	err |= __put_user( X86_FXSR_MAGIC, &buf->magic );
-	if ( err )
-		return -1;
-
-	if ( __copy_to_user( &buf->_fxsr_env[0], &tsk->thread.i387.fxsave,
-			     sizeof(struct i387_fxsave_struct) ) )
-		return -1;
-	return 1;
-}
+	{ 
+		extern void bad_user_i387_struct(void); 
+		if (sizeof(struct user_i387_struct) != sizeof(tsk->thread.i387.fxsave))
+			bad_user_i387_struct();
+	} 
 
-int save_i387( struct _fpstate *buf )
-{
-	if ( !current->used_math )
+	if (!tsk->used_math) 
 		return 0;
-
-	/* This will cause a "finit" to be triggered by the next
-	 * attempted FPU operation by the 'current' process.
-	 */
-	current->used_math = 0;
-
-	if ( HAVE_HWFP ) {
-		if ( cpu_has_fxsr ) {
-			return save_i387_fxsave( buf );
+	tsk->used_math = 0; /* trigger finit */ 
+	if (test_thread_flag(TIF_USEDFPU)) { 
+		err = save_i387_checking((struct i387_fxsave_struct *)buf);
+		if (err) return err;
+		stts();
 		} else {
-			return save_i387_fsave( buf );
-		}
+		if (__copy_to_user(buf, &tsk->thread.i387.fxsave, 
+				   sizeof(struct i387_fxsave_struct)))
+			return -1;
 	} 
-}
-
-static inline int restore_i387_fsave( struct _fpstate *buf )
-{
-	struct task_struct *tsk = current;
-	clear_fpu( tsk );
-	return __copy_from_user( &tsk->thread.i387.fsave, buf,
-				 sizeof(struct i387_fsave_struct) );
-}
-
-static inline int restore_i387_fxsave( struct _fpstate *buf )
-{
-	struct task_struct *tsk = current;
-	clear_fpu( tsk );
-	if ( __copy_from_user( &tsk->thread.i387.fxsave, &buf->_fxsr_env[0],
-			       sizeof(struct i387_fxsave_struct) ) )
 		return 1;
-	/* mxcsr bit 6 and 31-16 must be zero for security reasons */
-	tsk->thread.i387.fxsave.mxcsr &= 0xffbf;
-	return convert_fxsr_from_user( &tsk->thread.i387.fxsave, buf );
-}
-
-int restore_i387( struct _fpstate *buf )
-{
-	int err;
-
-	if ( HAVE_HWFP ) {
-		if ( cpu_has_fxsr ) {
-			err =  restore_i387_fxsave( buf );
-		} else {
-			err = restore_i387_fsave( buf );
-		}
-	} 
-	current->used_math = 1;
-	return err;
 }
 
 /*
  * ptrace request handlers.
  */
 
-int get_fpregs( struct user_i387_struct *buf, struct task_struct *tsk )
+int get_fpregs(struct user_i387_struct *buf, struct task_struct *tsk)
 {
-	if ( cpu_has_fxsr ) {
-		if (__copy_to_user( (void *)buf, &tsk->thread.i387.fxsave,
-				    sizeof(struct user_i387_struct) ))
-			return -EFAULT;
-		return 0;
-	} else {
-		return -EIO;
-	}
+	empty_fpu(tsk);
+	return __copy_to_user((void *)buf, &tsk->thread.i387.fxsave,
+			       sizeof(struct user_i387_struct)) ? -EFAULT : 0;
 }
 
-int set_fpregs( struct task_struct *tsk, struct user_i387_struct *buf )
+int set_fpregs(struct task_struct *tsk, struct user_i387_struct *buf)
 {
-	if ( cpu_has_fxsr ) {
-		__copy_from_user( &tsk->thread.i387.fxsave, (void *)buf,
-				  sizeof(struct user_i387_struct) );
-		/* mxcsr bit 6 and 31-16 must be zero for security reasons */
-		tsk->thread.i387.fxsave.mxcsr &= 0xffbf;
+	if (__copy_from_user(&tsk->thread.i387.fxsave, buf, 
+			     sizeof(struct user_i387_struct)))
+		return -EFAULT;
 		return 0;
-	} else {
-		return -EIO;
-	}
 }
 
 /*
  * FPU state for core dumps.
  */
 
-static inline void copy_fpu_fsave( struct task_struct *tsk,
-				   struct user_i387_struct *fpu )
-{
-	memcpy( fpu, &tsk->thread.i387.fsave,
-		sizeof(struct user_i387_struct) );
-}
-
-static inline void copy_fpu_fxsave( struct task_struct *tsk,
-				   struct user_i387_struct *fpu )
-{
-	unsigned short *to;
-	unsigned short *from;
-	int i;
-
-	memcpy( fpu, &tsk->thread.i387.fxsave, 7 * sizeof(u32) );
-
-	to = (unsigned short *)&fpu->st_space[0];
-	from = (unsigned short *)&tsk->thread.i387.fxsave.st_space[0];
-	for ( i = 0 ; i < 8 ; i++, to += 5, from += 8 ) {
-		memcpy( to, from, 5 * sizeof(unsigned short) );
-	}
-}
-
 int dump_fpu( struct pt_regs *regs, struct user_i387_struct *fpu )
 {
-	int fpvalid;
 	struct task_struct *tsk = current;
 
-	fpvalid = tsk->used_math && cpu_has_fxsr;
-	if ( fpvalid ) {
-		unlazy_fpu( tsk );
-		memcpy( fpu, &tsk->thread.i387.fxsave,
-			sizeof(struct user_i387_struct) );
-	}
+	if (!tsk->used_math) 
+		return 0;
 
-	return fpvalid;
+	unlazy_fpu(tsk);
+	memcpy(fpu, &tsk->thread.i387.fxsave, sizeof(struct user_i387_struct)); 
+	return 1; 
 }
diff -Nru a/arch/x86_64/kernel/i8259.c b/arch/x86_64/kernel/i8259.c
--- a/arch/x86_64/kernel/i8259.c	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/kernel/i8259.c	Mon Apr 22 15:32:19 2002
@@ -40,8 +40,6 @@
  * interrupt-controller happy.
  */
 
-BUILD_COMMON_IRQ()
-
 #define BI(x,y) \
 	BUILD_IRQ(x##y)
 
@@ -57,7 +55,7 @@
  */
 BUILD_16_IRQS(0x0)
 
-#ifdef CONFIG_X86_IO_APIC
+#ifdef CONFIG_X86_LOCAL_APIC
 /*
  * The IO-APIC gives us many more interrupt sources. Most of these 
  * are unused but an SMP system is supposed to have enough memory ...
@@ -78,31 +76,6 @@
 #undef BI
 
 
-/*
- * The following vectors are part of the Linux architecture, there
- * is no hardware IRQ pin equivalent for them, they are triggered
- * through the ICC by us (IPIs)
- */
-#ifdef CONFIG_SMP
-BUILD_SMP_INTERRUPT(task_migration_interrupt,TASK_MIGRATION_VECTOR);
-BUILD_SMP_INTERRUPT(reschedule_interrupt,RESCHEDULE_VECTOR);
-BUILD_SMP_INTERRUPT(invalidate_interrupt,INVALIDATE_TLB_VECTOR);
-BUILD_SMP_INTERRUPT(call_function_interrupt,CALL_FUNCTION_VECTOR); 
-#endif
-
-/*
- * every pentium local APIC has two 'local interrupts', with a
- * soft-definable vector attached to both interrupts, one of
- * which is a timer interrupt, the other one is error counter
- * overflow. Linux uses the local APIC timer interrupt to get
- * a much simpler SMP time architecture:
- */
-#ifdef CONFIG_X86_LOCAL_APIC
-BUILD_SMP_INTERRUPT(apic_timer_interrupt, LOCAL_TIMER_VECTOR);
-BUILD_SMP_INTERRUPT(error_interrupt,ERROR_APIC_VECTOR);
-BUILD_SMP_INTERRUPT(spurious_interrupt,SPURIOUS_APIC_VECTOR);
-#endif
-
 #define IRQ(x,y) \
 	IRQ##x##y##_interrupt
 
@@ -139,6 +112,13 @@
 
 static void end_8259A_irq (unsigned int irq)
 {
+	if (irq > 256) { 
+		char var;
+		printk("return %p stack %p ti %p\n", __builtin_return_address(0), &var, current->thread_info); 
+
+		BUG(); 
+	}
+
 	if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
 		enable_8259A_irq(irq);
 }
@@ -386,10 +366,7 @@
  * IRQ2 is cascade interrupt to second interrupt controller
  */
 
-#ifndef CONFIG_VISWS
 static struct irqaction irq2 = { no_action, 0, 0, "cascade", NULL, NULL};
-#endif
-
 
 void __init init_ISA_irqs (void)
 {
@@ -419,15 +396,18 @@
 	}
 }
 
+void apic_timer_interrupt(void);
+void spurious_interrupt(void);
+void error_interrupt(void);
+void reschedule_interrupt(void);
+void call_function_interrupt(void);
+void invalidate_interrupt(void);
+
 void __init init_IRQ(void)
 {
 	int i;
 
-#ifndef CONFIG_X86_VISWS_APIC
 	init_ISA_irqs();
-#else
-	init_VISWS_APIC_irqs();
-#endif
 	/*
 	 * Cover the whole vector space, no vector can escape
 	 * us. (some of these will be overridden and become
@@ -435,9 +415,10 @@
 	 */
 	for (i = 0; i < NR_IRQS; i++) {
 		int vector = FIRST_EXTERNAL_VECTOR + i;
-		if (vector != IA32_SYSCALL_VECTOR)
+		if (vector != IA32_SYSCALL_VECTOR && vector != KDB_VECTOR) { 
 			set_intr_gate(vector, interrupt[i]);
 	}
+	}
 
 #ifdef CONFIG_SMP
 	/*
@@ -452,9 +433,6 @@
 	 */
 	set_intr_gate(RESCHEDULE_VECTOR, reschedule_interrupt);
 
-	/* IPI for task migration */
-	set_intr_gate(TASK_MIGRATION_VECTOR, task_migration_interrupt);
-
 	/* IPI for invalidation */
 	set_intr_gate(INVALIDATE_TLB_VECTOR, invalidate_interrupt);
 
@@ -479,7 +457,5 @@
 	outb_p(LATCH & 0xff , 0x40);	/* LSB */
 	outb(LATCH >> 8 , 0x40);	/* MSB */
 
-#ifndef CONFIG_VISWS
 	setup_irq(2, &irq2);
-#endif
 }
diff -Nru a/arch/x86_64/kernel/init_task.c b/arch/x86_64/kernel/init_task.c
--- a/arch/x86_64/kernel/init_task.c	Mon Apr 22 15:32:18 2002
+++ b/arch/x86_64/kernel/init_task.c	Mon Apr 22 15:32:18 2002
@@ -7,7 +7,6 @@
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
 #include <asm/desc.h>
-#include <asm/thread_info.h>
 
 static struct fs_struct init_fs = INIT_FS;
 static struct files_struct init_files = INIT_FILES;
@@ -15,7 +14,7 @@
 struct mm_struct init_mm = INIT_MM(init_mm);
 
 /*
- * Initial thread structure.
+ * Initial task structure.
  *
  * We need to make sure that this is 8192-byte aligned due to the
  * way process stacks are handled. This is done by having a special
@@ -39,4 +38,9 @@
  * section. Since TSS's are completely CPU-local, we want them
  * on exact cacheline boundaries, to eliminate cacheline ping-pong.
  */ 
-struct tss_struct init_tss[NR_CPUS] __cacheline_aligned = { [0 ... NR_CPUS-1] = INIT_TSS };
+struct tss_struct init_tss[NR_CPUS] __cacheline_aligned;
+
+
+#define ALIGN_TO_4K __attribute__((section(".data.init_task")))
+
+pgd_t boot_vmalloc_pgt[512]  ALIGN_TO_4K;
diff -Nru a/arch/x86_64/kernel/io_apic.c b/arch/x86_64/kernel/io_apic.c
--- a/arch/x86_64/kernel/io_apic.c	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/kernel/io_apic.c	Mon Apr 22 15:32:19 2002
@@ -67,7 +67,7 @@
  * shared ISA-space IRQs, so we have to support them. We are super
  * fast in the common case, and fast for shared ISA-space IRQs.
  */
-static void add_pin_to_irq(unsigned int irq, int apic, int pin)
+static void __init add_pin_to_irq(unsigned int irq, int apic, int pin)
 {
 	static int first_free_entry = NR_IRQS;
 	struct irq_pin_list *entry = irq_2_pin + irq;
diff -Nru a/arch/x86_64/kernel/irq.c b/arch/x86_64/kernel/irq.c
--- a/arch/x86_64/kernel/irq.c	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/kernel/irq.c	Mon Apr 22 15:32:19 2002
@@ -354,7 +354,7 @@
  */
 void __global_cli(void)
 {
-	unsigned int flags;
+	unsigned long flags;
 
 	__save_flags(flags);
 	if (flags & (1 << EFLAGS_IF_SHIFT)) {
@@ -570,12 +570,14 @@
 	 * 0 return value means that this irq is already being
 	 * handled by some other CPU. (or is disabled)
 	 */
-	int irq = regs->orig_rax & 0xff; /* high bits used in ret_from_ code  */
+	unsigned irq = regs->orig_rax & 0xff; /* high bits used in ret_from_ code  */
 	int cpu = smp_processor_id();
 	irq_desc_t *desc = irq_desc + irq;
 	struct irqaction * action;
 	unsigned int status;
 
+	if (irq > 256) BUG();
+
 	kstat.irqs[cpu][irq]++;
 	spin_lock(&desc->lock);
 	desc->handler->ack(irq);
@@ -632,6 +634,7 @@
 	 * The ->end() handler has to deal with interrupts which got
 	 * disabled while the handler was running.
 	 */
+	if (irq > 256) BUG();
 	desc->handler->end(irq);
 	spin_unlock(&desc->lock);
 
diff -Nru a/arch/x86_64/kernel/ldt.c b/arch/x86_64/kernel/ldt.c
--- a/arch/x86_64/kernel/ldt.c	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/kernel/ldt.c	Mon Apr 22 15:32:19 2002
@@ -1,12 +1,16 @@
 /*
- * linux/kernel/ldt.c
+ * linux/arch/x86_64/kernel/ldt.c
  *
  * Copyright (C) 1992 Krishna Balasubramanian and Linus Torvalds
  * Copyright (C) 1999 Ingo Molnar <mingo@redhat.com>
+ * Copyright (C) 2002 Andi Kleen
+ * 
+ * This handles calls from both 32bit and 64bit mode.
  */
 
 /* 
- * FIXME: forbid code segment setting for 64bit mode. doesn't work with SYSCALL 
+ * FIXME:
+ * Need to add locking for LAR in load_gs_index.
  */ 
 
 #include <linux/errno.h>
@@ -51,35 +55,28 @@
 
 static int read_default_ldt(void * ptr, unsigned long bytecount)
 {
-	int err;
-	unsigned long size;
-	void *address;
-
-	err = 0;
-	address = &default_ldt[0];
-	size = sizeof(struct desc_struct);
-	if (size > bytecount)
-		size = bytecount;
-
-	err = size;
-	if (copy_to_user(ptr, address, size))
-		err = -EFAULT;
-
-	return err;
+	/* Arbitary number */ 
+	if (bytecount > 128) 
+		bytecount = 128; 	
+	if (clear_user(ptr, bytecount))
+		return -EFAULT;
+	return bytecount; 
 }
 
 static int write_ldt(void * ptr, unsigned long bytecount, int oldmode)
 {
-	struct mm_struct * mm = current->mm;
+	struct task_struct *me = current;
+	struct mm_struct * mm = me->mm;
 	__u32 entry_1, entry_2, *lp;
 	int error;
 	struct modify_ldt_ldt_s ldt_info;
 
 	error = -EINVAL;
+
 	if (bytecount != sizeof(ldt_info))
 		goto out;
 	error = -EFAULT; 	
-	if (copy_from_user(&ldt_info, ptr, sizeof(ldt_info)))
+	if (copy_from_user(&ldt_info, ptr, bytecount))
 		goto out;
 
 	error = -EINVAL;
@@ -92,8 +89,10 @@
 			goto out;
 	}
 
-	current->thread.fsindex = 0; 
-	current->thread.gsindex = 0; 
+	me->thread.fsindex = 0; 
+	me->thread.gsindex = 0; 
+	me->thread.gs = 0; 
+	me->thread.fs = 0; 
 
 	/*
 	 * the GDT index of the LDT is allocated dynamically, and is
@@ -122,7 +121,8 @@
 		     ldt_info.seg_32bit == 0		&&
 		     ldt_info.limit_in_pages == 0	&&
 		     ldt_info.seg_not_present == 1	&&
-		     ldt_info.useable == 0 )) {
+		     ldt_info.useable == 0 && 
+		     ldt_info.lm == 0)) {
 			entry_1 = 0;
 			entry_2 = 0;
 			goto install;
@@ -139,6 +139,7 @@
 		  ((ldt_info.seg_not_present ^ 1) << 15) |
 		  (ldt_info.seg_32bit << 22) |
 		  (ldt_info.limit_in_pages << 23) |
+		  (ldt_info.lm << 21) |
 		  0x7000;
 	if (!oldmode)
 		entry_2 |= (ldt_info.useable << 20);
diff -Nru a/arch/x86_64/kernel/mpparse.c b/arch/x86_64/kernel/mpparse.c
--- a/arch/x86_64/kernel/mpparse.c	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/kernel/mpparse.c	Mon Apr 22 15:32:19 2002
@@ -28,7 +28,9 @@
 #include <asm/pgalloc.h>
 
 /* Have we found an MP table */
-int smp_found_config = 0;
+int smp_found_config;
+
+int acpi_found_madt;
 
 /*
  * Various Linux-internal data structures created from the
@@ -61,6 +63,19 @@
 /* Bitmask of physically existing CPUs */
 unsigned long phys_cpu_present_map = 0;
 
+/* ACPI MADT entry parsing functions */
+#ifdef CONFIG_ACPI_BOOT
+extern struct acpi_boot_flags acpi_boot;
+#ifdef CONFIG_X86_LOCAL_APIC
+extern int acpi_parse_lapic (acpi_table_entry_header *header);
+extern int acpi_parse_lapic_addr_ovr (acpi_table_entry_header *header);
+extern int acpi_parse_lapic_nmi (acpi_table_entry_header *header);
+#endif /*CONFIG_X86_LOCAL_APIC*/
+#ifdef CONFIG_X86_IO_APIC
+extern int acpi_parse_ioapic (acpi_table_entry_header *header);
+#endif /*CONFIG_X86_IO_APIC*/
+#endif /*CONFIG_ACPI_BOOT*/
+
 /*
  * Intel MP BIOS table parsing routines:
  */
@@ -283,6 +298,7 @@
 	printk("APIC at: 0x%X\n",mpc->mpc_lapic);
 
 	/* save the local APIC address, it might be non-default */
+	if (!acpi_found_madt)
 	mp_lapic_addr = mpc->mpc_lapic;
 
 	/*
@@ -294,6 +310,7 @@
 			{
 				struct mpc_config_processor *m=
 					(struct mpc_config_processor *)mpt;
+				if (!acpi_found_madt)
 				MP_processor_info(m);
 				mpt += sizeof(*m);
 				count += sizeof(*m);
@@ -508,6 +525,21 @@
 void __init get_smp_config (void)
 {
 	struct intel_mp_floating *mpf = mpf_found;
+
+#ifdef CONFIG_ACPI_BOOT
+	/*
+	 * Check if the MADT exists, and if so, use it to get processor
+	 * information (ACPI_MADT_LAPIC).  The MADT supports the concept
+	 * of both logical (e.g. HT) and physical processor(s); where the
+	 * MPS only supports physical.
+	 */
+	if (acpi_boot.madt) {
+		acpi_found_madt = acpi_table_parse(ACPI_APIC, acpi_parse_madt);
+		if (acpi_found_madt > 0)
+			acpi_table_parse_madt(ACPI_MADT_LAPIC, acpi_parse_lapic);
+	}
+#endif /*CONFIG_ACPI_BOOT*/
+
 	printk("Intel MultiProcessor Specification v1.%d\n", mpf->mpf_specification);
 	if (mpf->mpf_feature2 & (1<<7)) {
 		printk("    IMCR and PIC compatibility mode.\n");
@@ -566,7 +598,7 @@
 
 static int __init smp_scan_config (unsigned long base, unsigned long length)
 {
-	unsigned long *bp = phys_to_virt(base);
+	unsigned int *bp = phys_to_virt(base);
 	struct intel_mp_floating *mpf;
 
 	Dprintk("Scan SMP from %p for %ld bytes.\n", bp,length);
diff -Nru a/arch/x86_64/kernel/mtrr.c b/arch/x86_64/kernel/mtrr.c
--- a/arch/x86_64/kernel/mtrr.c	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/kernel/mtrr.c	Mon Apr 22 15:32:19 2002
@@ -1,6 +1,8 @@
-/*  Generic MTRR (Memory Type Range Register) driver.
+/*  x86-64 MTRR (Memory Type Range Register) driver.
+	Based largely upon arch/i386/kernel/mtrr.c
 
     Copyright (C) 1997-2000  Richard Gooch
+	Copyright (C) 2002 Dave Jones.
 
     This library is free software; you can redistribute it and/or
     modify it under the terms of the GNU Library General Public
@@ -16,234 +18,9 @@
     License along with this library; if not, write to the Free
     Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
-    Richard Gooch may be reached by email at  rgooch@atnf.csiro.au
-    The postal address is:
-      Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia.
-
-    Source: "Pentium Pro Family Developer's Manual, Volume 3:
-    Operating System Writer's Guide" (Intel document number 242692),
-    section 11.11.7
-
-    ChangeLog
-
-    Prehistory Martin Tischhäuser <martin@ikcbarka.fzk.de>
-	       Initial register-setting code (from proform-1.0).
-    19971216   Richard Gooch <rgooch@atnf.csiro.au>
-               Original version for /proc/mtrr interface, SMP-safe.
-  v1.0
-    19971217   Richard Gooch <rgooch@atnf.csiro.au>
-               Bug fix for ioctls()'s.
-	       Added sample code in Documentation/mtrr.txt
-  v1.1
-    19971218   Richard Gooch <rgooch@atnf.csiro.au>
-               Disallow overlapping regions.
-    19971219   Jens Maurer <jmaurer@menuett.rhein-main.de>
-               Register-setting fixups.
-  v1.2
-    19971222   Richard Gooch <rgooch@atnf.csiro.au>
-               Fixups for kernel 2.1.75.
-  v1.3
-    19971229   David Wragg <dpw@doc.ic.ac.uk>
-               Register-setting fixups and conformity with Intel conventions.
-    19971229   Richard Gooch <rgooch@atnf.csiro.au>
-               Cosmetic changes and wrote this ChangeLog ;-)
-    19980106   Richard Gooch <rgooch@atnf.csiro.au>
-               Fixups for kernel 2.1.78.
-  v1.4
-    19980119   David Wragg <dpw@doc.ic.ac.uk>
-               Included passive-release enable code (elsewhere in PCI setup).
-  v1.5
-    19980131   Richard Gooch <rgooch@atnf.csiro.au>
-               Replaced global kernel lock with private spinlock.
-  v1.6
-    19980201   Richard Gooch <rgooch@atnf.csiro.au>
-               Added wait for other CPUs to complete changes.
-  v1.7
-    19980202   Richard Gooch <rgooch@atnf.csiro.au>
-               Bug fix in definition of <set_mtrr> for UP.
-  v1.8
-    19980319   Richard Gooch <rgooch@atnf.csiro.au>
-               Fixups for kernel 2.1.90.
-    19980323   Richard Gooch <rgooch@atnf.csiro.au>
-               Move SMP BIOS fixup before secondary CPUs call <calibrate_delay>
-  v1.9
-    19980325   Richard Gooch <rgooch@atnf.csiro.au>
-               Fixed test for overlapping regions: confused by adjacent regions
-    19980326   Richard Gooch <rgooch@atnf.csiro.au>
-               Added wbinvd in <set_mtrr_prepare>.
-    19980401   Richard Gooch <rgooch@atnf.csiro.au>
-               Bug fix for non-SMP compilation.
-    19980418   David Wragg <dpw@doc.ic.ac.uk>
-               Fixed-MTRR synchronisation for SMP and use atomic operations
-	       instead of spinlocks.
-    19980418   Richard Gooch <rgooch@atnf.csiro.au>
-	       Differentiate different MTRR register classes for BIOS fixup.
-  v1.10
-    19980419   David Wragg <dpw@doc.ic.ac.uk>
-	       Bug fix in variable MTRR synchronisation.
-  v1.11
-    19980419   Richard Gooch <rgooch@atnf.csiro.au>
-	       Fixups for kernel 2.1.97.
-  v1.12
-    19980421   Richard Gooch <rgooch@atnf.csiro.au>
-	       Safer synchronisation across CPUs when changing MTRRs.
-  v1.13
-    19980423   Richard Gooch <rgooch@atnf.csiro.au>
-	       Bugfix for SMP systems without MTRR support.
-  v1.14
-    19980427   Richard Gooch <rgooch@atnf.csiro.au>
-	       Trap calls to <mtrr_add> and <mtrr_del> on non-MTRR machines.
-  v1.15
-    19980427   Richard Gooch <rgooch@atnf.csiro.au>
-	       Use atomic bitops for setting SMP change mask.
-  v1.16
-    19980428   Richard Gooch <rgooch@atnf.csiro.au>
-	       Removed spurious diagnostic message.
-  v1.17
-    19980429   Richard Gooch <rgooch@atnf.csiro.au>
-	       Moved register-setting macros into this file.
-	       Moved setup code from init/main.c to i386-specific areas.
-  v1.18
-    19980502   Richard Gooch <rgooch@atnf.csiro.au>
-	       Moved MTRR detection outside conditionals in <mtrr_init>.
-  v1.19
-    19980502   Richard Gooch <rgooch@atnf.csiro.au>
-	       Documentation improvement: mention Pentium II and AGP.
-  v1.20
-    19980521   Richard Gooch <rgooch@atnf.csiro.au>
-	       Only manipulate interrupt enable flag on local CPU.
-	       Allow enclosed uncachable regions.
-  v1.21
-    19980611   Richard Gooch <rgooch@atnf.csiro.au>
-	       Always define <main_lock>.
-  v1.22
-    19980901   Richard Gooch <rgooch@atnf.csiro.au>
-	       Removed module support in order to tidy up code.
-	       Added sanity check for <mtrr_add>/<mtrr_del> before <mtrr_init>.
-	       Created addition queue for prior to SMP commence.
-  v1.23
-    19980902   Richard Gooch <rgooch@atnf.csiro.au>
-	       Ported patch to kernel 2.1.120-pre3.
-  v1.24
-    19980910   Richard Gooch <rgooch@atnf.csiro.au>
-	       Removed sanity checks and addition queue: Linus prefers an OOPS.
-  v1.25
-    19981001   Richard Gooch <rgooch@atnf.csiro.au>
-	       Fixed harmless compiler warning in include/asm-i386/mtrr.h
-	       Fixed version numbering and history for v1.23 -> v1.24.
-  v1.26
-    19990118   Richard Gooch <rgooch@atnf.csiro.au>
-	       Added devfs support.
-  v1.27
-    19990123   Richard Gooch <rgooch@atnf.csiro.au>
-	       Changed locking to spin with reschedule.
-	       Made use of new <smp_call_function>.
-  v1.28
-    19990201   Zoltán Böszörményi <zboszor@mail.externet.hu>
-	       Extended the driver to be able to use Cyrix style ARRs.
-    19990204   Richard Gooch <rgooch@atnf.csiro.au>
-	       Restructured Cyrix support.
-  v1.29
-    19990204   Zoltán Böszörményi <zboszor@mail.externet.hu>
-	       Refined ARR support: enable MAPEN in set_mtrr_prepare()
-	       and disable MAPEN in set_mtrr_done().
-    19990205   Richard Gooch <rgooch@atnf.csiro.au>
-	       Minor cleanups.
-  v1.30
-    19990208   Zoltán Böszörményi <zboszor@mail.externet.hu>
-               Protect plain 6x86s (and other processors without the
-               Page Global Enable feature) against accessing CR4 in
-               set_mtrr_prepare() and set_mtrr_done().
-    19990210   Richard Gooch <rgooch@atnf.csiro.au>
-	       Turned <set_mtrr_up> and <get_mtrr> into function pointers.
-  v1.31
-    19990212   Zoltán Böszörményi <zboszor@mail.externet.hu>
-               Major rewrite of cyrix_arr_init(): do not touch ARRs,
-               leave them as the BIOS have set them up.
-               Enable usage of all 8 ARRs.
-               Avoid multiplications by 3 everywhere and other
-               code clean ups/speed ups.
-    19990213   Zoltán Böszörményi <zboszor@mail.externet.hu>
-               Set up other Cyrix processors identical to the boot cpu.
-               Since Cyrix don't support Intel APIC, this is l'art pour l'art.
-               Weigh ARRs by size:
-               If size <= 32M is given, set up ARR# we were given.
-               If size >  32M is given, set up ARR7 only if it is free,
-               fail otherwise.
-    19990214   Zoltán Böszörményi <zboszor@mail.externet.hu>
-               Also check for size >= 256K if we are to set up ARR7,
-               mtrr_add() returns the value it gets from set_mtrr()
-    19990218   Zoltán Böszörményi <zboszor@mail.externet.hu>
-               Remove Cyrix "coma bug" workaround from here.
-               Moved to linux/arch/i386/kernel/setup.c and
-               linux/include/asm-i386/bugs.h
-    19990228   Richard Gooch <rgooch@atnf.csiro.au>
-	       Added MTRRIOC_KILL_ENTRY ioctl(2)
-	       Trap for counter underflow in <mtrr_file_del>.
-	       Trap for 4 MiB aligned regions for PPro, stepping <= 7.
-    19990301   Richard Gooch <rgooch@atnf.csiro.au>
-	       Created <get_free_region> hook.
-    19990305   Richard Gooch <rgooch@atnf.csiro.au>
-	       Temporarily disable AMD support now MTRR capability flag is set.
-  v1.32
-    19990308   Zoltán Böszörményi <zboszor@mail.externet.hu>
-	       Adjust my changes (19990212-19990218) to Richard Gooch's
-	       latest changes. (19990228-19990305)
-  v1.33
-    19990309   Richard Gooch <rgooch@atnf.csiro.au>
-	       Fixed typo in <printk> message.
-    19990310   Richard Gooch <rgooch@atnf.csiro.au>
-	       Support K6-II/III based on Alan Cox's <alan@redhat.com> patches.
-  v1.34
-    19990511   Bart Hartgers <bart@etpmod.phys.tue.nl>
-	       Support Centaur C6 MCR's.
-    19990512   Richard Gooch <rgooch@atnf.csiro.au>
-	       Minor cleanups.
-  v1.35
-    19990707   Zoltán Böszörményi <zboszor@mail.externet.hu>
-               Check whether ARR3 is protected in cyrix_get_free_region()
-               and mtrr_del(). The code won't attempt to delete or change it
-               from now on if the BIOS protected ARR3. It silently skips ARR3
-               in cyrix_get_free_region() or returns with an error code from
-               mtrr_del().
-    19990711   Zoltán Böszörményi <zboszor@mail.externet.hu>
-               Reset some bits in the CCRs in cyrix_arr_init() to disable SMM
-               if ARR3 isn't protected. This is needed because if SMM is active
-               and ARR3 isn't protected then deleting and setting ARR3 again
-               may lock up the processor. With SMM entirely disabled, it does
-               not happen.
-    19990812   Zoltán Böszörményi <zboszor@mail.externet.hu>
-               Rearrange switch() statements so the driver accomodates to
-               the fact that the AMD Athlon handles its MTRRs the same way
-               as Intel does.
-    19990814   Zoltán Böszörményi <zboszor@mail.externet.hu>
-	       Double check for Intel in mtrr_add()'s big switch() because
-	       that revision check is only valid for Intel CPUs.
-    19990819   Alan Cox <alan@redhat.com>
-               Tested Zoltan's changes on a pre production Athlon - 100%
-               success.
-    19991008   Manfred Spraul <manfreds@colorfullife.com>
-    	       replaced spin_lock_reschedule() with a normal semaphore.
-  v1.36
-    20000221   Richard Gooch <rgooch@atnf.csiro.au>
-               Compile fix if procfs and devfs not enabled.
-	       Formatting changes.
-  v1.37
-    20001109   H. Peter Anvin <hpa@zytor.com>
-	       Use the new centralized CPU feature detects.
-
-  v1.38
-    20010309   Dave Jones <davej@suse.de>
-	       Add support for Cyrix III.
-
-  v1.39
-    20010312   Dave Jones <davej@suse.de>
-               Ugh, I broke AMD support.
-	       Reworked fix by Troels Walsted Hansen <troels@thule.no>
-
-  v1.40
-    20010327   Dave Jones <davej@suse.de>
-	       Adapted Cyrix III support to include VIA C3.
+	(For earlier history, see arch/i386/kernel/mtrr.c)
+	September 2001	Dave Jones <davej@suse.de>
+		Initial rewrite for x86-64.
 
 */
 #include <linux/types.h>
@@ -264,7 +41,6 @@
 #include <linux/devfs_fs_kernel.h>
 #include <linux/mm.h>
 #include <linux/module.h>
-#include <linux/pci.h>
 #define MTRR_NEED_STRINGS
 #include <asm/mtrr.h>
 #include <linux/init.h>
@@ -284,28 +60,11 @@
 #include <asm/hardirq.h>
 #include <linux/irq.h>
 
-#define MTRR_VERSION            "1.40 (20010327)"
+#define MTRR_VERSION            "2.00 (20020207)"
 
 #define TRUE  1
 #define FALSE 0
 
-/*
- * The code assumes all processors support the same MTRR
- * interface.  This is generally a good assumption, but could
- * potentially be a problem.
- */
-enum mtrr_if_type {
-    MTRR_IF_NONE,		/* No MTRRs supported */
-    MTRR_IF_INTEL,		/* Intel (P6) standard MTRRs */
-    MTRR_IF_AMD_K6,		/* AMD pre-Athlon MTRRs */
-    MTRR_IF_CYRIX_ARR,		/* Cyrix ARRs */
-    MTRR_IF_CENTAUR_MCR,	/* Centaur MCRs */
-} mtrr_if = MTRR_IF_NONE;
-
-static __initdata char *mtrr_if_name[] = {
-    "none", "Intel", "AMD K6", "Cyrix ARR", "Centaur MCR"
-};
-
 #define MTRRcap_MSR     0x0fe
 #define MTRRdefType_MSR 0x2ff
 
@@ -326,31 +85,28 @@
 #define MTRRfix4K_F8000_MSR 0x26f
 
 #ifdef CONFIG_SMP
-#  define MTRR_CHANGE_MASK_FIXED     0x01
-#  define MTRR_CHANGE_MASK_VARIABLE  0x02
-#  define MTRR_CHANGE_MASK_DEFTYPE   0x04
+#define MTRR_CHANGE_MASK_FIXED     0x01
+#define MTRR_CHANGE_MASK_VARIABLE  0x02
+#define MTRR_CHANGE_MASK_DEFTYPE   0x04
 #endif
 
-/* In the Intel processor's MTRR interface, the MTRR type is always held in
-   an 8 bit field: */
 typedef u8 mtrr_type;
 
 #define LINE_SIZE      80
-#define JIFFIE_TIMEOUT 100
 
 #ifdef CONFIG_SMP
-#  define set_mtrr(reg,base,size,type) set_mtrr_smp (reg, base, size, type)
+#define set_mtrr(reg,base,size,type) set_mtrr_smp (reg, base, size, type)
 #else
-#  define set_mtrr(reg,base,size,type) (*set_mtrr_up) (reg, base, size, type, \
+#define set_mtrr(reg,base,size,type) (*set_mtrr_up) (reg, base, size, type, \
 						       TRUE)
 #endif
 
 #if defined(CONFIG_PROC_FS) || defined(CONFIG_DEVFS_FS)
-# define USERSPACE_INTERFACE
+#define USERSPACE_INTERFACE
 #endif
 
 #ifndef USERSPACE_INTERFACE
-#  define compute_ascii() while (0)
+#define compute_ascii() while (0)
 #endif
 
 #ifdef USERSPACE_INTERFACE
@@ -358,176 +114,99 @@
 static unsigned int ascii_buf_bytes;
 #endif
 static unsigned int *usage_table;
-static DECLARE_MUTEX(main_lock);
+static DECLARE_MUTEX (main_lock);
 
 /*  Private functions  */
 #ifdef USERSPACE_INTERFACE
 static void compute_ascii (void);
 #endif
 
-
-struct set_mtrr_context
-{
+struct set_mtrr_context {
     unsigned long flags;
     unsigned long deftype_lo;
     unsigned long deftype_hi;
     unsigned long cr4val;
-    unsigned long ccr3;
 };
 
-static int arr3_protected;
 
 /*  Put the processor into a state where MTRRs can be safely set  */
-static void set_mtrr_prepare_save (struct set_mtrr_context *ctxt)
+static void set_mtrr_prepare (struct set_mtrr_context *ctxt)
 {
-    /*  Disable interrupts locally  */
-    __save_flags (ctxt->flags); __cli ();
+	unsigned long cr0;
 
-    if ( mtrr_if != MTRR_IF_INTEL && mtrr_if != MTRR_IF_CYRIX_ARR )
-	 return;
+	/* Disable interrupts locally */
+	__save_flags(ctxt->flags);
+	__cli();
 
     /*  Save value of CR4 and clear Page Global Enable (bit 7)  */
-    if ( test_bit(X86_FEATURE_PGE, &boot_cpu_data.x86_capability) ) {
+	if (test_bit(X86_FEATURE_PGE, &boot_cpu_data.x86_capability)) {
 	 ctxt->cr4val = read_cr4();
-	 write_cr4(ctxt->cr4val & ~(1<<7));
+		write_cr4(ctxt->cr4val & ~(1UL << 7));
     }
 
     /*  Disable and flush caches. Note that wbinvd flushes the TLBs as
 	a side-effect  */
-
-    { 
-           long cr0 = read_cr0() | 0x40000000;
+	cr0 = read_cr0() | 0x40000000;
            wbinvd();
-           write_cr0( cr0 );
+	write_cr0(cr0);
            wbinvd();
-    }
 
-    if ( mtrr_if == MTRR_IF_INTEL ) {
-	/*  Save MTRR state */
-	rdmsr (MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi);
-    } else {
-	/* Cyrix ARRs - everything else were excluded at the top */
-	ctxt->ccr3 = getCx86 (CX86_CCR3);
-    }
-}   /*  End Function set_mtrr_prepare_save  */
-
-static void set_mtrr_cache_disable (struct set_mtrr_context *ctxt)
-{
-    if ( mtrr_if != MTRR_IF_INTEL && mtrr_if != MTRR_IF_CYRIX_ARR )
-	 return;
-
-    if ( mtrr_if == MTRR_IF_INTEL ) {
 	/*  Disable MTRRs, and set the default type to uncached  */
-	wrmsr (MTRRdefType_MSR, ctxt->deftype_lo & 0xf300UL, ctxt->deftype_hi);
-    } else {
-	/* Cyrix ARRs - everything else were excluded at the top */
-	setCx86 (CX86_CCR3, (ctxt->ccr3 & 0x0f) | 0x10);
-    }
-}   /*  End Function set_mtrr_cache_disable  */
+	rdmsr(MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi);
+	wrmsr(MTRRdefType_MSR, ctxt->deftype_lo & 0xf300UL, ctxt->deftype_hi);
+}
+
 
 /*  Restore the processor after a set_mtrr_prepare  */
 static void set_mtrr_done (struct set_mtrr_context *ctxt)
 {
-    if ( mtrr_if != MTRR_IF_INTEL && mtrr_if != MTRR_IF_CYRIX_ARR ) {
-	 __restore_flags (ctxt->flags);
-	 return;
-    }
-
     /*  Flush caches and TLBs  */
     wbinvd();
 
     /*  Restore MTRRdefType  */
-    if ( mtrr_if == MTRR_IF_INTEL ) {
-	/* Intel (P6) standard MTRRs */
-	wrmsr (MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi);
-    } else {
-	/* Cyrix ARRs - everything else was excluded at the top */
-	setCx86 (CX86_CCR3, ctxt->ccr3);
-    }
+	wrmsr(MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi);
 
     /*  Enable caches  */
-    write_cr0( read_cr0() & 0xbfffffff );
+	write_cr0(read_cr0() & 0xbfffffff);
 
     /*  Restore value of CR4  */
-    if ( test_bit(X86_FEATURE_PGE, &boot_cpu_data.x86_capability) )
-	 write_cr4(ctxt->cr4val);
+	if (test_bit(X86_FEATURE_PGE, &boot_cpu_data.x86_capability))
+		write_cr4 (ctxt->cr4val);
 
     /*  Re-enable interrupts locally (if enabled previously)  */
-    __restore_flags (ctxt->flags);
-}   /*  End Function set_mtrr_done  */
+	__restore_flags(ctxt->flags);
+}
+
 
 /*  This function returns the number of variable MTRRs  */
 static unsigned int get_num_var_ranges (void)
 {
     unsigned long config, dummy;
 
-    switch ( mtrr_if )
-    {
-    case MTRR_IF_INTEL:
 	rdmsr (MTRRcap_MSR, config, dummy);
 	return (config & 0xff);
-    case MTRR_IF_AMD_K6:
-	return 2;
-    case MTRR_IF_CYRIX_ARR:
-	return 8;
-    case MTRR_IF_CENTAUR_MCR:
-	return 8;
-    default:
-	return 0;
-    }
-}   /*  End Function get_num_var_ranges  */
+}
+
 
 /*  Returns non-zero if we have the write-combining memory type  */
 static int have_wrcomb (void)
 {
     unsigned long config, dummy;
-    struct pci_dev *dev = NULL;
-    
-   /* ServerWorks LE chipsets have problems with  write-combining 
-      Don't allow it and  leave room for other chipsets to be tagged */
-
-    if ((dev = pci_find_class(PCI_CLASS_BRIDGE_HOST << 8, NULL)) != NULL) {
-	switch(dev->vendor) {
-        case PCI_VENDOR_ID_SERVERWORKS:
- 	    switch (dev->device) {
-	    case PCI_DEVICE_ID_SERVERWORKS_LE:
-		return 0;
-		break;
-	    default:
-		break;
-	    }
-	    break;
-	default:
-	    break;
-	}
-    }
 
-
-    switch ( mtrr_if )
-    {
-    case MTRR_IF_INTEL:
 	rdmsr (MTRRcap_MSR, config, dummy);
-	return (config & (1<<10));
-	return 1;
-    case MTRR_IF_AMD_K6:
-    case MTRR_IF_CENTAUR_MCR:
-    case MTRR_IF_CYRIX_ARR:
-	return 1;
-    default:
-	return 0;
-    }
-}   /*  End Function have_wrcomb  */
+	return (config & (1 << 10));
+}
+
 
 static u32 size_or_mask, size_and_mask;
 
-static void intel_get_mtrr (unsigned int reg, unsigned long *base,
-			    unsigned long *size, mtrr_type *type)
+static void get_mtrr (unsigned int reg, unsigned long *base,
+		unsigned long *size, mtrr_type * type)
 {
     unsigned long mask_lo, mask_hi, base_lo, base_hi;
 
-    rdmsr (MTRRphysMask_MSR(reg), mask_lo, mask_hi);
-    if ( (mask_lo & 0x800) == 0 )
-    {
+	rdmsr (MTRRphysMask_MSR (reg), mask_lo, mask_hi);
+	if ((mask_lo & 0x800) == 0) {
 	/*  Invalid (i.e. free) range  */
 	*base = 0;
 	*size = 0;
@@ -535,7 +214,7 @@
 	return;
     }
 
-    rdmsr(MTRRphysBase_MSR(reg), base_lo, base_hi);
+	rdmsr (MTRRphysBase_MSR (reg), base_lo, base_hi);
 
     /* Work out the shifted address mask. */
     mask_lo = size_or_mask | mask_hi << (32 - PAGE_SHIFT)
@@ -546,140 +225,11 @@
      *size = -mask_lo;
      *base = base_hi << (32 - PAGE_SHIFT) | base_lo >> PAGE_SHIFT;
      *type = base_lo & 0xff;
-}   /*  End Function intel_get_mtrr  */
-
-static void cyrix_get_arr (unsigned int reg, unsigned long *base,
-			   unsigned long *size, mtrr_type *type)
-{
-    unsigned long flags;
-    unsigned char arr, ccr3, rcr, shift;
-
-    arr = CX86_ARR_BASE + (reg << 1) + reg; /* avoid multiplication by 3 */
-
-    /* Save flags and disable interrupts */
-    __save_flags (flags); __cli ();
-
-    ccr3 = getCx86 (CX86_CCR3);
-    setCx86 (CX86_CCR3, (ccr3 & 0x0f) | 0x10);		/* enable MAPEN */
-    ((unsigned char *) base)[3]  = getCx86 (arr);
-    ((unsigned char *) base)[2]  = getCx86 (arr+1);
-    ((unsigned char *) base)[1]  = getCx86 (arr+2);
-    rcr = getCx86(CX86_RCR_BASE + reg);
-    setCx86 (CX86_CCR3, ccr3);				/* disable MAPEN */
-
-    /* Enable interrupts if it was enabled previously */
-    __restore_flags (flags);
-    shift = ((unsigned char *) base)[1] & 0x0f;
-    *base >>= PAGE_SHIFT;
-
-    /* Power of two, at least 4K on ARR0-ARR6, 256K on ARR7
-     * Note: shift==0xf means 4G, this is unsupported.
-     */
-    if (shift)
-      *size = (reg < 7 ? 0x1UL : 0x40UL) << (shift - 1);
-    else
-      *size = 0;
-
-    /* Bit 0 is Cache Enable on ARR7, Cache Disable on ARR0-ARR6 */
-    if (reg < 7)
-    {
-	switch (rcr)
-	{
-	  case  1: *type = MTRR_TYPE_UNCACHABLE; break;
-	  case  8: *type = MTRR_TYPE_WRBACK;     break;
-	  case  9: *type = MTRR_TYPE_WRCOMB;     break;
-	  case 24:
-	  default: *type = MTRR_TYPE_WRTHROUGH;  break;
-	}
-    } else
-    {
-	switch (rcr)
-	{
-	  case  0: *type = MTRR_TYPE_UNCACHABLE; break;
-	  case  8: *type = MTRR_TYPE_WRCOMB;     break;
-	  case  9: *type = MTRR_TYPE_WRBACK;     break;
-	  case 25:
-	  default: *type = MTRR_TYPE_WRTHROUGH;  break;
-	}
-    }
-}   /*  End Function cyrix_get_arr  */
-
-static void amd_get_mtrr (unsigned int reg, unsigned long *base,
-			  unsigned long *size, mtrr_type *type)
-{
-    unsigned long low, high;
-
-    rdmsr (0xC0000085, low, high);
-    /*  Upper dword is region 1, lower is region 0  */
-    if (reg == 1) low = high;
-    /*  The base masks off on the right alignment  */
-    *base = (low & 0xFFFE0000) >> PAGE_SHIFT;
-    *type = 0;
-    if (low & 1) *type = MTRR_TYPE_UNCACHABLE;
-    if (low & 2) *type = MTRR_TYPE_WRCOMB;
-    if ( !(low & 3) )
-    {
-	*size = 0;
-	return;
-    }
-    /*
-     *	This needs a little explaining. The size is stored as an
-     *	inverted mask of bits of 128K granularity 15 bits long offset
-     *	2 bits
-     *
-     *	So to get a size we do invert the mask and add 1 to the lowest
-     *	mask bit (4 as its 2 bits in). This gives us a size we then shift
-     *	to turn into 128K blocks
-     *
-     *	eg		111 1111 1111 1100      is 512K
-     *
-     *	invert		000 0000 0000 0011
-     *	+1		000 0000 0000 0100
-     *	*128K	...
-     */
-    low = (~low) & 0x1FFFC;
-    *size = (low + 4) << (15 - PAGE_SHIFT);
-    return;
-}   /*  End Function amd_get_mtrr  */
-
-static struct
-{
-    unsigned long high;
-    unsigned long low;
-} centaur_mcr[8];
-
-static u8 centaur_mcr_reserved;
-static u8 centaur_mcr_type;		/* 0 for winchip, 1 for winchip2 */
-
-/*
- *	Report boot time MCR setups 
- */
- 
-void mtrr_centaur_report_mcr(int mcr, u32 lo, u32 hi)
-{
-	centaur_mcr[mcr].low = lo;
-	centaur_mcr[mcr].high = hi;
 }
 
-static void centaur_get_mcr (unsigned int reg, unsigned long *base,
-			     unsigned long *size, mtrr_type *type)
-{
-    *base = centaur_mcr[reg].high >> PAGE_SHIFT;
-    *size = -(centaur_mcr[reg].low & 0xfffff000) >> PAGE_SHIFT;
-    *type = MTRR_TYPE_WRCOMB;	/*  If it is there, it is write-combining  */
-    if(centaur_mcr_type==1 && ((centaur_mcr[reg].low&31)&2))
-    	*type = MTRR_TYPE_UNCACHABLE;
-    if(centaur_mcr_type==1 && (centaur_mcr[reg].low&31)==25)
-    	*type = MTRR_TYPE_WRBACK;
-    if(centaur_mcr_type==0 && (centaur_mcr[reg].low&31)==31)
-    	*type = MTRR_TYPE_WRBACK;
-    
-}   /*  End Function centaur_get_mcr  */
 
-static void (*get_mtrr) (unsigned int reg, unsigned long *base,
-			 unsigned long *size, mtrr_type *type);
 
-static void intel_set_mtrr_up (unsigned int reg, unsigned long base,
+static void set_mtrr_up (unsigned int reg, unsigned long base,
 			       unsigned long size, mtrr_type type, int do_safe)
 /*  [SUMMARY] Set variable MTRR register on the local CPU.
     <reg> The register to set.
@@ -693,259 +243,117 @@
 {
     struct set_mtrr_context ctxt;
 
-    if (do_safe) {
-	set_mtrr_prepare_save (&ctxt);
-	set_mtrr_cache_disable (&ctxt);
-	}
-    if (size == 0)
-    {
+	if (do_safe)
+		set_mtrr_prepare (&ctxt);
+
+	if (size == 0) {
 	/* The invalid bit is kept in the mask, so we simply clear the
 	   relevant mask register to disable a range. */
 	wrmsr (MTRRphysMask_MSR (reg), 0, 0);
-    }
-    else
-    {
+	} else {
 	wrmsr (MTRRphysBase_MSR (reg), base << PAGE_SHIFT | type,
 		(base & size_and_mask) >> (32 - PAGE_SHIFT));
 	wrmsr (MTRRphysMask_MSR (reg), -size << PAGE_SHIFT | 0x800,
 		(-size & size_and_mask) >> (32 - PAGE_SHIFT));
     }
-    if (do_safe) set_mtrr_done (&ctxt);
-}   /*  End Function intel_set_mtrr_up  */
-
-static void cyrix_set_arr_up (unsigned int reg, unsigned long base,
-			      unsigned long size, mtrr_type type, int do_safe)
-{
-    struct set_mtrr_context ctxt;
-    unsigned char arr, arr_type, arr_size;
-
-    arr = CX86_ARR_BASE + (reg << 1) + reg; /* avoid multiplication by 3 */
-
-    /* count down from 32M (ARR0-ARR6) or from 2G (ARR7) */
-    if (reg >= 7)
-	size >>= 6;
-
-    size &= 0x7fff; /* make sure arr_size <= 14 */
-    for(arr_size = 0; size; arr_size++, size >>= 1);
-
-    if (reg<7)
-    {
-	switch (type) {
-	  case MTRR_TYPE_UNCACHABLE:	arr_type =  1; break;
-	  case MTRR_TYPE_WRCOMB:		arr_type =  9; break;
-	  case MTRR_TYPE_WRTHROUGH:	arr_type = 24; break;
-	  default:			arr_type =  8; break;
-	}
-    }
-    else
-    {
-	switch (type)
-	{
-	  case MTRR_TYPE_UNCACHABLE:	arr_type =  0; break;
-	  case MTRR_TYPE_WRCOMB:		arr_type =  8; break;
-	  case MTRR_TYPE_WRTHROUGH:	arr_type = 25; break;
-	  default:			arr_type =  9; break;
-	}
-    }
-
-    if (do_safe) {
-	set_mtrr_prepare_save (&ctxt);
-	set_mtrr_cache_disable (&ctxt);
-    }
-    base <<= PAGE_SHIFT;
-    setCx86(arr,    ((unsigned char *) &base)[3]);
-    setCx86(arr+1,  ((unsigned char *) &base)[2]);
-    setCx86(arr+2, (((unsigned char *) &base)[1]) | arr_size);
-    setCx86(CX86_RCR_BASE + reg, arr_type);
-    if (do_safe) set_mtrr_done (&ctxt);
-}   /*  End Function cyrix_set_arr_up  */
-
-static void amd_set_mtrr_up (unsigned int reg, unsigned long base,
-			     unsigned long size, mtrr_type type, int do_safe)
-/*  [SUMMARY] Set variable MTRR register on the local CPU.
-    <reg> The register to set.
-    <base> The base address of the region.
-    <size> The size of the region. If this is 0 the region is disabled.
-    <type> The type of the region.
-    <do_safe> If TRUE, do the change safely. If FALSE, safety measures should
-    be done externally.
-    [RETURNS] Nothing.
-*/
-{
-    u32 regs[2];
-    struct set_mtrr_context ctxt;
-
-    if (do_safe) {
-	set_mtrr_prepare_save (&ctxt);
-	set_mtrr_cache_disable (&ctxt);
-    }
-    /*
-     *	Low is MTRR0 , High MTRR 1
-     */
-    rdmsr (0xC0000085, regs[0], regs[1]);
-    /*
-     *	Blank to disable
-     */
-    if (size == 0)
-	regs[reg] = 0;
-    else
-	/* Set the register to the base, the type (off by one) and an
-	   inverted bitmask of the size The size is the only odd
-	   bit. We are fed say 512K We invert this and we get 111 1111
-	   1111 1011 but if you subtract one and invert you get the   
-	   desired 111 1111 1111 1100 mask
-
-	   But ~(x - 1) == ~x + 1 == -x. Two's complement rocks!  */
-	regs[reg] = (-size>>(15-PAGE_SHIFT) & 0x0001FFFC)
-				| (base<<PAGE_SHIFT) | (type+1);
-
-    /*
-     *	The writeback rule is quite specific. See the manual. Its
-     *	disable local interrupts, write back the cache, set the mtrr
-     */
-    wbinvd();
-    wrmsr (0xC0000085, regs[0], regs[1]);
-    if (do_safe) set_mtrr_done (&ctxt);
-}   /*  End Function amd_set_mtrr_up  */
-
+	if (do_safe)
+		set_mtrr_done (&ctxt);
+}
 
-static void centaur_set_mcr_up (unsigned int reg, unsigned long base,
-				unsigned long size, mtrr_type type,
-				int do_safe)
-{
-    struct set_mtrr_context ctxt;
-    unsigned long low, high;
-
-    if (do_safe) {
-	set_mtrr_prepare_save (&ctxt);
-	set_mtrr_cache_disable (&ctxt);
-    }
-    if (size == 0)
-    {
-        /*  Disable  */
-        high = low = 0;
-    }
-    else
-    {
-	high = base << PAGE_SHIFT;
-	if(centaur_mcr_type == 0)
-		low = -size << PAGE_SHIFT | 0x1f; /* only support write-combining... */
-	else
-	{
-		if(type == MTRR_TYPE_UNCACHABLE)
-			low = -size << PAGE_SHIFT | 0x02;	/* NC */
-		else
-			low = -size << PAGE_SHIFT | 0x09;	/* WWO,WC */
-	}
-    }
-    centaur_mcr[reg].high = high;
-    centaur_mcr[reg].low = low;
-    wrmsr (0x110 + reg, low, high);
-    if (do_safe) set_mtrr_done( &ctxt );
-}   /*  End Function centaur_set_mtrr_up  */
-
-static void (*set_mtrr_up) (unsigned int reg, unsigned long base,
-			    unsigned long size, mtrr_type type,
-			    int do_safe);
 
 #ifdef CONFIG_SMP
 
-struct mtrr_var_range
-{
+struct mtrr_var_range {
     unsigned long base_lo;
     unsigned long base_hi;
     unsigned long mask_lo;
     unsigned long mask_hi;
 };
 
-
 /*  Get the MSR pair relating to a var range  */
 static void __init get_mtrr_var_range (unsigned int index,
 					   struct mtrr_var_range *vr)
 {
     rdmsr (MTRRphysBase_MSR (index), vr->base_lo, vr->base_hi);
     rdmsr (MTRRphysMask_MSR (index), vr->mask_lo, vr->mask_hi);
-}   /*  End Function get_mtrr_var_range  */
+}
 
 
 /*  Set the MSR pair relating to a var range. Returns TRUE if
     changes are made  */
-static int __init set_mtrr_var_range_testing (unsigned int index,
-						  struct mtrr_var_range *vr)
+static int __init
+set_mtrr_var_range_testing (unsigned int index, struct mtrr_var_range *vr)
 {
     unsigned int lo, hi;
     int changed = FALSE;
 
-    rdmsr(MTRRphysBase_MSR(index), lo, hi);
-    if ( (vr->base_lo & 0xfffff0ffUL) != (lo & 0xfffff0ffUL)
-	 || (vr->base_hi & 0xfUL) != (hi & 0xfUL) )
-    {
-	wrmsr (MTRRphysBase_MSR(index), vr->base_lo, vr->base_hi);
+	rdmsr (MTRRphysBase_MSR (index), lo, hi);
+	if ((vr->base_lo & 0xfffff0ffUL) != (lo & 0xfffff0ffUL)
+	    || (vr->base_hi & 0xfUL) != (hi & 0xfUL)) {
+		wrmsr (MTRRphysBase_MSR (index), vr->base_lo, vr->base_hi);
 	changed = TRUE;
     }
 
-    rdmsr (MTRRphysMask_MSR(index), lo, hi);
+	rdmsr (MTRRphysMask_MSR (index), lo, hi);
 
-    if ( (vr->mask_lo & 0xfffff800UL) != (lo & 0xfffff800UL)
-	 || (vr->mask_hi & 0xfUL) != (hi & 0xfUL) )
-    {
-	wrmsr(MTRRphysMask_MSR(index), vr->mask_lo, vr->mask_hi);
+	if ((vr->mask_lo & 0xfffff800UL) != (lo & 0xfffff800UL)
+	    || (vr->mask_hi & 0xfUL) != (hi & 0xfUL)) {
+		wrmsr (MTRRphysMask_MSR (index), vr->mask_lo, vr->mask_hi);
 	changed = TRUE;
     }
     return changed;
-}   /*  End Function set_mtrr_var_range_testing  */
+}
 
-static void __init get_fixed_ranges(mtrr_type *frs)
+
+static void __init get_fixed_ranges (mtrr_type * frs)
 {
-    unsigned long *p = (unsigned long *)frs;
+	unsigned long *p = (unsigned long *) frs;
     int i;
 
-    rdmsr(MTRRfix64K_00000_MSR, p[0], p[1]);
+	rdmsr (MTRRfix64K_00000_MSR, p[0], p[1]);
 
     for (i = 0; i < 2; i++)
-	rdmsr(MTRRfix16K_80000_MSR + i, p[2 + i*2], p[3 + i*2]);
+		rdmsr (MTRRfix16K_80000_MSR + i, p[2 + i * 2], p[3 + i * 2]);
     for (i = 0; i < 8; i++)
-	rdmsr(MTRRfix4K_C0000_MSR + i, p[6 + i*2], p[7 + i*2]);
-}   /*  End Function get_fixed_ranges  */
+		rdmsr (MTRRfix4K_C0000_MSR + i, p[6 + i * 2], p[7 + i * 2]);
+}
+
 
-static int __init set_fixed_ranges_testing(mtrr_type *frs)
+static int __init set_fixed_ranges_testing (mtrr_type * frs)
 {
-    unsigned long *p = (unsigned long *)frs;
+	unsigned long *p = (unsigned long *) frs;
     int changed = FALSE;
     int i;
     unsigned long lo, hi;
 
-    rdmsr(MTRRfix64K_00000_MSR, lo, hi);
-    if (p[0] != lo || p[1] != hi)
-    {
+	rdmsr (MTRRfix64K_00000_MSR, lo, hi);
+	if (p[0] != lo || p[1] != hi) {
 	wrmsr (MTRRfix64K_00000_MSR, p[0], p[1]);
 	changed = TRUE;
     }
 
-    for (i = 0; i < 2; i++)
-    {
+	for (i = 0; i < 2; i++) {
 	rdmsr (MTRRfix16K_80000_MSR + i, lo, hi);
-	if (p[2 + i*2] != lo || p[3 + i*2] != hi)
-	{
-	    wrmsr (MTRRfix16K_80000_MSR + i, p[2 + i*2], p[3 + i*2]);
+		if (p[2 + i * 2] != lo || p[3 + i * 2] != hi) {
+			wrmsr (MTRRfix16K_80000_MSR + i, p[2 + i * 2],
+			       p[3 + i * 2]);
 	    changed = TRUE;
 	}
     }
 
-    for (i = 0; i < 8; i++)
-    {
+	for (i = 0; i < 8; i++) {
 	rdmsr (MTRRfix4K_C0000_MSR + i, lo, hi);
-	if (p[6 + i*2] != lo || p[7 + i*2] != hi)
-	{
-	    wrmsr(MTRRfix4K_C0000_MSR + i, p[6 + i*2], p[7 + i*2]);
+		if (p[6 + i * 2] != lo || p[7 + i * 2] != hi) {
+			wrmsr (MTRRfix4K_C0000_MSR + i, p[6 + i * 2],
+			       p[7 + i * 2]);
 	    changed = TRUE;
 	}
     }
     return changed;
-}   /*  End Function set_fixed_ranges_testing  */
+}
 
-struct mtrr_state
-{
+
+struct mtrr_state {
     unsigned int num_var_ranges;
     struct mtrr_var_range *var_ranges;
     mtrr_type fixed_ranges[NUM_FIXED_RANGES];
@@ -955,13 +363,13 @@
 
 
 /*  Grab all of the MTRR state for this CPU into *state  */
-static void __init get_mtrr_state(struct mtrr_state *state)
+static void __init get_mtrr_state (struct mtrr_state *state)
 {
     unsigned int nvrs, i;
     struct mtrr_var_range *vrs;
     unsigned long lo, dummy;
 
-    nvrs = state->num_var_ranges = get_num_var_ranges();
+	nvrs = state->num_var_ranges = get_num_var_ranges ();
     vrs = state->var_ranges
               = kmalloc (nvrs * sizeof (struct mtrr_var_range), GFP_KERNEL);
     if (vrs == NULL)
@@ -974,14 +382,15 @@
     rdmsr (MTRRdefType_MSR, lo, dummy);
     state->def_type = (lo & 0xff);
     state->enabled = (lo & 0xc00) >> 10;
-}   /*  End Function get_mtrr_state  */
+}
 
 
 /*  Free resources associated with a struct mtrr_state  */
-static void __init finalize_mtrr_state(struct mtrr_state *state)
+static void __init finalize_mtrr_state (struct mtrr_state *state)
 {
-    if (state->var_ranges) kfree (state->var_ranges);
-}   /*  End Function finalize_mtrr_state  */
+	if (state->var_ranges)
+		kfree (state->var_ranges);
+}
 
 
 static unsigned long __init set_mtrr_state (struct mtrr_state *state,
@@ -997,31 +406,28 @@
     unsigned long change_mask = 0;
 
     for (i = 0; i < state->num_var_ranges; i++)
-	if ( set_mtrr_var_range_testing (i, &state->var_ranges[i]) )
+		if (set_mtrr_var_range_testing (i, &state->var_ranges[i]))
 	    change_mask |= MTRR_CHANGE_MASK_VARIABLE;
 
-    if ( set_fixed_ranges_testing(state->fixed_ranges) )
+	if (set_fixed_ranges_testing (state->fixed_ranges))
 	change_mask |= MTRR_CHANGE_MASK_FIXED;
     /*  Set_mtrr_restore restores the old value of MTRRdefType,
 	so to set it we fiddle with the saved value  */
-    if ( (ctxt->deftype_lo & 0xff) != state->def_type
-	 || ( (ctxt->deftype_lo & 0xc00) >> 10 ) != state->enabled)
-    {
+	if ((ctxt->deftype_lo & 0xff) != state->def_type
+	    || ((ctxt->deftype_lo & 0xc00) >> 10) != state->enabled) {
 	ctxt->deftype_lo |= (state->def_type | state->enabled << 10);
 	change_mask |= MTRR_CHANGE_MASK_DEFTYPE;
     }
 
     return change_mask;
-}   /*  End Function set_mtrr_state  */
+}
 
 
 static atomic_t undone_count;
-static volatile int wait_barrier_cache_disable = FALSE;
 static volatile int wait_barrier_execute = FALSE;
 static volatile int wait_barrier_cache_enable = FALSE;
 
-struct set_mtrr_data
-{
+struct set_mtrr_data {
     unsigned long smp_base;
     unsigned long smp_size;
     unsigned int smp_reg;
@@ -1035,23 +441,26 @@
 {
     struct set_mtrr_data *data = info;
     struct set_mtrr_context ctxt;
-    set_mtrr_prepare_save (&ctxt);
-    /*  Notify master that I've flushed and disabled my cache  */
-    atomic_dec (&undone_count);
-    while (wait_barrier_cache_disable) { rep_nop(); barrier(); }
-    set_mtrr_cache_disable (&ctxt);
+
+	set_mtrr_prepare (&ctxt);
     /*  Notify master that I've flushed and disabled my cache  */
     atomic_dec (&undone_count);
-    while (wait_barrier_execute) { rep_nop(); barrier(); }
+	while (wait_barrier_execute)
+		barrier ();
+
     /*  The master has cleared me to execute  */
     (*set_mtrr_up) (data->smp_reg, data->smp_base, data->smp_size,
 		    data->smp_type, FALSE);
+
     /*  Notify master CPU that I've executed the function  */
     atomic_dec (&undone_count);
+
     /*  Wait for master to clear me to enable cache and return  */
-    while (wait_barrier_cache_enable) { rep_nop(); barrier(); }
+	while (wait_barrier_cache_enable)
+		barrier ();
     set_mtrr_done (&ctxt);
-}   /*  End Function ipi_handler  */
+}
+
 
 static void set_mtrr_smp (unsigned int reg, unsigned long base,
 			  unsigned long size, mtrr_type type)
@@ -1063,42 +472,43 @@
     data.smp_base = base;
     data.smp_size = size;
     data.smp_type = type;
-    wait_barrier_cache_disable = TRUE;
     wait_barrier_execute = TRUE;
     wait_barrier_cache_enable = TRUE;
     atomic_set (&undone_count, smp_num_cpus - 1);
+
     /*  Start the ball rolling on other CPUs  */
     if (smp_call_function (ipi_handler, &data, 1, 0) != 0)
 	panic ("mtrr: timed out waiting for other CPUs\n");
+
     /* Flush and disable the local CPU's cache */
-    set_mtrr_prepare_save (&ctxt);
-    /*  Wait for all other CPUs to flush and disable their caches  */
-    while (atomic_read (&undone_count) > 0) { rep_nop(); barrier(); }
-    /* Set up for completion wait and then release other CPUs to change MTRRs*/
-    atomic_set (&undone_count, smp_num_cpus - 1);
-    wait_barrier_cache_disable = FALSE;
-    set_mtrr_cache_disable (&ctxt);
+	set_mtrr_prepare (&ctxt);
 
     /*  Wait for all other CPUs to flush and disable their caches  */
-    while (atomic_read (&undone_count) > 0) { rep_nop(); barrier(); }
-    /* Set up for completion wait and then release other CPUs to change MTRRs*/
+	while (atomic_read (&undone_count) > 0)
+		barrier ();
+
+	/* Set up for completion wait and then release other CPUs to change MTRRs */
     atomic_set (&undone_count, smp_num_cpus - 1);
     wait_barrier_execute = FALSE;
     (*set_mtrr_up) (reg, base, size, type, FALSE);
+
     /*  Now wait for other CPUs to complete the function  */
-    while (atomic_read (&undone_count) > 0) { rep_nop(); barrier(); }
+	while (atomic_read (&undone_count) > 0)
+		barrier ();
+
     /*  Now all CPUs should have finished the function. Release the barrier to
 	allow them to re-enable their caches and return from their interrupt,
 	then enable the local cache and return  */
     wait_barrier_cache_enable = FALSE;
     set_mtrr_done (&ctxt);
-}   /*  End Function set_mtrr_smp  */
+}
 
 
 /*  Some BIOS's are fucked and don't set all MTRRs the same!  */
-static void __init mtrr_state_warn(unsigned long mask)
+static void __init mtrr_state_warn (unsigned long mask)
 {
-    if (!mask) return;
+	if (!mask)
+		return;
     if (mask & MTRR_CHANGE_MASK_FIXED)
 	printk ("mtrr: your CPUs had inconsistent fixed MTRR settings\n");
     if (mask & MTRR_CHANGE_MASK_VARIABLE)
@@ -1106,59 +516,43 @@
     if (mask & MTRR_CHANGE_MASK_DEFTYPE)
 	printk ("mtrr: your CPUs had inconsistent MTRRdefType settings\n");
     printk ("mtrr: probably your BIOS does not setup all CPUs\n");
-}   /*  End Function mtrr_state_warn  */
+}
 
 #endif  /*  CONFIG_SMP  */
 
-static char *attrib_to_str (int x)
+
+static char inline * attrib_to_str (int x)
 {
     return (x <= 6) ? mtrr_strings[x] : "?";
-}   /*  End Function attrib_to_str  */
+}
+
 
-static void init_table (void)
+static void __init init_table (void)
 {
     int i, max;
 
     max = get_num_var_ranges ();
-    if ( ( usage_table = kmalloc (max * sizeof *usage_table, GFP_KERNEL) )
-	 == NULL )
-    {
+	if ((usage_table = kmalloc (max * sizeof *usage_table, GFP_KERNEL))==NULL) {
 	printk ("mtrr: could not allocate\n");
 	return;
     }
-    for (i = 0; i < max; i++) usage_table[i] = 1;
+
+	for (i = 0; i < max; i++)
+		usage_table[i] = 1;
+
 #ifdef USERSPACE_INTERFACE
-    if ( ( ascii_buffer = kmalloc (max * LINE_SIZE, GFP_KERNEL) ) == NULL )
-    {
+	if ((ascii_buffer = kmalloc (max * LINE_SIZE, GFP_KERNEL)) == NULL) {
 	printk ("mtrr: could not allocate\n");
 	return;
     }
     ascii_buf_bytes = 0;
     compute_ascii ();
 #endif
-}   /*  End Function init_table  */
-
-static int generic_get_free_region (unsigned long base, unsigned long size)
-/*  [SUMMARY] Get a free MTRR.
-    <base> The starting (base) address of the region.
-    <size> The size (in bytes) of the region.
-    [RETURNS] The index of the region on success, else -1 on error.
-*/
-{
-    int i, max;
-    mtrr_type ltype;
-    unsigned long lbase, lsize;
+}
 
-    max = get_num_var_ranges ();
-    for (i = 0; i < max; ++i)
-    {
-	(*get_mtrr) (i, &lbase, &lsize, &ltype);
-	if (lsize == 0) return i;
-    }
-    return -ENOSPC;
-}   /*  End Function generic_get_free_region  */
 
-static int centaur_get_free_region (unsigned long base, unsigned long size)
+static int generic_get_free_region (unsigned long base,
+		unsigned long size)
 /*  [SUMMARY] Get a free MTRR.
     <base> The starting (base) address of the region.
     <size> The size (in bytes) of the region.
@@ -1170,48 +564,14 @@
     unsigned long lbase, lsize;
 
     max = get_num_var_ranges ();
-    for (i = 0; i < max; ++i)
-    {
-    	if(centaur_mcr_reserved & (1<<i))
-    		continue;
+	for (i = 0; i < max; ++i) {
 	(*get_mtrr) (i, &lbase, &lsize, &ltype);
-	if (lsize == 0) return i;
+		if (lsize == 0)
+			return i;
     }
     return -ENOSPC;
-}   /*  End Function generic_get_free_region  */
-
-static int cyrix_get_free_region (unsigned long base, unsigned long size)
-/*  [SUMMARY] Get a free ARR.
-    <base> The starting (base) address of the region.
-    <size> The size (in bytes) of the region.
-    [RETURNS] The index of the region on success, else -1 on error.
-*/
-{
-    int i;
-    mtrr_type ltype;
-    unsigned long lbase, lsize;
+}
 
-    /* If we are to set up a region >32M then look at ARR7 immediately */
-    if (size > 0x2000)
-    {
-	cyrix_get_arr (7, &lbase, &lsize, &ltype);
-	if (lsize == 0) return 7;
-	/*  Else try ARR0-ARR6 first  */
-    }
-    else
-    {
-	for (i = 0; i < 7; i++)
-	{
-	    cyrix_get_arr (i, &lbase, &lsize, &ltype);
-	    if ((i == 3) && arr3_protected) continue;
-	    if (lsize == 0) return i;
-	}
-	/* ARR0-ARR6 isn't free, try ARR7 but its size must be at least 256K */
-	cyrix_get_arr (i, &lbase, &lsize, &ltype);
-	if ((lsize == 0) && (size >= 0x40)) return i;
-    }
-    return -ENOSPC;
-}   /*  End Function cyrix_get_free_region  */
 
 static int (*get_free_region) (unsigned long base,
 			       unsigned long size) = generic_get_free_region;
@@ -1252,7 +612,8 @@
  *	failures and do not wish system log messages to be sent.
  */
 
-int mtrr_add_page(unsigned long base, unsigned long size, unsigned int type, char increment)
+int mtrr_add_page (unsigned long base, unsigned long size,
+		unsigned int type, char increment)
 {
 /*  [SUMMARY] Add an MTRR entry.
     <base> The starting (base, in pages) address of the region.
@@ -1268,98 +629,39 @@
     mtrr_type ltype;
     unsigned long lbase, lsize, last;
 
-    switch ( mtrr_if )
-    {
-    case MTRR_IF_NONE:
-	return -ENXIO;		/* No MTRRs whatsoever */
-
-    case MTRR_IF_AMD_K6:
-	/* Apply the K6 block alignment and size rules
-	   In order
-	   o Uncached or gathering only
-	   o 128K or bigger block
-	   o Power of 2 block
-	   o base suitably aligned to the power
-	*/
-	if ( type > MTRR_TYPE_WRCOMB || size < (1 << (17-PAGE_SHIFT)) ||
-	     (size & ~(size-1))-size || ( base & (size-1) ) )
-	    return -EINVAL;
-	break;
-
-    case MTRR_IF_INTEL:
-	/*  For Intel PPro stepping <= 7, must be 4 MiB aligned 
-	    and not touch 0x70000000->0x7003FFFF */
-	if ( boot_cpu_data.x86_vendor == X86_VENDOR_INTEL &&
-	     boot_cpu_data.x86 == 6 &&
-	     boot_cpu_data.x86_model == 1 &&
-	     boot_cpu_data.x86_mask <= 7 )
-	{
-	    if ( base & ((1 << (22-PAGE_SHIFT))-1) )
-	    {
-		printk (KERN_WARNING "mtrr: base(0x%lx000) is not 4 MiB aligned\n", base);
-		return -EINVAL;
-	    }
-	    if (!(base + size < 0x70000000 || base > 0x7003FFFF) &&
-		 (type == MTRR_TYPE_WRCOMB || type == MTRR_TYPE_WRBACK))
-	    {
-		printk (KERN_WARNING "mtrr: writable mtrr between 0x70000000 and 0x7003FFFF may hang the CPU.\n");
-	        return -EINVAL;
-	    }
-	}
-	/* Fall through */
-	
-    case MTRR_IF_CYRIX_ARR:
-    case MTRR_IF_CENTAUR_MCR:
-        if ( mtrr_if == MTRR_IF_CENTAUR_MCR )
-	{
-	    /*
-	     *	FIXME: Winchip2 supports uncached
-	     */
-	    if (type != MTRR_TYPE_WRCOMB && (centaur_mcr_type == 0 || type != MTRR_TYPE_UNCACHABLE))
-	    {
-		printk (KERN_WARNING "mtrr: only write-combining%s supported\n",
-			centaur_mcr_type?" and uncacheable are":" is");
-		return -EINVAL;
-	    }
-	}
-	else if (base + size < 0x100)
-	{
-	    printk (KERN_WARNING "mtrr: cannot set region below 1 MiB (0x%lx000,0x%lx000)\n",
+	if (base + size < 0x100) {
+		printk (KERN_WARNING
+			"mtrr: cannot set region below 1 MiB (0x%lx000,0x%lx000)\n",
 		    base, size);
 	    return -EINVAL;
 	}
+
 	/*  Check upper bits of base and last are equal and lower bits are 0
 	    for base and 1 for last  */
 	last = base + size - 1;
 	for (lbase = base; !(lbase & 1) && (last & 1);
-	     lbase = lbase >> 1, last = last >> 1);
-	if (lbase != last)
-	{
-	    printk (KERN_WARNING "mtrr: base(0x%lx000) is not aligned on a size(0x%lx000) boundary\n",
-		    base, size);
-	    return -EINVAL;
-	}
-	break;
+	     lbase = lbase >> 1, last = last >> 1) ;
 
-    default:
+	if (lbase != last) {
+		printk (KERN_WARNING
+			"mtrr: base(0x%lx000) is not aligned on a size(0x%lx000) boundary\n",
+			base, size);
 	return -EINVAL;
     }
 
-    if (type >= MTRR_NUM_TYPES)
-    {
+	if (type >= MTRR_NUM_TYPES) {
 	printk ("mtrr: type: %u illegal\n", type);
 	return -EINVAL;
     }
 
     /*  If the type is WC, check that this processor supports it  */
-    if ( (type == MTRR_TYPE_WRCOMB) && !have_wrcomb () )
-    {
-        printk (KERN_WARNING "mtrr: your processor doesn't support write-combining\n");
+	if ((type == MTRR_TYPE_WRCOMB) && !have_wrcomb ()) {
+		printk (KERN_WARNING
+			"mtrr: your processor doesn't support write-combining\n");
         return -ENOSYS;
     }
 
-    if ( base & size_or_mask || size  & size_or_mask )
-    {
+	if (base & size_or_mask || size & size_or_mask) {
 	printk ("mtrr: base or size exceeds the MTRR width\n");
 	return -EINVAL;
     }
@@ -1367,49 +669,54 @@
     increment = increment ? 1 : 0;
     max = get_num_var_ranges ();
     /*  Search for existing MTRR  */
-    down(&main_lock);
-    for (i = 0; i < max; ++i)
-    {
+	down (&main_lock);
+	for (i = 0; i < max; ++i) {
 	(*get_mtrr) (i, &lbase, &lsize, &ltype);
-	if (base >= lbase + lsize) continue;
-	if ( (base < lbase) && (base + size <= lbase) ) continue;
+		if (base >= lbase + lsize)
+			continue;
+		if ((base < lbase) && (base + size <= lbase))
+			continue;
+
 	/*  At this point we know there is some kind of overlap/enclosure  */
-	if ( (base < lbase) || (base + size > lbase + lsize) )
-	{
-	    up(&main_lock);
-	    printk (KERN_WARNING "mtrr: 0x%lx000,0x%lx000 overlaps existing"
-		    " 0x%lx000,0x%lx000\n",
-		    base, size, lbase, lsize);
+		if ((base < lbase) || (base + size > lbase + lsize)) {
+			up (&main_lock);
+			printk (KERN_WARNING
+				"mtrr: 0x%lx000,0x%lx000 overlaps existing"
+				" 0x%lx000,0x%lx000\n", base, size, lbase,
+				lsize);
 	    return -EINVAL;
 	}
 	/*  New region is enclosed by an existing region  */
-	if (ltype != type)
-	{
-	    if (type == MTRR_TYPE_UNCACHABLE) continue;
-	    up(&main_lock);
-	    printk ( "mtrr: type mismatch for %lx000,%lx000 old: %s new: %s\n",
-		     base, size, attrib_to_str (ltype), attrib_to_str (type) );
+		if (ltype != type) {
+			if (type == MTRR_TYPE_UNCACHABLE)
+				continue;
+			up (&main_lock);
+			printk
+			    ("mtrr: type mismatch for %lx000,%lx000 old: %s new: %s\n",
+			     base, size, attrib_to_str (ltype),
+			     attrib_to_str (type));
 	    return -EINVAL;
 	}
-	if (increment) ++usage_table[i];
+		if (increment)
+			++usage_table[i];
 	compute_ascii ();
-	up(&main_lock);
+		up (&main_lock);
 	return i;
     }
     /*  Search for an empty MTRR  */
     i = (*get_free_region) (base, size);
-    if (i < 0)
-    {
-	up(&main_lock);
+	if (i < 0) {
+		up (&main_lock);
 	printk ("mtrr: no more MTRRs available\n");
 	return i;
     }
     set_mtrr (i, base, size, type);
     usage_table[i] = 1;
     compute_ascii ();
-    up(&main_lock);
+	up (&main_lock);
     return i;
-}   /*  End Function mtrr_add_page  */
+}
+
 
 /**
  *	mtrr_add - Add a memory type region
@@ -1447,7 +754,8 @@
  *	failures and do not wish system log messages to be sent.
  */
 
-int mtrr_add(unsigned long base, unsigned long size, unsigned int type, char increment)
+int mtrr_add (unsigned long base, unsigned long size, unsigned int type,
+		char increment)
 {
 /*  [SUMMARY] Add an MTRR entry.
     <base> The starting (base) address of the region.
@@ -1459,14 +767,15 @@
     the error code.
 */
 
-    if ( (base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1)) )
-    {
+	if ((base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1))) {
 	printk ("mtrr: size and base must be multiples of 4 kiB\n");
 	printk ("mtrr: size: 0x%lx  base: 0x%lx\n", size, base);
 	return -EINVAL;
     }
-    return mtrr_add_page(base >> PAGE_SHIFT, size >> PAGE_SHIFT, type, increment);
-}   /*  End Function mtrr_add  */
+	return mtrr_add_page (base >> PAGE_SHIFT, size >> PAGE_SHIFT, type,
+			      increment);
+}
+
 
 /**
  *	mtrr_del_page - delete a memory type region
@@ -1498,62 +807,51 @@
     mtrr_type ltype;
     unsigned long lbase, lsize;
 
-    if ( mtrr_if == MTRR_IF_NONE ) return -ENXIO;
-
     max = get_num_var_ranges ();
     down (&main_lock);
-    if (reg < 0)
-    {
+	if (reg < 0) {
 	/*  Search for existing MTRR  */
-	for (i = 0; i < max; ++i)
-	{
+		for (i = 0; i < max; ++i) {
 	    (*get_mtrr) (i, &lbase, &lsize, &ltype);
-	    if (lbase == base && lsize == size)
-	    {
+			if (lbase == base && lsize == size) {
 		reg = i;
 		break;
 	    }
 	}
-	if (reg < 0)
-	{
-	    up(&main_lock);
-	    printk ("mtrr: no MTRR for %lx000,%lx000 found\n", base, size);
+		if (reg < 0) {
+			up (&main_lock);
+			printk ("mtrr: no MTRR for %lx000,%lx000 found\n", base,
+				size);
 	    return -EINVAL;
 	}
     }
-    if (reg >= max)
-    {
+
+	if (reg >= max) {
 	up (&main_lock);
 	printk ("mtrr: register: %d too big\n", reg);
 	return -EINVAL;
     }
-    if ( mtrr_if == MTRR_IF_CYRIX_ARR )
-    {
-	if ( (reg == 3) && arr3_protected )
-	{
-	    up (&main_lock);
-	    printk ("mtrr: ARR3 cannot be changed\n");
-	    return -EINVAL;
-	}
-    }
     (*get_mtrr) (reg, &lbase, &lsize, &ltype);
-    if (lsize < 1)
-    {
+
+	if (lsize < 1) {
 	up (&main_lock);
 	printk ("mtrr: MTRR %d not used\n", reg);
 	return -EINVAL;
     }
-    if (usage_table[reg] < 1)
-    {
+
+	if (usage_table[reg] < 1) {
 	up (&main_lock);
 	printk ("mtrr: reg: %d has count=0\n", reg);
 	return -EINVAL;
     }
-    if (--usage_table[reg] < 1) set_mtrr (reg, 0, 0, 0);
+
+	if (--usage_table[reg] < 1)
+		set_mtrr (reg, 0, 0, 0);
     compute_ascii ();
     up (&main_lock);
     return reg;
-}   /*  End Function mtrr_del_page  */
+}
+
 
 /**
  *	mtrr_del - delete a memory type region
@@ -1580,15 +878,15 @@
     the error code.
 */
 {
-    if ( (base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1)) )
-    {
+	if ((base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1))) {
 	printk ("mtrr: size and base must be multiples of 4 kiB\n");
 	printk ("mtrr: size: 0x%lx  base: 0x%lx\n", size, base);
 	return -EINVAL;
     }
-    return mtrr_del_page(reg, base >> PAGE_SHIFT, size >> PAGE_SHIFT);
+	return mtrr_del_page (reg, base >> PAGE_SHIFT, size >> PAGE_SHIFT);
 }
 
+
 #ifdef USERSPACE_INTERFACE
 
 static int mtrr_file_add (unsigned long base, unsigned long size,
@@ -1598,30 +896,34 @@
     unsigned int *fcount = file->private_data;
 
     max = get_num_var_ranges ();
-    if (fcount == NULL)
-    {
-	if ( ( fcount = kmalloc (max * sizeof *fcount, GFP_KERNEL) ) == NULL )
-	{
+	if (fcount == NULL) {
+		if ((fcount =
+		     kmalloc (max * sizeof *fcount, GFP_KERNEL)) == NULL) {
 	    printk ("mtrr: could not allocate\n");
 	    return -ENOMEM;
 	}
 	memset (fcount, 0, max * sizeof *fcount);
 	file->private_data = fcount;
     }
+
     if (!page) {
-	if ( (base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1)) )
-	{
-	    printk ("mtrr: size and base must be multiples of 4 kiB\n");
+		if ((base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1))) {
+			printk
+			    ("mtrr: size and base must be multiples of 4 kiB\n");
 	    printk ("mtrr: size: 0x%lx  base: 0x%lx\n", size, base);
 	    return -EINVAL;
 	}
 	base >>= PAGE_SHIFT;
 	size >>= PAGE_SHIFT;
     }
+
     reg = mtrr_add_page (base, size, type, 1);
-    if (reg >= 0) ++fcount[reg];
+
+	if (reg >= 0)
+		++fcount[reg];
     return reg;
-}   /*  End Function mtrr_file_add  */
+}
+
 
 static int mtrr_file_del (unsigned long base, unsigned long size,
 			  struct file *file, int page)
@@ -1630,9 +932,9 @@
     unsigned int *fcount = file->private_data;
 
     if (!page) {
-	if ( (base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1)) )
-	{
-	    printk ("mtrr: size and base must be multiples of 4 kiB\n");
+		if ((base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1))) {
+			printk
+			    ("mtrr: size and base must be multiples of 4 kiB\n");
 	    printk ("mtrr: size: 0x%lx  base: 0x%lx\n", size, base);
 	    return -EINVAL;
 	}
@@ -1640,25 +942,36 @@
 	size >>= PAGE_SHIFT;
     }
     reg = mtrr_del_page (-1, base, size);
-    if (reg < 0) return reg;
-    if (fcount == NULL) return reg;
-    if (fcount[reg] < 1) return -EINVAL;
+	if (reg < 0)
+		return reg;
+	if (fcount == NULL)
+		return reg;
+	if (fcount[reg] < 1)
+		return -EINVAL;
     --fcount[reg];
     return reg;
-}   /*  End Function mtrr_file_del  */
+}
+
 
 static ssize_t mtrr_read (struct file *file, char *buf, size_t len,
-			  loff_t *ppos)
+		loff_t * ppos)
 {
-    if (*ppos >= ascii_buf_bytes) return 0;
-    if (*ppos + len > ascii_buf_bytes) len = ascii_buf_bytes - *ppos;
-    if ( copy_to_user (buf, ascii_buffer + *ppos, len) ) return -EFAULT;
+	if (*ppos >= ascii_buf_bytes)
+		return 0;
+
+	if (*ppos + len > ascii_buf_bytes)
+		len = ascii_buf_bytes - *ppos;
+
+	if (copy_to_user (buf, ascii_buffer + *ppos, len))
+		return -EFAULT;
+
     *ppos += len;
     return len;
-}   /*  End Function mtrr_read  */
+}
 
-static ssize_t mtrr_write (struct file *file, const char *buf, size_t len,
-			   loff_t *ppos)
+
+static ssize_t mtrr_write (struct file *file, const char *buf,
+		size_t len, loff_t * ppos)
 /*  Format of control line:
     "base=%Lx size=%Lx type=%s"     OR:
     "disable=%d"
@@ -1670,60 +983,80 @@
     char *ptr;
     char line[LINE_SIZE];
 
-    if ( !suser () ) return -EPERM;
+	if (!suser ())
+		return -EPERM;
+
     /*  Can't seek (pwrite) on this device  */
-    if (ppos != &file->f_pos) return -ESPIPE;
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
     memset (line, 0, LINE_SIZE);
-    if (len > LINE_SIZE) len = LINE_SIZE;
-    if ( copy_from_user (line, buf, len - 1) ) return -EFAULT;
+
+	if (len > LINE_SIZE)
+		len = LINE_SIZE;
+
+	if (copy_from_user (line, buf, len - 1))
+		return -EFAULT;
     ptr = line + strlen (line) - 1;
-    if (*ptr == '\n') *ptr = '\0';
-    if ( !strncmp (line, "disable=", 8) )
-    {
+
+	if (*ptr == '\n')
+		*ptr = '\0';
+
+	if (!strncmp (line, "disable=", 8)) {
 	reg = simple_strtoul (line + 8, &ptr, 0);
 	err = mtrr_del_page (reg, 0, 0);
-	if (err < 0) return err;
+		if (err < 0)
+			return err;
 	return len;
     }
-    if ( strncmp (line, "base=", 5) )
-    {
+
+	if (strncmp (line, "base=", 5)) {
 	printk ("mtrr: no \"base=\" in line: \"%s\"\n", line);
 	return -EINVAL;
     }
+
     base = simple_strtoull (line + 5, &ptr, 0);
-    for (; isspace (*ptr); ++ptr);
-    if ( strncmp (ptr, "size=", 5) )
-    {
+
+	for (; isspace (*ptr); ++ptr) ;
+
+	if (strncmp (ptr, "size=", 5)) {
 	printk ("mtrr: no \"size=\" in line: \"%s\"\n", line);
 	return -EINVAL;
     }
+
     size = simple_strtoull (ptr + 5, &ptr, 0);
-    if ( (base & 0xfff) || (size & 0xfff) )
-    {
+
+	if ((base & 0xfff) || (size & 0xfff)) {
 	printk ("mtrr: size and base must be multiples of 4 kiB\n");
 	printk ("mtrr: size: 0x%Lx  base: 0x%Lx\n", size, base);
 	return -EINVAL;
     }
-    for (; isspace (*ptr); ++ptr);
-    if ( strncmp (ptr, "type=", 5) )
-    {
+
+	for (; isspace (*ptr); ++ptr) ;
+
+	if (strncmp (ptr, "type=", 5)) {
 	printk ("mtrr: no \"type=\" in line: \"%s\"\n", line);
 	return -EINVAL;
     }
     ptr += 5;
-    for (; isspace (*ptr); ++ptr);
-    for (i = 0; i < MTRR_NUM_TYPES; ++i)
-    {
-	if ( strcmp (ptr, mtrr_strings[i]) ) continue;
+
+	for (; isspace (*ptr); ++ptr) ;
+
+	for (i = 0; i < MTRR_NUM_TYPES; ++i) {
+		if (strcmp (ptr, mtrr_strings[i]))
+			continue;
 	base >>= PAGE_SHIFT;
 	size >>= PAGE_SHIFT;
-	err = mtrr_add_page ((unsigned long)base, (unsigned long)size, i, 1);
-	if (err < 0) return err;
+		err =
+		    mtrr_add_page ((unsigned long) base, (unsigned long) size,
+				   i, 1);
+		if (err < 0)
+			return err;
 	return len;
     }
     printk ("mtrr: illegal type: \"%s\"\n", ptr);
     return -EINVAL;
-}   /*  End Function mtrr_write  */
+}
+
 
 static int mtrr_ioctl (struct inode *inode, struct file *file,
 		       unsigned int cmd, unsigned long arg)
@@ -1733,46 +1066,62 @@
     struct mtrr_sentry sentry;
     struct mtrr_gentry gentry;
 
-    switch (cmd)
-    {
+	switch (cmd) {
       default:
 	return -ENOIOCTLCMD;
+
       case MTRRIOC_ADD_ENTRY:
-	if ( !suser () ) return -EPERM;
-	if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) )
+		if (!suser ())
+			return -EPERM;
+		if (copy_from_user (&sentry, (void *) arg, sizeof sentry))
 	    return -EFAULT;
-	err = mtrr_file_add (sentry.base, sentry.size, sentry.type, 1, file, 0);
-	if (err < 0) return err;
+		err =
+		    mtrr_file_add (sentry.base, sentry.size, sentry.type, 1,
+				   file, 0);
+		if (err < 0)
+			return err;
 	break;
+
       case MTRRIOC_SET_ENTRY:
-	if ( !suser () ) return -EPERM;
-	if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) )
+		if (!suser ())
+			return -EPERM;
+		if (copy_from_user (&sentry, (void *) arg, sizeof sentry))
 	    return -EFAULT;
 	err = mtrr_add (sentry.base, sentry.size, sentry.type, 0);
-	if (err < 0) return err;
+		if (err < 0)
+			return err;
 	break;
+
       case MTRRIOC_DEL_ENTRY:
-	if ( !suser () ) return -EPERM;
-	if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) )
+		if (!suser ())
+			return -EPERM;
+		if (copy_from_user (&sentry, (void *) arg, sizeof sentry))
 	    return -EFAULT;
 	err = mtrr_file_del (sentry.base, sentry.size, file, 0);
-	if (err < 0) return err;
+		if (err < 0)
+			return err;
 	break;
+
       case MTRRIOC_KILL_ENTRY:
-	if ( !suser () ) return -EPERM;
-	if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) )
+		if (!suser ())
+			return -EPERM;
+		if (copy_from_user (&sentry, (void *) arg, sizeof sentry))
 	    return -EFAULT;
 	err = mtrr_del (-1, sentry.base, sentry.size);
-	if (err < 0) return err;
+		if (err < 0)
+			return err;
 	break;
+
       case MTRRIOC_GET_ENTRY:
-	if ( copy_from_user (&gentry, (void *) arg, sizeof gentry) )
+		if (copy_from_user (&gentry, (void *) arg, sizeof gentry))
 	    return -EFAULT;
-	if ( gentry.regnum >= get_num_var_ranges () ) return -EINVAL;
+		if (gentry.regnum >= get_num_var_ranges ())
+			return -EINVAL;
 	(*get_mtrr) (gentry.regnum, &gentry.base, &gentry.size, &type);
 
 	/* Hide entries that go above 4GB */
-	if (gentry.base + gentry.size > 0x100000 || gentry.size == 0x100000)
+		if (gentry.base + gentry.size > 0x100000
+		    || gentry.size == 0x100000)
 	    gentry.base = gentry.size = gentry.type = 0;
 	else {
 	    gentry.base <<= PAGE_SHIFT;
@@ -1780,85 +1129,103 @@
 	    gentry.type = type;
 	}
 
-	if ( copy_to_user ( (void *) arg, &gentry, sizeof gentry) )
+		if (copy_to_user ((void *) arg, &gentry, sizeof gentry))
 	     return -EFAULT;
 	break;
+
       case MTRRIOC_ADD_PAGE_ENTRY:
-	if ( !suser () ) return -EPERM;
-	if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) )
+		if (!suser ())
+			return -EPERM;
+		if (copy_from_user (&sentry, (void *) arg, sizeof sentry))
 	    return -EFAULT;
-	err = mtrr_file_add (sentry.base, sentry.size, sentry.type, 1, file, 1);
-	if (err < 0) return err;
+		err =
+		    mtrr_file_add (sentry.base, sentry.size, sentry.type, 1,
+				   file, 1);
+		if (err < 0)
+			return err;
 	break;
+
       case MTRRIOC_SET_PAGE_ENTRY:
-	if ( !suser () ) return -EPERM;
-	if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) )
+		if (!suser ())
+			return -EPERM;
+		if (copy_from_user (&sentry, (void *) arg, sizeof sentry))
 	    return -EFAULT;
 	err = mtrr_add_page (sentry.base, sentry.size, sentry.type, 0);
-	if (err < 0) return err;
+		if (err < 0)
+			return err;
 	break;
+
       case MTRRIOC_DEL_PAGE_ENTRY:
-	if ( !suser () ) return -EPERM;
-	if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) )
+		if (!suser ())
+			return -EPERM;
+		if (copy_from_user (&sentry, (void *) arg, sizeof sentry))
 	    return -EFAULT;
 	err = mtrr_file_del (sentry.base, sentry.size, file, 1);
-	if (err < 0) return err;
+		if (err < 0)
+			return err;
 	break;
+
       case MTRRIOC_KILL_PAGE_ENTRY:
-	if ( !suser () ) return -EPERM;
-	if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) )
+		if (!suser ())
+			return -EPERM;
+		if (copy_from_user (&sentry, (void *) arg, sizeof sentry))
 	    return -EFAULT;
 	err = mtrr_del_page (-1, sentry.base, sentry.size);
-	if (err < 0) return err;
+		if (err < 0)
+			return err;
 	break;
+
       case MTRRIOC_GET_PAGE_ENTRY:
-	if ( copy_from_user (&gentry, (void *) arg, sizeof gentry) )
+		if (copy_from_user (&gentry, (void *) arg, sizeof gentry))
 	    return -EFAULT;
-	if ( gentry.regnum >= get_num_var_ranges () ) return -EINVAL;
+		if (gentry.regnum >= get_num_var_ranges ())
+			return -EINVAL;
 	(*get_mtrr) (gentry.regnum, &gentry.base, &gentry.size, &type);
 	gentry.type = type;
 
-	if ( copy_to_user ( (void *) arg, &gentry, sizeof gentry) )
+		if (copy_to_user ((void *) arg, &gentry, sizeof gentry))
 	     return -EFAULT;
 	break;
     }
     return 0;
-}   /*  End Function mtrr_ioctl  */
+}
+
 
 static int mtrr_close (struct inode *ino, struct file *file)
 {
     int i, max;
     unsigned int *fcount = file->private_data;
 
-    if (fcount == NULL) return 0;
+	if (fcount == NULL)
+		return 0;
+
+	lock_kernel ();
     max = get_num_var_ranges ();
-    for (i = 0; i < max; ++i)
-    {
-	while (fcount[i] > 0)
-	{
-	    if (mtrr_del (i, 0, 0) < 0) printk ("mtrr: reg %d not used\n", i);
+	for (i = 0; i < max; ++i) {
+		while (fcount[i] > 0) {
+			if (mtrr_del (i, 0, 0) < 0)
+				printk ("mtrr: reg %d not used\n", i);
 	    --fcount[i];
 	}
     }
+	unlock_kernel ();
     kfree (fcount);
     file->private_data = NULL;
     return 0;
-}   /*  End Function mtrr_close  */
+}
 
-static struct file_operations mtrr_fops =
-{
+
+static struct file_operations mtrr_fops = {
     owner:	THIS_MODULE,
     read:	mtrr_read,
     write:	mtrr_write,
     ioctl:	mtrr_ioctl,
-    release:	mtrr_close,
+	release:mtrr_close,
 };
 
-#  ifdef CONFIG_PROC_FS
-
+#ifdef CONFIG_PROC_FS
 static struct proc_dir_entry *proc_root_mtrr;
-
-#  endif  /*  CONFIG_PROC_FS  */
+#endif
 
 static devfs_handle_t devfs_handle;
 
@@ -1871,20 +1238,16 @@
 
     ascii_buf_bytes = 0;
     max = get_num_var_ranges ();
-    for (i = 0; i < max; i++)
-    {
+	for (i = 0; i < max; i++) {
 	(*get_mtrr) (i, &base, &size, &type);
-	if (size == 0) usage_table[i] = 0;
-	else
-	{
-	    if (size < (0x100000 >> PAGE_SHIFT))
-	    {
+		if (size == 0)
+			usage_table[i] = 0;
+		else {
+			if (size < (0x100000 >> PAGE_SHIFT)) {
 		/* less than 1MB */
 		factor = 'K';
 		size <<= PAGE_SHIFT - 10;
-	    }
-	    else
-	    {
+			} else {
 		factor = 'M';
 		size >>= 20 - PAGE_SHIFT;
 	    }
@@ -1893,344 +1256,59 @@
 		 "reg%02i: base=0x%05lx000 (%4liMB), size=%4li%cB: %s, count=%d\n",
 		 i, base, base >> (20 - PAGE_SHIFT), size, factor,
 		 attrib_to_str (type), usage_table[i]);
-	    ascii_buf_bytes += strlen (ascii_buffer + ascii_buf_bytes);
+			ascii_buf_bytes +=
+			    strlen (ascii_buffer + ascii_buf_bytes);
 	}
     }
     devfs_set_file_size (devfs_handle, ascii_buf_bytes);
-#  ifdef CONFIG_PROC_FS
+#ifdef CONFIG_PROC_FS
     if (proc_root_mtrr)
 	proc_root_mtrr->size = ascii_buf_bytes;
-#  endif  /*  CONFIG_PROC_FS  */
-}   /*  End Function compute_ascii  */
-
-#endif  /*  USERSPACE_INTERFACE  */
-
-EXPORT_SYMBOL(mtrr_add);
-EXPORT_SYMBOL(mtrr_del);
-
-#ifdef CONFIG_SMP
-
-typedef struct
-{
-    unsigned long base;
-    unsigned long size;
-    mtrr_type type;
-} arr_state_t;
-
-arr_state_t arr_state[8] __initdata =
-{
-    {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL},
-    {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL}
-};
-
-unsigned char ccr_state[7] __initdata = { 0, 0, 0, 0, 0, 0, 0 };
-
-static void __init cyrix_arr_init_secondary(void)
-{
-    struct set_mtrr_context ctxt;
-    int i;
-
-    /* flush cache and enable MAPEN */
-    set_mtrr_prepare_save (&ctxt);
-    set_mtrr_cache_disable (&ctxt);
-
-     /* the CCRs are not contiguous */
-    for(i=0; i<4; i++) setCx86(CX86_CCR0 + i, ccr_state[i]);
-    for(   ; i<7; i++) setCx86(CX86_CCR4 + i, ccr_state[i]);
-    for(i=0; i<8; i++)
-      cyrix_set_arr_up(i,
-        arr_state[i].base, arr_state[i].size, arr_state[i].type, FALSE);
-
-    set_mtrr_done (&ctxt); /* flush cache and disable MAPEN */
-}   /*  End Function cyrix_arr_init_secondary  */
-
-#endif
-
-/*
- * On Cyrix 6x86(MX) and M II the ARR3 is special: it has connection
- * with the SMM (System Management Mode) mode. So we need the following:
- * Check whether SMI_LOCK (CCR3 bit 0) is set
- *   if it is set, write a warning message: ARR3 cannot be changed!
- *     (it cannot be changed until the next processor reset)
- *   if it is reset, then we can change it, set all the needed bits:
- *   - disable access to SMM memory through ARR3 range (CCR1 bit 7 reset)
- *   - disable access to SMM memory (CCR1 bit 2 reset)
- *   - disable SMM mode (CCR1 bit 1 reset)
- *   - disable write protection of ARR3 (CCR6 bit 1 reset)
- *   - (maybe) disable ARR3
- * Just to be sure, we enable ARR usage by the processor (CCR5 bit 5 set)
- */
-static void __init cyrix_arr_init(void)
-{
-    struct set_mtrr_context ctxt;
-    unsigned char ccr[7];
-    int ccrc[7] = { 0, 0, 0, 0, 0, 0, 0 };
-#ifdef CONFIG_SMP
-    int i;
-#endif
-
-    /* flush cache and enable MAPEN */
-    set_mtrr_prepare_save (&ctxt);
-    set_mtrr_cache_disable (&ctxt);
-
-    /* Save all CCRs locally */
-    ccr[0] = getCx86 (CX86_CCR0);
-    ccr[1] = getCx86 (CX86_CCR1);
-    ccr[2] = getCx86 (CX86_CCR2);
-    ccr[3] = ctxt.ccr3;
-    ccr[4] = getCx86 (CX86_CCR4);
-    ccr[5] = getCx86 (CX86_CCR5);
-    ccr[6] = getCx86 (CX86_CCR6);
-
-    if (ccr[3] & 1)
-    {
-	ccrc[3] = 1;
-	arr3_protected = 1;
-    }
-    else
-    {
-	/* Disable SMM mode (bit 1), access to SMM memory (bit 2) and
-	 * access to SMM memory through ARR3 (bit 7).
-	 */
-	if (ccr[1] & 0x80) { ccr[1] &= 0x7f; ccrc[1] |= 0x80; }
-	if (ccr[1] & 0x04) { ccr[1] &= 0xfb; ccrc[1] |= 0x04; }
-	if (ccr[1] & 0x02) { ccr[1] &= 0xfd; ccrc[1] |= 0x02; }
-	arr3_protected = 0;
-	if (ccr[6] & 0x02) {
-	    ccr[6] &= 0xfd; ccrc[6] = 1; /* Disable write protection of ARR3 */
-	    setCx86 (CX86_CCR6, ccr[6]);
-	}
-	/* Disable ARR3. This is safe now that we disabled SMM. */
-	/* cyrix_set_arr_up (3, 0, 0, 0, FALSE); */
-    }
-    /* If we changed CCR1 in memory, change it in the processor, too. */
-    if (ccrc[1]) setCx86 (CX86_CCR1, ccr[1]);
-
-    /* Enable ARR usage by the processor */
-    if (!(ccr[5] & 0x20))
-    {
-	ccr[5] |= 0x20; ccrc[5] = 1;
-	setCx86 (CX86_CCR5, ccr[5]);
-    }
-
-#ifdef CONFIG_SMP
-    for(i=0; i<7; i++) ccr_state[i] = ccr[i];
-    for(i=0; i<8; i++)
-      cyrix_get_arr(i,
-        &arr_state[i].base, &arr_state[i].size, &arr_state[i].type);
 #endif
+}
 
-    set_mtrr_done (&ctxt); /* flush cache and disable MAPEN */
-
-    if ( ccrc[5] ) printk ("mtrr: ARR usage was not enabled, enabled manually\n");
-    if ( ccrc[3] ) printk ("mtrr: ARR3 cannot be changed\n");
-/*
-    if ( ccrc[1] & 0x80) printk ("mtrr: SMM memory access through ARR3 disabled\n");
-    if ( ccrc[1] & 0x04) printk ("mtrr: SMM memory access disabled\n");
-    if ( ccrc[1] & 0x02) printk ("mtrr: SMM mode disabled\n");
-*/
-    if ( ccrc[6] ) printk ("mtrr: ARR3 was write protected, unprotected\n");
-}   /*  End Function cyrix_arr_init  */
-
-/*
- *	Initialise the later (saner) Winchip MCR variant. In this version
- *	the BIOS can pass us the registers it has used (but not their values)
- *	and the control register is read/write
- */
- 
-static void __init centaur_mcr1_init(void)
-{
-    unsigned i;
-    u32 lo, hi;
-
-    /* Unfortunately, MCR's are read-only, so there is no way to
-     * find out what the bios might have done.
-     */
-     
-    rdmsr(0x120, lo, hi);
-    if(((lo>>17)&7)==1)		/* Type 1 Winchip2 MCR */
-    {
-    	lo&= ~0x1C0;		/* clear key */
-    	lo|= 0x040;		/* set key to 1 */
-	wrmsr(0x120, lo, hi);	/* unlock MCR */
-    }    
-    
-    centaur_mcr_type = 1;
-    
-    /*
-     *	Clear any unconfigured MCR's.
-     */
-
-    for (i = 0; i < 8; ++i)
-    {
-    	if(centaur_mcr[i]. high == 0 && centaur_mcr[i].low == 0)
-    	{
-    		if(!(lo & (1<<(9+i))))
-			wrmsr (0x110 + i , 0, 0);
-		else
-			/*
-			 *	If the BIOS set up an MCR we cannot see it
-			 *	but we don't wish to obliterate it
-			 */
-			centaur_mcr_reserved |= (1<<i);
-	}
-    }
-    /*  
-     *	Throw the main write-combining switch... 
-     *	However if OOSTORE is enabled then people have already done far
-     *  cleverer things and we should behave. 
-     */
-
-    lo |= 15;			/* Write combine enables */
-    wrmsr(0x120, lo, hi);
-}   /*  End Function centaur_mcr1_init  */
-
-/*
- *	Initialise the original winchip with read only MCR registers
- *	no used bitmask for the BIOS to pass on and write only control
- */
- 
-static void __init centaur_mcr0_init(void)
-{
-    unsigned i;
-
-    /* Unfortunately, MCR's are read-only, so there is no way to
-     * find out what the bios might have done.
-     */
-     
-    /* Clear any unconfigured MCR's.
-     * This way we are sure that the centaur_mcr array contains the actual
-     * values. The disadvantage is that any BIOS tweaks are thus undone.
-     *
-     */
-    for (i = 0; i < 8; ++i)
-    {
-    	if(centaur_mcr[i]. high == 0 && centaur_mcr[i].low == 0)
-		wrmsr (0x110 + i , 0, 0);
-    }
+#endif	/*  USERSPACE_INTERFACE  */
 
-    wrmsr(0x120, 0x01F0001F, 0);	/* Write only */
-}   /*  End Function centaur_mcr0_init  */
+EXPORT_SYMBOL (mtrr_add);
+EXPORT_SYMBOL (mtrr_del);
 
-/*
- *	Initialise Winchip series MCR registers
- */
  
-static void __init centaur_mcr_init(void)
+static void __init mtrr_setup (void)
 {
-    struct set_mtrr_context ctxt;
-
-    set_mtrr_prepare_save (&ctxt);
-    set_mtrr_cache_disable (&ctxt);
+	printk ("mtrr: v%s)\n", MTRR_VERSION);
 
-    if(boot_cpu_data.x86_model==4)
-    	centaur_mcr0_init();
-    else if(boot_cpu_data.x86_model==8 || boot_cpu_data.x86_model == 9)
-    	centaur_mcr1_init();
-
-    set_mtrr_done (&ctxt);
-}   /*  End Function centaur_mcr_init  */
-
-static int __init mtrr_setup(void)
-{
-    if ( test_bit(X86_FEATURE_MTRR, &boot_cpu_data.x86_capability) ) {
-	/* Intel (P6) standard MTRRs */
-	mtrr_if = MTRR_IF_INTEL;
-	get_mtrr = intel_get_mtrr;
-	set_mtrr_up = intel_set_mtrr_up;
-	switch (boot_cpu_data.x86_vendor) {
-
-	case X86_VENDOR_AMD:
-		/* The original Athlon docs said that
-		   total addressable memory is 44 bits wide.
-		   It was not really clear whether its MTRRs
-		   follow this or not. (Read: 44 or 36 bits).
-		   However, "x86-64_overview.pdf" explicitly
-		   states that "previous implementations support
-		   36 bit MTRRs" and also provides a way to
-		   query the width (in bits) of the physical
-		   addressable memory on the Hammer family.
-		 */
-		if (boot_cpu_data.x86 == 7 && (cpuid_eax(0x80000000) >= 0x80000008)) {
+	if (test_bit (X86_FEATURE_MTRR, &boot_cpu_data.x86_capability)) {
+		 /* Query the width (in bits) of the physical
+		   addressable memory on the Hammer family. */
+		if ((cpuid_eax (0x80000000) >= 0x80000008)) {
 			u32	phys_addr;
-			phys_addr = cpuid_eax(0x80000008) & 0xff ;
-			size_or_mask = ~((1 << (phys_addr - PAGE_SHIFT)) - 1);
+			phys_addr = cpuid_eax (0x80000008) & 0xff;
+			size_or_mask =
+			    ~((1 << (phys_addr - PAGE_SHIFT)) - 1);
 			size_and_mask = ~size_or_mask & 0xfff00000;
-			break;
-		}
-		size_or_mask  = 0xff000000; /* 36 bits */
-		size_and_mask = 0x00f00000;
-		break;
-
-	case X86_VENDOR_CENTAUR:
-		/* VIA Cyrix family have Intel style MTRRs, but don't support PAE */
-		if (boot_cpu_data.x86 == 6) {
-			size_or_mask  = 0xfff00000; /* 32 bits */
-			size_and_mask = 0;
-		}
-		break;
-
-	default:
-		/* Intel, etc. */
+		} else {
+			/* FIXME: This is to make it work on Athlon during debugging. */
 		size_or_mask  = 0xff000000; /* 36 bits */
 		size_and_mask = 0x00f00000;
-		break;
 	}
 
-    } else if ( test_bit(X86_FEATURE_K6_MTRR, &boot_cpu_data.x86_capability) ) {
-	/* Pre-Athlon (K6) AMD CPU MTRRs */
-	mtrr_if = MTRR_IF_AMD_K6;
-	get_mtrr = amd_get_mtrr;
-	set_mtrr_up = amd_set_mtrr_up;
-	size_or_mask  = 0xfff00000; /* 32 bits */
-	size_and_mask = 0;
-    } else if ( test_bit(X86_FEATURE_CYRIX_ARR, &boot_cpu_data.x86_capability) ) {
-	/* Cyrix ARRs */
-	mtrr_if = MTRR_IF_CYRIX_ARR;
-	get_mtrr = cyrix_get_arr;
-	set_mtrr_up = cyrix_set_arr_up;
-	get_free_region = cyrix_get_free_region;
-	cyrix_arr_init();
-	size_or_mask  = 0xfff00000; /* 32 bits */
-	size_and_mask = 0;
-    } else if ( test_bit(X86_FEATURE_CENTAUR_MCR, &boot_cpu_data.x86_capability) ) {
-	/* Centaur MCRs */
-	mtrr_if = MTRR_IF_CENTAUR_MCR;
-	get_mtrr = centaur_get_mcr;
-	set_mtrr_up = centaur_set_mcr_up;
-	get_free_region = centaur_get_free_region;
-	centaur_mcr_init();
-	size_or_mask  = 0xfff00000; /* 32 bits */
-	size_and_mask = 0;
-    } else {
-	/* No supported MTRR interface */
-	mtrr_if = MTRR_IF_NONE;
-    }
-
-    printk ("mtrr: v%s Richard Gooch (rgooch@atnf.csiro.au)\n"
-	    "mtrr: detected mtrr type: %s\n",
-	    MTRR_VERSION, mtrr_if_name[mtrr_if]);
-
-    return (mtrr_if != MTRR_IF_NONE);
-}   /*  End Function mtrr_setup  */
+		printk ("mtrr: detected mtrr type: x86-64\n");
+    }
+}
 
 #ifdef CONFIG_SMP
 
 static volatile unsigned long smp_changes_mask __initdata = 0;
-static struct mtrr_state smp_mtrr_state __initdata = {0, 0};
+static struct mtrr_state smp_mtrr_state __initdata = { 0, 0 };
 
-void __init mtrr_init_boot_cpu(void)
+void __init mtrr_init_boot_cpu (void)
 {
-    if ( !mtrr_setup () )
-	return;
-
-    if ( mtrr_if == MTRR_IF_INTEL ) {
-	/* Only for Intel MTRRs */
+	mtrr_setup();
 	get_mtrr_state (&smp_mtrr_state);
-    }
-}   /*  End Function mtrr_init_boot_cpu  */
+}
 
-static void __init intel_mtrr_init_secondary_cpu(void)
+
+void __init mtrr_init_secondary_cpu (void)
 {
     unsigned long mask, count;
     struct set_mtrr_context ctxt;
@@ -2238,53 +1316,30 @@
     /*  Note that this is not ideal, since the cache is only flushed/disabled
 	for this CPU while the MTRRs are changed, but changing this requires
 	more invasive changes to the way the kernel boots  */
-    set_mtrr_prepare_save (&ctxt);
-    set_mtrr_cache_disable (&ctxt);
+	set_mtrr_prepare (&ctxt);
     mask = set_mtrr_state (&smp_mtrr_state, &ctxt);
     set_mtrr_done (&ctxt);
+
     /*  Use the atomic bitops to update the global mask  */
-    for (count = 0; count < sizeof mask * 8; ++count)
-    {
-	if (mask & 0x01) set_bit (count, &smp_changes_mask);
+	for (count = 0; count < sizeof mask * 8; ++count) {
+		if (mask & 0x01)
+			set_bit (count, &smp_changes_mask);
 	mask >>= 1;
     }
-}   /*  End Function intel_mtrr_init_secondary_cpu  */
+}
 
-void __init mtrr_init_secondary_cpu(void)
-{
-    switch ( mtrr_if ) {
-    case MTRR_IF_INTEL:
-	/* Intel (P6) standard MTRRs */
-	intel_mtrr_init_secondary_cpu();
-	break;
-    case MTRR_IF_CYRIX_ARR:
-	/* This is _completely theoretical_!
-	 * I assume here that one day Cyrix will support Intel APIC.
-	 * In reality on non-Intel CPUs we won't even get to this routine.
-	 * Hopefully no one will plug two Cyrix processors in a dual P5 board.
-	 *  :-)
-	 */
-	cyrix_arr_init_secondary ();
-	break;
-    default:
-	/* I see no MTRRs I can support in SMP mode... */
-	printk ("mtrr: SMP support incomplete for this vendor\n");
-    }
-}   /*  End Function mtrr_init_secondary_cpu  */
 #endif  /*  CONFIG_SMP  */
 
-int __init mtrr_init(void)
+
+int __init mtrr_init (void)
 {
 #ifdef CONFIG_SMP
     /* mtrr_setup() should already have been called from mtrr_init_boot_cpu() */
 
-    if ( mtrr_if == MTRR_IF_INTEL ) {
 	finalize_mtrr_state (&smp_mtrr_state);
 	mtrr_state_warn (smp_changes_mask);
-    }
 #else
-    if ( !mtrr_setup() )
-	return 0;		/* MTRRs not supported? */
+	mtrr_setup();
 #endif
 
 #ifdef CONFIG_PROC_FS
@@ -2294,17 +1349,12 @@
 	proc_root_mtrr->proc_fops = &mtrr_fops;
     }
 #endif
+#ifdef CONFIG_DEVFS_FS
     devfs_handle = devfs_register (NULL, "cpu/mtrr", DEVFS_FL_DEFAULT, 0, 0,
 				   S_IFREG | S_IRUGO | S_IWUSR,
 				   &mtrr_fops, NULL);
+#endif
     init_table ();
     return 0;
-}   /*  End Function mtrr_init  */
+}
 
-/*
- * Local Variables:
- * mode:c
- * c-file-style:"k&r"
- * c-basic-offset:4
- * End:
- */
diff -Nru a/arch/x86_64/kernel/pci-dma.c b/arch/x86_64/kernel/pci-dma.c
--- a/arch/x86_64/kernel/pci-dma.c	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/kernel/pci-dma.c	Mon Apr 22 15:32:19 2002
@@ -14,8 +14,6 @@
 	void *ret;
 	int gfp = GFP_ATOMIC;
 
-	/* We need to always allocate below 4Gig. We probably need new
-	   GPF mask to say that */ 
 	gfp |= GFP_DMA;
 	ret = (void *)__get_free_pages(gfp, get_order(size));
 
diff -Nru a/arch/x86_64/kernel/pci-irq.c b/arch/x86_64/kernel/pci-irq.c
--- a/arch/x86_64/kernel/pci-irq.c	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/kernel/pci-irq.c	Mon Apr 22 15:32:19 2002
@@ -12,7 +12,6 @@
 #include <linux/slab.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
-
 #include <asm/io.h>
 #include <asm/smp.h>
 #include <asm/io_apic.h>
@@ -22,6 +21,8 @@
 #define PIRQ_SIGNATURE	(('$' << 0) + ('P' << 8) + ('I' << 16) + ('R' << 24))
 #define PIRQ_VERSION 0x0100
 
+int pci_use_acpi_routing = 0;
+
 static struct irq_routing_table *pirq_table;
 
 /*
@@ -461,6 +462,8 @@
 	{ "VLSI 82C534", PCI_VENDOR_ID_VLSI, PCI_DEVICE_ID_VLSI_82C534, pirq_vlsi_get, pirq_vlsi_set },
 	{ "ServerWorks", PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4,
 	  pirq_serverworks_get, pirq_serverworks_set },
+	{ "ServerWorks", PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5,
+	  pirq_serverworks_get, pirq_serverworks_set },
 	{ "AMD756 VIPER", PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_740B,
 		pirq_amd756_get, pirq_amd756_set },
 
@@ -529,6 +532,54 @@
 {
 }
 
+#ifdef CONFIG_ACPI_PCI
+
+static int acpi_lookup_irq (
+	struct pci_dev		*dev,
+	u8			pin,
+	int			assign)
+{
+	int			result = 0;
+	int			irq = 0;
+
+	/* TBD: Select IRQ from possible to improve routing performance. */
+
+	result = acpi_prt_get_irq(dev, pin, &irq);
+	if (!irq)
+		result = -ENODEV;
+	if (0 != result) {
+		printk(KERN_WARNING "PCI: No IRQ known for interrupt pin %c of device %s\n",
+		       'A'+pin, dev->slot_name);
+		return result;
+	}
+
+	dev->irq = irq;
+
+	if (!assign) {
+		/* only check for the IRQ */
+		printk(KERN_INFO "PCI: Found IRQ %d for device %s\n", irq, 
+		       dev->slot_name);
+		return 1;
+	}
+
+	/* also assign an IRQ */
+	if (irq && (dev->class >> 8) != PCI_CLASS_DISPLAY_VGA) {
+		result = acpi_prt_set_irq(dev, pin, irq);
+		if (0 != result) {
+			printk(KERN_WARNING "PCI: Could not assign IRQ %d to device %s\n", irq, dev->slot_name);
+			return result;
+		}
+
+		eisa_set_level_irq(irq);
+
+		printk(KERN_INFO "PCI: Assigned IRQ %d for device %s\n", irq, dev->slot_name);
+	}
+
+	return 1;
+}
+
+#endif /* CONFIG_ACPI_PCI */
+
 static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
 {
 	u8 pin;
@@ -540,10 +591,7 @@
 	struct pci_dev *dev2;
 	char *msg = NULL;
 
-	if (!pirq_table)
-		return 0;
-
-	/* Find IRQ routing entry */
+	/* Find IRQ pin */
 	pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
 	if (!pin) {
 		DBG(" -> no interrupt pin\n");
@@ -551,6 +599,17 @@
 	}
 	pin = pin - 1;
 	
+#ifdef CONFIG_ACPI_PCI
+	/* Use ACPI to lookup IRQ */
+	if (pci_use_acpi_routing)
+		return acpi_lookup_irq(dev, pin, assign);
+#endif
+
+	/* Find IRQ routing entry */
+
+	if (!pirq_table)
+		return 0;
+	
 	DBG("IRQ for %s:%d", dev->slot_name, pin);
 	info = pirq_get_info(dev);
 	if (!info) {
@@ -571,6 +630,10 @@
 	 * reported by the device if possible.
 	 */
 	newirq = dev->irq;
+	if (!((1 << newirq) & mask)) {
+		if ( pci_probe & PCI_USE_PIRQ_MASK) newirq = 0;
+		else printk(KERN_WARNING "PCI: IRQ %i for device %s doesn't match PIRQ mask - try pci=usepirqmask\n", newirq, dev->slot_name);
+	}
 	if (!newirq && assign) {
 		for (i = 0; i < 16; i++) {
 			if (!(mask & (1 << i)))
@@ -589,7 +652,8 @@
 		irq = pirq & 0xf;
 		DBG(" -> hardcoded IRQ %d\n", irq);
 		msg = "Hardcoded";
-	} else if (r->get && (irq = r->get(pirq_router_dev, dev, pirq))) {
+	} else if ( r->get && (irq = r->get(pirq_router_dev, dev, pirq)) && \
+	((!(pci_probe & PCI_USE_PIRQ_MASK)) || ((1 << irq) & mask)) ) {
 		DBG(" -> got IRQ %d\n", irq);
 		msg = "Found";
 	} else if (newirq && r->set && (dev->class >> 8) != PCI_CLASS_DISPLAY_VGA) {
@@ -623,7 +687,9 @@
 			continue;
 		if (info->irq[pin].link == pirq) {
 			/* We refuse to override the dev->irq information. Give a warning! */
-		    	if (dev2->irq && dev2->irq != irq) {
+		    	if ( dev2->irq && dev2->irq != irq && \
+			(!(pci_probe & PCI_USE_PIRQ_MASK) || \
+			((1 << dev2->irq) & mask)) ) {
 		    		printk(KERN_INFO "IRQ routing conflict for %s, have irq %d, want irq %d\n",
 				       dev2->slot_name, dev2->irq, irq);
 		    		continue;
@@ -640,7 +706,21 @@
 void __init pcibios_irq_init(void)
 {
 	DBG("PCI: IRQ init\n");
+
+#ifdef CONFIG_ACPI_PCI
+	if (!(pci_probe & PCI_NO_ACPI_ROUTING)) {
+		if (acpi_prts.count) {
+			printk(KERN_INFO "PCI: Using ACPI for IRQ routing\n");
+			pci_use_acpi_routing = 1;
+			return;
+		}
+		else
+			printk(KERN_WARNING "PCI: Invalid ACPI-PCI IRQ routing table\n");
+	}
+#endif
+
 	pirq_table = pirq_find_routing_table();
+
 #ifdef CONFIG_PCI_BIOS
 	if (!pirq_table && (pci_probe & PCI_BIOS_IRQ_SCAN))
 		pirq_table = pcibios_get_irq_routing_table();
diff -Nru a/arch/x86_64/kernel/pci-pc.c b/arch/x86_64/kernel/pci-pc.c
--- a/arch/x86_64/kernel/pci-pc.c	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/kernel/pci-pc.c	Mon Apr 22 15:32:19 2002
@@ -406,6 +406,12 @@
 		return NULL;
 	}
 #endif
+#ifdef CONFIG_ACPI_PCI
+	else if (!strcmp(str, "noacpi")) {
+		pci_probe |= PCI_NO_ACPI_ROUTING;
+		return NULL;
+	}
+#endif
 	else if (!strcmp(str, "rom")) {
 		pci_probe |= PCI_ASSIGN_ROMS;
 		return NULL;
@@ -417,6 +423,9 @@
 		return NULL;
 	} else if (!strncmp(str, "lastbus=", 8)) {
 		pcibios_last_bus = simple_strtol(str+8, NULL, 0);
+		return NULL;
+	} else if (!strcmp(str, "usepirqmask")) { 
+		pci_probe |= PCI_USE_PIRQ_MASK;
 		return NULL;
 	}
 	return str;
diff -Nru a/arch/x86_64/kernel/pci-x86_64.h b/arch/x86_64/kernel/pci-x86_64.h
--- a/arch/x86_64/kernel/pci-x86_64.h	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/kernel/pci-x86_64.h	Mon Apr 22 15:32:19 2002
@@ -18,9 +18,11 @@
 #define PCI_NO_SORT		0x0100
 #define PCI_BIOS_SORT		0x0200
 #define PCI_NO_CHECKS		0x0400
+#define PCI_USE_PIRQ_MASK	0x0800
 #define PCI_ASSIGN_ROMS		0x1000
 #define PCI_BIOS_IRQ_SCAN	0x2000
 #define PCI_ASSIGN_ALL_BUSSES	0x4000
+#define PCI_NO_ACPI_ROUTING	0x8000
 
 extern unsigned int pci_probe;
 
@@ -66,6 +68,8 @@
 } __attribute__((packed));
 
 extern unsigned int pcibios_irq_mask;
+
+extern int pci_use_acpi_routing;
 
 void pcibios_irq_init(void);
 void pcibios_fixup_irqs(void);
diff -Nru a/arch/x86_64/kernel/process.c b/arch/x86_64/kernel/process.c
--- a/arch/x86_64/kernel/process.c	Mon Apr 22 15:32:18 2002
+++ b/arch/x86_64/kernel/process.c	Mon Apr 22 15:32:18 2002
@@ -19,9 +19,9 @@
 #define __KERNEL_SYSCALLS__
 #include <stdarg.h>
 
+#include <linux/compiler.h>
 #include <linux/errno.h>
 #include <linux/sched.h>
-#include <linux/fs.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
 #include <linux/smp.h>
@@ -38,6 +38,7 @@
 #include <linux/delay.h>
 #include <linux/reboot.h>
 #include <linux/init.h>
+#include <linux/ctype.h>
 
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
@@ -47,11 +48,12 @@
 #include <asm/processor.h>
 #include <asm/i387.h>
 #include <asm/desc.h>
+#include <asm/mmu_context.h>
 #include <asm/pda.h>
 #include <asm/prctl.h>
+#include <asm/kdebug.h>
 
 #include <linux/irq.h>
-#include <linux/err.h>
 
 asmlinkage extern void ret_from_fork(void);
 
@@ -81,7 +83,7 @@
  * We use this if we don't have any better
  * idle routine..
  */
-static void default_idle(void)
+void default_idle(void)
 {
 	if (!hlt_counter) {
 		__cli();
@@ -157,13 +159,10 @@
 
 static long no_idt[3];
 static int reboot_mode;
-int reboot_thru_bios;
 
 #ifdef CONFIG_SMP
 int reboot_smp = 0;
 static int reboot_cpu = -1;
-/* shamelessly grabbed from lib/vsprintf.c for readability */
-#define is_digit(c)	((c) >= '0' && (c) <= '9')
 #endif
 static int __init reboot_setup(char *str)
 {
@@ -175,20 +174,13 @@
 		case 'c': /* "cold" reboot (with memory testing etc) */
 			reboot_mode = 0x0;
 			break;
-		case 'b': /* "bios" reboot by jumping through the BIOS */
-			reboot_thru_bios = 1;
-			break;
-		case 'h': /* "hard" reboot by toggling RESET and/or crashing the CPU */
-			reboot_thru_bios = 0;
-			break;
 #ifdef CONFIG_SMP
 		case 's': /* "smp" reboot by executing reset on BSP or other CPU*/
 			reboot_smp = 1;
-			if (is_digit(*(str+1))) {
-				reboot_cpu = (int) (*(str+1) - '0');
-				if (is_digit(*(str+2))) 
-					reboot_cpu = reboot_cpu*10 + (int)(*(str+2) - '0');
-			}
+			if (isdigit(str[1]))
+				sscanf(str+1, "%d", &reboot_cpu);		
+			else if (!strncmp(str,"smp",3))
+				sscanf(str+3, "%d", &reboot_cpu); 
 				/* we will leave sorting out the final value 
 				when we are ready to reboot, since we might not
  				have set up boot_cpu_id or smp_num_cpu */
@@ -214,23 +206,6 @@
 			break;
 }
 
-/*
- * Switch to real mode and then execute the code
- * specified by the code and length parameters.
- * We assume that length will aways be less that 100!
- */
-void machine_real_restart(unsigned char *code, int length)
-{
-	cli();
-
-	/* This will have to be rewritten for sledgehammer. It would
-	   help if sledgehammer have simple option to reset itself.
-	*/
-
-	panic( "real_restart is hard to do.\n" );
-	while(1);
-}
-
 void machine_restart(char * __unused)
 {
 #if CONFIG_SMP
@@ -266,16 +241,16 @@
 	 * Stop all CPUs and turn off local APICs and the IO-APIC, so
 	 * other OSs see a clean IRQ state.
 	 */
+	if (notify_die(DIE_STOP,"cpustop",0,0) != NOTIFY_BAD)
 	smp_send_stop();
 	disable_IO_APIC();
 #endif
 
-
-	if(!reboot_thru_bios) {
 		/* rebooting needs to touch the page at absolute addr 0 */
 		*((unsigned short *)__va(0x472)) = reboot_mode;
 		for (;;) {
 			int i;
+		/* First fondle with the keyboard controller. */ 
 			for (i=0; i<100; i++) {
 				kb_wait();
 				udelay(50);
@@ -286,10 +261,8 @@
 			__asm__ __volatile__("lidt %0": :"m" (no_idt));
 			__asm__ __volatile__("int3");
 		}
-	}
 
-	printk("no bios restart currently\n"); 
-	for (;;); 
+	/* Could do reset through the northbridge of Hammer here. */
 }
 
 void machine_halt(void)
@@ -305,7 +278,7 @@
 /* Prints also some state that isn't saved in the pt_regs */ 
 void show_regs(struct pt_regs * regs)
 {
-	unsigned long cr0 = 0L, cr2 = 0L, cr3 = 0L, cr4 = 0L, fs, gs;
+	unsigned long cr0 = 0L, cr2 = 0L, cr3 = 0L, cr4 = 0L, fs, gs, shadowgs;
 	unsigned int fsindex,gsindex;
 	unsigned int ds,cs,es; 
 
@@ -317,7 +290,7 @@
 	       regs->rax, regs->rbx, regs->rcx);
 	printk("RDX: %016lx RSI: %016lx RDI: %016lx\n",
 	       regs->rdx, regs->rsi, regs->rdi); 
-	printk("RBP: %016lx R08: %016lx R09: %08lx\n",
+	printk("RBP: %016lx R08: %016lx R09: %016lx\n",
 	       regs->rbp, regs->r8, regs->r9); 
 	printk("R10: %016lx R11: %016lx R12: %016lx\n",
 	       regs->r10, regs->r11, regs->r12); 
@@ -325,21 +298,23 @@
 	       regs->r13, regs->r14, regs->r15); 
 
 	asm("movl %%ds,%0" : "=r" (ds)); 
-	asm("movl %%es,%0" : "=r" (es)); 
 	asm("movl %%cs,%0" : "=r" (cs)); 
+	asm("movl %%es,%0" : "=r" (es)); 
 	asm("movl %%fs,%0" : "=r" (fsindex));
 	asm("movl %%gs,%0" : "=r" (gsindex));
 
-	rdmsrl(0xc0000100, fs);
-	rdmsrl(0xc0000101, gs); 
+	rdmsrl(MSR_FS_BASE, fs);
+	rdmsrl(MSR_GS_BASE, gs); 
+	rdmsrl(MSR_KERNEL_GS_BASE, shadowgs); 
 
 	asm("movq %%cr0, %0": "=r" (cr0));
 	asm("movq %%cr2, %0": "=r" (cr2));
 	asm("movq %%cr3, %0": "=r" (cr3));
 	asm("movq %%cr4, %0": "=r" (cr4));
 
-	printk("FS: %016lx(%04x) GS:%016lx(%04x)\n", fs,fsindex,gs,gsindex); 
-	printk("CS: %04x DS:%04x ES:%04x CR0: %016lx\n", cs, ds, es, cr0); 
+	printk("FS:  %016lx(%04x) GS:%016lx(%04x) knlGS:%016lx\n", 
+	       fs,fsindex,gs,gsindex,shadowgs); 
+	printk("CS:  %04x DS: %04x ES: %04x CR0: %016lx\n", cs, ds, es, cr0); 
 	printk("CR2: %016lx CR3: %016lx CR4: %016lx\n", cr2, cr3, cr4);
 }
 
@@ -360,6 +335,24 @@
 	}
 }
 
+void load_gs_index(unsigned gs)
+{
+	int access; 
+	/* should load gs in syscall exit after swapgs instead */ 
+	/* XXX need to add LDT locking for SMP to protect against parallel changes */ 
+	asm volatile("pushf\n\t" 
+		     "cli\n\t"
+		     "swapgs\n\t"
+		     "lar %1,%0\n\t"
+		     "jnz 1f\n\t"
+		     "movl %1,%%eax\n\t"
+		     "movl %%eax,%%gs\n\t"
+		     "jmp 2f\n\t"
+		     "1: movl %2,%%gs\n\t"
+		     "2: swapgs\n\t"
+		     "popf" : "=g" (access) : "g" (gs), "r" (0) : "rax"); 
+}
+	
 #define __STR(x) #x
 #define __STR2(x) __STR(x)
 
@@ -461,54 +454,6 @@
 }
 
 /*
- * fill in the user structure for a core dump..
- */
-void dump_thread(struct pt_regs * regs, struct user * dump)
-{
-	int i;
-
-/* changed the size calculations - should hopefully work better. lbt */
-	dump->magic = CMAGIC;
-	dump->start_code = 0;
-	dump->start_stack = regs->rsp & ~(PAGE_SIZE - 1);
-	dump->u_tsize = ((unsigned long) current->mm->end_code) >> PAGE_SHIFT;
-	dump->u_dsize = ((unsigned long) (current->mm->brk + (PAGE_SIZE-1))) >> PAGE_SHIFT;
-	dump->u_dsize -= dump->u_tsize;
-	dump->u_ssize = 0;
-	for (i = 0; i < 8; i++)
-		dump->u_debugreg[i] = current->thread.debugreg[i];  
-
-	if (dump->start_stack < TASK_SIZE)
-		dump->u_ssize = ((unsigned long) (TASK_SIZE - dump->start_stack)) >> PAGE_SHIFT;
-
-#define SAVE(reg) dump->regs.reg = regs->reg
-	SAVE(rax);
-	SAVE(rbx);
-	SAVE(rcx);
-	SAVE(rdx);
-	SAVE(rsi);
-	SAVE(rdi);
-	SAVE(rbp);
-	SAVE(r8);
-	SAVE(r9);
-	SAVE(r10);
-	SAVE(r11);
-	SAVE(r12);
-	SAVE(r13);
-	SAVE(r14);
-	SAVE(r15);
-	SAVE(orig_rax); 
-	SAVE(rip); 
-#undef SAVE
-
-	/* FIXME: Should use symbolic names for msr-s! */
-	rdmsrl(0xc0000100, dump->regs.fs_base);
-	rdmsrl(0xc0000101, dump->regs.kernel_gs_base); 
-
-	dump->u_fpvalid = dump_fpu (regs, &dump->i387);
-}
-
-/*
  * This special macro can be used to load a debugging register
  */
 #define loaddebug(thread,register) \
@@ -531,6 +476,7 @@
 				 *next = &next_p->thread;
 	struct tss_struct *tss = init_tss + smp_processor_id();
 
+
 	unlazy_fpu(prev_p);
 
 	/*
@@ -540,50 +486,40 @@
 
 	/* 
 	 * Switch DS and ES.
-	 * XXX: check if this is safe on SMP
 	 */
 	asm volatile("movl %%es,%0" : "=m" (prev->es)); 
-	if (unlikely(next->es != prev->es))
+	if (unlikely(next->es | prev->es))
 		loadsegment(es, next->es); 
 	
 	asm volatile ("movl %%ds,%0" : "=m" (prev->ds)); 
-	if (unlikely(next->ds != prev->ds))
+	if (unlikely(next->ds | prev->ds))
 		loadsegment(ds, next->ds);
 
 	/* 
 	 * Switch FS and GS.
+	 * XXX Check if this is safe on SMP (!= -> |)
 	 */
 	{ 
 		unsigned int fsindex;
 
 		asm volatile("movl %%fs,%0" : "=g" (fsindex)); 
+		if (unlikely(fsindex != next->fsindex)) /* or likely? */
+			loadsegment(fs, next->fsindex);
 		if (unlikely(fsindex != prev->fsindex))
 			prev->fs = 0;				
-		if (unlikely((fsindex | next->fsindex) || prev->fs))
-			loadsegment(fs, next->fsindex);
-		/* Should use a shortcut via a GDT entry if next->fs is 32bit */
-		if (fsindex != prev->fsindex || next->fs != prev->fs)
+		if ((fsindex != prev->fsindex) || (prev->fs != next->fs))
 			wrmsrl(MSR_FS_BASE, next->fs); 
 		prev->fsindex = fsindex;
 	}
-
 	{
 		unsigned int gsindex;
 
 		asm volatile("movl %%gs,%0" : "=g" (gsindex)); 
+		if (unlikely(gsindex != next->gsindex))
+			load_gs_index(next->gs); 
 		if (unlikely(gsindex != prev->gsindex)) 
 			prev->gs = 0;				
-		if (unlikely((gsindex | next->gsindex) || prev->gs)) { 
-			unsigned long flags; 
-			/* could load gs in syscall exit after swapgs instead */ 
-			int nr = smp_processor_id(); 
-			__save_flags(flags); 
-			__cli(); 
-			loadsegment(gs, next->gsindex); 
-			wrmsrl(MSR_GS_BASE, cpu_pda+nr); 
-			__restore_flags(flags); 
-		}
-		if (gsindex != prev->gsindex || (prev->gs | next->gs))
+		if (gsindex != prev->gsindex || prev->gs != next->gs)
 			wrmsrl(MSR_KERNEL_GS_BASE, next->gs); 
 		prev->gsindex = gsindex;
 	}
@@ -594,8 +530,7 @@
 	prev->userrsp = read_pda(oldrsp); 
 	write_pda(oldrsp, next->userrsp); 
 	write_pda(pcurrent, next_p); 
-	write_pda(kernelstack, 
-		  (unsigned long)next_p->thread_info + THREAD_SIZE - PDA_STACKOFFSET);
+	write_pda(kernelstack, (unsigned long)next_p->thread_info + THREAD_SIZE - PDA_STACKOFFSET);
 
 	/*
 	 * Now maybe reload the debug registers
@@ -614,7 +549,7 @@
 	/* 
 	 * Handle the IO bitmap 
 	 */ 
-	if (unlikely(prev->ioperm | next->ioperm)) {
+	if (unlikely(prev->ioperm || next->ioperm)) {
 		if (next->ioperm) {
 			/*
 			 * 4 cachelines copy ... not good, but not that
@@ -706,15 +641,25 @@
 #define first_sched	((unsigned long) scheduling_functions_start_here)
 #define last_sched	((unsigned long) scheduling_functions_end_here)
 
-/* 
- * Do a traceback through the scheduler to find where a process sleeps.
- * 
- * Currently sched.c is compiled with -fno-omit-frame-pointer, so we
- * just go through the stack frames. 
- */
 unsigned long get_wchan(struct task_struct *p)
 {
-	return -1;
+	u64 fp,rip;
+	int count = 0;
+
+	if (!p || p == current || p->state==TASK_RUNNING)
+		return 0; 
+	if (p->thread.rsp < (u64)p || p->thread.rsp > (u64)p + THREAD_SIZE)
+		return 0;
+	fp = *(u64 *)(p->thread.rsp);
+	do { 
+		if (fp < (unsigned long)p || fp > (unsigned long)p+THREAD_SIZE)
+			return 0; 
+		rip = *(u64 *)(fp+8); 
+		if (rip < first_sched || rip >= last_sched)
+			return rip; 
+		fp = *(u64 *)fp; 
+	} while (count++ < 16); 
+	return 0;
 }
 #undef last_sched
 #undef first_sched
@@ -723,13 +668,20 @@
 { 
 	int ret = 0; 
 	unsigned long tmp; 
+
 	switch (code) { 
 	case ARCH_SET_GS:
+		if (addr >= TASK_SIZE) 
+			return -EPERM; 
 		asm volatile("movw %%gs,%0" : "=g" (current->thread.gsindex)); 
 		current->thread.gs = addr;
 		ret = checking_wrmsrl(MSR_KERNEL_GS_BASE, addr); 
 		break;
 	case ARCH_SET_FS:
+		/* Not strictly needed for fs, but do it for symmetry
+		   with gs */
+		if (addr >= TASK_SIZE)
+			return -EPERM; 
 		asm volatile("movw %%fs,%0" : "=g" (current->thread.fsindex)); 
 		current->thread.fs = addr;
 		ret = checking_wrmsrl(MSR_FS_BASE, addr); 
diff -Nru a/arch/x86_64/kernel/ptrace.c b/arch/x86_64/kernel/ptrace.c
--- a/arch/x86_64/kernel/ptrace.c	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/kernel/ptrace.c	Mon Apr 22 15:32:19 2002
@@ -3,6 +3,8 @@
 /*
  * Pentium III FXSR, SSE support
  *	Gareth Hughes <gareth@valinux.com>, May 2000
+ * 
+ * x86-64 port 2000-2002 Andi Kleen
  */
 
 #include <linux/kernel.h>
@@ -88,42 +90,67 @@
 	unsigned long regno, unsigned long value)
 {
 	unsigned long tmp; 
-	switch (regno >> 2) {
-		// XXX: add 64bit setting. 
-		case FS:
+	switch (regno) {
+		case offsetof(struct user_regs_struct,fs):
 			if (value && (value & 3) != 3)
 				return -EIO;
-			child->thread.fs = value;
+			child->thread.fsindex = value & 0xffff; 
+			return 0;
+		case offsetof(struct user_regs_struct,gs):
+			if (value && (value & 3) != 3)
+				return -EIO;
+			child->thread.gsindex = value & 0xffff;
 			return 0;
-		case GS:
+		case offsetof(struct user_regs_struct,ds):
 			if (value && (value & 3) != 3)
 				return -EIO;
+			child->thread.ds = value & 0xffff;
+			return 0;
+		case offsetof(struct user_regs_struct,es): 
+			if (value && (value & 3) != 3)
+				return -EIO;
+			child->thread.es = value & 0xffff;
+			return 0;
+		case offsetof(struct user_regs_struct,fs_base):
+			if (!((value >> 48) == 0 || (value >> 48) == 0xffff))
+				return -EIO; 
+			child->thread.fs = value;
+			return 0;
+		case offsetof(struct user_regs_struct,gs_base):
+			if (!((value >> 48) == 0 || (value >> 48) == 0xffff))
+				return -EIO; 
 			child->thread.gs = value;
 			return 0;
-		case EFLAGS:
+		case offsetof(struct user_regs_struct, eflags):
 			value &= FLAG_MASK;
 			tmp = get_stack_long(child, EFL_OFFSET); 
 			tmp &= ~FLAG_MASK; 
 			value |= tmp;
 			break;
+		case offsetof(struct user_regs_struct,cs): 
+			if (value && (value & 3) != 3)
+				return -EIO;
+			value &= 0xffff;
+			break;
 	}
-	/* assumption about sizes... */
-	if (regno > GS*4)
-		regno -= 2*4;
-	/* This has to be changes to put_stack_64() */
-	/* Hmm, with 32 bit applications being around... this will be
-	   rather funny */
 	put_stack_long(child, regno - sizeof(struct pt_regs), value);
 	return 0;
 }
 
-static unsigned long getreg(struct task_struct *child,
-	unsigned long regno)
+static unsigned long getreg(struct task_struct *child, unsigned long regno)
 {
-	switch (regno >> 3) {
-		case FS:
+	switch (regno) {
+		case offsetof(struct user_regs_struct, fs):
+			return child->thread.fsindex;
+		case offsetof(struct user_regs_struct, gs):
+			return child->thread.gsindex;
+		case offsetof(struct user_regs_struct, ds):
+			return child->thread.ds;
+		case offsetof(struct user_regs_struct, es):
+			return child->thread.es; 
+		case offsetof(struct user_regs_struct, fs_base):
 			return child->thread.fs;
-		case GS:
+		case offsetof(struct user_regs_struct, gs_base):
 			return child->thread.gs;
 		default:
 			regno = regno - sizeof(struct pt_regs);
@@ -167,15 +194,10 @@
 		ret = ptrace_attach(child);
 		goto out_tsk;
 	}
-	ret = -ESRCH;
-	if (!(child->ptrace & PT_PTRACED))
-		goto out_tsk;
-	if (child->state != TASK_STOPPED) {
-		if (request != PTRACE_KILL)
-			goto out_tsk;
-	}
-	if (child->p_pptr != current)
+	ret = ptrace_check_attach(child, request == PTRACE_KILL); 
+	if (ret < 0) 
 		goto out_tsk;
+
 	switch (request) {
 	/* when I and D space are separate, these will need to be fixed. */
 	case PTRACE_PEEKTEXT: /* read word at location addr. */ 
@@ -201,7 +223,7 @@
 			break;
 
 		tmp = 0;  /* Default return condition */
-		if(addr < 20*sizeof(long))
+		if(addr < sizeof(struct user_regs_struct))
 			tmp = getreg(child, addr);
 		if(addr >= (long) &dummy->u_debugreg[0] &&
 		   addr <= (long) &dummy->u_debugreg[7]){
@@ -228,7 +250,7 @@
 		    addr > sizeof(struct user) - 3)
 			break;
 
-		if (addr < 20*sizeof(long)) {
+		if (addr < sizeof(struct user_regs_struct)) {
 			ret = putreg(child, addr, data);
 			break;
 		}
@@ -267,12 +289,10 @@
 		ret = -EIO;
 		if ((unsigned long) data > _NSIG)
 			break;
-		if (request == PTRACE_SYSCALL) {
-			set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
-		}
-		else {
-			clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
-		}
+		if (request == PTRACE_SYSCALL)
+			set_tsk_thread_flag(child,TIF_SYSCALL_TRACE);
+		else
+			clear_tsk_thread_flag(child,TIF_SYSCALL_TRACE);
 		child->exit_code = data;
 	/* make sure the single step bit is not set. */
 		tmp = get_stack_long(child, EFL_OFFSET);
@@ -308,7 +328,7 @@
 		ret = -EIO;
 		if ((unsigned long) data > _NSIG)
 			break;
-		clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+		clear_tsk_thread_flag(child,TIF_SYSCALL_TRACE);
 		if ((child->ptrace & PT_DTRACE) == 0) {
 			/* Spurious delayed TF traps may occur */
 			child->ptrace |= PT_DTRACE;
@@ -332,7 +352,7 @@
 			ret = -EIO;
 			break;
 		}
-		for ( i = 0; i < FRAME_SIZE; i += sizeof(long) ) {
+		for ( i = 0; i < sizeof(struct user_regs_struct); i += sizeof(long) ) {
 			__put_user(getreg(child, i),(unsigned long *) data);
 			data += sizeof(long);
 		}
@@ -346,7 +366,7 @@
 			ret = -EIO;
 			break;
 		}
-		for ( i = 0; i < FRAME_SIZE; i += sizeof(long) ) {
+		for ( i = 0; i < sizeof(struct user_regs_struct); i += sizeof(long) ) {
 			__get_user(tmp, (unsigned long *) data);
 			putreg(child, i, tmp);
 			data += sizeof(long);
@@ -361,13 +381,6 @@
 			ret = -EIO;
 			break;
 		}
-		if ( !child->used_math ) {
-			/* Simulate an empty FPU. */
-			set_fpu_cwd(child, 0x037f);
-			set_fpu_swd(child, 0x0000);
-			set_fpu_twd(child, 0xffff);
-			set_fpu_mxcsr(child, 0x1f80);
-		}
 		ret = get_fpregs((struct user_i387_struct *)data, child);
 		break;
 	}
@@ -420,11 +433,9 @@
 	
 	current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
 					? 0x80 : 0);
-	preempt_disable();
 	current->state = TASK_STOPPED;
 	notify_parent(current, SIGCHLD);
 	schedule();
-	preempt_enable();
 	/*
 	 * this isn't the same as continuing with a signal, but it will do
 	 * for normal use.  strace only continues with a signal if the
diff -Nru a/arch/x86_64/kernel/semaphore.c b/arch/x86_64/kernel/semaphore.c
--- a/arch/x86_64/kernel/semaphore.c	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/kernel/semaphore.c	Mon Apr 22 15:32:19 2002
@@ -14,7 +14,7 @@
  */
 #include <linux/config.h>
 #include <linux/sched.h>
-#include <linux/err.h>
+#include <asm/errno.h>
 
 #include <asm/semaphore.h>
 
@@ -168,58 +168,4 @@
 	return 1;
 }
 
-
-/*
- * The semaphore operations have a special calling sequence that
- * allow us to do a simpler in-line version of them. These routines
- * need to convert that sequence back into the C sequence when
- * there is contention on the semaphore.
- *
- * %rcx contains the semaphore pointer on entry. Save all the callee
- * clobbered registers.  It would be better if the compiler had a way 
- * to specify that for the callee.
- */
-
-
-#define PUSH_CLOBBER "pushq %rdi ; pushq %rsi ; pushq %rdx ; pushq %rcx ;" \
- "pushq %rbx ; pushq %r8 ; push %r9\n\t" 
-#define POP_CLOBBER "popq %r9 ; popq %r8 ; popq %rbx ; popq %rcx ; " \
-	"popq %rdx ; popq %rsi ; popq %rdi\n\t"     
-
-#define SEM_ENTRY(label, name) asm( \
-	".p2align\n\t.globl " #label "\n\t" \
-	#label ":\n\t" PUSH_CLOBBER "call " #name "\n\t" POP_CLOBBER "ret" )
-
-SEM_ENTRY(__down_failed, __down);
-SEM_ENTRY(__down_failed_interruptible, __down_interruptible); 
-SEM_ENTRY(__down_failed_trylock, __down_trylock); 
-SEM_ENTRY(__up_wakeup, __up);
-
-
-#if defined(CONFIG_SMP)
-asm(
-".p2align"
-"\n.globl	__write_lock_failed"
-"\n__write_lock_failed:"
-"\n	" LOCK "addl	$" RW_LOCK_BIAS_STR ",(%rax)"
-"\n1:	rep; nop; cmpl	$" RW_LOCK_BIAS_STR ",(%rax)"
-"\n	jne	1b"
-
-"\n	" LOCK "subl	$" RW_LOCK_BIAS_STR ",(%rax)"
-"\n	jnz	__write_lock_failed"
-"\n	ret"
-
-
-"\n.p2align"
-"\n.globl	__read_lock_failed"
-"\n__read_lock_failed:"
-"\n	lock ; incl	(%rax)"
-"\n1:	rep; nop; cmpl	$1,(%rax)"
-"\n	js	1b"
-
-"\n	lock ; decl	(%rax)"
-"\n	js	__read_lock_failed"
-"\n	ret"
-);
-#endif
 
diff -Nru a/arch/x86_64/kernel/setup.c b/arch/x86_64/kernel/setup.c
--- a/arch/x86_64/kernel/setup.c	Mon Apr 22 15:32:18 2002
+++ b/arch/x86_64/kernel/setup.c	Mon Apr 22 15:32:18 2002
@@ -49,14 +49,15 @@
 #include <asm/mpspec.h>
 #include <asm/mmu_context.h>
 #include <asm/bootsetup.h>
+#include <asm/smp.h>
 
 /*
  * Machine setup..
  */
 
 extern void mcheck_init(struct cpuinfo_x86 *c);
+extern void init_memory_mapping(void);
 
-char ignore_irq13;		/* set if exception 16 works */
 struct cpuinfo_x86 boot_cpu_data = { 0, 0, 0, 0, -1, 1, 0, 0, -1 };
 
 unsigned long mmu_cr4_features;
@@ -69,7 +70,6 @@
  */
 struct drive_info_struct { char dummy[32]; } drive_info;
 struct screen_info screen_info;
-struct apm_info apm_info;
 struct sys_desc_table_struct {
 	unsigned short length;
 	unsigned char table[0];
@@ -81,7 +81,6 @@
 
 extern int root_mountflags;
 extern char _text, _etext, _edata, _end;
-extern unsigned long cpu_khz;
 
 static int disable_x86_fxsr __initdata = 0;
 
@@ -201,8 +200,8 @@
 
 	for (i = 0; i < e820.nr_map; i++) {
 		printk(" %s: %016Lx - %016Lx ", who,
-			(unsigned long long)e820.map[i].addr,
-			(unsigned long long)(e820.map[i].addr + e820.map[i].size));
+			(unsigned long long) e820.map[i].addr,
+			(unsigned long long) (e820.map[i].addr + e820.map[i].size));
 		switch (e820.map[i].type) {
 		case E820_RAM:	printk("(usable)\n");
 				break;
@@ -215,7 +214,7 @@
 		case E820_NVS:
 				printk("(ACPI NVS)\n");
 				break;
-		default:	printk("type %lu\n", (unsigned long)e820.map[i].type);
+		default:	printk("type %u\n", e820.map[i].type);
 				break;
 		}
 	}
@@ -544,16 +543,16 @@
 	}
 }
 
+unsigned long start_pfn, end_pfn; 
+
 void __init setup_arch(char **cmdline_p)
 {
 	unsigned long bootmap_size, low_mem_size;
-	unsigned long start_pfn, max_pfn, max_low_pfn;
 	int i;
 
  	ROOT_DEV = to_kdev_t(ORIG_ROOT_DEV);
  	drive_info = DRIVE_INFO;
  	screen_info = SCREEN_INFO;
-	apm_info.bios = APM_BIOS_INFO;
 	aux_device_present = AUX_DEVICE_INFO;
 
 #ifdef CONFIG_BLK_DEV_RAM
@@ -581,20 +580,20 @@
 #define PFN_DOWN(x)	((x) >> PAGE_SHIFT)
 #define PFN_PHYS(x)	((x) << PAGE_SHIFT)
 
-#define VMALLOC_RESERVE	(unsigned long)(4096 << 20)
-#define MAXMEM		(unsigned long)(-PAGE_OFFSET-VMALLOC_RESERVE)
+#define MAXMEM		(120UL * 1024 * 1024 * 1024 * 1024)  /* 120TB */ 
 #define MAXMEM_PFN	PFN_DOWN(MAXMEM)
+#define MAX_NONPAE_PFN	(1 << 20)
 
 	/*
 	 * partially used pages are not usable - thus
 	 * we are rounding upwards:
 	 */
-	start_pfn = PFN_UP(__pa(&_end));
+	start_pfn = PFN_UP(__pa_symbol(&_end));
 
 	/*
 	 * Find the highest page frame number we have available
 	 */
-	max_pfn = 0;
+	end_pfn = 0;
 	for (i = 0; i < e820.nr_map; i++) {
 		unsigned long start, end;
 		/* RAM? */
@@ -604,23 +603,20 @@
 		end = PFN_DOWN(e820.map[i].addr + e820.map[i].size);
 		if (start >= end)
 			continue;
-		if (end > max_pfn)
-			max_pfn = end;
+		if (end > end_pfn)
+			end_pfn = end;
 	}
 
-	/*
-	 * Determine low and high memory ranges:
-	 */
-	max_low_pfn = max_pfn;
-	if (max_low_pfn > MAXMEM_PFN) {
-		max_low_pfn = MAXMEM_PFN;
+	if (end_pfn > MAXMEM_PFN) {
+		end_pfn = MAXMEM_PFN;
 	}
 
+	init_memory_mapping(); 
 
 	/*
 	 * Initialize the boot-time allocator (with low memory only):
 	 */
-	bootmap_size = init_bootmem(start_pfn, max_low_pfn);
+	bootmap_size = init_bootmem(start_pfn, end_pfn);
 
 	/*
 	 * Register fully available low RAM pages with the bootmem allocator.
@@ -636,15 +632,15 @@
 		 * We are rounding up the start address of usable memory:
 		 */
 		curr_pfn = PFN_UP(e820.map[i].addr);
-		if (curr_pfn >= max_low_pfn)
+		if (curr_pfn >= end_pfn)
 			continue;
 		/*
 		 * ... and at the end of the usable range downwards:
 		 */
 		last_pfn = PFN_DOWN(e820.map[i].addr + e820.map[i].size);
 
-		if (last_pfn > max_low_pfn)
-			last_pfn = max_low_pfn;
+		if (last_pfn > end_pfn)
+			last_pfn = end_pfn;
 
 		/*
 		 * .. finally, did all the rounding and playing
@@ -679,7 +675,12 @@
 	 */
 	reserve_bootmem(PAGE_SIZE, PAGE_SIZE);
 #endif
-
+#ifdef CONFIG_ACPI_SLEEP
+       /*
+        * Reserve low memory region for sleep support.
+        */
+       acpi_reserve_bootmem();
+#endif
 #ifdef CONFIG_X86_LOCAL_APIC
 	/*
 	 * Find and reserve possible boot-time SMP configuration:
@@ -713,6 +714,15 @@
 	smp_alloc_memory(); /* AP processor realmode stacks in low memory*/
 #endif
 	paging_init();
+#ifdef CONFIG_ACPI_BOOT
+       /*
+        * Initialize the ACPI boot-time table parser (gets the RSDP and SDT).
+        * Must do this after paging_init (due to reliance on fixmap, and thus
+        * the bootmem allocator) but before get_smp_config (to allow parsing
+        * of MADT).
+        */
+       acpi_boot_init(*cmdline_p);
+#endif
 #ifdef CONFIG_X86_LOCAL_APIC
 	/*
 	 * get boot-time SMP configuration:
@@ -760,6 +770,7 @@
 		request_resource(&ioport_resource, standard_io_resources+i);
 
 	/* Tell the PCI layer not to allocate too close to the RAM area.. */
+	/* ??? move this up on x86-64 */
 	low_mem_size = ((max_low_pfn << PAGE_SHIFT) + 0xfffff) & ~0xfffff;
 	if (low_mem_size > pci_mem_start)
 		pci_mem_start = low_mem_size;
@@ -834,6 +845,15 @@
 	clear_bit(0*32+31, &c->x86_capability);
 	
 	r = get_model_name(c);
+	if (!r) { 
+		switch (c->x86) { 
+		case 15:
+			/* Should distingush Models here, but this is only
+			   a fallback anyways. */
+			strcpy(c->x86_model_id, "Hammer");
+			break; 
+		} 
+	} 
 	display_cacheinfo(c);
 	return r;
 }
@@ -1016,7 +1036,6 @@
 static int show_cpuinfo(struct seq_file *m, void *v)
 {
 	struct cpuinfo_x86 *c = v;
-	int index = c - cpu_data;
 
 	/* 
 	 * These flag bits must match the definitions in <asm/cpufeature.h>.
@@ -1036,7 +1055,7 @@
 		/* AMD-defined */
 		NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
 		NULL, NULL, NULL, "syscall", NULL, NULL, NULL, NULL,
-		NULL, NULL, NULL, NULL, NULL, NULL, "mmxext", NULL,
+		NULL, NULL, NULL, NULL, "nx", NULL, "mmxext", NULL,
 		NULL, NULL, NULL, NULL, NULL, "lm", "3dnowext", "3dnow",
 
 		/* Transmeta-defined */
@@ -1057,15 +1076,15 @@
 		return 0;
 #endif
 
-	seq_printf(m,"processor\t: %d\n"
+	seq_printf(m,"processor\t: %u\n"
 		     "vendor_id\t: %s\n"
 		     "cpu family\t: %d\n"
 		     "model\t\t: %d\n"
 		     "model name\t: %s\n",
-		   index,
+		     (unsigned)(c-cpu_data),
 		     c->x86_vendor_id[0] ? c->x86_vendor_id : "unknown",
 		     c->x86,
-		     c->x86_model,
+		     (int)c->x86_model,
 		     c->x86_model_id[0] ? c->x86_model_id : "unknown");
 	
 	if (c->x86_mask || c->cpuid_level >= 0)
@@ -1074,7 +1093,7 @@
 		seq_printf(m, "stepping\t: unknown\n");
 	
 	if ( test_bit(X86_FEATURE_TSC, &c->x86_capability) ) {
-		seq_printf(m, "cpu MHz\t\t: %lu.%03lu\n",
+		seq_printf(m, "cpu MHz\t\t: %u.%03u\n",
 			     cpu_khz / 1000, (cpu_khz % 1000));
 	}
 
diff -Nru a/arch/x86_64/kernel/setup64.c b/arch/x86_64/kernel/setup64.c
--- a/arch/x86_64/kernel/setup64.c	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/kernel/setup64.c	Mon Apr 22 15:32:19 2002
@@ -1,20 +1,24 @@
 /* 
- * X86-64 specific setup part.
+ * X86-64 specific CPU setup.
  * Copyright (C) 1995  Linus Torvalds
- * Copyright 2001 2002 SuSE Labs / Andi Kleen.
+ * Copyright 2001, 2002 SuSE Labs / Andi Kleen.
  * See setup.c for older changelog.
+ * $Id: setup64.c,v 1.12 2002/03/21 10:09:17 ak Exp $
  */ 
 #include <linux/config.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
+#include <linux/string.h>
 #include <asm/pda.h>
-#include <asm/pda.h>
+#include <asm/pgtable.h>
 #include <asm/processor.h>
 #include <asm/desc.h>
 #include <asm/bitops.h>
 #include <asm/atomic.h>
 #include <asm/mmu_context.h>
+#include <asm/smp.h>
+#include <asm/i387.h>
 
 char x86_boot_params[2048] __initdata = {0,};
 
@@ -25,52 +29,85 @@
 extern void system_call(void); 
 extern void ia32_cstar_target(void); 
 
+extern struct task_struct init_task;
+
 struct desc_ptr gdt_descr = { 0 /* filled in */, (unsigned long) gdt_table }; 
 struct desc_ptr idt_descr = { 256 * 16, (unsigned long) idt_table }; 
 
+char boot_cpu_stack[IRQSTACKSIZE] __cacheline_aligned;
+
 void pda_init(int cpu)
 { 
-	cpu_pda[cpu].me = &cpu_pda[cpu]; 
-	cpu_pda[cpu].cpunumber = cpu; 
-	cpu_pda[cpu].irqcount = -1;
-	cpu_pda[cpu].irqstackptr = cpu_pda[cpu].irqstack + sizeof(cpu_pda[0].irqstack);
-	/* others are initialized in smpboot.c */
+        pml4_t *level4;
+
 	if (cpu == 0) {
+		/* others are initialized in smpboot.c */
 		cpu_pda[cpu].pcurrent = &init_task;
-		cpu_pda[cpu].kernelstack = 
-			(unsigned long)&init_thread_union+THREAD_SIZE-PDA_STACKOFFSET;
+		cpu_pda[cpu].irqstackptr = boot_cpu_stack; 
+		level4 = init_level4_pgt; 
+	} else {
+		cpu_pda[cpu].irqstackptr = (char *)
+			__get_free_pages(GFP_ATOMIC, IRQSTACK_ORDER);
+		if (!cpu_pda[cpu].irqstackptr)
+			panic("cannot allocate irqstack for cpu %d\n", cpu); 
+		level4 = (pml4_t *)__get_free_pages(GFP_ATOMIC, 0); 
 	}
-	asm volatile("movl %0,%%gs ; movl %0,%%fs" :: "r" (0)); 
+	if (!level4) 
+		panic("Cannot allocate top level page for cpu %d", cpu); 
+
+	cpu_pda[cpu].level4_pgt = (unsigned long *)level4; 
+	if (level4 != init_level4_pgt)
+		memcpy(level4, &init_level4_pgt, PAGE_SIZE); 
+	set_pml4(level4 + 510, mk_kernel_pml4(__pa_symbol(boot_vmalloc_pgt)));
+	asm volatile("movq %0,%%cr3" :: "r" (__pa(level4))); 
+
+	cpu_pda[cpu].irqstackptr += IRQSTACKSIZE-64;
+	cpu_pda[cpu].cpunumber = cpu; 
+	cpu_pda[cpu].irqcount = -1;
+	cpu_pda[cpu].kernelstack = 
+		(unsigned long)stack_thread_info() - PDA_STACKOFFSET + THREAD_SIZE; 
+	cpu_pda[cpu].me = &cpu_pda[cpu];
+
+	asm volatile("movl %0,%%fs ; movl %0,%%gs" :: "r" (0)); 
 	wrmsrl(MSR_GS_BASE, cpu_pda + cpu);
 } 
 
+#define EXCEPTION_STK_ORDER 0 /* >= N_EXCEPTION_STACKS*EXCEPTION_STKSZ */
+char boot_exception_stacks[N_EXCEPTION_STACKS*EXCEPTION_STKSZ];
+
+
 /*
  * cpu_init() initializes state that is per-CPU. Some data is already
  * initialized (naturally) in the bootstrap process, such as the GDT
  * and IDT. We reload them nevertheless, this function acts as a
  * 'CPU state barrier', nothing should get across.
+ * A lot of state is already set up in PDA init.
  */
 void __init cpu_init (void)
 {
 #ifdef CONFIG_SMP
-	int nr = current_thread_info()->cpu;
+	int nr = stack_smp_processor_id();
 #else
 	int nr = smp_processor_id();
 #endif
 	struct tss_struct * t = &init_tss[nr];
 	unsigned long v; 
+	char *estacks; 
 
 	/* CPU 0 is initialised in head64.c */
-	if (nr != 0) 
+	if (nr != 0) {
+		estacks = (char *)__get_free_pages(GFP_ATOMIC, 0); 
+		if (!estacks)
+			panic("Can't allocate exception stacks for CPU %d\n",nr);
 		pda_init(nr);  
+	} else 
+		estacks = boot_exception_stacks; 
+
+	if (test_and_set_bit(nr, &cpu_initialized))
+		panic("CPU#%d already initialized!\n", nr);
 
-	if (test_and_set_bit(nr, &cpu_initialized)) {
-		printk("CPU#%d already initialized!\n", nr);
-		for (;;) __sti();
-	}
 	printk("Initializing CPU#%d\n", nr);
 
-	if (cpu_has_vme || cpu_has_tsc || cpu_has_de)
 		clear_in_cr4(X86_CR4_VME|X86_CR4_PVI|X86_CR4_TSD|X86_CR4_DE);
 
 	gdt_descr.size = (__u8*) gdt_end - (__u8*)gdt_table; 
@@ -82,7 +119,7 @@
 	 * Delete NT
 	 */
 
-	__asm__ volatile("pushfq ; popq %%rax ; btr $14,%%rax ; pushq %%rax ; popfq" :: : "eax");
+	asm volatile("pushfq ; popq %%rax ; btr $14,%%rax ; pushq %%rax ; popfq" ::: "eax");
 
 	/* 
 	 * LSTAR and STAR live in a bit strange symbiosis.
@@ -96,29 +133,29 @@
 	wrmsrl(MSR_CSTAR, ia32_cstar_target); 
 #endif
 
-	rdmsrl(MSR_EFER, v); 
-	wrmsrl(MSR_EFER, v|1); 
-	
 	/* Flags to clear on syscall */
 	wrmsrl(MSR_SYSCALL_MASK, EF_TF|EF_DF|EF_IE); 
 
-
 	wrmsrl(MSR_FS_BASE, 0);
 	wrmsrl(MSR_KERNEL_GS_BASE, 0);
 	barrier(); 
 
 	/*
-	 * set up and load the per-CPU TSS and LDT
+	 * set up and load the per-CPU TSS
 	 */
+	estacks += EXCEPTION_STKSZ;
+	for (v = 0; v < N_EXCEPTION_STACKS; v++) {
+		t->ist[v] = (unsigned long)estacks;
+		estacks += EXCEPTION_STKSZ;
+	}
+
 	atomic_inc(&init_mm.mm_count);
 	current->active_mm = &init_mm;
 	if(current->mm)
 		BUG();
 	enter_lazy_tlb(&init_mm, current, nr);
 
-	set_tssldt_descriptor((__u8 *)tss_start + (nr*16), (unsigned long) t, 
-			      DESC_TSS, 
-			      offsetof(struct tss_struct, io_bitmap)); 
+	set_tss_desc(nr, t);
 	load_TR(nr);
 	load_LDT(&init_mm);
 
@@ -133,10 +170,5 @@
 	set_debug(0UL, 6);
 	set_debug(0UL, 7);
 
-	/*
-	 * Force FPU initialization:
-	 */
-	clear_thread_flag(TIF_USEDFPU); 
-	current->used_math = 0;
-	stts();
+	fpu_init(); 
 }
diff -Nru a/arch/x86_64/kernel/signal.c b/arch/x86_64/kernel/signal.c
--- a/arch/x86_64/kernel/signal.c	Mon Apr 22 15:32:18 2002
+++ b/arch/x86_64/kernel/signal.c	Mon Apr 22 15:32:18 2002
@@ -24,12 +24,13 @@
 #include <linux/stddef.h>
 #include <linux/tty.h>
 #include <linux/personality.h>
+#include <linux/compiler.h>
 #include <linux/binfmts.h>
 #include <asm/ucontext.h>
 #include <asm/uaccess.h>
 #include <asm/i387.h>
 
-#define DEBUG_SIG 0
+/* #define DEBUG_SIG 1 */
 
 #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
 
@@ -120,8 +121,7 @@
 	char *pretcode;
 	struct ucontext uc;
 	struct siginfo info;
-	struct _fpstate fpstate;
-	char retcode[8];
+	struct _fpstate fpstate __attribute__((aligned(8)));
 };
 
 static int
@@ -129,26 +129,17 @@
 {
 	unsigned int err = 0;
 
+
 #define COPY(x)		err |= __get_user(regs->x, &sc->x)
 
-#define COPY_SEG(seg)							\
-	{ unsigned short tmp;						\
-	  err |= __get_user(tmp, &sc->seg);				\
-	  regs->x##seg = tmp; }
-
-#define COPY_SEG_STRICT(seg)						\
-	{ unsigned short tmp;						\
-	  err |= __get_user(tmp, &sc->seg);				\
-	  regs->x##seg = tmp|3; }
-
-#define GET_SEG(seg)							\
-	{ unsigned short tmp;						\
-	  err |= __get_user(tmp, &sc->seg);				\
-	  loadsegment(seg,tmp); }
-
-	/* XXX: rdmsr for 64bits */ 
-	GET_SEG(gs);
-	GET_SEG(fs);
+	{ 
+		unsigned int seg; 
+		err |= __get_user(seg, &sc->gs); 
+		load_gs_index(seg); 
+		err |= __get_user(seg, &sc->fs);
+		loadsegment(fs,seg);
+	}
+
 	COPY(rdi); COPY(rsi); COPY(rbp); COPY(rsp); COPY(rbx);
 	COPY(rdx); COPY(rcx); COPY(rip);
 	COPY(r8);
@@ -160,7 +151,6 @@
 	COPY(r14);
 	COPY(r15);
 
-	
 	{
 		unsigned int tmpflags;
 		err |= __get_user(tmpflags, &sc->eflags);
@@ -235,6 +225,7 @@
 		 struct pt_regs *regs, unsigned long mask)
 {
 	int tmp, err = 0;
+	struct task_struct *me = current;
 
 	tmp = 0;
 	__asm__("movl %%gs,%0" : "=r"(tmp): "0"(tmp));
@@ -258,11 +249,12 @@
 	err |= __put_user(regs->r13, &sc->r13);
 	err |= __put_user(regs->r14, &sc->r14);
 	err |= __put_user(regs->r15, &sc->r15);
-	err |= __put_user(current->thread.trap_no, &sc->trapno);
-	err |= __put_user(current->thread.error_code, &sc->err);
+	err |= __put_user(me->thread.trap_no, &sc->trapno);
+	err |= __put_user(me->thread.error_code, &sc->err);
 	err |= __put_user(regs->rip, &sc->rip);
 	err |= __put_user(regs->eflags, &sc->eflags);
-	err |= __put_user(regs->rsp, &sc->rsp_at_signal);
+	err |= __put_user(mask, &sc->oldmask);
+	err |= __put_user(me->thread.cr2, &sc->cr2);
 
 	tmp = save_i387(fpstate);
 	if (tmp < 0)
@@ -270,10 +262,6 @@
 	else
 	  err |= __put_user(tmp ? fpstate : NULL, &sc->fpstate);
 
-	/* non-iBCS2 extensions.. */
-	err |= __put_user(mask, &sc->oldmask);
-	err |= __put_user(current->thread.cr2, &sc->cr2);
-
 	return err;
 }
 
@@ -294,13 +282,20 @@
 			rsp = current->sas_ss_sp + current->sas_ss_size;
 	}
 
-	return (void *)((rsp - frame_size) & -16UL);
+	{ 
+		extern void bad_sigframe(void); 
+		/* beginning of sigframe is 8 bytes misaligned, but fpstate
+		   must end up on a 16byte boundary */ 
+		if ((offsetof(struct rt_sigframe, fpstate) & 16) != 0) 
+			bad_sigframe(); 		
+	} 
+
+	return (void *)((rsp - frame_size) & ~(15UL)) - 8;
 }
 
 static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
 			   sigset_t *set, struct pt_regs * regs)
 {
-	struct thread_info *ti; 
 	struct rt_sigframe *frame;
 	int err = 0;
 
@@ -346,16 +341,16 @@
 	printk("%d old rip %lx old rsp %lx old rax %lx\n", current->pid,regs->rip,regs->rsp,regs->rax);
 #endif
 
-	ti = current_thread_info(); 
 
 	/* Set up registers for signal handler */
-	regs->rdi = (ti->exec_domain
-		     && ti->exec_domain->signal_invmap
-		     && sig < 32
-		     ? ti->exec_domain->signal_invmap[sig]
-		     : sig);
-	regs->rax = 0;	/* In case the signal handler was declared without prototypes */ 
-
+	{ 
+		struct exec_domain *ed = current_thread_info()->exec_domain;
+		if (unlikely(ed && ed->signal_invmap && sig < 32))
+			sig = ed->signal_invmap[sig];
+	} 
+	regs->rdi = sig;
+	/* In case the signal handler was declared without prototypes */ 
+	regs->rax = 0;	
 
 	/* This also works for non SA_SIGINFO handlers because they expect the
 	   next argument after the signal number on the stack. */
@@ -365,7 +360,6 @@
 	regs->rip = (unsigned long) ka->sa.sa_handler;
 
 	set_fs(USER_DS);
-	// XXX: cs
 	regs->eflags &= ~TF_MASK;
 
 #if DEBUG_SIG
@@ -473,11 +467,9 @@
 		if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) {
 			/* Let the debugger run.  */
 			current->exit_code = signr;
-			preempt_disable();
 			current->state = TASK_STOPPED;
 			notify_parent(current, SIGCHLD);
 			schedule();
-			preempt_enable();
 
 			/* We're back.  Did the debugger cancel the sig?  */
 			if (!(signr = current->exit_code))
@@ -493,8 +485,8 @@
 				info.si_signo = signr;
 				info.si_errno = 0;
 				info.si_code = SI_USER;
-				info.si_pid = current->p_pptr->pid;
-				info.si_uid = current->p_pptr->uid;
+				info.si_pid = current->parent->pid;
+				info.si_uid = current->parent->uid;
 			}
 
 			/* If the (new) signal is now blocked, requeue it.  */
@@ -535,7 +527,7 @@
 				preempt_disable(); 
 				current->state = TASK_STOPPED;
 				current->exit_code = signr;
-				sig = current->p_pptr->sig;
+				sig = current->parent->sig;
 				if (sig && !(sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP))
 					notify_parent(current, SIGCHLD);
 				schedule();
@@ -575,7 +567,7 @@
 		    regs->rax == -ERESTARTSYS ||
 		    regs->rax == -ERESTARTNOINTR) {
 			regs->rax = regs->orig_rax;
-			regs->rcx -= 2;
+			regs->rip -= 2;
 		}
 	}
 	return 0;
diff -Nru a/arch/x86_64/kernel/smp.c b/arch/x86_64/kernel/smp.c
--- a/arch/x86_64/kernel/smp.c	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/kernel/smp.c	Mon Apr 22 15:32:19 2002
@@ -20,90 +20,12 @@
 
 #include <asm/mtrr.h>
 #include <asm/pgalloc.h>
-
-/*
- *	Some notes on x86 processor bugs affecting SMP operation:
- *
- *	Pentium, Pentium Pro, II, III (and all CPUs) have bugs.
- *	The Linux implications for SMP are handled as follows:
- *
- *	Pentium III / [Xeon]
- *		None of the E1AP-E3AP errata are visible to the user.
- *
- *	E1AP.	see PII A1AP
- *	E2AP.	see PII A2AP
- *	E3AP.	see PII A3AP
- *
- *	Pentium II / [Xeon]
- *		None of the A1AP-A3AP errata are visible to the user.
- *
- *	A1AP.	see PPro 1AP
- *	A2AP.	see PPro 2AP
- *	A3AP.	see PPro 7AP
- *
- *	Pentium Pro
- *		None of 1AP-9AP errata are visible to the normal user,
- *	except occasional delivery of 'spurious interrupt' as trap #15.
- *	This is very rare and a non-problem.
- *
- *	1AP.	Linux maps APIC as non-cacheable
- *	2AP.	worked around in hardware
- *	3AP.	fixed in C0 and above steppings microcode update.
- *		Linux does not use excessive STARTUP_IPIs.
- *	4AP.	worked around in hardware
- *	5AP.	symmetric IO mode (normal Linux operation) not affected.
- *		'noapic' mode has vector 0xf filled out properly.
- *	6AP.	'noapic' mode might be affected - fixed in later steppings
- *	7AP.	We do not assume writes to the LVT deassering IRQs
- *	8AP.	We do not enable low power mode (deep sleep) during MP bootup
- *	9AP.	We do not use mixed mode
- *
- *	Pentium
- *		There is a marginal case where REP MOVS on 100MHz SMP
- *	machines with B stepping processors can fail. XXX should provide
- *	an L1cache=Writethrough or L1cache=off option.
- *
- *		B stepping CPUs may hang. There are hardware work arounds
- *	for this. We warn about it in case your board doesnt have the work
- *	arounds. Basically thats so I can tell anyone with a B stepping
- *	CPU and SMP problems "tough".
- *
- *	Specific items [From Pentium Processor Specification Update]
- *
- *	1AP.	Linux doesn't use remote read
- *	2AP.	Linux doesn't trust APIC errors
- *	3AP.	We work around this
- *	4AP.	Linux never generated 3 interrupts of the same priority
- *		to cause a lost local interrupt.
- *	5AP.	Remote read is never used
- *	6AP.	not affected - worked around in hardware
- *	7AP.	not affected - worked around in hardware
- *	8AP.	worked around in hardware - we get explicit CS errors if not
- *	9AP.	only 'noapic' mode affected. Might generate spurious
- *		interrupts, we log only the first one and count the
- *		rest silently.
- *	10AP.	not affected - worked around in hardware
- *	11AP.	Linux reads the APIC between writes to avoid this, as per
- *		the documentation. Make sure you preserve this as it affects
- *		the C stepping chips too.
- *	12AP.	not affected - worked around in hardware
- *	13AP.	not affected - worked around in hardware
- *	14AP.	we always deassert INIT during bootup
- *	15AP.	not affected - worked around in hardware
- *	16AP.	not affected - worked around in hardware
- *	17AP.	not affected - worked around in hardware
- *	18AP.	not affected - worked around in hardware
- *	19AP.	not affected - worked around in BIOS
- *
- *	If this sounds worrying believe me these bugs are either ___RARE___,
- *	or are signal timing bugs worked around in hardware and there's
- *	about nothing of note with C stepping upwards.
- */
+#include <asm/tlbflush.h>
 
 /* The 'big kernel lock' */
 spinlock_t kernel_flag __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
 
-struct tlb_state cpu_tlbstate[NR_CPUS] __cacheline_aligned = {[0 ... NR_CPUS-1] = { &init_mm, 0, }};
+struct tlb_state cpu_tlbstate[NR_CPUS] = {[0 ... NR_CPUS-1] = { &init_mm, 0 }};
 
 /*
  * the following functions deal with sending IPIs between CPUs.
@@ -111,9 +33,12 @@
  * We use 'broadcast', CPU->CPU IPIs and self-IPIs too.
  */
 
-static inline int __prepare_ICR (unsigned int shortcut, int vector)
+static inline unsigned int __prepare_ICR (unsigned int shortcut, int vector)
 {
-	return APIC_DM_FIXED | shortcut | vector | APIC_DEST_LOGICAL;
+	unsigned int icr =  APIC_DM_FIXED | shortcut | vector | APIC_DEST_LOGICAL;
+	if (vector == KDB_VECTOR) 
+		icr = (icr & (~APIC_VECTOR_MASK)) | APIC_DM_NMI; 		
+	return icr;
 }
 
 static inline int __prepare_ICR2 (unsigned int mask)
@@ -406,54 +331,20 @@
 	do_flush_tlb_all_local();
 }
 
-static spinlock_t migration_lock = SPIN_LOCK_UNLOCKED;
-static task_t *new_task;
-
-/*
- * This function sends a 'task migration' IPI to another CPU.
- * Must be called from syscall contexts, with interrupts *enabled*.
- */
-void smp_migrate_task(int cpu, task_t *p)
+void smp_kdb_stop(void)
 {
-	/*
-	 * The target CPU will unlock the migration spinlock:
-	 */
-	_raw_spin_lock(&migration_lock);
-	new_task = p;
-	send_IPI_mask(1 << cpu, TASK_MIGRATION_VECTOR);
+	send_IPI_allbutself(KDB_VECTOR);
 }
 
 /*
- * Task migration callback.
- */
-asmlinkage void smp_task_migration_interrupt(void)
-{
-	task_t *p;
-
-	ack_APIC_irq();
-	p = new_task;
-	_raw_spin_unlock(&migration_lock);
-	sched_task_migrated(p);
-}
-/*
  * this function sends a 'reschedule' IPI to another CPU.
  * it goes straight through and wastes no time serializing
  * anything. Worst case is that we lose a reschedule ...
  */
+
 void smp_send_reschedule(int cpu)
 {
 	send_IPI_mask(1 << cpu, RESCHEDULE_VECTOR);
-}
-
-/*
- * this function sends a reschedule IPI to all (other) CPUs.
- * This should only be used if some 'global' task became runnable,
- * such as a RT task, that must be handled now. The first CPU
- * that manages to grab the task will run it.
- */
-void smp_send_reschedule_all(void)
-{
-	send_IPI_allbutself(RESCHEDULE_VECTOR);
 }
 
 /*
diff -Nru a/arch/x86_64/kernel/smpboot.c b/arch/x86_64/kernel/smpboot.c
--- a/arch/x86_64/kernel/smpboot.c	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/kernel/smpboot.c	Mon Apr 22 15:32:19 2002
@@ -47,6 +47,8 @@
 #include <asm/mtrr.h>
 #include <asm/pgalloc.h>
 #include <asm/desc.h>
+#include <asm/kdebug.h>
+#include <asm/tlbflush.h>
 
 /* Set if we find a B stepping CPU			*/
 static int smp_b_stepping;
@@ -117,8 +119,8 @@
 
 static unsigned long __init setup_trampoline(void)
 {
-	extern __u32 tramp_gdt_ptr; 
-	tramp_gdt_ptr = (__u32)virt_to_phys(&gdt_table); 
+	extern volatile __u32 tramp_gdt_ptr; 
+	tramp_gdt_ptr = __pa_symbol(&gdt_table); 
 	memcpy(trampoline_base, trampoline_data, trampoline_end - trampoline_data);
 	return virt_to_phys(trampoline_base);
 }
@@ -199,34 +201,11 @@
 
 #define NR_LOOPS 5
 
-extern unsigned long fast_gettimeoffset_quotient;
+extern unsigned int fast_gettimeoffset_quotient;
 
-/*
- * accurate 64-bit/32-bit division, expanded to 32-bit divisions and 64-bit
- * multiplication. Not terribly optimized but we need it at boot time only
- * anyway.
- *
- * result == a / b
- *	== (a1 + a2*(2^32)) / b
- *	== a1/b + a2*(2^32/b)
- *	== a1/b + a2*((2^32-1)/b) + a2/b + (a2*((2^32-1) % b))/b
- *		    ^---- (this multiplication can overflow)
- */
-
-static unsigned long long div64 (unsigned long long a, unsigned long b0)
+static inline unsigned long long div64 (unsigned long long a, unsigned long b)
 {
-	unsigned int a1, a2;
-	unsigned long long res;
-
-	a1 = ((unsigned int*)&a)[0];
-	a2 = ((unsigned int*)&a)[1];
-
-	res = a1/b0 +
-		(unsigned long long)a2 * (unsigned long long)(0xffffffff/b0) +
-		a2 / b0 +
-		(a2 * (0xffffffff % b0)) / b0;
-
-	return res;
+	return a/b;
 }
 
 static void __init synchronize_tsc_bp (void)
@@ -308,14 +287,14 @@
 			if (tsc_values[i] < avg)
 				realdelta = -realdelta;
 
-			printk("BIOS BUG: CPU#%d improperly initialized, has %ld usecs TSC skew! FIXED.\n", i, realdelta);
+			printk("BIOS BUG: CPU#%d improperly initialized, has %ld usecs TSC skew! FIXED.\n",
+				i, realdelta);
 		}
 
 		sum += delta;
 	}
 	if (!buggy)
 		printk("passed.\n");
-		;
 }
 
 static void __init synchronize_tsc_ap (void)
@@ -428,7 +407,8 @@
 	 */
  	smp_store_cpu_info(cpuid);
 
-	disable_APIC_timer();
+	notify_die(DIE_CPUINIT, "cpuinit", NULL, 0);
+
 	/*
 	 * Allow the master to continue.
 	 */
@@ -450,6 +430,9 @@
  */
 int __init start_secondary(void *unused)
 {
+	int var;
+	printk("rsp %p\n",&var);
+
 	/*
 	 * Dont put anything before smp_callin(), SMP
 	 * booting is too fragile that we want to limit the
@@ -459,7 +442,6 @@
 	smp_callin();
 	while (!atomic_read(&smp_commenced))
 		rep_nop();
-	enable_APIC_timer();
 	/*
 	 * low-memory mappings have been cleared, flush them from
 	 * the local TLBs too.
@@ -477,6 +459,8 @@
  */
 void __init initialize_secondary(void)
 {
+	struct task_struct *me = stack_current();
+
 	/*
 	 * We don't actually need to load the full TSS,
 	 * basically just the stack pointer and the eip.
@@ -486,17 +470,17 @@
 		"movq %0,%%rsp\n\t"
 		"jmp *%1"
 		:
-		:"r" (current->thread.rsp),"r" (current->thread.rip));
+		:"r" (me->thread.rsp),"r" (me->thread.rip));
 }
 
-extern void *init_rsp; 
+extern volatile unsigned long init_rsp; 
 extern void (*initial_code)(void);
 
 static int __init fork_by_hand(void)
 {
 	struct pt_regs regs;
 	/*
-	 * don't care about the eip and regs settings since
+	 * don't care about the rip and regs settings since
 	 * we'll never reschedule the forked task.
 	 */
 	return do_fork(CLONE_VM|CLONE_PID, 0, &regs, 0);
@@ -547,6 +531,8 @@
 	int timeout, num_starts, j, cpu;
 	unsigned long start_eip;
 
+	printk("do_boot_cpu cpucount = %d\n", cpucount); 
+
 	cpu = ++cpucount;
 	/*
 	 * We can't use kernel_thread since we must avoid to
@@ -567,21 +553,21 @@
 
 	x86_cpu_to_apicid[cpu] = apicid;
 	x86_apicid_to_cpu[apicid] = cpu;
-	idle->thread.rip = (unsigned long) start_secondary;
-
-	init_rsp = (void *) (THREAD_SIZE + (char *)idle->thread_info);
+	idle->thread.rip = (unsigned long)start_secondary;
+//	idle->thread.rsp = (unsigned long)idle->thread_info + THREAD_SIZE - 512;
 
 	unhash_process(idle);
+
 	cpu_pda[cpu].pcurrent = idle;
-	cpu_pda[cpu].kernelstack = init_rsp - PDA_STACKOFFSET; 
 
 	/* start_eip had better be page-aligned! */
 	start_eip = setup_trampoline();
 
-	/* So we see what's up   */
-	printk("Booting processor %d/%d eip %lx\n", cpu, apicid, start_eip);
-
+	init_rsp = (unsigned long)idle->thread_info + PAGE_SIZE + 1024;
 	initial_code = initialize_secondary;
+
+	printk("Booting processor %d/%d rip %lx rsp %lx rsp2 %lx\n", cpu, apicid, 
+	       start_eip, idle->thread.rsp, init_rsp);
 
 	/*
 	 * This grunge runs the startup process for
diff -Nru a/arch/x86_64/kernel/sys_x86_64.c b/arch/x86_64/kernel/sys_x86_64.c
--- a/arch/x86_64/kernel/sys_x86_64.c	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/kernel/sys_x86_64.c	Mon Apr 22 15:32:19 2002
@@ -69,13 +69,15 @@
 	struct vm_area_struct *vma;
 	unsigned long end = TASK_SIZE;
 
+	if (test_thread_flag(TIF_IA32))
+		flags |= MAP_32BIT; 
 	if (flags & MAP_32BIT) 
-		end = 0xffffffff;
-	if (len > TASK_SIZE)
+		end = 0xffffffff-1;
+	if (len > end)
 		return -ENOMEM;
 	if (!addr) { 
 		addr = TASK_UNMAPPED_64;
-		if (test_thread_flag(TIF_IA32) || (flags & MAP_32BIT)) {
+		if (flags & MAP_32BIT) {
 			addr = TASK_UNMAPPED_32;
 		}
 	} 
@@ -83,7 +85,7 @@
 
 	for (vma = find_vma(current->mm, addr); ; vma = vma->vm_next) {
 		/* At this point:  (!vma || addr < vma->vm_end). */
-		if (TASK_SIZE - len < addr)
+		if (end - len < addr)
 			return -ENOMEM;
 		if (!vma || addr + len <= vma->vm_start)
 			return addr;
@@ -111,3 +113,20 @@
 	schedule();
 	return -ERESTARTNOHAND;
 }
+
+asmlinkage long wrap_sys_shmat(int shmid, char *shmaddr, int shmflg, 
+			       unsigned long *raddr_user)
+{
+	unsigned long raddr;
+	return sys_shmat(shmid,shmaddr,shmflg,&raddr) ?: put_user(raddr,raddr_user);
+} 
+
+asmlinkage long wrap_sys_semctl(int semid, int semnum, int cmd, unsigned long *ptr)
+{	
+	unsigned long val; 
+	/* XXX: for cmd==SETVAL the manpage says ptr is the value directly. i386
+	   seems to always get it via a pointer. Follow i386 here. Check this. */
+	if (get_user(val, ptr))
+		return -EFAULT;
+	return sys_semctl(semid, semnum, cmd, (union semun)(void *)val);
+} 
diff -Nru a/arch/x86_64/kernel/syscall.c b/arch/x86_64/kernel/syscall.c
--- a/arch/x86_64/kernel/syscall.c	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/kernel/syscall.c	Mon Apr 22 15:32:19 2002
@@ -3,6 +3,13 @@
 #include <linux/linkage.h>
 #include <linux/sys.h>
 #include <linux/cache.h>
+#include <linux/config.h>
+
+/* No comment. */
+#if defined(CONFIG_NFSD) || defined(CONFIG_NFSD_MODULE)
+#else
+#define sys_nfsservctl		sys_ni_syscall
+#endif
 
 #define __NO_STUBS
 
diff -Nru a/arch/x86_64/kernel/time.c b/arch/x86_64/kernel/time.c
--- a/arch/x86_64/kernel/time.c	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/kernel/time.c	Mon Apr 22 15:32:19 2002
@@ -63,19 +63,19 @@
 #include <linux/irq.h>
 
 
-unsigned long cpu_khz;	/* Detected as we calibrate the TSC */
+unsigned int cpu_khz;	/* Detected as we calibrate the TSC */
 
 /* Number of usecs that the last interrupt was delayed */
 int __delay_at_last_interrupt __section_delay_at_last_interrupt;
 
-unsigned long __last_tsc_low __section_last_tsc_low; /* lsb 32 bits of Time Stamp Counter */
+unsigned int __last_tsc_low __section_last_tsc_low; /* lsb 32 bits of Time Stamp Counter */
 
 /* Cached *multiplier* to convert TSC counts to microseconds.
  * (see the equation below).
  * Equal to 2^32 * (1 / (clocks per usec) ).
  * Initialized in time_init.
  */
-unsigned long __fast_gettimeoffset_quotient __section_fast_gettimeoffset_quotient;
+unsigned int __fast_gettimeoffset_quotient __section_fast_gettimeoffset_quotient;
 
 extern rwlock_t xtime_lock;
 struct timeval __xtime __section_xtime;
@@ -85,9 +85,9 @@
  
 spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED;
 
-static inline unsigned long do_gettimeoffset(void)
+inline unsigned long do_gettimeoffset(void)
 {
-	register unsigned long eax, edx;
+	register unsigned int eax, edx;
 
 	/* Read the Time Stamp Counter */
 
@@ -105,48 +105,21 @@
 	 * in the critical path.
          */
 
-	edx = (eax*fast_gettimeoffset_quotient) >> 32;
+	__asm__("mull %2"
+		:"=a" (eax), "=d" (edx)
+		:"rm" (fast_gettimeoffset_quotient),
+		 "0" (eax));
 
 	/* our adjusted time offset in microseconds */
 	return delay_at_last_interrupt + edx;
 }
 
-
-
-
 #define TICK_SIZE tick
 
 spinlock_t i8253_lock = SPIN_LOCK_UNLOCKED;
 
 extern spinlock_t i8259A_lock;
 
-
-static inline unsigned long do_fast_gettimeoffset(void)
-{
-	register unsigned long eax, edx;
-
-	/* Read the Time Stamp Counter */
-
-	rdtsc(eax,edx);
-
-	/* .. relative to previous jiffy (32 bits is enough) */
-	eax -= last_tsc_low;	/* tsc_low delta */
-
-	/*
-         * Time offset = (tsc_low delta) * fast_gettimeoffset_quotient
-         *             = (tsc_low delta) * (usecs_per_clock)
-         *             = (tsc_low delta) * (usecs_per_jiffy / clocks_per_jiffy)
-	 *
-	 * Using a mull instead of a divl saves up to 31 clock cycles
-	 * in the critical path.
-         */
-
-	edx = (eax*fast_gettimeoffset_quotient) >> 32;
-
-	/* our adjusted time offset in microseconds */
-	return delay_at_last_interrupt + edx;
-}
-
 /*
  * This version of gettimeofday has microsecond resolution
  * and better than microsecond precision on fast x86 machines with TSC.
@@ -390,15 +363,15 @@
 unsigned long get_cmos_time(void)
 {
 	unsigned int year, mon, day, hour, min, sec;
+	int i;
 
+	spin_lock(&rtc_lock); 
 	/* The Linux interpretation of the CMOS clock register contents:
 	 * When the Update-In-Progress (UIP) flag goes from 1 to 0, the
 	 * RTC registers show the second which has precisely just started.
 	 * Let's hope other operating systems interpret the RTC the same way.
 	 */
-#ifndef CONFIG_SIMNOW
-	int i;
-	/* FIXME: This would take eons in emulated environment */
+
 	/* read RTC exactly on falling edge of update flag */
 	for (i = 0 ; i < 1000000 ; i++)	/* may take up to 1 second... */
 		if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)
@@ -406,7 +379,6 @@
 	for (i = 0 ; i < 1000000 ; i++)	/* must try at least 2.228 ms */
 		if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP))
 			break;
-#endif
 	do { /* Isn't this overkill ? UIP above should guarantee consistency */
 		sec = CMOS_READ(RTC_SECONDS);
 		min = CMOS_READ(RTC_MINUTES);
@@ -424,6 +396,7 @@
 	    BCD_TO_BIN(mon);
 	    BCD_TO_BIN(year);
 	  }
+	spin_unlock(&rtc_lock); 
 	if ((year += 1900) < 1970)
 		year += 100;
 	return mktime(year, mon, day, hour, min, sec);
@@ -443,6 +416,7 @@
 #define CALIBRATE_LATCH	(5 * LATCH)
 #define CALIBRATE_TIME	(5 * 1000020/HZ)
 
+/* Could use 64bit arithmetic on x86-64, but the code is too fragile */
 static unsigned long __init calibrate_tsc(void)
 {
        /* Set the Gate high, disable speaker */
@@ -460,40 +434,41 @@
 	outb(CALIBRATE_LATCH >> 8, 0x42);	/* MSB of count */
 
 	{
-		unsigned long start;
-		unsigned long end;
-		unsigned long count;
-
-		{
-			int low, high;
-			rdtsc(low,high);
-			start = ((u64)high)<<32 | low;
-		}
+		unsigned int startlow, starthigh;
+		unsigned int endlow, endhigh;
+		unsigned int count;
+
+		rdtsc(startlow,starthigh);
 		count = 0;
 		do {
 			count++;
 		} while ((inb(0x61) & 0x20) == 0);
+		rdtsc(endlow,endhigh);
 
-		{
-			int low, high;
-			rdtsc(low,high);
-			end = ((u64)high)<<32 | low;
-			last_tsc_low = low;
-		}
-
+		last_tsc_low = endlow; 
 
 		/* Error: ECTCNEVERSET */
 		if (count <= 1)
 			goto bad_ctc;
 
-		end -= start;
+		__asm__("subl %2,%0\n\t"
+			"sbbl %3,%1"
+			:"=a" (endlow), "=d" (endhigh)
+			:"g" (startlow), "g" (starthigh),
+			 "0" (endlow), "1" (endhigh));
 
+		/* Error: ECPUTOOFAST */
+		if (endhigh)
+			goto bad_ctc;
 		/* Error: ECPUTOOSLOW */
-		if (end  <= CALIBRATE_TIME)
+		if (endlow  <= CALIBRATE_TIME)
 			goto bad_ctc;
 
-		end = (((u64)CALIBRATE_TIME)<<32)/end;
-		return end;
+		__asm__("divl %2"
+			:"=a" (endlow), "=d" (endhigh)
+			:"r" (endlow), "0" (0), "1" (CALIBRATE_TIME));
+      	
+		return endlow;
 	}
 
 	/*
@@ -525,7 +500,7 @@
  */
 
 	if (cpu_has_tsc) {
-		unsigned long tsc_quotient = calibrate_tsc();
+		unsigned int tsc_quotient = calibrate_tsc();
 		if (tsc_quotient) {
 			fast_gettimeoffset_quotient = tsc_quotient;
 			use_tsc = 1;
@@ -539,9 +514,13 @@
 			 * The formula is (10^6 * 2^32) / (2^32 * 1 / (clocks/us)) =
 			 * clock/second. Our precision is about 100 ppm.
 			 */
-			{			
-			        cpu_khz = ((1000*(1UL<<32)) / tsc_quotient); 
-				printk("Detected %ld Hz processor.\n", cpu_khz);
+			{	unsigned int eax=0, edx=1000;
+				__asm__("divl %2"
+		       		:"=a" (cpu_khz), "=d" (edx)
+        	       		:"r" (tsc_quotient),
+	                	"0" (eax), "1" (edx));
+				printk("Detected %u.%03u MHz processor.\n", 
+				       cpu_khz / 1000, cpu_khz % 1000);
 			}
 		}
 	}
diff -Nru a/arch/x86_64/kernel/trampoline.S b/arch/x86_64/kernel/trampoline.S
--- a/arch/x86_64/kernel/trampoline.S	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/kernel/trampoline.S	Mon Apr 22 15:32:19 2002
@@ -46,13 +46,17 @@
 	lidt	idt_48 - r_base	# load idt with 0, 0
 	lgdt	gdt_48 - r_base	# load gdt with whatever is appropriate
 
+	movw    $__KERNEL_DS,%ax
+	movw    %ax,%ds
+	movw    %ax,%es
+	
 	xor	%ax, %ax
 	inc	%ax		# protected mode (PE) bit
 	lmsw	%ax		# into protected mode
 	jmp	flush_instr
 flush_instr:
-	ljmpl	$__KERNEL_CS, $0x00100000
-			# jump to startup_32 in arch/i386/kernel/head.S
+	ljmpl	$__KERNEL32_CS, $0x00100000
+			# jump to startup_32 in arch/x86_64/kernel/head.S
 
 idt_48:
 	.word	0			# idt limit = 0
@@ -68,5 +72,5 @@
 					# relocations. 
 	
 
-.globl SYMBOL_NAME(trampoline_end)
-SYMBOL_NAME_LABEL(trampoline_end)
+.globl trampoline_end
+trampoline_end:	
diff -Nru a/arch/x86_64/kernel/traps.c b/arch/x86_64/kernel/traps.c
--- a/arch/x86_64/kernel/traps.c	Mon Apr 22 15:32:18 2002
+++ b/arch/x86_64/kernel/traps.c	Mon Apr 22 15:32:18 2002
@@ -6,10 +6,12 @@
  *
  *  Pentium III FXSR, SSE support
  *	Gareth Hughes <gareth@valinux.com>, May 2000
+ *
+ *  $Id: traps.c,v 1.36 2002/03/24 11:09:10 ak Exp $
  */
 
 /*
- * 'traps.c' handles hardware traps and faults after we have saved some
+ * 'Traps.c' handles hardware traps and faults after we have saved some
  * state in 'entry.S'.
  */
 #include <linux/config.h>
@@ -34,7 +36,6 @@
 #include <asm/desc.h>
 #include <asm/i387.h>
 #include <asm/kdebug.h>
-#include <asm/uaccess.h>
 
 #include <asm/smp.h>
 #include <asm/pgalloc.h>
@@ -51,12 +52,6 @@
 char stackfault_stack[4*1024];
 #endif
 
-/* We really need to get rid of F0 0F workaround. Setting up new idts
-   is not an option */
-
-struct desc_struct default_ldt[] = { { 0, 0 }, { 0, 0 }, { 0, 0 },
-		{ 0, 0 }, { 0, 0 } };
-
 extern struct gate_struct idt_table[256]; 
 
 asmlinkage void divide_error(void);
@@ -78,11 +73,44 @@
 asmlinkage void simd_coprocessor_error(void);
 asmlinkage void reserved(void);
 asmlinkage void alignment_check(void);
+asmlinkage void machine_check(void);
 asmlinkage void spurious_interrupt_bug(void);
+asmlinkage void call_debug(void);
+
+extern char iret_address[];
 
 struct notifier_block *die_chain;
 
-int kstack_depth_to_print = 24;
+int kstack_depth_to_print = 10;
+
+#ifdef CONFIG_KALLSYMS
+#include <linux/kallsyms.h> 
+int printk_address(unsigned long address)
+{ 
+	unsigned long dummy; 
+	const char *modname, *secname, *symname;
+	unsigned long symstart; 
+	char *delim = ":"; 
+
+	/* What a function call! */
+	if (!kallsyms_address_to_symbol(address, 
+					&modname, &dummy, &dummy, 
+					&secname, &dummy, &dummy,
+					&symname, &symstart, &dummy)) {
+		return printk("[<%016lx>]", address);
+	} 
+	if (!strcmp(modname, "kernel"))
+		modname = delim = ""; 		
+        return printk("[%016lx%s%s%s%s%+ld]",
+		      address,delim,modname,delim,symname,address-symstart); 
+} 
+#else
+int printk_address(unsigned long address)
+{ 
+	return printk("[<%016lx>]", address);
+} 
+#endif
+
 
 #ifdef CONFIG_MODULES
 
@@ -138,11 +166,12 @@
 
 	printk("\nCall Trace: ");
 
-	irqstack = (unsigned long *) &(cpu_pda[cpu].irqstack);
-	irqstack_end = (unsigned long *) ((char *)irqstack + sizeof_field(struct x8664_pda, irqstack)); 
+	irqstack_end = (unsigned long *) (cpu_pda[cpu].irqstackptr);
+	irqstack = (unsigned long *) (cpu_pda[cpu].irqstackptr - IRQSTACKSIZE + 64);
 
 	i = 1;
 	if (stack >= irqstack && stack < irqstack_end) {
+		unsigned long *tstack;
 		while (stack < irqstack_end) {
 			addr = *stack++;
 			/*
@@ -154,20 +183,22 @@
 			 * out the call path that was taken.
 			 */
 			 if (kernel_text_address(addr)) {  
-				if (i && ((i % 6) == 0))	
+				 i += printk_address(addr);
+				 i += printk(" "); 
+				 if (i > 50) { 
 					printk("\n       ");
-				printk("[<%016lx>] ", addr);
-				i++;
+					 i = 0;
+				 } 
 			}
 		} 
 		stack = (unsigned long *) (irqstack_end[-1]);
 		printk(" <EOI> ");
 #if 1
-		if (stack < (unsigned long *)current || 
-		    (char*)stack > ((char*)current->thread_info)+THREAD_SIZE) 
+		tstack = (unsigned long *)(current_thread_info()+1);
+		if (stack < tstack || (char*)stack > (char*)tstack+THREAD_SIZE) 
 			printk("\n" KERN_DEBUG 
-			"no stack at the end of irqstack; stack:%p, cur:%p/%p\n",
-			       stack, current, ((char*)current)+THREAD_SIZE); 
+		       "no stack at the end of irqstack; stack:%lx, curstack %lx\n",
+			       stack, tstack); 
 #endif			       
 	} 
 
@@ -175,19 +206,13 @@
 
 	while (((long) stack & (THREAD_SIZE-1)) != 0) {
 		addr = *stack++;
-		/*
-		 * If the address is either in the text segment of the
-		 * kernel, or in the region which contains vmalloc'ed
-		 * memory, it *may* be the address of a calling
-		 * routine; if so, print it so that someone tracing
-		 * down the cause of the crash will be able to figure
-		 * out the call path that was taken.
-		 */
 		if (kernel_text_address(addr)) { 	 
-			if (i && ((i % 6) == 0))	
+			i += printk_address(addr);
+			i += printk(" "); 
+			if (i > 50) { 
 				printk("\n       ");
-			printk("[<%016lx>] ", addr);
-			i++;
+					 i = 0;
+			} 
 		}
 	}
 	printk("\n");
@@ -207,6 +232,9 @@
 {
 	unsigned long *stack;
 	int i;
+	const int cpu = smp_processor_id();
+	unsigned long *irqstack_end = (unsigned long *) (cpu_pda[cpu].irqstackptr);
+	unsigned long *irqstack = (unsigned long *) (cpu_pda[cpu].irqstackptr - IRQSTACKSIZE);    
 
 	// debugging aid: "show_stack(NULL);" prints the
 	// back trace for this cpu.
@@ -216,16 +244,22 @@
 
 	stack = rsp;
 	for(i=0; i < kstack_depth_to_print; i++) {
+		if (stack >= irqstack && stack <= irqstack_end) {
+			if (stack == irqstack_end) {
+				stack = (unsigned long *) (irqstack_end[-1]);
+				printk(" <EOI> ");
+			}
+		} else {
 		if (((long) stack & (THREAD_SIZE-1)) == 0)
 			break;
-		if (i && ((i % 8) == 0))
+		}
+		if (i && ((i % 4) == 0))
 			printk("\n       ");
 		printk("%016lx ", *stack++);
 	}
+	show_trace((unsigned long *)rsp);
 }
 
-extern void dump_pagetable(void); 
-
 void show_registers(struct pt_regs *regs)
 {
 	int i;
@@ -246,10 +280,8 @@
 	}
 	printk("CPU %d ", cpu);
 	show_regs(regs);
-	printk("Process %s (pid: %d, thread_info:%p task:%p)\n",
-		cur->comm, cur->pid, cur->thread_info, cur);
-
-	dump_pagetable();
+	printk("Process %s (pid: %d, stackpage=%08lx)\n",
+		cur->comm, cur->pid, 4096+(unsigned long)cur);
 
 	/*
 	 * When in-kernel, we also print out the stack and code at the
@@ -276,7 +308,6 @@
 		}
 	}
 	printk("\n");
-	show_trace((unsigned long *)rsp);
 }	
 
 void handle_BUG(struct pt_regs *regs)
@@ -284,37 +315,56 @@
 	struct bug_frame f;
 	char tmp;
 
-	if ((regs->cs & 3) || regs->rip < __PAGE_OFFSET)
+	if (regs->cs & 3)
 		return; 
-	if (__copy_from_user(&f, (struct bug_frame *) regs->rip, sizeof(struct bug_frame)))
+	if (__copy_from_user(&f, (struct bug_frame *) regs->rip, 
+			     sizeof(struct bug_frame)))
 		return; 
-	if ((unsigned long)f.filename < __PAGE_OFFSET) 
+	if ((unsigned long)f.filename < __PAGE_OFFSET || 
+	    f.ud2[0] != 0x0f || f.ud2[1] != 0x0b) 
 		return;
 	if (__get_user(tmp, f.filename))
 		f.filename = "unmapped filename"; 
-	printk("Kernel BUG at %.30s:%d\n", f.filename, f.line); 	
+	printk("Kernel BUG at %.50s:%d\n", f.filename, f.line); 	
+} 
+
+void out_of_line_bug(void)
+{ 
+	BUG(); 
 } 
 
 spinlock_t die_lock = SPIN_LOCK_UNLOCKED;
+int die_owner = -1;
 
 void die(const char * str, struct pt_regs * regs, long err)
 {
+	int cpu;
 	struct die_args args = { regs, str, err };
 	console_verbose();
 	notifier_call_chain(&die_chain,  DIE_DIE, &args); 
-	spin_lock_irq(&die_lock);
 	bust_spinlocks(1);
 	handle_BUG(regs); 
 	printk("%s: %04lx\n", str, err & 0xffff);
+	cpu = smp_processor_id(); 
+	/* racy, but better than risking deadlock. */ 
+	__cli();
+	if (!spin_trylock(&die_lock)) { 
+		if (cpu == die_owner) 
+			/* nested oops. should stop eventually */;
+		else
+			spin_lock(&die_lock); 
+	}
+	die_owner = cpu; 
 	show_registers(regs);
 	bust_spinlocks(0);
 	spin_unlock_irq(&die_lock);
+	notify_die(DIE_OOPS, (char *)str, regs, err);
 	do_exit(SIGSEGV);
 }
 
 static inline void die_if_kernel(const char * str, struct pt_regs * regs, long err)
 {
-	if (!(regs->eflags & VM_MASK) && (regs->rip >= TASK_SIZE))
+	if (!(regs->eflags & VM_MASK) && (regs->cs == __KERNEL_CS))
 		die(str, regs, err);
 }
 
@@ -327,21 +377,16 @@
 	return address;
 }
 
-static void inline do_trap(int trapnr, int signr, char *str, int vm86,
+static void do_trap(int trapnr, int signr, char *str, 
 			   struct pt_regs * regs, long error_code, siginfo_t *info)
 {
-	if ((regs->cs & 3) == 0)
-		goto kernel_trap;
-
+	if ((regs->cs & 3)  != 0) { 
+		struct task_struct *tsk = current;
 
-#if 0
-	printk("%d/%s trap %d sig %d %s rip:%lx rsp:%lx error_code:%lx\n",
-	       current->pid, current->comm,
-	       trapnr, signr, str, regs->rip, regs->rsp, error_code);
-#endif
+		if (trapnr != 3)
+			printk("%s[%d] trap %s at rip:%lx rsp:%lx err:%lx\n",
+		       tsk->comm, tsk->pid, str, regs->rip, regs->rsp, error_code);
 
-	{
-		struct task_struct *tsk = current;
 		tsk->thread.error_code = error_code;
 		tsk->thread.trap_no = trapnr;
 		if (info)
@@ -351,11 +396,19 @@
 		return;
 	}
 
-	kernel_trap: {	     
+
+	/* kernel trap */ 
+	{	     
 		unsigned long fixup = search_exception_table(regs->rip);
-		if (fixup)
+		if (fixup) {
+			extern int exception_trace; 
+			if (exception_trace)
+	       printk(KERN_ERR
+	             "%s: fixed kernel exception at %lx err:%ld\n",
+	             current->comm, regs->rip, error_code);
+		
 			regs->rip = fixup;
-		else	
+		} else	
 			die(str, regs, error_code);
 		return;
 	}
@@ -364,7 +417,7 @@
 #define DO_ERROR(trapnr, signr, str, name) \
 asmlinkage void do_##name(struct pt_regs * regs, long error_code) \
 { \
-	do_trap(trapnr, signr, str, 0, regs, error_code, NULL); \
+	do_trap(trapnr, signr, str, regs, error_code, NULL); \
 }
 
 #define DO_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr) \
@@ -375,31 +428,14 @@
 	info.si_errno = 0; \
 	info.si_code = sicode; \
 	info.si_addr = (void *)siaddr; \
-	do_trap(trapnr, signr, str, 0, regs, error_code, &info); \
+	do_trap(trapnr, signr, str, regs, error_code, &info); \
 }
 
-#define DO_VM86_ERROR(trapnr, signr, str, name) \
-asmlinkage void do_##name(struct pt_regs * regs, long error_code) \
-{ \
-	do_trap(trapnr, signr, str, 1, regs, error_code, NULL); \
-}
-
-#define DO_VM86_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr) \
-asmlinkage void do_##name(struct pt_regs * regs, long error_code) \
-{ \
-	siginfo_t info; \
-	info.si_signo = signr; \
-	info.si_errno = 0; \
-	info.si_code = sicode; \
-	info.si_addr = (void *)siaddr; \
-	do_trap(trapnr, signr, str, 1, regs, error_code, &info); \
-}
-
-DO_VM86_ERROR_INFO( 0, SIGFPE,  "divide error", divide_error, FPE_INTDIV, regs->rip)
-DO_VM86_ERROR( 4, SIGSEGV, "overflow", overflow)
-DO_VM86_ERROR( 5, SIGSEGV, "bounds", bounds)
+DO_ERROR_INFO( 0, SIGFPE,  "divide error", divide_error, FPE_INTDIV, regs->rip)
+DO_ERROR( 4, SIGSEGV, "overflow", overflow)
+DO_ERROR( 5, SIGSEGV, "bounds", bounds)
 DO_ERROR_INFO( 6, SIGILL,  "invalid operand", invalid_op, ILL_ILLOPN, regs->rip)
-DO_VM86_ERROR( 7, SIGSEGV, "device not available", device_not_available)
+DO_ERROR( 7, SIGSEGV, "device not available", device_not_available)
 DO_ERROR( 8, SIGSEGV, "double fault", double_fault)
 DO_ERROR( 9, SIGFPE,  "coprocessor segment overrun", coprocessor_segment_overrun)
 DO_ERROR(10, SIGSEGV, "invalid TSS", invalid_TSS)
@@ -410,22 +446,23 @@
 
 asmlinkage void do_int3(struct pt_regs * regs, long error_code)
 {
-	struct die_args args = { regs, "int3", error_code }; 
-	notifier_call_chain(&die_chain, DIE_INT3, &args); 
-	do_trap(3, SIGTRAP, "int3", 1, regs, error_code, NULL);
+	if (notify_die(DIE_INT3, "int3", regs, error_code) == NOTIFY_BAD)
+		return;
+	do_trap(3, SIGTRAP, "int3", regs, error_code, NULL);
 }
 
+extern void dump_pagetable(unsigned long);
+
 asmlinkage void do_general_protection(struct pt_regs * regs, long error_code)
 {
-	if ((regs->cs & 3)==0)
-		goto gp_in_kernel;
-
+	if ((regs->cs & 3)!=0) { 
 	current->thread.error_code = error_code;
 	current->thread.trap_no = 13;
 	force_sig(SIGSEGV, current);
 	return;
+	} 
 
-gp_in_kernel:
+	/* kernel gp */
 	{
 		unsigned long fixup;
 		fixup = search_exception_table(regs->rip);
@@ -433,6 +470,7 @@
 			regs->rip = fixup;
 			return;
 		}
+//		dump_pagetable(regs->rip); 
 		die("general protection fault", regs, error_code);
 	}
 }
@@ -487,6 +525,8 @@
 		unknown_nmi_error(reason, regs);
 		return;
 	}
+	if (notify_die(DIE_NMI, "nmi", regs, reason) == NOTIFY_BAD)
+		return; 
 	if (reason & 0x80)
 		mem_parity_error(reason, regs);
 	if (reason & 0x40)
@@ -509,6 +549,9 @@
 
 	asm("movq %%db6,%0" : "=r" (condition));
 
+	if (notify_die(DIE_DEBUG, "debug", regs, error_code) == NOTIFY_BAD)
+		return; 
+
 	/* Mask out spurious debug traps due to lazy DR7 setting */
 	if (condition & (DR_TRAP0|DR_TRAP1|DR_TRAP2|DR_TRAP3)) {
 		if (!tsk->thread.debugreg[7]) { 
@@ -529,12 +572,13 @@
 		 * allowing programs to debug themselves without the ptrace()
 		 * interface.
 		 */
+                if ((regs->cs & 3) == 0)
+                       goto clear_TF;
 		if ((tsk->ptrace & (PT_DTRACE|PT_PTRACED)) == PT_DTRACE)
 			goto clear_TF;
 	}
 
 	/* Ok, finally something we can handle */
-	/* XXX: add die_chain here */
 	tsk->thread.trap_no = 1;
 	tsk->thread.error_code = error_code;
 	info.si_signo = SIGTRAP;
@@ -557,7 +601,7 @@
  * the correct behaviour even in the presence of the asynchronous
  * IRQ13 behaviour
  */
-void math_error(void *eip)
+void math_error(void *rip)
 {
 	struct task_struct * task;
 	siginfo_t info;
@@ -573,7 +617,7 @@
 	info.si_signo = SIGFPE;
 	info.si_errno = 0;
 	info.si_code = __SI_FAULT;
-	info.si_addr = eip;
+	info.si_addr = rip;
 	/*
 	 * (~cwd & swd) will mask out exceptions that are not set to unmasked
 	 * status.  0x3f is the exception bits in these regs, 0x200 is the
@@ -614,7 +658,6 @@
 
 asmlinkage void do_coprocessor_error(struct pt_regs * regs, long error_code)
 {
-	ignore_irq13 = 1;
 	math_error((void *)regs->rip);
 }
 
@@ -623,7 +666,7 @@
 	printk("bad interrupt"); 
 }
 
-void simd_math_error(void *eip)
+static inline void simd_math_error(void *rip)
 {
 	struct task_struct * task;
 	siginfo_t info;
@@ -639,7 +682,7 @@
 	info.si_signo = SIGFPE;
 	info.si_errno = 0;
 	info.si_code = __SI_FAULT;
-	info.si_addr = eip;
+	info.si_addr = rip;
 	/*
 	 * The SIMD FPU exceptions are handled a little differently, as there
 	 * is only a single status/control register.  Thus, to determine which
@@ -674,29 +717,11 @@
 asmlinkage void do_simd_coprocessor_error(struct pt_regs * regs,
 					  long error_code)
 {
-	if (cpu_has_xmm) {
-		/* Handle SIMD FPU exceptions on PIII+ processors. */
-		ignore_irq13 = 1;
 		simd_math_error((void *)regs->rip);
-	} else {
-		/*
-		 * Handle strange cache flush from user space exception
-		 * in all other cases.  This is undocumented behaviour.
-		 */
-		die_if_kernel("cache flush denied", regs, error_code);
-		current->thread.trap_no = 19;
-		current->thread.error_code = error_code;
-		force_sig(SIGSEGV, current);
-	}
 }
 
-asmlinkage void do_spurious_interrupt_bug(struct pt_regs * regs,
-					  long error_code)
+asmlinkage void do_spurious_interrupt_bug(struct pt_regs * regs)
 {
-#if 0
-	/* No need to warn about this any longer. */
-	printk("Ignoring P6 Local APIC Spurious Interrupt Bug...\n");
-#endif
 }
 
 /*
@@ -705,15 +730,14 @@
  *
  * Careful.. There are problems with IBM-designed IRQ13 behaviour.
  * Don't touch unless you *really* know how it works.
- *
- * Must be called with kernel preemption disabled.
  */
 asmlinkage void math_state_restore(void)
 {
+	struct task_struct *me = current;
 	clts();			/* Allow maths ops (or we recurse) */
 
-	if (current->used_math) {
-		restore_fpu(current);
+	if (me->used_math) {
+		restore_fpu_checking(&me->thread.i387.fxsave);
 	} else {
 		init_fpu();
 	}
@@ -722,23 +746,25 @@
 
 asmlinkage void math_emulate(void)
 {
-	printk("math-emulation not enabled and no coprocessor found.\n");
-	printk("killing %s.\n",current->comm);
-	force_sig(SIGFPE,current);
-	schedule();
+	BUG();
+}
+
+void do_call_debug(struct pt_regs *regs) 
+{ 
+	notify_die(DIE_CALL, "debug call", regs, 0); 
 }
 
 void __init trap_init(void)
 {
 	set_intr_gate(0,&divide_error);
 	set_intr_gate(1,&debug);
-	set_intr_gate(2,&nmi);
+	set_intr_gate_ist(2,&nmi,NMI_STACK);
 	set_system_gate(3,&int3);	/* int3-5 can be called from all */
 	set_system_gate(4,&overflow);
 	set_system_gate(5,&bounds);
 	set_intr_gate(6,&invalid_op);
 	set_intr_gate(7,&device_not_available);
-	set_intr_gate_ist(8,&double_fault, 1);
+	set_intr_gate_ist(8,&double_fault, DOUBLEFAULT_STACK);
 	set_intr_gate(9,&coprocessor_segment_overrun);
 	set_intr_gate(10,&invalid_TSS);
 	set_intr_gate(11,&segment_not_present);
@@ -748,24 +774,19 @@
 	set_intr_gate(15,&spurious_interrupt_bug);
 	set_intr_gate(16,&coprocessor_error);
 	set_intr_gate(17,&alignment_check);
+	set_intr_gate(18,&machine_check); 
 	set_intr_gate(19,&simd_coprocessor_error);
 
 #ifdef CONFIG_IA32_EMULATION
 	set_intr_gate(IA32_SYSCALL_VECTOR, ia32_syscall);
 #endif
        
-#if 0
-	/*
-	 * default LDT is a single-entry callgate to lcall7 for iBCS
-	 * and a callgate to lcall27 for Solaris/x86 binaries
-	 */
-	set_call_gate(&default_ldt[0],lcall7);
-	set_call_gate(&default_ldt[4],lcall27);
-#endif
+	set_intr_gate(KDB_VECTOR, call_debug);
+       
+	notify_die(DIE_TRAPINIT, "traps initialized", 0, 0); 
 	
 	/*
 	 * Should be a barrier for any external CPU state.
 	 */
 	cpu_init();
 }
-
diff -Nru a/arch/x86_64/kernel/vsyscall.c b/arch/x86_64/kernel/vsyscall.c
--- a/arch/x86_64/kernel/vsyscall.c	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/kernel/vsyscall.c	Mon Apr 22 15:32:19 2002
@@ -12,7 +12,7 @@
  *  vsyscalls. One vsyscall can reserve more than 1 slot to avoid
  *  jumping out of line if necessary.
  *
- *  $Id: vsyscall.c,v 1.4 2001/09/27 17:58:13 ak Exp $
+ *  $Id: vsyscall.c,v 1.9 2002/03/21 13:42:58 ak Exp $
  */
 
 /*
@@ -44,8 +44,19 @@
 #include <asm/fixmap.h>
 #include <asm/errno.h>
 
+
 #define __vsyscall(nr) __attribute__ ((unused,__section__(".vsyscall_" #nr)))
 
+//#define NO_VSYSCALL 1
+
+#ifdef NO_VSYSCALL
+#include <asm/unistd.h>
+
+static int errno __section_vxtime_sequence; 
+
+__syscall2(static inline int,int,gettimeofday,struct timeval *,tv,struct timezone *,tz)
+
+#else
 static inline void timeval_normalize(struct timeval * tv)
 {
 	time_t __sec;
@@ -60,7 +71,7 @@
 
 long __vxtime_sequence[2] __section_vxtime_sequence;
 
-inline void do_vgettimeofday(struct timeval * tv)
+static inline void do_vgettimeofday(struct timeval * tv)
 {
 	long sequence;
 	unsigned long usec, sec;
@@ -120,18 +131,29 @@
 		rmb();
 	} while (sequence != __vxtime_sequence[0]);
 }
+#endif
 
 static int __vsyscall(0) vgettimeofday(struct timeval * tv, struct timezone * tz)
 {
+#ifdef NO_VSYSCALL
+	return gettimeofday(tv,tz); 
+#else
 	if (tv)
 		do_vgettimeofday(tv);
 	if (tz)
 		do_get_tz(tz);
 	return 0;
+#endif
 }
 
-static time_t __vsyscall(1) vtime(time_t * time)
+static time_t __vsyscall(1) vtime(time_t * t)
 {
+#ifdef NO_VSYSCALL
+	struct timeval tv; 
+	gettimeofday(&tv,NULL); 
+	if (t) *t = tv.tv_sec; 
+	return tv.tv_sec; 
+#else
 	long sequence;
 	time_t __time;
 
@@ -144,9 +166,10 @@
 		rmb();
 	} while (sequence != __vxtime_sequence[0]);
 
-	if (time)
-		*time = __time;
+	if (t)
+		*t = __time;
 	return __time;
+#endif
 }
 
 static long __vsyscall(2) venosys_0(void)
diff -Nru a/arch/x86_64/kernel/x8664_ksyms.c b/arch/x86_64/kernel/x8664_ksyms.c
--- a/arch/x86_64/kernel/x8664_ksyms.c	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/kernel/x8664_ksyms.c	Mon Apr 22 15:32:19 2002
@@ -2,7 +2,6 @@
 #include <linux/module.h>
 #include <linux/smp.h>
 #include <linux/user.h>
-#include <linux/elfcore.h>
 #include <linux/mca.h>
 #include <linux/sched.h>
 #include <linux/in6.h>
@@ -28,18 +27,13 @@
 #include <asm/desc.h>
 #include <asm/pgtable.h>
 #include <asm/pgalloc.h>
+#include <asm/kdebug.h>
 
-extern void dump_thread(struct pt_regs *, struct user *);
 extern spinlock_t rtc_lock;
 
-#if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE)
-extern void machine_real_restart(unsigned char *, int);
-EXPORT_SYMBOL(machine_real_restart);
-#endif
-
 #ifdef CONFIG_SMP
-extern void FASTCALL( __write_lock_failed(rwlock_t *rw));
-extern void FASTCALL( __read_lock_failed(rwlock_t *rw));
+extern void __write_lock_failed(rwlock_t *rw);
+extern void __read_lock_failed(rwlock_t *rw);
 #endif
 
 #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_HD) || defined(CONFIG_BLK_DEV_IDE_MODULE) || defined(CONFIG_BLK_DEV_HD_MODULE)
@@ -51,7 +45,6 @@
 
 /* platform dependent support */
 EXPORT_SYMBOL(boot_cpu_data);
-EXPORT_SYMBOL(dump_thread);
 EXPORT_SYMBOL(dump_fpu);
 EXPORT_SYMBOL(__ioremap);
 EXPORT_SYMBOL(iounmap);
@@ -63,7 +56,6 @@
 EXPORT_SYMBOL(pm_idle);
 EXPORT_SYMBOL(pm_power_off);
 EXPORT_SYMBOL(get_cmos_time);
-EXPORT_SYMBOL(apm_info);
 
 #ifdef CONFIG_IO_DEBUG
 EXPORT_SYMBOL(__io_virt_debug);
@@ -83,9 +75,11 @@
 EXPORT_SYMBOL_NOVERS(__get_user_1);
 EXPORT_SYMBOL_NOVERS(__get_user_2);
 EXPORT_SYMBOL_NOVERS(__get_user_4);
+EXPORT_SYMBOL_NOVERS(__get_user_8);
 EXPORT_SYMBOL_NOVERS(__put_user_1);
 EXPORT_SYMBOL_NOVERS(__put_user_2);
 EXPORT_SYMBOL_NOVERS(__put_user_4);
+EXPORT_SYMBOL_NOVERS(__put_user_8);
 
 EXPORT_SYMBOL(strpbrk);
 EXPORT_SYMBOL(strstr);
@@ -129,8 +123,6 @@
 EXPORT_SYMBOL(__global_restore_flags);
 EXPORT_SYMBOL(smp_call_function);
 
-/* TLB flushing */
-EXPORT_SYMBOL(flush_tlb_page);
 #endif
 
 #ifdef CONFIG_MCA
@@ -145,12 +137,43 @@
 
 EXPORT_SYMBOL(rtc_lock);
 
+/* Export string functions. We normally rely on gcc builtin for most of these,
+   but gcc sometimes decides not to inline them. */    
 #undef memcpy
 #undef memset
+#undef memmove
+#undef memchr
+#undef strlen
+#undef strcpy
+#undef strncmp
+#undef strncpy
+#undef strchr	
+#undef strcmp 
+#undef bcopy
+#undef strcpy 
+
 extern void * memset(void *,int,__kernel_size_t);
-extern void * memcpy(void *,const void *,__kernel_size_t);
-EXPORT_SYMBOL_NOVERS(memcpy);
+extern size_t strlen(const char *);
+extern char * bcopy(const char * src, char * dest, int count);
+extern void * memmove(void * dest,const void *src,size_t count);
+extern char * strcpy(char * dest,const char *src);
+extern int strcmp(const char * cs,const char * ct);
+extern void *memchr(const void *s, int c, size_t n);
 EXPORT_SYMBOL_NOVERS(memset);
+EXPORT_SYMBOL_NOVERS(strlen);
+EXPORT_SYMBOL_NOVERS(memmove);
+EXPORT_SYMBOL_NOVERS(strcpy);
+EXPORT_SYMBOL_NOVERS(strncmp);
+EXPORT_SYMBOL_NOVERS(strncpy);
+EXPORT_SYMBOL_NOVERS(strchr);
+EXPORT_SYMBOL_NOVERS(strcmp); 
+EXPORT_SYMBOL_NOVERS(strcat);
+EXPORT_SYMBOL_NOVERS(strncat);
+EXPORT_SYMBOL_NOVERS(memchr);
+EXPORT_SYMBOL_NOVERS(strrchr);
+EXPORT_SYMBOL_NOVERS(strnlen);
+EXPORT_SYMBOL_NOVERS(memscan);
+EXPORT_SYMBOL_NOVERS(bcopy);
 
 EXPORT_SYMBOL(empty_zero_page);
 
@@ -158,3 +181,10 @@
 EXPORT_SYMBOL(atomic_dec_and_lock);
 #endif
 
+EXPORT_SYMBOL(die_chain);
+
+extern void do_softirq_thunk(void);
+EXPORT_SYMBOL_NOVERS(do_softirq_thunk);
+
+void out_of_line_bug(void);
+EXPORT_SYMBOL(out_of_line_bug);
diff -Nru a/arch/x86_64/lib/Makefile b/arch/x86_64/lib/Makefile
--- a/arch/x86_64/lib/Makefile	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/lib/Makefile	Mon Apr 22 15:32:19 2002
@@ -8,10 +8,9 @@
 L_TARGET = lib.a
 obj-y  = generic-checksum.o old-checksum.o delay.o \
 	usercopy.o getuser.o putuser.o  \
-	checksum_copy.o rwsem_thunk.o
+	checksum_copy.o thunk.o mmx.o
 
 obj-$(CONFIG_IO_DEBUG) += iodebug.o
-obj-$(CONFIG_X86_USE_3DNOW) += mmx.o
 obj-$(CONFIG_HAVE_DEC_LOCK) += dec_and_lock.o
 
 include $(TOPDIR)/Rules.make
diff -Nru a/arch/x86_64/lib/mmx.c b/arch/x86_64/lib/mmx.c
--- a/arch/x86_64/lib/mmx.c	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/lib/mmx.c	Mon Apr 22 15:32:19 2002
@@ -1,11 +1,11 @@
-#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/string.h>
 #include <linux/sched.h>
+#include <linux/compiler.h>
 
 #include <asm/i387.h>
 #include <asm/hardirq.h> 
-
+#include <asm/page.h>
 
 /*
  *	MMX 3DNow! library helper functions
@@ -24,46 +24,50 @@
  *	22/09/2000 - Arjan van de Ven 
  *		Improved for non-egineering-sample Athlons 
  *
+ *	2002 Andi Kleen. Some cleanups and changes for x86-64. 
+ *	Not really tuned yet. Using the Athlon version for now.
+ *      This currenly uses MMX for 8 byte stores, but on hammer we could
+ *      use integer 8 byte stores too and avoid the FPU save overhead.
+ *	Disadvantage is that the integer load/stores have strong ordering
+ *	model and may be slower.
+ * 
+ *	$Id$
  */
 
-#error Don't use these for now, but we'll have to provide optimized functions in future
+#ifdef MMX_MEMCPY_THRESH
+
  
 void *_mmx_memcpy(void *to, const void *from, size_t len)
 {
  	void *p;
  	int i;
   
- 	if (in_interrupt())
- 		return __memcpy(to, from, len);
- 
  	p = to;
+
+ 	if (unlikely(in_interrupt()))
+		goto standard;
+	
+	/* XXX: check if this is still memory bound with unaligned to/from.
+	   if not align them here to 8bytes. */
  	i = len >> 6; /* len/64 */
  
  	kernel_fpu_begin();
 
 	__asm__ __volatile__ (
-		"1: prefetch (%0)\n"		/* This set is 28 bytes */
+		"   prefetch (%0)\n"		/* This set is 28 bytes */
 		"   prefetch 64(%0)\n"
 		"   prefetch 128(%0)\n"
 		"   prefetch 192(%0)\n"
 		"   prefetch 256(%0)\n"
-		"2:  \n"
-		".section .fixup, \"ax\"\n"
-		"3: movw $0x1AEB, 1b\n"	/* jmp on 26 bytes */
-		"   jmp 2b\n"
-		".previous\n"
-		".section __ex_table,\"a\"\n"
-		"	.align 4\n"
-		"	.long 1b, 3b\n"
-		".previous"
+		"\n"
 		: : "r" (from) );
 		
 	
 	for(; i>0; i--)
 	{
 		__asm__ __volatile__ (
-		"1:  prefetch 320(%0)\n"
-		"2:  movq (%0), %%mm0\n"
+		"  prefetch 320(%0)\n"
+		"  movq (%0), %%mm0\n"
 		"  movq 8(%0), %%mm1\n"
 		"  movq 16(%0), %%mm2\n"
 		"  movq 24(%0), %%mm3\n"
@@ -79,34 +83,24 @@
 		"  movq %%mm1, 40(%1)\n"
 		"  movq %%mm2, 48(%1)\n"
 		"  movq %%mm3, 56(%1)\n"
-		".section .fixup, \"ax\"\n"
-		"3: movw $0x05EB, 1b\n"	/* jmp on 5 bytes */
-		"   jmp 2b\n"
-		".previous\n"
-		".section __ex_table,\"a\"\n"
-		"	.align 4\n"
-		"	.long 1b, 3b\n"
-		".previous"
 		: : "r" (from), "r" (to) : "memory");
 		from+=64;
 		to+=64;
 	}
+	len &= 63; 	
+	kernel_fpu_end();
+
 	/*
 	 *	Now do the tail of the block
 	 */
-	__memcpy(to, from, len&63);
-	kernel_fpu_end();
+
+ standard:
+	__inline_memcpy(to, from, len);
 	return p;
 }
+#endif
 
-#ifdef CONFIG_MK7
-
-/*
- *	The K7 has streaming cache bypass load/store. The Cyrix III, K6 and
- *	other MMX using processors do not.
- */
-
-static void fast_clear_page(void *page)
+static inline void fast_clear_page(void *page)
 {
 	int i;
 
@@ -139,7 +133,7 @@
 	kernel_fpu_end();
 }
 
-static void fast_copy_page(void *to, void *from)
+static inline void fast_copy_page(void *to, void *from)
 {
 	int i;
 
@@ -149,27 +143,18 @@
 	 * but that is for later. -AV
 	 */
 	__asm__ __volatile__ (
-		"1: prefetch (%0)\n"
+		"   prefetch (%0)\n"
 		"   prefetch 64(%0)\n"
 		"   prefetch 128(%0)\n"
 		"   prefetch 192(%0)\n"
 		"   prefetch 256(%0)\n"
-		"2:  \n"
-		".section .fixup, \"ax\"\n"
-		"3: movw $0x1AEB, 1b\n"	/* jmp on 26 bytes */
-		"   jmp 2b\n"
-		".previous\n"
-		".section __ex_table,\"a\"\n"
-		"	.align 4\n"
-		"	.long 1b, 3b\n"
-		".previous"
 		: : "r" (from) );
 
 	for(i=0; i<(4096-320)/64; i++)
 	{
 		__asm__ __volatile__ (
-		"1: prefetch 320(%0)\n"
-		"2: movq (%0), %%mm0\n"
+		"   prefetch 320(%0)\n"
+		"   movq (%0), %%mm0\n"
 		"   movntq %%mm0, (%1)\n"
 		"   movq 8(%0), %%mm1\n"
 		"   movntq %%mm1, 8(%1)\n"
@@ -185,14 +170,6 @@
 		"   movntq %%mm6, 48(%1)\n"
 		"   movq 56(%0), %%mm7\n"
 		"   movntq %%mm7, 56(%1)\n"
-		".section .fixup, \"ax\"\n"
-		"3: movw $0x05EB, 1b\n"	/* jmp on 5 bytes */
-		"   jmp 2b\n"
-		".previous\n"
-		".section __ex_table,\"a\"\n"
-		"	.align 4\n"
-		"	.long 1b, 3b\n"
-		".previous"
 		: : "r" (from), "r" (to) : "memory");
 		from+=64;
 		to+=64;
@@ -229,149 +206,28 @@
 	kernel_fpu_end();
 }
 
-#else
-
-/*
- *	Generic MMX implementation without K7 specific streaming
- */
- 
-static void fast_clear_page(void *page)
-{
-	int i;
-	
-	kernel_fpu_begin();
-	
-	__asm__ __volatile__ (
-		"  pxor %%mm0, %%mm0\n" : :
-	);
-
-	for(i=0;i<4096/128;i++)
-	{
-		__asm__ __volatile__ (
-		"  movq %%mm0, (%0)\n"
-		"  movq %%mm0, 8(%0)\n"
-		"  movq %%mm0, 16(%0)\n"
-		"  movq %%mm0, 24(%0)\n"
-		"  movq %%mm0, 32(%0)\n"
-		"  movq %%mm0, 40(%0)\n"
-		"  movq %%mm0, 48(%0)\n"
-		"  movq %%mm0, 56(%0)\n"
-		"  movq %%mm0, 64(%0)\n"
-		"  movq %%mm0, 72(%0)\n"
-		"  movq %%mm0, 80(%0)\n"
-		"  movq %%mm0, 88(%0)\n"
-		"  movq %%mm0, 96(%0)\n"
-		"  movq %%mm0, 104(%0)\n"
-		"  movq %%mm0, 112(%0)\n"
-		"  movq %%mm0, 120(%0)\n"
-		: : "r" (page) : "memory");
-		page+=128;
-	}
-
-	kernel_fpu_end();
-}
-
-static void fast_copy_page(void *to, void *from)
-{
-	int i;
-	
-	
-	kernel_fpu_begin();
-
-	__asm__ __volatile__ (
-		"1: prefetch (%0)\n"
-		"   prefetch 64(%0)\n"
-		"   prefetch 128(%0)\n"
-		"   prefetch 192(%0)\n"
-		"   prefetch 256(%0)\n"
-		"2:  \n"
-		".section .fixup, \"ax\"\n"
-		"3: movw $0x1AEB, 1b\n"	/* jmp on 26 bytes */
-		"   jmp 2b\n"
-		".previous\n"
-		".section __ex_table,\"a\"\n"
-		"	.align 4\n"
-		"	.long 1b, 3b\n"
-		".previous"
-		: : "r" (from) );
-
-	for(i=0; i<4096/64; i++)
-	{
-		__asm__ __volatile__ (
-		"1: prefetch 320(%0)\n"
-		"2: movq (%0), %%mm0\n"
-		"   movq 8(%0), %%mm1\n"
-		"   movq 16(%0), %%mm2\n"
-		"   movq 24(%0), %%mm3\n"
-		"   movq %%mm0, (%1)\n"
-		"   movq %%mm1, 8(%1)\n"
-		"   movq %%mm2, 16(%1)\n"
-		"   movq %%mm3, 24(%1)\n"
-		"   movq 32(%0), %%mm0\n"
-		"   movq 40(%0), %%mm1\n"
-		"   movq 48(%0), %%mm2\n"
-		"   movq 56(%0), %%mm3\n"
-		"   movq %%mm0, 32(%1)\n"
-		"   movq %%mm1, 40(%1)\n"
-		"   movq %%mm2, 48(%1)\n"
-		"   movq %%mm3, 56(%1)\n"
-		".section .fixup, \"ax\"\n"
-		"3: movw $0x05EB, 1b\n"	/* jmp on 5 bytes */
-		"   jmp 2b\n"
-		".previous\n"
-		".section __ex_table,\"a\"\n"
-		"	.align 4\n"
-		"	.long 1b, 3b\n"
-		".previous"
-		: : "r" (from), "r" (to) : "memory");
-		from+=64;
-		to+=64;
-	}
-	kernel_fpu_end();
-}
-
-
-#endif
-
-/*
- *	Favour MMX for page clear and copy. 
- */
-
-static void slow_zero_page(void * page)
-{
-	int d0, d1;
-	__asm__ __volatile__( \
-		"cld\n\t" \
-		"rep ; stosl" \
-		: "=&c" (d0), "=&D" (d1)
-		:"a" (0),"1" (page),"0" (1024)
-		:"memory");
-}
- 
 void mmx_clear_page(void * page)
 {
-	if(in_interrupt())
-		slow_zero_page(page);
+#if 1 
+	__builtin_memset(page,0,PAGE_SIZE);
+#else
+	/* AK: these in_interrupt checks should not be needed. */
+	if(unlikely(in_interrupt()))
+		__builtin_memset(page,0,PAGE_SIZE);
 	else
 		fast_clear_page(page);
+#endif
 }
 
-static void slow_copy_page(void *to, void *from)
-{
-	int d0, d1, d2;
-	__asm__ __volatile__( \
-		"cld\n\t" \
-		"rep ; movsl" \
-		: "=&c" (d0), "=&D" (d1), "=&S" (d2) \
-		: "0" (1024),"1" ((long) to),"2" ((long) from) \
-		: "memory");
-}
-  
-
 void mmx_copy_page(void *to, void *from)
 {
-	if(in_interrupt())
-		slow_copy_page(to, from);
+#if 1
+	__builtin_memcpy(to,from,PAGE_SIZE);
+#else
+	/* AK: these in_interrupt checks should not be needed. */
+	if(unlikely(in_interrupt()))
+		__builtin_memcpy(to,from,PAGE_SIZE);
 	else
 		fast_copy_page(to, from);
+#endif
 }
diff -Nru a/arch/x86_64/lib/thunk.S b/arch/x86_64/lib/thunk.S
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/arch/x86_64/lib/thunk.S	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,81 @@
+	/*
+	 * Save registers before calling assembly functions. This avoids
+	 * disturbance of register allocation in some inline assembly constructs.
+	 * Copyright 2001,2002 by Andi Kleen, SuSE Labs.
+	 * Subject to the GNU public license, v.2. No warranty of any kind.
+	 * $Id: thunk.S,v 1.2 2002/03/13 20:06:58 ak Exp $
+	 */
+
+	#include <linux/config.h>
+	#include <linux/linkage.h>
+	#include <asm/calling.h>			
+	#include <asm/rwlock.h>
+		
+	/* rdi:	arg1 ... normal C conventions. rax is saved/restored. */ 	
+	.macro thunk name,func
+	.globl \name
+\name:	
+	SAVE_ARGS
+	call \func
+	jmp  restore
+	.endm
+
+	/* rdi:	arg1 ... normal C conventions. rax is passed from C. */ 	
+	.macro thunk_retrax name,func
+	.globl \name
+\name:	
+	SAVE_ARGS
+	call \func
+	jmp  restore_norax
+	.endm
+	
+
+#ifdef CONFIG_RWSEM_XCHGADD_ALGORITHM
+	thunk rwsem_down_read_failed_thunk,rwsem_down_read_failed
+	thunk rwsem_down_write_failed_thunk,rwsem_down_write_failed
+	thunk rwsem_wake_thunk,rwsem_wake
+#endif	
+	thunk do_softirq_thunk,do_softirq
+	
+	thunk __down_failed,__down
+	thunk_retrax __down_failed_interruptible,__down_interruptible
+	thunk_retrax __down_failed_trylock,__down_trylock
+	thunk __up_wakeup,__up
+	
+restore:
+	RESTORE_ARGS
+	ret	
+	
+restore_norax:	
+	RESTORE_ARGS 1
+	ret
+
+#ifdef CONFIG_SMP
+/* Support for read/write spinlocks. */
+	
+/* rax:	pointer to rwlock_t */	
+ENTRY(__write_lock_failed)
+	lock
+	addl $RW_LOCK_BIAS,(%rax)
+1:	rep
+	nop
+	cmpl $RW_LOCK_BIAS,(%rax)
+	jne 1b
+	lock 
+	subl $RW_LOCK_BIAS,(%rax)
+	jnz  __write_lock_failed
+	ret
+
+/* rax:	pointer to rwlock_t */	
+ENTRY(__read_lock_failed)
+	lock
+	incl (%rax)
+1:	rep
+	nop
+	cmpl $1,(%rax)
+	js 1b
+	lock
+	decl (%rax)
+	js __read_lock_failed
+	ret
+#endif
diff -Nru a/arch/x86_64/mm/Makefile b/arch/x86_64/mm/Makefile
--- a/arch/x86_64/mm/Makefile	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/mm/Makefile	Mon Apr 22 15:32:19 2002
@@ -8,6 +8,6 @@
 # Note 2! The CFLAGS definition is now in the main makefile...
 
 O_TARGET := mm.o
-obj-y	 := init.o fault.o ioremap.o extable.o
+obj-y	 := init.o fault.o ioremap.o extable.o modutil.o
 
 include $(TOPDIR)/Rules.make
diff -Nru a/arch/x86_64/mm/fault.c b/arch/x86_64/mm/fault.c
--- a/arch/x86_64/mm/fault.c	Mon Apr 22 15:32:18 2002
+++ b/arch/x86_64/mm/fault.c	Mon Apr 22 15:32:18 2002
@@ -2,8 +2,10 @@
  *  linux/arch/x86-64/mm/fault.c
  *
  *  Copyright (C) 1995  Linus Torvalds
+ *  Copyright (C) 2001,2002 Andi Kleen, SuSE Labs.
  */
 
+#include <linux/config.h>
 #include <linux/signal.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
@@ -19,17 +21,17 @@
 #include <linux/init.h>
 #include <linux/tty.h>
 #include <linux/vt_kern.h>		/* For unblank_screen() */
+#include <linux/compiler.h>
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
 #include <asm/pgalloc.h>
 #include <asm/hardirq.h>
+#include <asm/smp.h>
+#include <asm/tlbflush.h>
 
 extern void die(const char *,struct pt_regs *,long);
 
-asmlinkage void do_invalid_op(struct pt_regs *, unsigned long);
-extern unsigned long idt;
-
 extern spinlock_t console_lock, timerlist_lock;
 
 void bust_spinlocks(int yes)
@@ -57,33 +59,34 @@
 	}
 }
 
-void do_BUG(const char *file, int line)
-{
-	bust_spinlocks(1);
-	printk("kernel BUG at %s:%d!\n", file, line);
-}
-
-
 void dump_pagetable(unsigned long address)
 {
 	static char *name[] = { "PML4", "PGD", "PDE", "PTE" }; 
 	int i, shift;
 	unsigned long page;
 
-	asm("movq %%cr3,%0":"=r" (page));
 	shift = 9+9+9+12;
 	address &= ~0xFFFF000000000000UL;
+	asm("movq %%cr3,%0" : "=r" (page)); 
 	for (i = 0; i < 4; i++) { 
-		page = ((unsigned long *) __va(page))[(address >> shift) & 0x1FFU];   
+		unsigned long *padr = (unsigned long *) __va(page); 
+		padr += (address >> shift) & 0x1FFU;
+		if (__get_user(page, padr)) { 
+			printk("%s: bad %p\n", name[i], padr); 
+			break;
+		}
 		printk("%s: %016lx ", name[i], page); 
 		if ((page & (1 | (1<<7))) != 1) /* Not present or 2MB page */
 			break;
 		page &= ~0xFFFUL;
-		shift -= 9;
+		shift -= (i == 0) ? 12 : 9;
 	} 
 	printk("\n");
 }
 
+int page_fault_trace; 
+int exception_trace = 1;
+
 /*
  * This routine handles page faults.  It determines the address,
  * and the problem, and then passes it off to one of the appropriate
@@ -93,6 +96,7 @@
  *	bit 0 == 0 means no page found, 1 means protection fault
  *	bit 1 == 0 means read, 1 means write
  *	bit 2 == 0 means kernel, 1 means user-mode
+ *      bit 3 == 1 means fault was an instruction fetch
  */
 asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code)
 {
@@ -107,15 +111,20 @@
 	/* get the address */
 	__asm__("movq %%cr2,%0":"=r" (address));
 
+	if (page_fault_trace) 
+		printk("pagefault rip:%lx rsp:%lx cs:%lu ss:%lu address %lx error %lx\n",
+		       regs->rip,regs->rsp,regs->cs,regs->ss,address,error_code); 
 
 	tsk = current;
 	mm = tsk->mm;
 	info.si_code = SEGV_MAPERR;
 
-	if (address >= TASK_SIZE && !(error_code & 5))
+	/* 5 => page not present and from supervisor mode */
+	if (unlikely(!(error_code & 5) &&
+		     ((address >= VMALLOC_START && address <= VMALLOC_END) ||
+		      (address >= MODULES_VADDR && address <= MODULES_END))))
 		goto vmalloc_fault;
 
-
 	/*
 	 * If we're in an interrupt or have no user
 	 * context, we must not take the fault..
@@ -123,19 +132,10 @@
 	if (in_interrupt() || !mm)
 		goto no_context;
 
+ again:
 	down_read(&mm->mmap_sem);
 
 	vma = find_vma(mm, address);
-
-#if 0
-	printk("fault at %lx rip:%lx rsp:%lx err:%lx thr:%x ", address,regs->rip,regs->rsp,error_code,tsk->thread.flags);
-	if (vma) 
-		printk("vma %lx-%lx prot:%lx flags:%lx",vma->vm_start,vma->vm_end,
-		       vma->vm_page_prot,vma->vm_flags); 
-	printk("\n");
-#endif
-
-
 	if (!vma)
 		goto bad_area;
 	if (vma->vm_start <= address)
@@ -171,7 +171,6 @@
 				goto bad_area;
 	}
 
-survive:
 	/*
 	 * If for any reason at all we couldn't handle the fault,
 	 * make sure we exit gracefully rather than endlessly redo
@@ -204,10 +203,9 @@
 
 	/* User mode accesses just cause a SIGSEGV */
 	if (error_code & 4) {
-		
-		printk(KERN_ERR "%.20s[%d] segfaulted rip:%lx rsp:%lx adr:%lx err:%lx\n", 
-		       tsk->comm, tsk->pid, 
-		       regs->rip, regs->rsp, address, error_code); 
+		printk("%s[%d] segfault at rip:%lx rsp:%lx adr:%lx err:%lx\n",
+		       tsk->comm, tsk->pid, regs->rip, regs->rsp, address, 
+		       error_code);
        
 		tsk->thread.cr2 = address;
 		tsk->thread.error_code = error_code;
@@ -225,6 +223,10 @@
 	/* Are we prepared to handle this kernel fault?  */
 	if ((fixup = search_exception_table(regs->rip)) != 0) {
 		regs->rip = fixup;
+		if (exception_trace) 
+		printk(KERN_ERR 
+		       "%s: fixed kernel exception at %lx address %lx err:%ld\n", 
+		       current->comm, regs->rip, address, error_code);
 		return;
 	}
 
@@ -255,8 +257,7 @@
 	up_read(&mm->mmap_sem);
 	if (current->pid == 1) { 
 		yield();
-		down_read(&mm->mmap_sem);
-		goto survive;
+		goto again;
 	}
 	printk("VM: killing process %s\n", tsk->comm);
 	if (error_code & 4)
@@ -282,43 +283,39 @@
 	/* Kernel mode? Handle exceptions or die */
 	if (!(error_code & 4))
 		goto no_context;
+	return;
 
 
 vmalloc_fault:
 	{
+		pgd_t *pgd;
+		pmd_t *pmd;
+		pte_t *pte; 
+
 		/*
-		 * Synchronize the kernel space top level page-table
-		 * with the 'reference' page table.
-		 * Currently it only works for first and last 512 GB of
-		 * kernel memory FIXME
-		 *
+		 * x86-64 has the same kernel 3rd level pages for all CPUs.
+		 * But for vmalloc/modules the TLB synchronization works lazily,
+		 * so it can happen that we get a page fault for something
+		 * that is really already in the page table. Just check if it
+		 * is really there and when yes flush the local TLB. 
 		 */
-		level4_t *l4pd = level4_offset_k(address);
-		int offset = __pgd_offset(address);
-		pgd_t *pgd, *pgd_k;
-		pmd_t *pmd, *pmd_k;
-
-		if (! level4_val(*l4pd)) {
-			printk(KERN_ERR "fatal - no entry in level4_page for %lx\n",
-				address);
-			goto bad_area_nosemaphore;
-		}
-		pgd = level3_offset_k(l4pd, address);
-		pgd_k = init_mm.pgd + offset;
-
-		if (!pgd_present(*pgd)) {
-			if (!pgd_present(*pgd_k))
+		pgd = pgd_offset_k(address);
+		if (pgd != current_pgd_offset_k(address)) 
+			BUG(); 
+		if (!pgd_present(*pgd))
 				goto bad_area_nosemaphore;
-			set_pgd(pgd, *pgd_k);
-			return;
-		}
-
 		pmd = pmd_offset(pgd, address);
-		pmd_k = pmd_offset(pgd_k, address);
-
-		if (pmd_present(*pmd) || !pmd_present(*pmd_k))
+		if (!pmd_present(*pmd))
+			goto bad_area_nosemaphore;
+		pte = pte_offset_kernel(pmd, address); 
+		if (!pte_present(*pte))
 			goto bad_area_nosemaphore;
-		set_pmd(pmd, *pmd_k);
+
+		/* Strictly a flush_tlb_all because vmalloc is global,
+		   but this only applies to new global pages so it should
+		   not be needed. vmalloc will likely touch multiple ptes,
+		   so do a full flush instead of a partial one. */ 
+		__flush_tlb();		
 		return;
 	}
 }
diff -Nru a/arch/x86_64/mm/init.c b/arch/x86_64/mm/init.c
--- a/arch/x86_64/mm/init.c	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/mm/init.c	Mon Apr 22 15:32:19 2002
@@ -35,6 +35,7 @@
 #include <asm/e820.h>
 #include <asm/apic.h>
 #include <asm/tlb.h>
+#include <asm/mmu_context.h>
 
 mmu_gather_t mmu_gathers[NR_CPUS];
 
@@ -93,20 +94,20 @@
 static void set_pte_phys(unsigned long vaddr,
 			 unsigned long phys, pgprot_t prot)
 {
-	level4_t *level4;
+	pml4_t *level4;
 	pgd_t *pgd;
 	pmd_t *pmd;
 	pte_t *pte;
 
-	level4 = level4_offset_k(vaddr);
-	if (level4_none(*level4)) {
-		printk("LEVEL4 FIXMAP MISSING, it should be setup in head.S!\n");
+	level4 = pml4_offset_k(vaddr);
+	if (pml4_none(*level4)) {
+		printk("PML4 FIXMAP MISSING, it should be setup in head.S!\n");
 		return;
 	}
 	pgd = level3_offset_k(level4, vaddr);
 	if (pgd_none(*pgd)) {
 		pmd = (pmd_t *) spp_getpage(); 
-		set_pgd(pgd, __pgd(__pa(pmd) + 0x7));
+		set_pgd(pgd, __pgd(__pa(pmd) | _KERNPG_TABLE | _PAGE_USER));
 		if (pmd != pmd_offset(pgd, 0)) {
 			printk("PAGETABLE BUG #01!\n");
 			return;
@@ -115,7 +116,7 @@
 	pmd = pmd_offset(pgd, vaddr);
 	if (pmd_none(*pmd)) {
 		pte = (pte_t *) spp_getpage();
-		set_pmd(pmd, __pmd(__pa(pte) + 0x7));
+		set_pmd(pmd, __pmd(__pa(pte) | _KERNPG_TABLE | _PAGE_USER));
 		if (pte != pte_offset_kernel(pmd, 0)) {
 			printk("PAGETABLE BUG #02!\n");
 			return;
@@ -145,94 +146,124 @@
 	set_pte_phys(address, phys, prot);
 }
 
-static void __init pagetable_init (void)
-{
-	unsigned long paddr, end;
-	pgd_t *pgd;
-	int i, j;
+extern unsigned long start_pfn, end_pfn; 
+extern pmd_t temp_boot_pmds[]; 
+
+static  struct temp_map { 
 	pmd_t *pmd;
+	void  *address; 
+	int    allocated; 
+} temp_mappings[] __initdata = { 
+	{ &temp_boot_pmds[0], (void *)(40UL * 1024 * 1024) },
+	{ &temp_boot_pmds[1], (void *)(42UL * 1024 * 1024) }, 
+	{}
+}; 
 
-	/*
-	 * This can be zero as well - no problem, in that case we exit
-	 * the loops anyway due to the PTRS_PER_* conditions.
-	 */
-	end = (unsigned long) max_low_pfn*PAGE_SIZE;
-	if (end > 0x8000000000) {
-		printk("Temporary supporting only 512G of global RAM\n");
-		end = 0x8000000000;
-		max_low_pfn = 0x8000000000 >> PAGE_SHIFT;
-	}
+static __init void *alloc_low_page(int *index, unsigned long *phys) 
+{ 
+	struct temp_map *ti;
+	int i; 
+	unsigned long pfn = start_pfn++, paddr; 
+	void *adr;
+
+	if (pfn >= end_pfn) 
+		panic("alloc_low_page: ran out of memory"); 
+	for (i = 0; temp_mappings[i].allocated; i++) {
+		if (!temp_mappings[i].pmd) 
+			panic("alloc_low_page: ran out of temp mappings"); 
+	} 
+	ti = &temp_mappings[i];
+	paddr = (pfn & (~511)) << PAGE_SHIFT; 
+	set_pmd(ti->pmd, __pmd(paddr | _KERNPG_TABLE | _PAGE_PSE)); 
+	ti->allocated = 1; 
+	__flush_tlb(); 	       
+	adr = ti->address + (pfn & 511)*PAGE_SIZE; 
+	*index = i; 
+	*phys  = pfn * PAGE_SIZE;  
+	return adr; 
+} 
 
-	i = __pgd_offset(PAGE_OFFSET);
-	pgd = level3_physmem_pgt + i;
+static __init void unmap_low_page(int i)
+{ 
+	struct temp_map *ti = &temp_mappings[i];
+	set_pmd(ti->pmd, __pmd(0));
+	ti->allocated = 0; 
+} 
+
+static void __init phys_pgd_init(pgd_t *pgd, unsigned long address, unsigned long end)
+{ 
+	long i, j; 
 
+	i = pgd_index(address);
+	pgd = pgd + i;
 	for (; i < PTRS_PER_PGD; pgd++, i++) {
-		paddr = i*PGDIR_SIZE;
-		if (paddr >= end)
+		int map; 
+		unsigned long paddr = i*PGDIR_SIZE, pmd_phys;
+		pmd_t *pmd;
+
+		if (paddr >= end) { 
+			for (; i < PTRS_PER_PGD; i++, pgd++) 
+				set_pgd(pgd, __pgd(0)); 
 			break;
-		if (i)
-			pmd = (pmd_t *) alloc_bootmem_low_pages(PAGE_SIZE);
-		else
-			pmd = level2_kernel_pgt;
-
-		set_pgd(pgd, __pgd(__pa(pmd) + 0x7));
+		} 
+		pmd = alloc_low_page(&map, &pmd_phys);
+		set_pgd(pgd, __pgd(pmd_phys | _KERNPG_TABLE));
 		for (j = 0; j < PTRS_PER_PMD; pmd++, j++) {
-			unsigned long __pe;
+			unsigned long pe;
 
 			paddr = i*PGDIR_SIZE + j*PMD_SIZE;
-			if (paddr >= end)
+			if (paddr >= end) { 
+				for (; j < PTRS_PER_PMD; j++, pmd++)
+					set_pmd(pmd,  __pmd(0)); 
 				break;
-
-			__pe = _KERNPG_TABLE + _PAGE_PSE + paddr + _PAGE_GLOBAL;
-			set_pmd(pmd, __pmd(__pe));
 		}
+			pe = _PAGE_PSE | _KERNPG_TABLE | _PAGE_GLOBAL | paddr;
+			set_pmd(pmd, __pmd(pe));
+		}
+		unmap_low_page(map);
 	}
+	__flush_tlb();
+} 
 
-	/*
-	 * Add low memory identity-mappings - SMP needs it when
-	 * starting up on an AP from real-mode. In the non-PAE
-	 * case we already have these mappings through head.S.
-	 * All user-space mappings are explicitly cleared after
-	 * SMP startup.
-	 */
-#ifdef FIXME
-	pgd_base [0] is not what you think, this needs to be rewritten for SMP.
-	pgd_base[0] = pgd_base[USER_PTRS_PER_PGD];
-#endif
+/* Setup the direct mapping of the physical memory at PAGE_OFFSET.
+   This runs before bootmem is initialized and gets pages directly from the 
+   physical memory. To access them they are temporarily mapped. */
+void __init init_memory_mapping(void) 
+{ 
+	unsigned long adr;	       
+	unsigned long end;
+	unsigned long next; 
+
+	end = PAGE_OFFSET + (end_pfn * PAGE_SIZE); 
+	for (adr = PAGE_OFFSET; adr < end; adr = next) { 
+		int map;
+		unsigned long pgd_phys; 
+		pgd_t *pgd = alloc_low_page(&map, &pgd_phys);
+		next = adr + (512UL * 1024 * 1024 * 1024);
+		if (next > end) 
+			next = end; 
+		phys_pgd_init(pgd, adr-PAGE_OFFSET, next-PAGE_OFFSET); 
+		set_pml4(init_level4_pgt + pml4_index(adr), mk_kernel_pml4(pgd_phys));
+		unmap_low_page(map);   
+	} 
+	asm volatile("movq %%cr4,%0" : "=r" (mmu_cr4_features));
+	__flush_tlb_all();
 }
 
+extern struct x8664_pda cpu_pda[NR_CPUS];
+
 void __init zap_low_mappings (void)
 {
 	int i;
-	/*
-	 * Zap initial low-memory mappings.
-	 *
-	 * Note that "pgd_clear()" doesn't do it for
-	 * us in this case, because pgd_clear() is a
-	 * no-op in the 2-level case (pmd_clear() is
-	 * the thing that clears the page-tables in
-	 * that case).
-	 */
-	for (i = 0; i < USER_PTRS_PER_PGD; i++)
-		pgd_clear(swapper_pg_dir+i);
+	for (i = 0; i < NR_CPUS; i++) {
+		if (cpu_pda[i].level4_pgt) 
+			cpu_pda[i].level4_pgt[0] = 0; 
+	}
 	flush_tlb_all();
 }
 
-/*
- * paging_init() sets up the page tables - note that the first 4MB are
- * already mapped by head.S.
- *
- * This routines also unmaps the page at virtual kernel address 0, so
- * that we can trap those pesky NULL-reference errors in the kernel.
- */
 void __init paging_init(void)
 {
-	asm volatile("movq %%cr4,%0" : "=r" (mmu_cr4_features));
-
-	pagetable_init();
-
-	__flush_tlb_all();
-
 	{
 		unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0};
 		unsigned int max_dma, low;
@@ -313,7 +344,7 @@
 		initsize >> 10);
 
 	/*
-	 * Subtle. SMP is doing it's boot stuff late (because it has to
+	 * Subtle. SMP is doing its boot stuff late (because it has to
 	 * fork idle threads) - but it also needs low mappings for the
 	 * protected-mode entry to work. We zap these entries only after
 	 * the WP-bit has been tested.
@@ -331,6 +362,9 @@
 	for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) {
 		ClearPageReserved(virt_to_page(addr));
 		set_page_count(virt_to_page(addr), 1);
+#ifdef CONFIG_INIT_DEBUG
+		memset(addr & ~(PAGE_SIZE-1), 0xcc, PAGE_SIZE); 
+#endif
 		free_page(addr);
 		totalram_pages++;
 	}
diff -Nru a/arch/x86_64/mm/ioremap.c b/arch/x86_64/mm/ioremap.c
--- a/arch/x86_64/mm/ioremap.c	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/mm/ioremap.c	Mon Apr 22 15:32:19 2002
@@ -1,5 +1,5 @@
 /*
- * arch/i386/mm/ioremap.c
+ * arch/x86_64/mm/ioremap.c
  *
  * Re-map IO memory to kernel address space so that we can access it.
  * This is needed for high PCI addresses that aren't mapped in the
@@ -11,6 +11,10 @@
 #include <linux/vmalloc.h>
 #include <asm/io.h>
 #include <asm/pgalloc.h>
+#include <asm/fixmap.h>
+#include <asm/cacheflush.h>
+#include <asm/tlbflush.h>
+
 
 static inline void remap_area_pte(pte_t * pte, unsigned long address, unsigned long size,
 	unsigned long phys_addr, unsigned long flags)
@@ -29,7 +33,7 @@
 			BUG();
 		}
 		set_pte(pte, mk_pte_phys(phys_addr, __pgprot(_PAGE_PRESENT | _PAGE_RW | 
-					_PAGE_DIRTY | _PAGE_ACCESSED | flags)));
+					_PAGE_GLOBAL | _PAGE_DIRTY | _PAGE_ACCESSED | flags)));
 		address += PAGE_SIZE;
 		phys_addr += PAGE_SIZE;
 		pte++;
@@ -67,7 +71,7 @@
 	unsigned long end = address + size;
 
 	phys_addr -= address;
-	dir = pgd_offset(&init_mm, address);
+	dir = pgd_offset_k(address);
 	flush_cache_all();
 	if (address >= end)
 		BUG();
diff -Nru a/arch/x86_64/mm/modutil.c b/arch/x86_64/mm/modutil.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/arch/x86_64/mm/modutil.c	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,68 @@
+/*  arch/x86_64/mm/modutil.c
+ *
+ *  Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ *  Based upon code written by Linus Torvalds and others.
+ * 
+ *  Blatantly copied from sparc64 for x86-64 by Andi Kleen. 
+ *  Should use direct mapping with 2MB pages. This would need extension
+ *  of the kernel mapping.
+ */
+ 
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+static struct vm_struct * modvmlist = NULL;
+
+void module_unmap (void * addr)
+{
+	struct vm_struct **p, *tmp;
+
+	if (!addr)
+		return;
+	if ((PAGE_SIZE-1) & (unsigned long) addr) {
+		printk("Trying to unmap module with bad address (%p)\n", addr);
+		return;
+	}
+	for (p = &modvmlist ; (tmp = *p) ; p = &tmp->next) {
+		if (tmp->addr == addr) {
+			*p = tmp->next;
+			vmfree_area_pages(VMALLOC_VMADDR(tmp->addr), tmp->size);
+			kfree(tmp);
+			return;
+		}
+	}
+	printk("Trying to unmap nonexistent module vm area (%p)\n", addr);
+}
+
+void * module_map (unsigned long size)
+{
+	void * addr;
+	struct vm_struct **p, *tmp, *area;
+
+	size = PAGE_ALIGN(size);
+	if (!size || size > MODULES_LEN) return NULL;
+		
+	addr = (void *) MODULES_VADDR;
+	for (p = &modvmlist; (tmp = *p) ; p = &tmp->next) {
+		if (size + (unsigned long) addr < (unsigned long) tmp->addr)
+			break;
+		addr = (void *) (tmp->size + (unsigned long) tmp->addr);
+	}
+	if ((unsigned long) addr + size >= MODULES_END) return NULL;
+	
+	area = (struct vm_struct *) kmalloc(sizeof(*area), GFP_KERNEL);
+	if (!area) return NULL;
+	area->size = size + PAGE_SIZE;
+	area->addr = addr;
+	area->next = *p;
+	*p = area;
+
+	if (vmalloc_area_pages(VMALLOC_VMADDR(addr), size, GFP_KERNEL, PAGE_KERNEL)) {
+		module_unmap(addr);
+		return NULL;
+	}
+	return addr;
+}
diff -Nru a/arch/x86_64/tools/offset.c b/arch/x86_64/tools/offset.c
--- a/arch/x86_64/tools/offset.c	Mon Apr 22 15:32:19 2002
+++ b/arch/x86_64/tools/offset.c	Mon Apr 22 15:32:19 2002
@@ -41,14 +41,14 @@
 	ENTRY(pcurrent); 
 	ENTRY(irqrsp);
 	ENTRY(irqcount);
-	ENTRY(irqstack); 
 	ENTRY(cpunumber);
 	ENTRY(irqstackptr);
-	ENTRY(me);
 	ENTRY(__softirq_pending); 
 	ENTRY(__local_irq_count);
 	ENTRY(__local_bh_count);
 	ENTRY(__ksoftirqd_task);
+	ENTRY(level4_pgt);
+	ENTRY(me);
 #undef ENTRY
 	output("#ifdef __ASSEMBLY__"); 
 #define CONST(t) outconst("#define " #t " %0", t)
diff -Nru a/arch/x86_64/vmlinux.lds b/arch/x86_64/vmlinux.lds
--- a/arch/x86_64/vmlinux.lds	Mon Apr 22 15:32:18 2002
+++ b/arch/x86_64/vmlinux.lds	Mon Apr 22 15:32:18 2002
@@ -2,7 +2,7 @@
  * Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>;
  */
 OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")
-OUTPUT_ARCH(i386)
+OUTPUT_ARCH(i386:x86-64)
 ENTRY(_start)
 SECTIONS
 {
@@ -29,6 +29,10 @@
   __ksymtab : { *(__ksymtab) }
   __stop___ksymtab = .;
 
+  __start___kallsyms = .;	/* All kernel symbols */
+  __kallsyms : { *(__kallsyms) }
+  __stop___kallsyms = .;
+
   .data : {			/* Data */
 	*(.data)
 	CONSTRUCTORS
@@ -71,6 +75,9 @@
   . = ALIGN(8192);		/* init_task */
   .data.init_task : { *(.data.init_task) }
 
+  . = ALIGN(4096); 
+  .data.boot_pgt : { *(.data.boot_pgt) }
+
   . = ALIGN(4096);		/* Init code and data */
   __init_begin = .;
   .text.init : { *(.text.init) }
@@ -90,6 +97,11 @@
 	*(.initcall7.init)
   }
   __initcall_end = .;
+  . = ALIGN(32);
+  __per_cpu_start = .;
+  . = ALIGN(64); 
+  .data.percpu  : { *(.data.percpu) }
+  __per_cpu_end = .;
   . = ALIGN(4096);
   __init_end = .;
 
@@ -101,12 +113,20 @@
 	*(.exitcall.exit)
 	}
 
-  /* Stabs debugging sections.  */
-  .stab 0 : { *(.stab) }
-  .stabstr 0 : { *(.stabstr) }
-  .stab.excl 0 : { *(.stab.excl) }
-  .stab.exclstr 0 : { *(.stab.exclstr) }
-  .stab.index 0 : { *(.stab.index) }
-  .stab.indexstr 0 : { *(.stab.indexstr) }
+  /* DWARF 2 */
+  .debug_info     0 : { *(.debug_info) }
+  .debug_abbrev   0 : { *(.debug_abbrev) }
+  .debug_line     0 : { *(.debug_line) }
+  .debug_frame    0 : { *(.debug_frame) }
+  .debug_str      0 : { *(.debug_str) }
+  .debug_loc      0 : { *(.debug_loc) }
+  .debug_macinfo  0 : { *(.debug_macinfo) }
+  /* SGI/MIPS DWARF 2 extensions */
+  .debug_weaknames 0 : { *(.debug_weaknames) }
+  .debug_funcnames 0 : { *(.debug_funcnames) }
+  .debug_typenames 0 : { *(.debug_typenames) }
+  .debug_varnames  0 : { *(.debug_varnames) }
+
+
   .comment 0 : { *(.comment) }
 }
diff -Nru a/drivers/acorn/block/Makefile b/drivers/acorn/block/Makefile
--- a/drivers/acorn/block/Makefile	Mon Apr 22 15:32:19 2002
+++ b/drivers/acorn/block/Makefile	Mon Apr 22 15:32:19 2002
@@ -1,13 +1,6 @@
 #
 # Makefile for the Acorn block device drivers.
 #
-# Note! Dependencies are done automagically by 'make dep', which also
-# removes any old dependencies. DON'T put your own dependencies here
-# unless it's something special (ie not a .c file).
-#
-# Note 2! The CFLAGS definition is now inherited from the
-# parent makefile.
-#
 
 USE_STANDARD_AS_RULE := true
 
@@ -19,7 +12,6 @@
 obj-		:=
 
 export-objs	:=
-list-multi	:= fd1772_mod.o mfmhd_mod.o
 fd1772_mod-objs	:= fd1772.o fd1772dma.o
 mfmhd_mod-objs	:= mfmhd.o mfm.o
 
@@ -27,9 +19,3 @@
 obj-$(CONFIG_BLK_DEV_MFM)	+= mfmhd_mod.o
 
 include $(TOPDIR)/Rules.make
-
-fd1772_mod.o: $(fd1772_mod-objs)
-	$(LD) -r -o $@ $(fd1772_mod-objs)
-
-mfmhd_mod.o: $(mfmhd_mod-objs)
-	$(LD) -r -o $@ $(mfmhd_mod-objs)
diff -Nru a/drivers/acorn/scsi/Makefile b/drivers/acorn/scsi/Makefile
--- a/drivers/acorn/scsi/Makefile	Mon Apr 22 15:32:18 2002
+++ b/drivers/acorn/scsi/Makefile	Mon Apr 22 15:32:18 2002
@@ -12,7 +12,6 @@
 obj-		:=
 
 export-objs		:= fas216.o queue.o msgqueue.o
-list-multi		:= acornscsi_mod.o
 acornscsi_mod-objs	:= acornscsi.o acornscsi-io.o
 
 obj-$(CONFIG_SCSI_ACORNSCSI_3)	+= acornscsi_mod.o queue.o msgqueue.o
@@ -25,6 +24,3 @@
 obj-$(CONFIG_SCSI_EESOXSCSI)	+= eesox.o fas216.o queue.o msgqueue.o
 
 include $(TOPDIR)/Rules.make
-
-acornscsi_mod.o: $(acornscsi_mod-objs)
-	$(LD) -r -o $@ $(acornscsi_mod-objs)
diff -Nru a/drivers/acorn/scsi/acornscsi.c b/drivers/acorn/scsi/acornscsi.c
--- a/drivers/acorn/scsi/acornscsi.c	Mon Apr 22 15:32:18 2002
+++ b/drivers/acorn/scsi/acornscsi.c	Mon Apr 22 15:32:18 2002
@@ -2915,13 +2915,18 @@
 	ecs[count]->irqaddr	= (char *)ioaddr(host->card.io_intr);
 	ecs[count]->irqmask	= 0x0a;
 
-	request_region(instance->io_port + 0x800,  2, "acornscsi(sbic)");
-	request_region(host->card.io_intr,  1, "acornscsi(intr)");
-	request_region(host->card.io_page,  1, "acornscsi(page)");
+	if (!request_region(instance->io_port + 0x800,  2, "acornscsi(sbic)"))
+		goto err_1;
+	if (!request_region(host->card.io_intr,  1, "acornscsi(intr)"))
+		goto err_2;
+	if (!request_region(host->card.io_page,  1, "acornscsi(page)"))
+		goto err_3;
 #ifdef USE_DMAC
-	request_region(host->dma.io_port, 256, "acornscsi(dmac)");
+	if (!request_region(host->dma.io_port, 256, "acornscsi(dmac)"))
+		goto err_4;
 #endif
-	request_region(instance->io_port, 2048, "acornscsi(ram)");
+	if (!request_region(instance->io_port, 2048, "acornscsi(ram)"))
+		goto err_5;
 
 	if (request_irq(host->scsi.irq, acornscsi_intr, SA_INTERRUPT, "acornscsi", host)) {
 	    printk(KERN_CRIT "scsi%d: IRQ%d not free, interrupts disabled\n",
@@ -2934,6 +2939,20 @@
 	++count;
     }
     return count;
+    
+err_5:
+#ifdef USE_DMAC
+    release_region(host->dma.io_port, 256);
+#endif
+err_4:
+    release_region(host->card.io_page, 1);
+err_3:
+    release_region(host->card.io_intr, 1);    
+err_2:
+    release_region(instance->io_port + 0x800, 2);
+err_1:
+    scsi_unregister(instance);
+    return 0;
 }
 
 /*
diff -Nru a/drivers/atm/Makefile b/drivers/atm/Makefile
--- a/drivers/atm/Makefile	Mon Apr 22 15:32:18 2002
+++ b/drivers/atm/Makefile	Mon Apr 22 15:32:18 2002
@@ -1,4 +1,3 @@
-# File: drivers/atm/Makefile
 #
 # Makefile for the Linux network (ATM) device drivers.
 #
@@ -57,7 +56,6 @@
 
 EXTRA_CFLAGS=-g
 
-list-multi	:= fore_200e.o
 fore_200e-objs	:= fore200e.o $(FORE200E_FW_OBJS)
 
 include $(TOPDIR)/Rules.make
@@ -91,9 +89,6 @@
 %.bin %.bin1 %.bin2: %.data
 	objcopy -Iihex $< -Obinary $@.gz
 	gzip -df $@.gz
-
-fore_200e.o: $(fore_200e-objs)
-	$(LD) -r -o $@ $(fore_200e-objs)
 
 # firmware dependency stuff taken from drivers/sound/Makefile
 FORE200E_FW_UP_TO_DATE :=
diff -Nru a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c
--- a/drivers/block/ll_rw_blk.c	Mon Apr 22 15:32:18 2002
+++ b/drivers/block/ll_rw_blk.c	Mon Apr 22 15:32:18 2002
@@ -899,10 +899,10 @@
 	spin_lock_prefetch(q->queue_lock);
 
 	generic_unplug_device(q);
-	add_wait_queue(&rl->wait, &wait);
+	add_wait_queue_exclusive(&rl->wait, &wait);
 	do {
 		set_current_state(TASK_UNINTERRUPTIBLE);
-		if (rl->count < batch_requests)
+		if (!rl->count)
 			schedule();
 		spin_lock_irq(q->queue_lock);
 		rq = get_request(q, rw);
diff -Nru a/drivers/block/nbd.c b/drivers/block/nbd.c
--- a/drivers/block/nbd.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/block/nbd.c	Mon Apr 22 15:32:19 2002
@@ -240,11 +240,11 @@
 {
 	struct request *req;
 
-	down (&lo->queue_lock);
+	down (&lo->tx_lock);
 	while (1) {
-		up (&lo->queue_lock);
+		up (&lo->tx_lock);
 		req = nbd_read_stat(lo);
-		down (&lo->queue_lock);
+		down (&lo->tx_lock);
 
 		if (!req) {
 			printk(KERN_ALERT "req should never be null\n" );
@@ -264,14 +264,14 @@
 		}
 #endif
 		blkdev_dequeue_request(req);
-		up (&lo->queue_lock);
+		up (&lo->tx_lock);
 		
 		nbd_end_request(req);
 
-		down (&lo->queue_lock);
+		down (&lo->tx_lock);
 	}
  out:
-	up (&lo->queue_lock);
+	up (&lo->tx_lock);
 }
 
 void nbd_clear_que(struct nbd_device *lo)
@@ -299,11 +299,11 @@
 #endif
 		req->errors++;
 		blkdev_dequeue_request(req);
-		up(&lo->queue_lock);
+		up(&lo->tx_lock);
 
 		nbd_end_request(req);
 
-		down(&lo->queue_lock);
+		down(&lo->tx_lock);
 	}
 }
 
@@ -351,10 +351,10 @@
 		blkdev_dequeue_request(req);
 		spin_unlock_irq(q->queue_lock);
 
-		down (&lo->queue_lock);
+		down (&lo->tx_lock);
 		list_add(&req->queuelist, &lo->queue_head);
 		nbd_send_req(lo->sock, req);	/* Why does this block?         */
-		up (&lo->queue_lock);
+		up (&lo->tx_lock);
 
 		spin_lock_irq(q->queue_lock);
 		continue;
@@ -396,14 +396,14 @@
                 return 0 ;
  
 	case NBD_CLEAR_SOCK:
-		down(&lo->queue_lock);
+		down(&lo->tx_lock);
 		nbd_clear_que(lo);
 		if (!list_empty(&lo->queue_head)) {
-			up(&lo->queue_lock);
+			up(&lo->tx_lock);
 			printk(KERN_ERR "nbd: Some requests are in progress -> can not turn off.\n");
 			return -EBUSY;
 		}
-		up(&lo->queue_lock);
+		up(&lo->tx_lock);
 		file = lo->file;
 		if (!file)
 			return -EINVAL;
@@ -527,7 +527,7 @@
 		nbd_dev[i].magic = LO_MAGIC;
 		nbd_dev[i].flags = 0;
 		INIT_LIST_HEAD(&nbd_dev[i].queue_head);
-		init_MUTEX(&nbd_dev[i].queue_lock);
+		init_MUTEX(&nbd_dev[i].tx_lock);
 		nbd_blksizes[i] = 1024;
 		nbd_blksize_bits[i] = 10;
 		nbd_bytesizes[i] = 0x7ffffc00; /* 2GB */
diff -Nru a/drivers/char/Makefile b/drivers/char/Makefile
--- a/drivers/char/Makefile	Mon Apr 22 15:32:19 2002
+++ b/drivers/char/Makefile	Mon Apr 22 15:32:19 2002
@@ -1,13 +1,6 @@
 #
 # Makefile for the kernel character device drivers.
 #
-# Note! Dependencies are done automagically by 'make dep', which also
-# removes any old dependencies. DON'T put your own dependencies here
-# unless it's something special (ie not a .c file).
-#
-# Note 2! The CFLAGS definitions are now inherited from the
-# parent makes..
-#
 
 #
 # This file contains the font map for the default (hardware) font
@@ -26,8 +19,6 @@
 			sonypi.o tty_io.o tty_ioctl.o generic_serial.o rtc.o
 
 mod-subdirs	:=	ftape drm pcmcia
-
-list-multi	:=	
 
 KEYMAP   =defkeymap.o
 KEYBD    =pc_keyb.o
diff -Nru a/drivers/char/agp/Makefile b/drivers/char/agp/Makefile
--- a/drivers/char/agp/Makefile	Mon Apr 22 15:32:19 2002
+++ b/drivers/char/agp/Makefile	Mon Apr 22 15:32:19 2002
@@ -7,12 +7,8 @@
 
 export-objs := agpgart_be.o
 
-list-multi := agpgart.o
 agpgart-objs := agpgart_fe.o agpgart_be.o
 
 obj-$(CONFIG_AGP) += agpgart.o
 
 include $(TOPDIR)/Rules.make
-
-agpgart.o: $(agpgart-objs)
-	$(LD) $(LD_RFLAG) -r -o $@ $(agpgart-objs)
diff -Nru a/drivers/char/console.c b/drivers/char/console.c
--- a/drivers/char/console.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/char/console.c	Mon Apr 22 15:32:19 2002
@@ -1420,7 +1420,8 @@
 	kbd_table[currcons].slockstate = 0;
 	kbd_table[currcons].ledmode = LED_SHOW_FLAGS;
 	kbd_table[currcons].ledflagstate = kbd_table[currcons].default_ledflagstate;
-	set_leds();
+	/* do not do set_leds here because this causes an endless tasklet loop
+	   when the keyboard hasn't been initialized yet */
 
 	cursor_type = CUR_DEFAULT;
 	complement_mask = s_complement_mask;
diff -Nru a/drivers/char/drm/Makefile b/drivers/char/drm/Makefile
--- a/drivers/char/drm/Makefile	Mon Apr 22 15:32:19 2002
+++ b/drivers/char/drm/Makefile	Mon Apr 22 15:32:19 2002
@@ -3,7 +3,6 @@
 # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
 
 O_TARGET	:= drm.o
-list-multi	:= gamma.o tdfx.o r128.o mga.o i810.o radeon.o ffb.o
 
 gamma-objs  := gamma_drv.o gamma_dma.o
 tdfx-objs   := tdfx_drv.o
@@ -22,24 +21,3 @@
 obj-$(CONFIG_DRM_FFB)   += ffb.o
 
 include $(TOPDIR)/Rules.make
-
-gamma.o: $(gamma-objs) $(lib)
-	$(LD) -r -o $@ $(gamma-objs) $(lib)
-
-tdfx.o: $(tdfx-objs) $(lib)
-	$(LD) -r -o $@ $(tdfx-objs) $(lib)
-
-mga.o: $(mga-objs) $(lib)
-	$(LD) -r -o $@ $(mga-objs) $(lib)
-
-i810.o: $(i810-objs) $(lib)
-	$(LD) -r -o $@ $(i810-objs) $(lib)
-
-r128.o: $(r128-objs) $(lib)
-	$(LD) -r -o $@ $(r128-objs) $(lib)
-
-radeon.o: $(radeon-objs) $(lib)
-	$(LD) -r -o $@ $(radeon-objs) $(lib)
-
-ffb.o: $(ffb-objs) $(lib)
-	$(LD) -r -o $@ $(ffb-objs) $(lib)
diff -Nru a/drivers/char/logibusmouse.c b/drivers/char/logibusmouse.c
--- a/drivers/char/logibusmouse.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/char/logibusmouse.c	Mon Apr 22 15:32:19 2002
@@ -128,25 +128,28 @@
 
 static int __init logi_busmouse_init(void)
 {
-	if (check_region(LOGIBM_BASE, LOGIBM_EXTENT))
+	if (!request_region(LOGIBM_BASE, LOGIBM_EXTENT, "busmouse"))
 		return -EIO;
 
 	outb(MSE_CONFIG_BYTE, MSE_CONFIG_PORT);
 	outb(MSE_SIGNATURE_BYTE, MSE_SIGNATURE_PORT);
 	udelay(100L);	/* wait for reply from mouse */
-	if (inb(MSE_SIGNATURE_PORT) != MSE_SIGNATURE_BYTE)
+	if (inb(MSE_SIGNATURE_PORT) != MSE_SIGNATURE_BYTE) {
+		release_region(LOGIBM_BASE, LOGIBM_EXTENT);
 		return -EIO;
+	}
 
 	outb(MSE_DEFAULT_MODE, MSE_CONFIG_PORT);
 	MSE_INT_OFF();
 	
-	request_region(LOGIBM_BASE, LOGIBM_EXTENT, "busmouse");
-
 	msedev = register_busmouse(&busmouse);
-	if (msedev < 0)
+	if (msedev < 0) {
+		release_region(LOGIBM_BASE, LOGIBM_EXTENT);
 		printk(KERN_WARNING "Unable to register busmouse driver.\n");
+	} 
 	else
 		printk(KERN_INFO "Logitech busmouse installed.\n");
+	
 	return msedev < 0 ? msedev : 0;
 }
 
diff -Nru a/drivers/char/msbusmouse.c b/drivers/char/msbusmouse.c
--- a/drivers/char/msbusmouse.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/char/msbusmouse.c	Mon Apr 22 15:32:19 2002
@@ -149,11 +149,15 @@
 	}
 	if (present == 0)
 		return -EIO;
+	if (!request_region(MS_MSE_CONTROL_PORT, 0x04, "MS Busmouse"))
+		return -EIO;
+	
 	MS_MSE_INT_OFF();
-	request_region(MS_MSE_CONTROL_PORT, 0x04, "MS Busmouse");
 	msedev = register_busmouse(&msbusmouse);
-	if (msedev < 0)
+	if (msedev < 0) {
 		printk(KERN_WARNING "Unable to register msbusmouse driver.\n");
+		release_region(MS_MSE_CONTROL_PORT, 0x04);
+	}
 	else
 		printk(KERN_INFO "Microsoft BusMouse detected and installed.\n");
 	return msedev < 0 ? msedev : 0;
diff -Nru a/drivers/char/n_hdlc.c b/drivers/char/n_hdlc.c
--- a/drivers/char/n_hdlc.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/char/n_hdlc.c	Mon Apr 22 15:32:19 2002
@@ -9,7 +9,7 @@
  *	Al Longyear <longyear@netcom.com>, Paul Mackerras <Paul.Mackerras@cs.anu.edu.au>
  *
  * Original release 01/11/99
- * $Id: n_hdlc.c,v 3.2 2000/11/06 22:34:38 paul Exp $
+ * $Id: n_hdlc.c,v 4.1 2002/04/10 19:30:58 paulkf Exp $
  *
  * This code is released under the GNU General Public License (GPL)
  *
@@ -78,7 +78,7 @@
  */
 
 #define HDLC_MAGIC 0x239e
-#define HDLC_VERSION "3.2"
+#define HDLC_VERSION "$Revision: 4.1 $"
 
 #include <linux/version.h>
 #include <linux/config.h>
@@ -159,11 +159,6 @@
 	struct tty_struct *tty;		/* ptr to TTY structure	*/
 	struct tty_struct *backup_tty;	/* TTY to use if tty gets closed */
 	
-	/* Queues for select() functionality */
-	wait_queue_head_t read_wait;
-	wait_queue_head_t write_wait;
-	wait_queue_head_t poll_wait;
-
 	int		tbusy;		/* reentrancy flag for tx wakeup code */
 	int		woke_up;
 	N_HDLC_BUF	*tbuf;		/* currently transmitting tx buffer */
@@ -234,9 +229,8 @@
 		printk("%s(%d)n_hdlc_release() called\n",__FILE__,__LINE__);
 		
 	/* Ensure that the n_hdlcd process is not hanging on select()/poll() */
-	wake_up_interruptible (&n_hdlc->read_wait);
-	wake_up_interruptible (&n_hdlc->poll_wait);
-	wake_up_interruptible (&n_hdlc->write_wait);
+	wake_up_interruptible (&tty->read_wait);
+	wake_up_interruptible (&tty->write_wait);
 
 	if (tty != NULL && tty->disc_data == n_hdlc)
 		tty->disc_data = NULL;	/* Break the tty->n_hdlc link */
@@ -431,8 +425,7 @@
 			n_hdlc->tbuf = NULL;
 			
 			/* wait up sleeping writers */
-			wake_up_interruptible(&n_hdlc->write_wait);
-			wake_up_interruptible(&n_hdlc->poll_wait);
+			wake_up_interruptible(&tty->write_wait);
 	
 			/* get next pending transmit buffer */
 			tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list);
@@ -574,8 +567,7 @@
 	n_hdlc_buf_put(&n_hdlc->rx_buf_list,buf);
 	
 	/* wake up any blocked reads and perform async signalling */
-	wake_up_interruptible (&n_hdlc->read_wait);
-	wake_up_interruptible (&n_hdlc->poll_wait);
+	wake_up_interruptible (&tty->read_wait);
 	if (n_hdlc->tty->fasync != NULL)
 		kill_fasync (&n_hdlc->tty->fasync, SIGIO, POLL_IN);
 
@@ -620,6 +612,9 @@
 	}
 
 	for (;;) {
+		if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
+			return -EIO;
+
 		n_hdlc = tty2n_hdlc (tty);
 		if (!n_hdlc || n_hdlc->magic != HDLC_MAGIC ||
 			 tty != n_hdlc->tty)
@@ -633,7 +628,7 @@
 		if (file->f_flags & O_NONBLOCK)
 			return -EAGAIN;
 			
-		interruptible_sleep_on (&n_hdlc->read_wait);
+		interruptible_sleep_on (&tty->read_wait);
 		if (signal_pending(current))
 			return -EINTR;
 	}
@@ -702,7 +697,7 @@
 		count = maxframe;
 	}
 	
-	add_wait_queue(&n_hdlc->write_wait, &wait);
+	add_wait_queue(&tty->write_wait, &wait);
 	set_current_state(TASK_INTERRUPTIBLE);
 	
 	/* Allocate transmit buffer */
@@ -725,7 +720,7 @@
 	}
 
 	set_current_state(TASK_RUNNING);
-	remove_wait_queue(&n_hdlc->write_wait, &wait);
+	remove_wait_queue(&tty->write_wait, &wait);
 
 	if (!error) {		
 		/* Retrieve the user's buffer */
@@ -835,12 +830,14 @@
 	if (n_hdlc && n_hdlc->magic == HDLC_MAGIC && tty == n_hdlc->tty) {
 		/* queue current process into any wait queue that */
 		/* may awaken in the future (read and write) */
-		poll_wait(filp, &n_hdlc->poll_wait, wait);
+
+		poll_wait(filp, &tty->read_wait, wait);
+		poll_wait(filp, &tty->write_wait, wait);
 
 		/* set bits for operations that wont block */
 		if(n_hdlc->rx_buf_list.head)
 			mask |= POLLIN | POLLRDNORM;	/* readable */
-		if(tty->flags & (1 << TTY_OTHER_CLOSED))
+		if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
 			mask |= POLLHUP;
 		if(tty_hung_up_p(filp))
 			mask |= POLLHUP;
@@ -894,11 +891,7 @@
 	
 	/* Initialize the control block */
 	n_hdlc->magic  = HDLC_MAGIC;
-
 	n_hdlc->flags  = 0;
-	init_waitqueue_head(&n_hdlc->read_wait);
-	init_waitqueue_head(&n_hdlc->poll_wait);
-	init_waitqueue_head(&n_hdlc->write_wait);
 	
 	return n_hdlc;
 	
diff -Nru a/drivers/char/nvram.c b/drivers/char/nvram.c
--- a/drivers/char/nvram.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/char/nvram.c	Mon Apr 22 15:32:19 2002
@@ -325,7 +325,7 @@
 		return( 0 );
 
 	  default:
-		return( -EINVAL );
+		return( -ENOTTY );
 	}
 }
 
diff -Nru a/drivers/char/rtc.c b/drivers/char/rtc.c
--- a/drivers/char/rtc.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/char/rtc.c	Mon Apr 22 15:32:19 2002
@@ -544,7 +544,7 @@
 		return 0;
 	}
 	default:
-		return -EINVAL;
+		return -ENOTTY;
 	}
 	return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0;
 }
diff -Nru a/drivers/char/serial.c b/drivers/char/serial.c
--- a/drivers/char/serial.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/char/serial.c	Mon Apr 22 15:32:19 2002
@@ -122,11 +122,6 @@
 #define ENABLE_SERIAL_ACPI
 #endif
 
-#ifdef __ISAPNP__
-#ifndef ENABLE_SERIAL_PNP
-#define ENABLE_SERIAL_PNP
-#endif
-#endif
 
 /* Set of debugging defines */
 
@@ -211,9 +206,14 @@
 #ifdef ENABLE_SERIAL_PCI
 #include <linux/pci.h>
 #endif
-#ifdef ENABLE_SERIAL_PNP
+
 #include <linux/isapnp.h>
+#ifdef __ISAPNP__
+#ifndef ENABLE_SERIAL_PNP
+#define ENABLE_SERIAL_PNP
 #endif
+#endif
+
 #ifdef CONFIG_MAGIC_SYSRQ
 #include <linux/sysrq.h>
 #endif
diff -Nru a/drivers/char/synclink.c b/drivers/char/synclink.c
--- a/drivers/char/synclink.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/char/synclink.c	Mon Apr 22 15:32:19 2002
@@ -1,7 +1,7 @@
 /*
  * linux/drivers/char/synclink.c
  *
- * $Id: synclink.c,v 3.12 2001/07/18 19:14:21 paulkf Exp $
+ * $Id: synclink.c,v 4.2 2002/04/10 20:45:13 paulkf Exp $
  *
  * Device driver for Microgate SyncLink ISA and PCI
  * high speed multiprotocol serial adapters.
@@ -60,8 +60,6 @@
 #  define BREAKPOINT() { }
 #endif
 
-#error Please convert me to Documentation/DMA-mapping.txt
-
 #define MAX_ISA_DEVICES 10
 #define MAX_PCI_DEVICES 10
 #define MAX_TOTAL_DEVICES 20
@@ -109,12 +107,8 @@
 #endif
 
 #ifdef CONFIG_SYNCLINK_SYNCPPP
-#if LINUX_VERSION_CODE < VERSION(2,4,3) 
-#include "../net/wan/syncppp.h"
-#else
 #include <net/syncppp.h>
 #endif
-#endif
 
 #define GET_USER(error,value,addr) error = get_user(value,addr)
 #define COPY_FROM_USER(error,dest,src,size) error = copy_from_user(dest,src,size) ? -EFAULT : 0
@@ -923,7 +917,7 @@
 MODULE_PARM(txholdbufs,"1-" __MODULE_STRING(MAX_TOTAL_DEVICES) "i");
 
 static char *driver_name = "SyncLink serial driver";
-static char *driver_version = "$Revision: 3.12 $";
+static char *driver_version = "$Revision: 4.2 $";
 
 static int synclink_init_one (struct pci_dev *dev,
 				     const struct pci_device_id *ent);
@@ -3985,7 +3979,7 @@
 		if ( info->buffer_list == NULL )
 			return -ENOMEM;
 			
-		info->buffer_list_phys = virt_to_bus(info->buffer_list);
+		info->buffer_list_phys = isa_virt_to_bus(info->buffer_list);
 	}
 
 	/* We got the memory for the buffer entry lists. */
@@ -4096,7 +4090,7 @@
 				kmalloc(DMABUFFERSIZE, GFP_KERNEL | GFP_DMA);
 			if ( BufferList[i].virt_addr == NULL )
 				return -ENOMEM;
-			phys_addr = virt_to_bus(BufferList[i].virt_addr);
+			phys_addr = isa_virt_to_bus(BufferList[i].virt_addr);
 		}
 		BufferList[i].phys_addr = phys_addr;
 	}
@@ -7990,10 +7984,6 @@
 	d->get_stats = mgsl_net_stats;
 	d->tx_timeout = mgsl_sppp_tx_timeout;
 	d->watchdog_timeo = 10*HZ;
-
-#if LINUX_VERSION_CODE < VERSION(2,4,4) 
-	dev_init_buffers(d);
-#endif
 
 	if (register_netdev(d) == -1) {
 		printk(KERN_WARNING "%s: register_netdev failed.\n", d->name);
diff -Nru a/drivers/char/tty_io.c b/drivers/char/tty_io.c
--- a/drivers/char/tty_io.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/char/tty_io.c	Mon Apr 22 15:32:19 2002
@@ -1835,6 +1835,9 @@
 	for_each_task(p) {
 		if ((p->tty == tty) ||
 		    ((session > 0) && (p->session == session))) {
+			printk(KERN_NOTICE "SAK: killed process %d"
+			    " (%s): p->session==tty->session\n",
+			    p->pid, p->comm);
 			send_sig(SIGKILL, p, 1);
 			continue;
 		}
@@ -1845,6 +1848,9 @@
 				filp = fcheck_files(p->files, i);
 				if (filp && (filp->f_op == &tty_fops) &&
 				    (filp->private_data == tty)) {
+					printk(KERN_NOTICE "SAK: killed process %d"
+					    " (%s): fd#%d opened to the tty\n",
+					    p->pid, p->comm, i);
 					send_sig(SIGKILL, p, 1);
 					break;
 				}
diff -Nru a/drivers/char/wdt977.c b/drivers/char/wdt977.c
--- a/drivers/char/wdt977.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/char/wdt977.c	Mon Apr 22 15:32:19 2002
@@ -90,7 +90,7 @@
 static int wdt977_open(struct inode *inode, struct file *file)
 {
 
-	if(timer_alive)
+	if( test_and_set_bit(0,&timer_alive) )
 		return -EBUSY;
 
 	/* convert seconds to minutes, rounding up */
@@ -104,7 +104,6 @@
 		/* do not permit disabling the watchdog by writing 0 to reg. 0xF2 */
 		if (!timeoutM) timeoutM = DEFAULT_TIMEOUT;
 	}
-	timer_alive++;
 
 	if (machine_is_netwinder())
 	{
@@ -165,8 +164,6 @@
 	 */
 	if (!nowayout)
 	{
-		lock_kernel();
-
 		/* unlock the SuperIO chip */
 		outb(0x87,0x370);
 		outb(0x87,0x370);
@@ -196,8 +193,7 @@
 		/* lock the SuperIO chip */
 		outb(0xAA,0x370);
 
-		timer_alive=0;
-		unlock_kernel();
+		clear_bit(0,&timer_alive);
 
 		printk(KERN_INFO "Wdt977 Watchdog: shutdown\n");
 	}
diff -Nru a/drivers/fc4/Makefile b/drivers/fc4/Makefile
--- a/drivers/fc4/Makefile	Mon Apr 22 15:32:19 2002
+++ b/drivers/fc4/Makefile	Mon Apr 22 15:32:19 2002
@@ -1,4 +1,3 @@
-# File: drivers/fc4/Makefile
 #
 # Makefile for the Linux Fibre Channel device drivers.
 #
@@ -7,7 +6,6 @@
 
 export-objs := fc_syms.o
 
-list-multi := fc4.o
 fc4-objs := fc.o fc_syms.o
 
 obj-$(CONFIG_FC4) += fc4.o
@@ -15,6 +13,3 @@
 obj-$(CONFIG_FC4_SOCAL) += socal.o
 
 include $(TOPDIR)/Rules.make
-
-fc4.o: $(fc4-objs)
-	$(LD) -r -o $@ $(fc4-objs)
diff -Nru a/drivers/ide/Config.help b/drivers/ide/Config.help
--- a/drivers/ide/Config.help	Mon Apr 22 15:32:19 2002
+++ b/drivers/ide/Config.help	Mon Apr 22 15:32:19 2002
@@ -160,7 +160,7 @@
   you can then use this emulation together with an appropriate SCSI
   device driver. In order to do this, say Y here and to "SCSI support"
   and "SCSI generic support", below. You must then provide the kernel
-  command line "hdx=scsi" (try "man bootparam" or see the
+  command line "hdx=ide-scsi" (try "man bootparam" or see the
   documentation of your boot loader (lilo or loadlin) about how to
   pass options to the kernel at boot time) for devices if you want the
   native EIDE sub-drivers to skip over the native support, so that
@@ -543,13 +543,13 @@
 
 CONFIG_BLK_DEV_PDC4030
   This driver provides support for the secondary IDE interface and
-  cache of Promise IDE chipsets, e.g. DC4030 and DC5030.  This driver
-  is known to incur timeouts/retries during heavy I/O to drives
-  attached to the secondary interface.  CD-ROM and TAPE devices are
-  not supported yet.  This driver is enabled at runtime using the
-  "ide0=dc4030" kernel boot parameter.  See the
-  <file:Documentation/ide.txt> and <file:drivers/ide/pdc4030.c> files
-  for more info.
+  cache of the original Promise IDE chipsets, e.g. DC4030 and DC5030.
+  It is nothing to do with the later range of Promise UDMA chipsets -
+  see the PDC_202XX support for these. CD-ROM and TAPE devices are not
+  supported (and probably never will be since I don't think the cards
+  support them). This driver is enabled at runtime using the "ide0=dc4030"
+  or "ide1=dc4030" kernel boot parameter. See the
+  <file:drivers/ide/pdc4030.c> file for more info.
 
 CONFIG_BLK_DEV_QD65XX
   This driver is enabled at runtime using the "ide0=qd65xx" kernel
@@ -629,6 +629,11 @@
   devices (hard disks, CD-ROM drives, etc.) that are connected to the
   builtin IDE interface.
 
+CONFIG_BLK_DEV_Q40IDE
+  Enable the on-board IDE controller in the Q40/Q60.  This should
+  normally be on; disable it only if you are running a custom hard
+  drive subsystem through an expansion card.
+
 CONFIG_BLK_DEV_IDE_ICSIDE
   On Acorn systems, say Y here if you wish to use the ICS IDE
   interface card.  This is not required for ICS partition support.
@@ -748,23 +753,30 @@
   Support for tagged command queueing on ATA disk drives. This enables
   the IDE layer to have multiple in-flight requests on hardware that
   supports it. For now this includes the IBM Deskstar series drives,
-  such as the GXP75, 40GV, GXP60, and GXP120 (ie any Deskstar made in
-  the last couple of years).
+  such as the 22GXP, 75GXP, 40GV, 60GXP, and 120GXP (ie any Deskstar made
+  in the last couple of years), and at least some of the Western
+  Digital drives in the Expert series.
 
   If you have such a drive, say Y here.
 
-CONFIG_BLK_DEV_IDE_TCQ_DEFAULT
-  Enabled tagged command queueing unconditionally on drives that report
-  support for it.
-
-  Generally say Y here.
-
 CONFIG_BLK_DEV_IDE_TCQ_DEPTH
   Maximum size of commands to enable per-drive. Any value between 1
   and 32 is valid, with 32 being the maxium that the hardware supports.
 
   You probably just want the default of 32 here. If you enter an invalid
   number, the default value will be used.
+
+CONFIG_BLK_DEV_IDE_TCQ_DEFAULT
+  Enabled tagged command queueing unconditionally on drives that report
+  support for it. Regardless of the chosen value here, tagging can be
+  controlled at run time:
+
+  echo "using_tcq:32" > /proc/ide/hdX/settings
+
+  where any value between 1-32 selects chosen queue depth and enables
+  TCQ, and 0 disables it.
+
+  Generally say Y here.
 
 CONFIG_BLK_DEV_IT8172
   Say Y here to support the on-board IDE controller on the Integrated
diff -Nru a/drivers/ide/Config.in b/drivers/ide/Config.in
--- a/drivers/ide/Config.in	Mon Apr 22 15:32:19 2002
+++ b/drivers/ide/Config.in	Mon Apr 22 15:32:19 2002
@@ -48,10 +48,10 @@
          dep_bool '    Enable DMA only for disks ' CONFIG_IDEDMA_ONLYDISK $CONFIG_IDEDMA_PCI_AUTO
 	 define_bool CONFIG_BLK_DEV_IDEDMA $CONFIG_BLK_DEV_IDEDMA_PCI
 	 dep_bool '    ATA tagged command queueing' CONFIG_BLK_DEV_IDE_TCQ $CONFIG_BLK_DEV_IDEDMA_PCI
-	   dep_bool '      TCQ on by default' CONFIG_BLK_DEV_IDE_TCQ_DEFAULT $CONFIG_BLK_DEV_IDE_TCQ
-	   if [ $CONFIG_BLK_DEV_IDE_TCQ_DEFAULT != "n" ]; then
-		int '      Default queue depth' CONFIG_BLK_DEV_IDE_TCQ_DEPTH 32
-	   fi
+	 dep_bool '      TCQ on by default' CONFIG_BLK_DEV_IDE_TCQ_DEFAULT $CONFIG_BLK_DEV_IDE_TCQ
+	 if [ "$CONFIG_BLK_DEV_IDE_TCQ" != "n" ]; then
+	    int '      Default queue depth' CONFIG_BLK_DEV_IDE_TCQ_DEPTH 32
+	 fi
 	 dep_bool '    ATA Work(s) In Progress (EXPERIMENTAL)' CONFIG_IDEDMA_PCI_WIP $CONFIG_BLK_DEV_IDEDMA_PCI $CONFIG_EXPERIMENTAL
 	 dep_bool '    Good-Bad DMA Model-Firmware (WIP)' CONFIG_IDEDMA_NEW_DRIVE_LISTINGS $CONFIG_IDEDMA_PCI_WIP
 	 dep_bool '    AEC62XX chipset support' CONFIG_BLK_DEV_AEC62XX $CONFIG_BLK_DEV_IDEDMA_PCI
diff -Nru a/drivers/ide/Makefile b/drivers/ide/Makefile
--- a/drivers/ide/Makefile	Mon Apr 22 15:32:18 2002
+++ b/drivers/ide/Makefile	Mon Apr 22 15:32:18 2002
@@ -11,7 +11,6 @@
 O_TARGET := idedriver.o
 
 export-objs		:= ide-taskfile.o ide.o ide-features.o ide-probe.o ataraid.o
-list-multi		:= ide-mod.o
 
 obj-y		:=
 obj-m		:=
@@ -78,6 +77,3 @@
 ide-mod-objs		:= ide-taskfile.o ide.o ide-probe.o ide-geometry.o ide-features.o ata-timing.o $(ide-obj-y)
 
 include $(TOPDIR)/Rules.make
-
-ide-mod.o: $(ide-mod-objs)
-	$(LD) -r -o $@ $(ide-mod-objs)
diff -Nru a/drivers/ide/amd74xx.c b/drivers/ide/amd74xx.c
--- a/drivers/ide/amd74xx.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/ide/amd74xx.c	Mon Apr 22 15:32:19 2002
@@ -422,9 +422,10 @@
 	hwif->speedproc = &amd_set_drive;
 	hwif->autodma = 0;
 
+	hwif->io_32bit = 1;
+	hwif->unmask = 1;
+
 	for (i = 0; i < 2; i++) {
-		hwif->drives[i].io_32bit = 1;
-		hwif->drives[i].unmask = 1;
 		hwif->drives[i].autotune = 1;
 		hwif->drives[i].dn = hwif->unit * 2 + i;
 	}
diff -Nru a/drivers/ide/cmd640.c b/drivers/ide/cmd640.c
--- a/drivers/ide/cmd640.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/ide/cmd640.c	Mon Apr 22 15:32:19 2002
@@ -21,7 +21,7 @@
  *
  *  A.Hartgers@stud.tue.nl, JZDQC@CUNYVM.CUNY.edu, abramov@cecmow.enet.dec.com,
  *  bardj@utopia.ppp.sn.no, bart@gaga.tue.nl, bbol001@cs.auckland.ac.nz,
- *  chrisc@dbass.demon.co.uk, dalecki@evision-ventures.com,
+ *  chrisc@dbass.demon.co.uk, martin@dalecki.de,
  *  derekn@vw.ece.cmu.edu, florian@btp2x3.phy.uni-bayreuth.de,
  *  flynn@dei.unipd.it, gadio@netvision.net.il, godzilla@futuris.net,
  *  j@pobox.com, jkemp1@mises.uni-paderborn.de, jtoppe@hiwaay.net,
@@ -403,19 +403,19 @@
  */
 static void __init check_prefetch (unsigned int index)
 {
-	ide_drive_t *drive = cmd_drives[index];
+	struct ata_device *drive = cmd_drives[index];
 	byte b = get_cmd640_reg(prefetch_regs[index]);
 
 	if (b & prefetch_masks[index]) {	/* is prefetch off? */
-		drive->no_unmask = 0;
-		drive->no_io_32bit = 1;
-		drive->io_32bit = 0;
+		drive->channel->no_unmask = 0;
+		drive->channel->no_io_32bit = 1;
+		drive->channel->io_32bit = 0;
 	} else {
 #if CMD640_PREFETCH_MASKS
-		drive->no_unmask = 1;
-		drive->unmask = 0;
+		drive->channel->no_unmask = 1;
+		drive->channel->unmask = 0;
 #endif
-		drive->no_io_32bit = 0;
+		drive->channel->no_io_32bit = 0;
 	}
 }
 
@@ -460,15 +460,15 @@
 	b = get_cmd640_reg(reg);
 	if (mode) {	/* want prefetch on? */
 #if CMD640_PREFETCH_MASKS
-		drive->no_unmask = 1;
-		drive->unmask = 0;
+		drive->channel->no_unmask = 1;
+		drive->channel->unmask = 0;
 #endif
-		drive->no_io_32bit = 0;
+		drive->channel->no_io_32bit = 0;
 		b &= ~prefetch_masks[index];	/* enable prefetch */
 	} else {
-		drive->no_unmask = 0;
-		drive->no_io_32bit = 1;
-		drive->io_32bit = 0;
+		drive->channel->no_unmask = 0;
+		drive->channel->no_io_32bit = 1;
+		drive->channel->io_32bit = 0;
 		b |= prefetch_masks[index];	/* disable prefetch */
 	}
 	put_cmd640_reg(reg, b);
@@ -827,7 +827,7 @@
 			retrieve_drive_counts (index);
 			check_prefetch (index);
 			printk("cmd640: drive%d timings/prefetch(%s) preserved",
-				index, drive->no_io_32bit ? "off" : "on");
+				index, drive->channel->no_io_32bit ? "off" : "on");
 			display_clocks(index);
 		}
 #else
@@ -836,7 +836,7 @@
 		 */
 		check_prefetch (index);
 		printk("cmd640: drive%d timings/prefetch(%s) preserved\n",
-			index, drive->no_io_32bit ? "off" : "on");
+			index, drive->channel->no_io_32bit ? "off" : "on");
 #endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
 	}
 
diff -Nru a/drivers/ide/dtc2278.c b/drivers/ide/dtc2278.c
--- a/drivers/ide/dtc2278.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/ide/dtc2278.c	Mon Apr 22 15:32:19 2002
@@ -88,8 +88,7 @@
 	/*
 	 * 32bit I/O has to be enabled for *both* drives at the same time.
 	 */
-	drive->io_32bit = 1;
-	drive->channel->drives[!drive->select.b.unit].io_32bit = 1;
+	drive->channel->io_32bit = 1;
 }
 
 void __init init_dtc2278 (void)
@@ -120,10 +119,11 @@
 	ide_hwifs[0].chipset = ide_dtc2278;
 	ide_hwifs[1].chipset = ide_dtc2278;
 	ide_hwifs[0].tuneproc = &tune_dtc2278;
-	ide_hwifs[0].drives[0].no_unmask = 1;
-	ide_hwifs[0].drives[1].no_unmask = 1;
-	ide_hwifs[1].drives[0].no_unmask = 1;
-	ide_hwifs[1].drives[1].no_unmask = 1;
+	/* FIXME: What about the following?!
+	ide_hwifs[1].tuneproc = &tune_dtc2278;
+	 */
+	ide_hwifs[0].no_unmask = 1;
+	ide_hwifs[1].no_unmask = 1;
 	ide_hwifs[0].unit = ATA_PRIMARY;
 	ide_hwifs[1].unit = ATA_SECONDARY;
 }
diff -Nru a/drivers/ide/ht6560b.c b/drivers/ide/ht6560b.c
--- a/drivers/ide/ht6560b.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/ide/ht6560b.c	Mon Apr 22 15:32:19 2002
@@ -261,11 +261,11 @@
 	 */
 	if (state) {
 		drive->drive_data |= t;   /* enable prefetch mode */
-		drive->no_unmask = 1;
-		drive->unmask = 0;
+		drive->channel->no_unmask = 1;
+		drive->channel->unmask = 0;
 	} else {
 		drive->drive_data &= ~t;  /* disable prefetch mode */
-		drive->no_unmask = 0;
+		drive->channel->no_unmask = 0;
 	}
 	
 	restore_flags (flags);	/* all CPUs */
diff -Nru a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c
--- a/drivers/ide/ide-cd.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/ide/ide-cd.c	Mon Apr 22 15:32:19 2002
@@ -1,6 +1,4 @@
 /*
- * linux/drivers/ide/ide-cd.c
- *
  * Copyright (C) 1994, 1995, 1996  scott snyder  <snyder@fnald0.fnal.gov>
  * Copyright (C) 1996-1998  Erik Andersen <andersee@debian.org>
  * Copyright (C) 1998-2000  Jens Axboe <axboe@suse.de>
@@ -13,9 +11,10 @@
  *
  * Suggestions are welcome. Patches that work are more welcome though. ;-)
  * For those wishing to work on this driver, please be sure you download
- * and comply with the latest Mt. Fuji (SFF8090 version 4) and ATAPI 
- * (SFF-8020i rev 2.6) standards. These documents can be obtained by 
+ * and comply with the latest Mt. Fuji (SFF8090 version 4) and ATAPI
+ * (SFF-8020i rev 2.6) standards. These documents can be obtained by
  * anonymous ftp from:
+ *
  * ftp://fission.dt.wdc.com/pub/standards/SFF_atapi/spec/SFF8020-r2.6/PS/8020r26.ps
  * ftp://ftp.avc-pioneer.com/Mtfuji4/Spec/Fuji4r10.pdf
  *
@@ -54,18 +53,18 @@
  *                       Aztech drives, which seem to have the same problem.
  * 2.04b May 30, 1995 -- Fix to match changes in ide.c version 3.16 -ml
  * 2.05  Jun  8, 1995 -- Don't attempt to retry after an illegal request
- *                        or data protect error.
+ *                       or data protect error.
  *                       Use HWIF and DEV_HWIF macros as in ide.c.
  *                       Always try to do a request_sense after
- *                        a failed command.
+ *                       a failed command.
  *                       Include an option to give textual descriptions
- *                        of ATAPI errors.
+ *                       of ATAPI errors.
  *                       Fix a bug in handling the sector cache which
- *                        showed up if the drive returned data in 512 byte
- *                        blocks (like Pioneer drives).  Thanks to
- *                        Richard Hirst <srh@gpt.co.uk> for diagnosing this.
+ *                       showed up if the drive returned data in 512 byte
+ *                       blocks (like Pioneer drives).  Thanks to
+ *                       Richard Hirst <srh@gpt.co.uk> for diagnosing this.
  *                       Properly supply the page number field in the
- *                        MODE_SELECT command.
+ *                       MODE_SELECT command.
  *                       PLAYAUDIO12 is broken on the Aztech; work around it.
  * 2.05x Aug 11, 1995 -- lots of data structure renaming/restructuring in ide.c
  *                       (my apologies to Scott, but now ide-cd.c is independent)
@@ -74,28 +73,28 @@
  *                       Use input_ide_data() and output_ide_data().
  *                       Add door locking.
  *                       Fix usage count leak in cdrom_open, which happened
- *                        when a read-write mount was attempted.
+ *                       when a read-write mount was attempted.
  *                       Try to load the disk on open.
  *                       Implement CDROMEJECT_SW ioctl (off by default).
  *                       Read total cdrom capacity during open.
  *                       Rearrange logic in cdrom_decode_status.  Issue
- *                        request sense commands for failed packet commands
- *                        from here instead of from cdrom_queue_packet_command.
- *                        Fix a race condition in retrieving error information.
+ *                       request sense commands for failed packet commands
+ *                       from here instead of from cdrom_queue_packet_command.
+ *                       Fix a race condition in retrieving error information.
  *                       Suppress printing normal unit attention errors and
- *                        some drive not ready errors.
+ *                       some drive not ready errors.
  *                       Implement CDROMVOLREAD ioctl.
  *                       Implement CDROMREADMODE1/2 ioctls.
  *                       Fix race condition in setting up interrupt handlers
- *                        when the `serialize' option is used.
+ *                       when the `serialize' option is used.
  * 3.01  Sep  2, 1995 -- Fix ordering of reenabling interrupts in
- *                        cdrom_queue_request.
+ *                       cdrom_queue_request.
  *                       Another try at using ide_[input,output]_data.
  * 3.02  Sep 16, 1995 -- Stick total disk capacity in partition table as well.
  *                       Make VERBOSE_IDE_CD_ERRORS dump failed command again.
  *                       Dump out more information for ILLEGAL REQUEST errs.
  *                       Fix handling of errors occurring before the
- *                        packet command is transferred.
+ *                       packet command is transferred.
  *                       Fix transfers with odd bytelengths.
  * 3.03  Oct 27, 1995 -- Some Creative drives have an id of just `CD'.
  *                       `DCI-2S10' drives are broken too.
@@ -103,17 +102,17 @@
  * 3.05  Dec  1, 1995 -- Changes to go with overhaul of ide.c and ide-tape.c
  * 3.06  Dec 16, 1995 -- Add support needed for partitions.
  *                       More workarounds for Vertos bugs (based on patches
- *                        from Holger Dietze <dietze@aix520.informatik.uni-leipzig.de>).
+ *                       from Holger Dietze <dietze@aix520.informatik.uni-leipzig.de>).
  *                       Try to eliminate byteorder assumptions.
  *                       Use atapi_cdrom_subchnl struct definition.
  *                       Add STANDARD_ATAPI compilation option.
  * 3.07  Jan 29, 1996 -- More twiddling for broken drives: Sony 55D,
- *                        Vertos 300.
+ *                       Vertos 300.
  *                       Add NO_DOOR_LOCKING configuration option.
  *                       Handle drive_cmd requests w/NULL args (for hdparm -t).
  *                       Work around sporadic Sony55e audio play problem.
  * 3.07a Feb 11, 1996 -- check drive->id for NULL before dereferencing, to fix
- *                        problem with "hde=cdrom" with no drive present.  -ml
+ *                       problem with "hde=cdrom" with no drive present.  -ml
  * 3.08  Mar  6, 1996 -- More Vertos workarounds.
  * 3.09  Apr  5, 1996 -- Add CDROMCLOSETRAY ioctl.
  *                       Switch to using MSF addressing for audio commands.
@@ -129,47 +128,47 @@
  * 3.14  May 29, 1996 -- Add work-around for Vertos 600.
  *                        (From Hennus Bergman <hennus@sky.ow.nl>.)
  * 3.15  July 2, 1996 -- Added support for Sanyo 3 CD changers
- *                        from Ben Galliart <bgallia@luc.edu> with 
- *                        special help from Jeff Lightfoot 
- *                        <jeffml@pobox.com>
+ *                       from Ben Galliart <bgallia@luc.edu> with
+ *                       special help from Jeff Lightfoot
+ *                       <jeffml@pobox.com>
  * 3.15a July 9, 1996 -- Improved Sanyo 3 CD changer identification
  * 3.16  Jul 28, 1996 -- Fix from Gadi to reduce kernel stack usage for ioctl.
  * 3.17  Sep 17, 1996 -- Tweak audio reads for some drives.
  *                       Start changing CDROMLOADFROMSLOT to CDROM_SELECT_DISC.
  * 3.18  Oct 31, 1996 -- Added module and DMA support.
- *                       
- *                       
+ *
+ *
  * 4.00  Nov 5, 1996   -- New ide-cd maintainer,
- *                                 Erik B. Andersen <andersee@debian.org>
+ *                        Erik B. Andersen <andersee@debian.org>
  *                     -- Newer Creative drives don't always set the error
- *                          register correctly.  Make sure we see media changes
- *                          regardless.
+ *                        register correctly.  Make sure we see media changes
+ *                        regardless.
  *                     -- Integrate with generic cdrom driver.
  *                     -- CDROMGETSPINDOWN and CDROMSETSPINDOWN ioctls, based on
- *                          a patch from Ciro Cattuto <>.
+ *                        a patch from Ciro Cattuto <>.
  *                     -- Call set_device_ro.
  *                     -- Implement CDROMMECHANISMSTATUS and CDROMSLOTTABLE
- *                          ioctls, based on patch by Erik Andersen
+ *                        ioctls, based on patch by Erik Andersen
  *                     -- Add some probes of drive capability during setup.
  *
  * 4.01  Nov 11, 1996  -- Split into ide-cd.c and ide-cd.h
- *                     -- Removed CDROMMECHANISMSTATUS and CDROMSLOTTABLE 
- *                          ioctls in favor of a generalized approach 
- *                          using the generic cdrom driver.
+ *                     -- Removed CDROMMECHANISMSTATUS and CDROMSLOTTABLE
+ *                        ioctls in favor of a generalized approach
+ *                        using the generic cdrom driver.
  *                     -- Fully integrated with the 2.1.X kernel.
  *                     -- Other stuff that I forgot (lots of changes)
  *
  * 4.02  Dec 01, 1996  -- Applied patch from Gadi Oxman <gadio@netvision.net.il>
- *                          to fix the drive door locking problems.
+ *                        to fix the drive door locking problems.
  *
  * 4.03  Dec 04, 1996  -- Added DSC overlap support.
- * 4.04  Dec 29, 1996  -- Added CDROMREADRAW ioclt based on patch 
- *                          by Ales Makarov (xmakarov@sun.felk.cvut.cz)
+ * 4.04  Dec 29, 1996  -- Added CDROMREADRAW ioclt based on patch
+ *                        by Aleks Makarov (xmakarov@sun.felk.cvut.cz)
  *
  * 4.05  Nov 20, 1997  -- Modified to print more drive info on init
  *                        Minor other changes
  *                        Fix errors on CDROMSTOP (If you have a "Dolphin",
- *                          you must define IHAVEADOLPHIN)
+ *                        you must define IHAVEADOLPHIN)
  *                        Added identifier so new Sanyo CD-changer works
  *                        Better detection if door locking isn't supported
  *
@@ -178,40 +177,40 @@
  * 4.08  Dec 18, 1997  -- spew less noise when tray is empty
  *                     -- fix speed display for ACER 24X, 18X
  * 4.09  Jan 04, 1998  -- fix handling of the last block so we return
- *                         an end of file instead of an I/O error (Gadi)
+ *                        an end of file instead of an I/O error (Gadi)
  * 4.10  Jan 24, 1998  -- fixed a bug so now changers can change to a new
- *                         slot when there is no disc in the current slot.
+ *                        slot when there is no disc in the current slot.
  *                     -- Fixed a memory leak where info->changer_info was
- *                         malloc'ed but never free'd when closing the device.
+ *                        malloc'ed but never free'd when closing the device.
  *                     -- Cleaned up the global namespace a bit by making more
- *                         functions static that should already have been.
+ *                        functions static that should already have been.
  * 4.11  Mar 12, 1998  -- Added support for the CDROM_SELECT_SPEED ioctl
- *                         based on a patch for 2.0.33 by Jelle Foks 
- *                         <jelle@scintilla.utwente.nl>, a patch for 2.0.33
- *                         by Toni Giorgino <toni@pcape2.pi.infn.it>, the SCSI
- *                         version, and my own efforts.  -erik
+ *                        based on a patch for 2.0.33 by Jelle Foks
+ *                        <jelle@scintilla.utwente.nl>, a patch for 2.0.33
+ *                        by Toni Giorgino <toni@pcape2.pi.infn.it>, the SCSI
+ *                        version, and my own efforts.  -erik
  *                     -- Fixed a stupid bug which egcs was kind enough to
- *                         inform me of where "Illegal mode for this track"
- *                         was never returned due to a comparison on data
- *                         types of limited range.
- * 4.12  Mar 29, 1998  -- Fixed bug in CDROM_SELECT_SPEED so write speed is 
- *                         now set ionly for CD-R and CD-RW drives.  I had 
- *                         removed this support because it produced errors.
- *                         It produced errors _only_ for non-writers. duh.
+ *                        inform me of where "Illegal mode for this track"
+ *                        was never returned due to a comparison on data
+ *                        types of limited range.
+ * 4.12  Mar 29, 1998  -- Fixed bug in CDROM_SELECT_SPEED so write speed is
+ *                        now set ionly for CD-R and CD-RW drives.  I had
+ *                        removed this support because it produced errors.
+ *                        It produced errors _only_ for non-writers. duh.
  * 4.13  May 05, 1998  -- Suppress useless "in progress of becoming ready"
- *                         messages, since this is not an error.
+ *                        messages, since this is not an error.
  *                     -- Change error messages to be const
  *                     -- Remove a "\t" which looks ugly in the syslogs
  * 4.14  July 17, 1998 -- Change to pointing to .ps version of ATAPI spec
- *                         since the .pdf version doesn't seem to work...
+ *                        since the .pdf version doesn't seem to work...
  *                     -- Updated the TODO list to something more current.
  *
- * 4.15  Aug 25, 1998  -- Updated ide-cd.h to respect mechine endianess, 
- *                         patch thanks to "Eddie C. Dost" <ecd@skynet.be>
+ * 4.15  Aug 25, 1998  -- Updated ide-cd.h to respect mechine endianess,
+ *                        patch thanks to "Eddie C. Dost" <ecd@skynet.be>
  *
  * 4.50  Oct 19, 1998  -- New maintainers!
- *                         Jens Axboe <axboe@image.dk>
- *                         Chris Zwilling <chris@cloudnet.com>
+ *                        Jens Axboe <axboe@image.dk>
+ *                        Chris Zwilling <chris@cloudnet.com>
  *
  * 4.51  Dec 23, 1998  -- Jens Axboe <axboe@image.dk>
  *                      - ide_cdrom_reset enabled since the ide subsystem
@@ -224,17 +223,17 @@
  *                      - Detect DVD-ROM/RAM drives
  *
  * 4.53  Feb 22, 1999   - Include other model Samsung and one Goldstar
- *                         drive in transfer size limit.
+ *                        drive in transfer size limit.
  *                      - Fix the I/O error when doing eject without a medium
- *                         loaded on some drives.
+ *                        loaded on some drives.
  *                      - CDROMREADMODE2 is now implemented through
- *                         CDROMREADRAW, since many drives don't support
- *                         MODE2 (even though ATAPI 2.6 says they must).
+ *                        CDROMREADRAW, since many drives don't support
+ *                        MODE2 (even though ATAPI 2.6 says they must).
  *                      - Added ignore parameter to ide-cd (as a module), eg
- *                         	insmod ide-cd ignore='hda hdb'
- *                         Useful when using ide-cd in conjunction with
- *                         ide-scsi. TODO: non-modular way of doing the
- *                         same.
+ *			  insmod ide-cd ignore='hda hdb'
+ *                        Useful when using ide-cd in conjunction with
+ *                        ide-scsi. TODO: non-modular way of doing the
+ *                        same.
  *
  * 4.54  Aug 5, 1999	- Support for MMC2 class commands through the generic
  *			  packet interface to cdrom.c.
@@ -270,7 +269,7 @@
  *			- Mode sense and mode select moved to the
  *			  Uniform layer.
  *			- Fixed a problem with WPI CDS-32X drive - it
- *			  failed the capabilities 
+ *			  failed the capabilities
  *
  * 4.57  Apr 7, 2000	- Fixed sense reporting.
  *			- Fixed possible oops in ide_cdrom_get_last_session()
@@ -293,7 +292,7 @@
  *			  Michael D Johnson <johnsom@orst.edu>
  *
  *************************************************************************/
- 
+
 #define IDECD_VERSION "4.59"
 
 #include <linux/config.h>
@@ -326,63 +325,70 @@
 static void cdrom_saw_media_change (ide_drive_t *drive)
 {
 	struct cdrom_info *info = drive->driver_data;
-	
+
 	CDROM_STATE_FLAGS (drive)->media_changed = 1;
 	CDROM_STATE_FLAGS (drive)->toc_valid = 0;
 	info->nsectors_buffered = 0;
 }
 
-static int cdrom_log_sense(ide_drive_t *drive, struct packet_command *pc,
-			   struct request_sense *sense)
+static
+void cdrom_analyze_sense_data(ide_drive_t *drive, struct request *rq)
 {
 	int log = 0;
+	/* FIXME --mdcki */
+	struct packet_command *pc = (struct packet_command *) rq->special;
+	struct packet_command *failed_command = pc->failed_command;
 
-	if (sense == NULL || pc == NULL || pc->quiet)
-		return 0;
+	/* Decode sense data from drive */
+	struct request_sense *sense = (struct request_sense *) (pc->buffer - rq->cmd[4]);
+	unsigned char fail_cmd;
 
+	if (sense == NULL || failed_command == NULL || failed_command->quiet)
+		return;
+
+	fail_cmd = rq->cmd[0];
+
+	/* Check whatever this error should be logged:
+	 */
 	switch (sense->sense_key) {
-		case NO_SENSE: case RECOVERED_ERROR:
+		case NO_SENSE:
+		case RECOVERED_ERROR:
 			break;
+
 		case NOT_READY:
-			/*
-			 * don't care about tray state messages for
-			 * e.g. capacity commands or in-progress or
-			 * becoming ready
+
+			/* Don't care about tray state messages for e.g.
+			 * capacity commands or in-progress or becoming ready.
 			 */
 			if (sense->asc == 0x3a || sense->asc == 0x04)
 				break;
 			log = 1;
 			break;
+
 		case UNIT_ATTENTION:
-			/*
-			 * Make good and sure we've seen this potential media
+
+			/* Make good and sure we've seen this potential media
 			 * change. Some drives (i.e. Creative) fail to present
 			 * the correct sense key in the error register.
 			 */
 			cdrom_saw_media_change(drive);
 			break;
+
 		default:
 			log = 1;
 			break;
 	}
-	return log;
-}
-
-static
-void cdrom_analyze_sense_data(ide_drive_t *drive,
-			      struct packet_command *failed_command,
-			      struct request_sense *sense)
-{
 
-	if (!cdrom_log_sense(drive, failed_command, sense))
+	if (!log)
 		return;
 
 	/*
-	 * If a read toc is executed for a CD-R or CD-RW medium where
-	 * the first toc has not been recorded yet, it will fail with
-	 * 05/24/00 (which is a confusing error)
+	 * If a read toc is executed for a CD-R or CD-RW medium where the first
+	 * toc has not been recorded yet, it will fail with 05/24/00 (which is
+	 * a confusing error).
 	 */
-	if (failed_command && failed_command->c[0] == GPCMD_READ_TOC_PMA_ATIP)
+
+	if (fail_cmd == GPCMD_READ_TOC_PMA_ATIP)
 		if (sense->sense_key == 0x05 && sense->asc == 0x24)
 			return;
 
@@ -445,28 +451,26 @@
 		printk("  %s -- (asc=0x%02x, ascq=0x%02x)\n",
 			s, sense->asc, sense->ascq);
 
-		if (failed_command != NULL) {
+		{
 
 			int lo=0, mid, hi= ARY_LEN (packet_command_texts);
 			s = NULL;
 
 			while (hi > lo) {
 				mid = (lo + hi) / 2;
-				if (packet_command_texts[mid].packet_command ==
-				    failed_command->c[0]) {
+				if (packet_command_texts[mid].packet_command == fail_cmd) {
 					s = packet_command_texts[mid].text;
 					break;
 				}
-				if (packet_command_texts[mid].packet_command >
-				    failed_command->c[0])
+				if (packet_command_texts[mid].packet_command > fail_cmd)
 					hi = mid;
 				else
 					lo = mid+1;
 			}
 
 			printk ("  The failed \"%s\" packet command was: \n  \"", s);
-			for (i=0; i<sizeof (failed_command->c); i++)
-				printk ("%02x ", failed_command->c[i]);
+			for (i=0; i < CDROM_PACKET_SIZE; i++)
+				printk ("%02x ", rq->cmd[i]);
 			printk ("\"\n");
 		}
 
@@ -495,7 +499,7 @@
 		}
 	}
 
-#else /* not VERBOSE_IDE_CD_ERRORS */
+#else
 
 	/* Suppress printing unit attention and `in progress of becoming ready'
 	   errors when we're not being verbose. */
@@ -509,7 +513,7 @@
 		drive->name,
 		sense->error_code, sense->sense_key,
 		sense->asc, sense->ascq);
-#endif /* not VERBOSE_IDE_CD_ERRORS */
+#endif
 }
 
 static void cdrom_queue_request_sense(ide_drive_t *drive,
@@ -524,17 +528,21 @@
 	if (sense == NULL)
 		sense = &info->sense_data;
 
-	memset(pc, 0, sizeof(struct packet_command));
-	pc->c[0] = GPCMD_REQUEST_SENSE;
-	pc->c[4] = pc->buflen = 18;
-	pc->buffer = (char *) sense;
-	pc->sense = (struct request_sense *) failed_command;
+	memset(pc, 0, sizeof(*pc));
+	pc->buffer = (void *) sense;
+	pc->buflen = 18;
+	pc->failed_command = failed_command;
 
 	/* stuff the sense request in front of our current request */
 	rq = &info->request_sense_request;
 	ide_init_drive_cmd(rq);
+	rq->cmd[0] = GPCMD_REQUEST_SENSE;
+	rq->cmd[4] = pc->buflen;
 	rq->flags = REQ_SENSE;
+
+	/* FIXME --mdcki */
 	rq->special = (char *) pc;
+
 	rq->waiting = wait;
 	ide_do_drive_cmd(drive, rq, ide_preempt);
 }
@@ -544,17 +552,16 @@
 {
 	struct request *rq = HWGROUP(drive)->rq;
 
-	if ((rq->flags & REQ_SENSE) && uptodate) {
-		struct packet_command *pc = (struct packet_command *) rq->special;
-		cdrom_analyze_sense_data(drive,
-			(struct packet_command *) pc->sense,
-			(struct request_sense *) (pc->buffer - pc->c[4]));
-	}
+	if ((rq->flags & REQ_SENSE) && uptodate)
+		cdrom_analyze_sense_data(drive, rq);
 
 	if ((rq->flags & REQ_CMD) && !rq->current_nr_sectors)
 		uptodate = 1;
 
+#if 0
+	/* FIXME --mdcki */
 	HWGROUP(drive)->rq->special = NULL;
+#endif
 	ide_end_request(drive, uptodate);
 }
 
@@ -567,7 +574,7 @@
 	struct request *rq = HWGROUP(drive)->rq;
 	int stat, err, sense_key;
 	struct packet_command *pc;
-	
+
 	/* Check for errors. */
 	stat = GET_STAT();
 	*stat_ret = stat;
@@ -590,14 +597,18 @@
 		   from the drive (probably while trying
 		   to recover from a former error).  Just give up. */
 
+		/* FIXME --mdcki */
 		pc = (struct packet_command *) rq->special;
 		pc->stat = 1;
 		cdrom_end_request(drive, 1);
 		*startstop = ide_error (drive, "request sense failure", stat);
+
 		return 1;
 	} else if (rq->flags & (REQ_PC | REQ_BLOCK_PC)) {
 		/* All other functions, except for READ. */
 		struct completion *wait = NULL;
+
+		/* FIXME --mdcki */
 		pc = (struct packet_command *) rq->special;
 
 		/* Check for tray open. */
@@ -612,15 +623,14 @@
 			/* Otherwise, print an error. */
 			ide_dump_status(drive, "packet command error", stat);
 		}
-		
+
 		/* Set the error flag and complete the request.
-		   Then, if we have a CHECK CONDITION status,
-		   queue a request sense command.  We must be careful,
-		   though: we don't want the thread in
-		   cdrom_queue_packet_command to wake up until
-		   the request sense has completed.  We do this
-		   by transferring the semaphore from the packet
-		   command request to the request sense request. */
+		   Then, if we have a CHECK CONDITION status, queue a request
+		   sense command.  We must be careful, though: we don't want
+		   the thread in cdrom_queue_packet_command to wake up until
+		   the request sense has completed.  We do this by transferring
+		   the semaphore from the packet command request to the request
+		   sense request. */
 
 		if ((stat & ERR_STAT) != 0) {
 			wait = rq->waiting;
@@ -630,6 +640,10 @@
 		pc->stat = 1;
 		cdrom_end_request(drive, 1);
 
+		/* FIXME: this is the only place where pc->sense get's used.
+		 * Think hard about how to get rid of it...
+		 */
+
 		if ((stat & ERR_STAT) != 0)
 			cdrom_queue_request_sense(drive, wait, pc->sense, pc);
 	} else if (rq->flags & REQ_CMD) {
@@ -657,6 +671,12 @@
 			   request or data protect error.*/
 			ide_dump_status (drive, "command error", stat);
 			cdrom_end_request(drive, 0);
+		} else if (sense_key == MEDIUM_ERROR) {
+			/* No point in re-trying a zillion times on a bad
+			 * sector.  The error is not correctable at all.
+			 */
+			ide_dump_status (drive, "media error (bad sector)", stat);
+			cdrom_end_request(drive, 0);
 		} else if ((err & ~ABRT_ERR) != 0) {
 			/* Go to the default handler
 			   for other errors. */
@@ -723,27 +743,26 @@
 		return startstop;
 
 	if (info->dma) {
-		if (info->cmd == READ) {
+		if (info->cmd == READ)
 			info->dma = !drive->channel->dmaproc(ide_dma_read, drive);
-		} else if (info->cmd == WRITE) {
+		else if (info->cmd == WRITE)
 			info->dma = !drive->channel->dmaproc(ide_dma_write, drive);
-		} else {
+		else
 			printk("ide-cd: DMA set, but not allowed\n");
-		}
 	}
 
 	/* Set up the controller registers. */
-	OUT_BYTE (info->dma, IDE_FEATURE_REG);
-	OUT_BYTE (0, IDE_NSECTOR_REG);
-	OUT_BYTE (0, IDE_SECTOR_REG);
+	OUT_BYTE(info->dma, IDE_FEATURE_REG);
+	OUT_BYTE(0, IDE_NSECTOR_REG);
+	OUT_BYTE(0, IDE_SECTOR_REG);
 
-	OUT_BYTE (xferlen & 0xff, IDE_LCYL_REG);
-	OUT_BYTE (xferlen >> 8  , IDE_HCYL_REG);
+	OUT_BYTE(xferlen & 0xff, IDE_LCYL_REG);
+	OUT_BYTE(xferlen >> 8  , IDE_HCYL_REG);
 	if (IDE_CONTROL_REG)
 		OUT_BYTE (drive->ctl, IDE_CONTROL_REG);
 
 	if (info->dma)
-		(void) drive->channel->dmaproc(ide_dma_begin, drive);
+		drive->channel->dmaproc(ide_dma_begin, drive);
 
 	if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) {
 		BUG_ON(HWGROUP(drive)->handler);
@@ -756,22 +775,15 @@
 	}
 }
 
-/* Send a packet command to DRIVE described by CMD_BUF and CMD_LEN.
-   The device registers must have already been prepared
-   by cdrom_start_packet_command.
-   HANDLER is the interrupt handler to call when the command completes
-   or there's data ready. */
 /*
- * changed 5 parameters to 3 for dvd-ram
- * struct packet_command *pc; now packet_command_t *pc;
+ * Send a packet command cmd to the drive.  The device registers must have
+ * already been prepared by cdrom_start_packet_command.  "handler" is the
+ * interrupt handler to call when the command completes or there's data ready.
  */
 static ide_startstop_t cdrom_transfer_packet_command (ide_drive_t *drive,
-					  struct packet_command *pc,
-					  ide_handler_t *handler)
+		unsigned char *cmd, unsigned long timeout,
+		ide_handler_t *handler)
 {
-	unsigned char *cmd_buf	= pc->c;
-	int cmd_len		= sizeof(pc->c);
-	unsigned int timeout	= pc->timeout;
 	ide_startstop_t startstop;
 
 	if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) {
@@ -780,20 +792,21 @@
 		int stat_dum;
 
 		/* Check for errors. */
-		if (cdrom_decode_status (&startstop, drive, DRQ_STAT, &stat_dum))
+		if (cdrom_decode_status(&startstop, drive, DRQ_STAT, &stat_dum))
 			return startstop;
 	} else {
 		/* Otherwise, we must wait for DRQ to get set. */
-		if (ide_wait_stat (&startstop, drive, DRQ_STAT, BUSY_STAT, WAIT_READY))
+		if (ide_wait_stat(&startstop, drive, DRQ_STAT, BUSY_STAT, WAIT_READY))
 			return startstop;
 	}
 
 	/* Arm the interrupt handler. */
 	BUG_ON(HWGROUP(drive)->handler);
-	ide_set_handler (drive, handler, timeout, cdrom_timer_expiry);
+	ide_set_handler(drive, handler, timeout, cdrom_timer_expiry);
 
 	/* Send the command to the device. */
-	atapi_output_bytes (drive, cmd_buf, cmd_len);
+	atapi_write(drive, cmd, CDROM_PACKET_SIZE);
+
 	return ide_started;
 }
 
@@ -831,7 +844,7 @@
 	/* Read the data into the buffer. */
 	dest = info->buffer + info->nsectors_buffered * SECTOR_SIZE;
 	while (sectors_to_buffer > 0) {
-		atapi_input_bytes (drive, dest, SECTOR_SIZE);
+		atapi_read(drive, dest, SECTOR_SIZE);
 		--sectors_to_buffer;
 		--sectors_to_transfer;
 		++info->nsectors_buffered;
@@ -841,7 +854,7 @@
 	/* Throw away any remaining data. */
 	while (sectors_to_transfer > 0) {
 		char dum[SECTOR_SIZE];
-		atapi_input_bytes (drive, dum, sizeof (dum));
+		atapi_read(drive, dum, sizeof (dum));
 		--sectors_to_transfer;
 	}
 }
@@ -867,7 +880,7 @@
 		   and quit this request. */
 		while (len > 0) {
 			int dum = 0;
-			atapi_output_bytes (drive, &dum, sizeof (dum));
+			atapi_write(drive, &dum, sizeof (dum));
 			len -= sizeof (dum);
 		}
 	} else  if (ireason == 1) {
@@ -889,7 +902,7 @@
 /*
  * Interrupt routine.  Called when a read request has completed.
  */
-static ide_startstop_t cdrom_read_intr (ide_drive_t *drive)
+static ide_startstop_t cdrom_read_intr(ide_drive_t *drive)
 {
 	int stat;
 	int ireason, len, sectors_to_transfer, nskip;
@@ -908,7 +921,7 @@
 
 	if (cdrom_decode_status (&startstop, drive, 0, &stat))
 		return startstop;
- 
+
 	if (dma) {
 		if (!dma_error) {
 			__ide_end_request(drive, 1, rq->nr_sectors);
@@ -964,7 +977,7 @@
 	while (nskip > 0) {
 		/* We need to throw away a sector. */
 		char dum[SECTOR_SIZE];
-		atapi_input_bytes (drive, dum, sizeof (dum));
+		atapi_read(drive, dum, SECTOR_SIZE);
 
 		--rq->current_nr_sectors;
 		--nskip;
@@ -995,7 +1008,7 @@
 			/* Read this_transfer sectors
 			   into the current buffer. */
 			while (this_transfer > 0) {
-				atapi_input_bytes(drive, rq->buffer, SECTOR_SIZE);
+				atapi_read(drive, rq->buffer, SECTOR_SIZE);
 				rq->buffer += SECTOR_SIZE;
 				--rq->nr_sectors;
 				--rq->current_nr_sectors;
@@ -1071,14 +1084,12 @@
 }
 
 /*
- * Routine to send a read packet command to the drive.
- * This is usually called directly from cdrom_start_read.
- * However, for drq_interrupt devices, it is called from an interrupt
- * when the drive is ready to accept the command.
+ * Routine to send a read packet command to the drive.  This is usually called
+ * directly from cdrom_start_read.  However, for drq_interrupt devices, it is
+ * called from an interrupt when the drive is ready to accept the command.
  */
-static ide_startstop_t cdrom_start_read_continuation (ide_drive_t *drive)
+static ide_startstop_t cdrom_start_read_continuation(ide_drive_t *drive)
 {
-	struct packet_command pc;
 	struct request *rq = HWGROUP(drive)->rq;
 	int nsect, sector, nframes, frame, nskip;
 
@@ -1117,15 +1128,11 @@
 
 	/* Largest number of frames was can transfer at once is 64k-1. For
 	   some drives we need to limit this even more. */
-	nframes = MIN (nframes, (CDROM_CONFIG_FLAGS (drive)->limit_nframes) ?
+	nframes = MIN(nframes, (CDROM_CONFIG_FLAGS (drive)->limit_nframes) ?
 		(65534 / CD_FRAMESIZE) : 65535);
 
-	/* Set up the command */
-	memcpy(pc.c, rq->cmd, sizeof(pc.c));
-	pc.timeout = WAIT_CMD;
-
 	/* Send the command to the drive and return. */
-	return cdrom_transfer_packet_command(drive, &pc, &cdrom_read_intr);
+	return cdrom_transfer_packet_command(drive, rq->cmd, WAIT_CMD, &cdrom_read_intr);
 }
 
 
@@ -1161,7 +1168,7 @@
 
 static ide_startstop_t cdrom_start_seek_continuation (ide_drive_t *drive)
 {
-	struct packet_command pc;
+	unsigned char cmd[CDROM_PACKET_SIZE];
 	struct request *rq = HWGROUP(drive)->rq;
 	int sector, frame, nskip;
 
@@ -1172,11 +1179,10 @@
 	frame = sector / SECTORS_PER_FRAME;
 
 	memset(rq->cmd, 0, sizeof(rq->cmd));
-	pc.c[0] = GPCMD_SEEK;
-	put_unaligned(cpu_to_be32(frame), (unsigned int *) &pc.c[2]);
+	cmd[0] = GPCMD_SEEK;
+	put_unaligned(cpu_to_be32(frame), (unsigned int *) &cmd[2]);
 
-	pc.timeout = WAIT_CMD;
-	return cdrom_transfer_packet_command(drive, &pc, &cdrom_seek_intr);
+	return cdrom_transfer_packet_command(drive, cmd, WAIT_CMD, &cdrom_seek_intr);
 }
 
 static ide_startstop_t cdrom_start_seek (ide_drive_t *drive, unsigned int block)
@@ -1211,13 +1217,22 @@
 /*
  * Start a read request from the CD-ROM.
  */
-static ide_startstop_t cdrom_start_read (ide_drive_t *drive, unsigned int block)
+static ide_startstop_t cdrom_start_read(struct ata_device *drive, struct ata_request *ar, unsigned int block)
 {
 	struct cdrom_info *info = drive->driver_data;
-	struct request *rq = HWGROUP(drive)->rq;
+	struct request *rq = ar->ar_rq;
+
+	if (ar->ar_flags & ATA_AR_QUEUED) {
+//		spin_lock_irqsave(DRIVE_LOCK(drive), flags);
+		blkdev_dequeue_request(rq);
+//		spin_unlock_irqrestore(DRIVE_LOCK(drive), flags);
+	}
+
 
 	restore_request(rq);
 
+	rq->special = ar;
+
 	/* Satisfy whatever we can of this request from our cached sector. */
 	if (cdrom_read_from_buffer(drive))
 		return ide_stopped;
@@ -1243,15 +1258,13 @@
  * Execute all other packet commands.
  */
 
-/* Forward declarations. */
-static int cdrom_lockdoor(ide_drive_t *drive, int lockflag,
-			  struct request_sense *sense);
-
 /* Interrupt routine for packet command completion. */
 static ide_startstop_t cdrom_pc_intr (ide_drive_t *drive)
 {
 	int ireason, len, stat, thislen;
 	struct request *rq = HWGROUP(drive)->rq;
+
+	/* FIXME --mdcki */
 	struct packet_command *pc = (struct packet_command *) rq->special;
 	ide_startstop_t startstop;
 
@@ -1268,7 +1281,7 @@
 	if ((stat & DRQ_STAT) == 0) {
 		/* Some of the trailing request sense fields are optional, and
 		   some drives don't send them.  Sigh. */
-		if (pc->c[0] == GPCMD_REQUEST_SENSE &&
+		if (rq->cmd[0] == GPCMD_REQUEST_SENSE &&
 		    pc->buflen > 0 &&
 		    pc->buflen <= 5) {
 			while (pc->buflen > 0) {
@@ -1300,13 +1313,14 @@
 	/* The drive wants to be written to. */
 	if ((ireason & 3) == 0) {
 		/* Transfer the data. */
-		atapi_output_bytes (drive, pc->buffer, thislen);
+		atapi_write(drive, pc->buffer, thislen);
 
 		/* If we haven't moved enough data to satisfy the drive,
 		   add some padding. */
 		while (len > thislen) {
 			int dum = 0;
-			atapi_output_bytes (drive, &dum, sizeof (dum));
+
+			atapi_write(drive, &dum, sizeof (dum));
 			len -= sizeof (dum);
 		}
 
@@ -1317,15 +1331,14 @@
 
 	/* Same drill for reading. */
 	else if ((ireason & 3) == 2) {
-
 		/* Transfer the data. */
-		atapi_input_bytes (drive, pc->buffer, thislen);
+		atapi_read(drive, pc->buffer, thislen);
 
 		/* If we haven't moved enough data to satisfy the drive,
 		   add some padding. */
 		while (len > thislen) {
 			int dum = 0;
-			atapi_input_bytes (drive, &dum, sizeof (dum));
+			atapi_read(drive, &dum, sizeof (dum));
 			len -= sizeof (dum);
 		}
 
@@ -1346,24 +1359,29 @@
 	return ide_started;
 }
 
-
-static ide_startstop_t cdrom_do_pc_continuation (ide_drive_t *drive)
+static ide_startstop_t cdrom_do_pc_continuation(ide_drive_t *drive)
 {
 	struct request *rq = HWGROUP(drive)->rq;
+	unsigned long timeout;
+
+	/* FIXME --mdcki */
 	struct packet_command *pc = (struct packet_command *) rq->special;
 
-	if (!pc->timeout)
-		pc->timeout = WAIT_CMD;
+	if (pc->timeout)
+		timeout = pc->timeout;
+	else
+		timeout = WAIT_CMD;
 
 	/* Send the command to the drive and return. */
-	return cdrom_transfer_packet_command(drive, pc, &cdrom_pc_intr);
+	return cdrom_transfer_packet_command(drive, rq->cmd, timeout, &cdrom_pc_intr);
 }
 
-
 static ide_startstop_t cdrom_do_packet_command (ide_drive_t *drive)
 {
 	int len;
 	struct request *rq = HWGROUP(drive)->rq;
+
+	/* FIXME --mdcki */
 	struct packet_command *pc = (struct packet_command *) rq->special;
 	struct cdrom_info *info = drive->driver_data;
 
@@ -1391,35 +1409,43 @@
 }
 
 static
-int cdrom_queue_packet_command(ide_drive_t *drive, struct packet_command *pc)
+int cdrom_queue_packet_command(ide_drive_t *drive, unsigned char *cmd,
+		struct request_sense *sense, struct packet_command *pc)
 {
-	struct request_sense sense;
-	struct request req;
+	struct request rq;
 	int retries = 10;
 
-	if (pc->sense == NULL)
-		pc->sense = &sense;
-
 	/* Start of retry loop. */
 	do {
-		ide_init_drive_cmd (&req);
-		req.flags = REQ_PC;
-		req.special = (char *) pc;
-		if (ide_do_drive_cmd (drive, &req, ide_wait)) {
+		ide_init_drive_cmd(&rq);
+		memcpy(rq.cmd, cmd, CDROM_PACKET_SIZE);
+
+		rq.flags = REQ_PC;
+
+		/* FIXME --mdcki */
+		rq.special = (void *) pc;
+		if (ide_do_drive_cmd(drive, &rq, ide_wait)) {
 			printk("%s: do_drive_cmd returned stat=%02x,err=%02x\n",
-				drive->name, req.buffer[0], req.buffer[1]);
+				drive->name, rq.buffer[0], rq.buffer[1]);
+
 			/* FIXME: we should probably abort/retry or something */
+			if (sense) {
+
+				/* Decode the error here at least for error
+				 * reporting to upper layers.!
+				 */
+
+			}
 		}
 		if (pc->stat != 0) {
 			/* The request failed.  Retry if it was due to a unit
 			   attention status
 			   (usually means media was changed). */
-			struct request_sense *reqbuf = pc->sense;
 
-			if (reqbuf->sense_key == UNIT_ATTENTION)
+			if (sense && sense->sense_key == UNIT_ATTENTION)
 				cdrom_saw_media_change (drive);
-			else if (reqbuf->sense_key == NOT_READY &&
-				 reqbuf->asc == 4 && reqbuf->ascq != 4) {
+			else if (sense && sense->sense_key == NOT_READY &&
+				 sense->asc == 4 && sense->ascq != 4) {
 				/* The drive is in the process of loading
 				   a disk.  Retry, but wait a little to give
 				   the drive time to complete the load. */
@@ -1458,7 +1484,7 @@
 		   and quit this request. */
 		while (len > 0) {
 			int dum = 0;
-			atapi_output_bytes(drive, &dum, sizeof(dum));
+			atapi_write(drive, &dum, sizeof(dum));
 			len -= sizeof(dum);
 		}
 	} else {
@@ -1493,7 +1519,7 @@
 		printk("ide-cd: write_intr decode_status bad\n");
 		return startstop;
 	}
- 
+
 	/*
 	 * using dma, transfer is complete now
 	 */
@@ -1549,7 +1575,7 @@
 		this_transfer = MIN(sectors_to_transfer,rq->current_nr_sectors);
 
 		while (this_transfer > 0) {
-			atapi_output_bytes(drive, rq->buffer, SECTOR_SIZE);
+			atapi_write(drive, rq->buffer, SECTOR_SIZE);
 			rq->buffer += SECTOR_SIZE;
 			--rq->nr_sectors;
 			--rq->current_nr_sectors;
@@ -1573,20 +1599,9 @@
 
 static ide_startstop_t cdrom_start_write_cont(ide_drive_t *drive)
 {
-	struct packet_command pc;	/* packet_command_t pc; */
 	struct request *rq = HWGROUP(drive)->rq;
-	unsigned nframes, frame;
-
-	nframes = rq->nr_sectors >> 2;
-	frame = rq->sector >> 2;
-
-	memcpy(pc.c, rq->cmd, sizeof(pc.c));
-#if 0	/* the immediate bit */
-	pc.c[1] = 1 << 3;
-#endif
-	pc.timeout = 2 * WAIT_CMD;
 
-	return cdrom_transfer_packet_command(drive, &pc, cdrom_write_intr);
+	return cdrom_transfer_packet_command(drive, rq->cmd, 2 * WAIT_CMD, cdrom_write_intr);
 }
 
 static ide_startstop_t cdrom_start_write(ide_drive_t *drive, struct request *rq)
@@ -1620,39 +1635,20 @@
 	return cdrom_start_packet_command(drive, 32768, cdrom_start_write_cont);
 }
 
-/*
- * just wrap this around cdrom_do_packet_command
- */
-static ide_startstop_t cdrom_do_block_pc(ide_drive_t *drive, struct request *rq)
-{
-	struct packet_command pc;
-	ide_startstop_t startstop;
-
-	memset(&pc, 0, sizeof(pc));
-	memcpy(pc.c, rq->cmd, sizeof(pc.c));
-	pc.quiet = 1;
-	pc.timeout = 60 * HZ;
-	rq->special = (char *) &pc;
-
-	startstop = cdrom_do_packet_command(drive);
-	if (pc.stat)
-		rq->errors++;
-
-	return startstop;
-}
-
 #define IDE_LARGE_SEEK(b1,b2,t)	(((b1) > (b2) + (t)) || ((b2) > (b1) + (t)))
 
 /****************************************************************************
  * cdrom driver request routine.
  */
 static ide_startstop_t
-ide_do_rw_cdrom (ide_drive_t *drive, struct request *rq, unsigned long block)
+ide_cdrom_do_request(struct ata_device *drive, struct request *rq, sector_t block)
 {
 	ide_startstop_t action;
 	struct cdrom_info *info = drive->driver_data;
 
 	if (rq->flags & REQ_CMD) {
+	
+
 		if (CDROM_CONFIG_FLAGS(drive)->seeking) {
 			unsigned long elpased = jiffies - info->start_seek;
 			int stat = GET_STAT();
@@ -1669,8 +1665,30 @@
 		if (IDE_LARGE_SEEK(info->last_block, block, IDECD_SEEK_THRESHOLD) && drive->dsc_overlap)
 			action = cdrom_start_seek (drive, block);
 		else {
+			unsigned long flags;
+			struct ata_request *ar;
+
+			/*
+			 * get a new command (push ar further down to avoid grabbing lock here
+			 */
+			spin_lock_irqsave(DRIVE_LOCK(drive), flags);
+
+			ar = ata_ar_get(drive);
+
+			/*
+			 * we've reached maximum queue depth, bail
+			 */
+			if (!ar) {
+				spin_unlock_irqrestore(DRIVE_LOCK(drive), flags);
+
+				return ide_started;
+			}
+
+			ar->ar_rq = rq;
+			spin_unlock_irqrestore(DRIVE_LOCK(drive), flags);
+
 			if (rq_data_dir(rq) == READ)
-				action = cdrom_start_read(drive, block);
+				action = cdrom_start_read(drive, ar, block);
 			else
 				action = cdrom_start_write(drive, rq);
 		}
@@ -1679,18 +1697,31 @@
 	} else if (rq->flags & (REQ_PC | REQ_SENSE)) {
 		return cdrom_do_packet_command(drive);
 	} else if (rq->flags & REQ_SPECIAL) {
-
 		/*
-		 * FIXME: Kill REQ_SEPCIAL and replace it will command commands
-		 * queued at the request queue instead as suggested (abd
-		 * rightly so) by Linus.
+		 * FIXME: Kill REQ_SEPCIAL and replace it with commands queued
+		 * at the request queue instead as suggested by Linus.
 		 *
 		 * right now this can only be a reset...
 		 */
-		cdrom_end_request(drive, 1); return ide_stopped;
 
+		cdrom_end_request(drive, 1);
+		return ide_stopped;
 	} else if (rq->flags & REQ_BLOCK_PC) {
-		return cdrom_do_block_pc(drive, rq);
+		struct packet_command pc;
+		ide_startstop_t startstop;
+
+		memset(&pc, 0, sizeof(pc));
+		pc.quiet = 1;
+		pc.timeout = 60 * HZ;
+
+		/* FIXME --mdcki */
+		rq->special = (char *) &pc;
+
+		startstop = cdrom_do_packet_command(drive);
+		if (pc.stat)
+			++rq->errors;
+
+		return startstop;
 	}
 
 	blk_dump_rq_flags(rq, "ide-cd bad flags");
@@ -1755,6 +1786,7 @@
 
 static int cdrom_check_status(ide_drive_t *drive, struct request_sense *sense)
 {
+	unsigned char cmd[CDROM_PACKET_SIZE];
 	struct packet_command pc;
 	struct cdrom_info *info = drive->driver_data;
 	struct cdrom_device_info *cdi = &info->devinfo;
@@ -1762,16 +1794,16 @@
 	memset(&pc, 0, sizeof(pc));
 	pc.sense = sense;
 
-	pc.c[0] = GPCMD_TEST_UNIT_READY;
+	cmd[0] = GPCMD_TEST_UNIT_READY;
 
-#if ! STANDARD_ATAPI
-        /* the Sanyo 3 CD changer uses byte 7 of TEST_UNIT_READY to 
+#if !STANDARD_ATAPI
+        /* the Sanyo 3 CD changer uses byte 7 of TEST_UNIT_READY to
            switch CDs instead of supporting the LOAD_UNLOAD opcode   */
 
-        pc.c[7] = cdi->sanyo_slot % 3;
-#endif /* not STANDARD_ATAPI */
+        cmd[7] = cdi->sanyo_slot % 3;
+#endif
 
-	return cdrom_queue_packet_command(drive, &pc);
+	return cdrom_queue_packet_command(drive, cmd, sense, &pc);
 }
 
 
@@ -1779,22 +1811,20 @@
 static int
 cdrom_lockdoor(ide_drive_t *drive, int lockflag, struct request_sense *sense)
 {
-	struct request_sense my_sense;
 	struct packet_command pc;
 	int stat;
 
-	if (sense == NULL)
-		sense = &my_sense;
-
 	/* If the drive cannot lock the door, just pretend. */
 	if (CDROM_CONFIG_FLAGS(drive)->no_doorlock) {
 		stat = 0;
 	} else {
+		unsigned char cmd[CDROM_PACKET_SIZE];
+
 		memset(&pc, 0, sizeof(pc));
 		pc.sense = sense;
-		pc.c[0] = GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL;
-		pc.c[4] = lockflag ? 1 : 0;
-		stat = cdrom_queue_packet_command (drive, &pc);
+		cmd[0] = GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL;
+		cmd[4] = lockflag ? 1 : 0;
+		stat = cdrom_queue_packet_command(drive, cmd, sense, &pc);
 	}
 
 	/* If we got an illegal field error, the drive
@@ -1807,7 +1837,7 @@
 		CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1;
 		stat = 0;
 	}
-	
+
 	/* no medium, that's alright. */
 	if (stat != 0 && sense->sense_key == NOT_READY && sense->asc == 0x3a)
 		stat = 0;
@@ -1825,10 +1855,11 @@
 		       struct request_sense *sense)
 {
 	struct packet_command pc;
+	unsigned char cmd[CDROM_PACKET_SIZE];
 
 	if (CDROM_CONFIG_FLAGS(drive)->no_eject && !ejectflag)
 		return -EDRIVE_CANT_DO_THIS;
-	
+
 	/* reload fails on some drives, if the tray is locked */
 	if (CDROM_STATE_FLAGS(drive)->door_locked && ejectflag)
 		return 0;
@@ -1836,9 +1867,9 @@
 	memset(&pc, 0, sizeof (pc));
 	pc.sense = sense;
 
-	pc.c[0] = GPCMD_START_STOP_UNIT;
-	pc.c[4] = 0x02 + (ejectflag != 0);
-	return cdrom_queue_packet_command (drive, &pc);
+	cmd[0] = GPCMD_START_STOP_UNIT;
+	cmd[4] = 0x02 + (ejectflag != 0);
+	return cdrom_queue_packet_command(drive, cmd, sense, &pc);
 }
 
 static int cdrom_read_capacity(ide_drive_t *drive, unsigned long *capacity,
@@ -1850,16 +1881,16 @@
 	} capbuf;
 
 	int stat;
+	unsigned char cmd[CDROM_PACKET_SIZE];
 	struct packet_command pc;
 
 	memset(&pc, 0, sizeof(pc));
 	pc.sense = sense;
 
-	pc.c[0] = GPCMD_READ_CDVD_CAPACITY;
+	cmd[0] = GPCMD_READ_CDVD_CAPACITY;
 	pc.buffer = (char *)&capbuf;
 	pc.buflen = sizeof(capbuf);
-
-	stat = cdrom_queue_packet_command(drive, &pc);
+	stat = cdrom_queue_packet_command(drive, cmd, sense, &pc);
 	if (stat == 0)
 		*capacity = 1 + be32_to_cpu(capbuf.lba);
 
@@ -1870,6 +1901,7 @@
 				int format, char *buf, int buflen,
 				struct request_sense *sense)
 {
+	unsigned char cmd[CDROM_PACKET_SIZE];
 	struct packet_command pc;
 
 	memset(&pc, 0, sizeof(pc));
@@ -1878,16 +1910,16 @@
 	pc.buffer =  buf;
 	pc.buflen = buflen;
 	pc.quiet = 1;
-	pc.c[0] = GPCMD_READ_TOC_PMA_ATIP;
-	pc.c[6] = trackno;
-	pc.c[7] = (buflen >> 8);
-	pc.c[8] = (buflen & 0xff);
-	pc.c[9] = (format << 6);
 
+	cmd[0] = GPCMD_READ_TOC_PMA_ATIP;
 	if (msf_flag)
-		pc.c[1] = 2;
+		cmd[1] = 2;
+	cmd[6] = trackno;
+	cmd[7] = (buflen >> 8);
+	cmd[8] = (buflen & 0xff);
+	cmd[9] = (format << 6);
 
-	return cdrom_queue_packet_command (drive, &pc);
+	return cdrom_queue_packet_command(drive, cmd, sense, &pc);
 }
 
 
@@ -1916,7 +1948,7 @@
 
 	/* Check to see if the existing data is still valid.
 	   If it is, just return. */
-	(void) cdrom_check_status(drive, sense);
+	cdrom_check_status(drive, sense);
 
 	if (CDROM_STATE_FLAGS(drive)->toc_valid)
 		return 0;
@@ -2049,6 +2081,7 @@
 static int cdrom_read_subchannel(ide_drive_t *drive, int format, char *buf,
 				 int buflen, struct request_sense *sense)
 {
+	unsigned char cmd[CDROM_PACKET_SIZE];
 	struct packet_command pc;
 
 	memset(&pc, 0, sizeof(pc));
@@ -2056,13 +2089,14 @@
 
 	pc.buffer = buf;
 	pc.buflen = buflen;
-	pc.c[0] = GPCMD_READ_SUBCHANNEL;
-	pc.c[1] = 2;     /* MSF addressing */
-	pc.c[2] = 0x40;  /* request subQ data */
-	pc.c[3] = format;
-	pc.c[7] = (buflen >> 8);
-	pc.c[8] = (buflen & 0xff);
-	return cdrom_queue_packet_command(drive, &pc);
+	cmd[0] = GPCMD_READ_SUBCHANNEL;
+	cmd[1] = 2;     /* MSF addressing */
+	cmd[2] = 0x40;  /* request subQ data */
+	cmd[3] = format;
+	cmd[7] = (buflen >> 8);
+	cmd[8] = (buflen & 0xff);
+
+	return cdrom_queue_packet_command(drive, cmd, sense, &pc);
 }
 
 /* ATAPI cdrom drives are free to select the speed you request or any slower
@@ -2070,6 +2104,7 @@
 static int cdrom_select_speed(ide_drive_t *drive, int speed,
 			      struct request_sense *sense)
 {
+	unsigned char cmd[CDROM_PACKET_SIZE];
 	struct packet_command pc;
 	memset(&pc, 0, sizeof(pc));
 	pc.sense = sense;
@@ -2079,36 +2114,37 @@
 	else
 		speed *= 177;   /* Nx to kbytes/s */
 
-	pc.c[0] = GPCMD_SET_SPEED;
+	cmd[0] = GPCMD_SET_SPEED;
 	/* Read Drive speed in kbytes/second MSB */
-	pc.c[2] = (speed >> 8) & 0xff;	
+	cmd[2] = (speed >> 8) & 0xff;
 	/* Read Drive speed in kbytes/second LSB */
-	pc.c[3] = speed & 0xff;
+	cmd[3] = speed & 0xff;
 	if (CDROM_CONFIG_FLAGS(drive)->cd_r ||
 	    CDROM_CONFIG_FLAGS(drive)->cd_rw ||
 	    CDROM_CONFIG_FLAGS(drive)->dvd_r) {
 		/* Write Drive speed in kbytes/second MSB */
-		pc.c[4] = (speed >> 8) & 0xff;
+		cmd[4] = (speed >> 8) & 0xff;
 		/* Write Drive speed in kbytes/second LSB */
-		pc.c[5] = speed & 0xff;
+		cmd[5] = speed & 0xff;
        }
 
-	return cdrom_queue_packet_command(drive, &pc);
+	return cdrom_queue_packet_command(drive, cmd, sense, &pc);
 }
 
 static int cdrom_play_audio(ide_drive_t *drive, int lba_start, int lba_end)
 {
 	struct request_sense sense;
+	unsigned char cmd[CDROM_PACKET_SIZE];
 	struct packet_command pc;
 
 	memset(&pc, 0, sizeof (pc));
 	pc.sense = &sense;
 
-	pc.c[0] = GPCMD_PLAY_AUDIO_MSF;
-	lba_to_msf(lba_start, &pc.c[3], &pc.c[4], &pc.c[5]);
-	lba_to_msf(lba_end-1, &pc.c[6], &pc.c[7], &pc.c[8]);
+	cmd[0] = GPCMD_PLAY_AUDIO_MSF;
+	lba_to_msf(lba_start, &cmd[3], &cmd[4], &cmd[5]);
+	lba_to_msf(lba_end-1, &cmd[6], &cmd[7], &cmd[8]);
 
-	return cdrom_queue_packet_command(drive, &pc);
+	return cdrom_queue_packet_command(drive, cmd, &sense, &pc);
 }
 
 static int cdrom_get_toc_entry(ide_drive_t *drive, int track,
@@ -2152,15 +2188,15 @@
 	   layer. the packet must be complete, as we do not
 	   touch it at all. */
 	memset(&pc, 0, sizeof(pc));
-	memcpy(pc.c, cgc->cmd, CDROM_PACKET_SIZE);
 	pc.buffer = cgc->buffer;
 	pc.buflen = cgc->buflen;
 	pc.quiet = cgc->quiet;
 	pc.timeout = cgc->timeout;
 	pc.sense = cgc->sense;
-	cgc->stat = cdrom_queue_packet_command(drive, &pc);
+	cgc->stat = cdrom_queue_packet_command(drive, cgc->cmd, cgc->sense, &pc);
 	if (!cgc->stat)
 		cgc->buflen -= pc.buflen;
+
 	return cgc->stat;
 }
 
@@ -2177,34 +2213,34 @@
 
 	/* These will be moved into the Uniform layer shortly... */
 	switch (cmd) {
- 	case CDROMSETSPINDOWN: {
- 		char spindown;
- 
- 		if (copy_from_user(&spindown, (void *) arg, sizeof(char)))
+	case CDROMSETSPINDOWN: {
+		char spindown;
+
+		if (copy_from_user(&spindown, (void *) arg, sizeof(char)))
 			return -EFAULT;
- 
+
                 if ((stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CDROM_PAGE, 0)))
 			return stat;
 
- 		buffer[11] = (buffer[11] & 0xf0) | (spindown & 0x0f);
+		buffer[11] = (buffer[11] & 0xf0) | (spindown & 0x0f);
+
+		return cdrom_mode_select(cdi, &cgc);
+	}
+
+	case CDROMGETSPINDOWN: {
+		char spindown;
 
- 		return cdrom_mode_select(cdi, &cgc);
- 	} 
- 
- 	case CDROMGETSPINDOWN: {
- 		char spindown;
- 
                 if ((stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CDROM_PAGE, 0)))
 			return stat;
- 
- 		spindown = buffer[11] & 0x0f;
- 
+
+		spindown = buffer[11] & 0x0f;
+
 		if (copy_to_user((void *) arg, &spindown, sizeof (char)))
 			return -EFAULT;
- 
- 		return 0;
- 	}
-  
+
+		return 0;
+	}
+
 	default:
 		return -EINVAL;
 	}
@@ -2214,7 +2250,6 @@
 static
 int ide_cdrom_audio_ioctl (struct cdrom_device_info *cdi,
 			   unsigned int cmd, void *arg)
-			   
 {
 	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
 	struct cdrom_info *info = drive->driver_data;
@@ -2297,7 +2332,7 @@
 	struct request req;
 	int ret;
 
-	ide_init_drive_cmd (&req);
+	ide_init_drive_cmd(&req);
 	req.flags = REQ_SPECIAL;
 	ret = ide_do_drive_cmd(drive, &req, ide_wait);
 
@@ -2306,31 +2341,34 @@
 	 * lock it again.
 	 */
 	if (CDROM_STATE_FLAGS(drive)->door_locked)
-		(void) cdrom_lockdoor(drive, 1, &sense);
+		cdrom_lockdoor(drive, 1, &sense);
 
 	return ret;
 }
 
 
 static
-int ide_cdrom_tray_move (struct cdrom_device_info *cdi, int position)
+int ide_cdrom_tray_move(struct cdrom_device_info *cdi, int position)
 {
 	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
 	struct request_sense sense;
 
 	if (position) {
 		int stat = cdrom_lockdoor(drive, 0, &sense);
-		if (stat) return stat;
+		if (stat)
+			return stat;
 	}
 
 	return cdrom_eject(drive, !position, &sense);
 }
 
 static
-int ide_cdrom_lock_door (struct cdrom_device_info *cdi, int lock)
+int ide_cdrom_lock_door(struct cdrom_device_info *cdi, int lock)
 {
 	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
-	return cdrom_lockdoor(drive, lock, NULL);
+	struct request_sense sense;
+
+	return cdrom_lockdoor(drive, lock, &sense);
 }
 
 static
@@ -2433,9 +2471,9 @@
 {
 	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
 	int retval;
-	
+
 	if (slot_nr == CDSL_CURRENT) {
-		(void) cdrom_check_status(drive, NULL);
+		cdrom_check_status(drive, NULL);
 		retval = CDROM_STATE_FLAGS (drive)->media_changed;
 		CDROM_STATE_FLAGS (drive)->media_changed = 0;
 		return retval;
@@ -2502,7 +2540,7 @@
 	*(int *)&devinfo->capacity = nslots;
 	devinfo->handle = (void *) drive;
 	strcpy(devinfo->name, drive->name);
-	
+
 	/* set capability mask to match the probe. */
 	if (!CDROM_CONFIG_FLAGS (drive)->cd_r)
 		devinfo->mask |= CDC_CD_R;
@@ -2578,8 +2616,8 @@
 	int nslots = 1;
 
 	if (CDROM_CONFIG_FLAGS (drive)->nec260) {
-		CDROM_CONFIG_FLAGS (drive)->no_eject = 0;                       
-		CDROM_CONFIG_FLAGS (drive)->audio_play = 1;       
+		CDROM_CONFIG_FLAGS (drive)->no_eject = 0;
+		CDROM_CONFIG_FLAGS (drive)->audio_play = 1;
 		return nslots;
 	}
 
@@ -2632,14 +2670,14 @@
 
 	/* The ACER/AOpen 24X cdrom has the speed fields byte-swapped */
 	if (drive->id && !drive->id->model[0] && !strncmp(drive->id->fw_rev, "241N", 4)) {
-		CDROM_STATE_FLAGS (drive)->current_speed  = 
+		CDROM_STATE_FLAGS (drive)->current_speed  =
 			(((unsigned int)cap.curspeed) + (176/2)) / 176;
-		CDROM_CONFIG_FLAGS (drive)->max_speed = 
+		CDROM_CONFIG_FLAGS (drive)->max_speed =
 			(((unsigned int)cap.maxspeed) + (176/2)) / 176;
 	} else {
-		CDROM_STATE_FLAGS (drive)->current_speed  = 
+		CDROM_STATE_FLAGS (drive)->current_speed  =
 			(ntohs(cap.curspeed) + (176/2)) / 176;
-		CDROM_CONFIG_FLAGS (drive)->max_speed = 
+		CDROM_CONFIG_FLAGS (drive)->max_speed =
 			(ntohs(cap.maxspeed) + (176/2)) / 176;
 	}
 
@@ -2651,19 +2689,19 @@
 	printk(" %s", CDROM_CONFIG_FLAGS(drive)->dvd ? "DVD-ROM" : "CD-ROM");
 
 	if (CDROM_CONFIG_FLAGS (drive)->dvd_r|CDROM_CONFIG_FLAGS (drive)->dvd_ram)
-        	printk (" DVD%s%s", 
-        	(CDROM_CONFIG_FLAGS (drive)->dvd_r)? "-R" : "", 
-        	(CDROM_CONFIG_FLAGS (drive)->dvd_ram)? "-RAM" : "");
-
-        if (CDROM_CONFIG_FLAGS (drive)->cd_r|CDROM_CONFIG_FLAGS (drive)->cd_rw) 
-        	printk (" CD%s%s", 
-        	(CDROM_CONFIG_FLAGS (drive)->cd_r)? "-R" : "", 
-        	(CDROM_CONFIG_FLAGS (drive)->cd_rw)? "/RW" : "");
-
-        if (CDROM_CONFIG_FLAGS (drive)->is_changer) 
-        	printk (" changer w/%d slots", nslots);
-        else 	
-        	printk (" drive");
+		printk (" DVD%s%s",
+		(CDROM_CONFIG_FLAGS (drive)->dvd_r)? "-R" : "",
+		(CDROM_CONFIG_FLAGS (drive)->dvd_ram)? "-RAM" : "");
+
+        if (CDROM_CONFIG_FLAGS (drive)->cd_r|CDROM_CONFIG_FLAGS (drive)->cd_rw)
+		printk (" CD%s%s",
+		(CDROM_CONFIG_FLAGS (drive)->cd_r)? "-R" : "",
+		(CDROM_CONFIG_FLAGS (drive)->cd_rw)? "/RW" : "");
+
+        if (CDROM_CONFIG_FLAGS (drive)->is_changer)
+		printk (" changer w/%d slots", nslots);
+        else
+		printk (" drive");
 
 	printk (", %dkB Cache", be16_to_cpu(cap.buffer_size));
 
@@ -2678,7 +2716,8 @@
 
 static void ide_cdrom_add_settings(ide_drive_t *drive)
 {
-	ide_add_setting(drive,	"dsc_overlap",		SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1,	1, &drive->dsc_overlap, NULL);
+	ide_add_setting(drive, "dsc_overlap",
+			SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1,	1, &drive->dsc_overlap, NULL);
 }
 
 static
@@ -2728,7 +2767,7 @@
 	CDROM_CONFIG_FLAGS (drive)->supp_disc_present = 0;
 	CDROM_CONFIG_FLAGS (drive)->audio_play = 0;
 	CDROM_CONFIG_FLAGS (drive)->close_tray = 1;
-	
+
 	/* limit transfer size per interrupt. */
 	CDROM_CONFIG_FLAGS (drive)->limit_nframes = 0;
 	if (drive->id != NULL) {
@@ -2763,16 +2802,12 @@
 			CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd = 1;
 			CDROM_CONFIG_FLAGS (drive)->playmsf_as_bcd = 1;
 			CDROM_CONFIG_FLAGS (drive)->subchan_as_bcd = 1;
-		}
-
-		else if (strcmp (drive->id->model, "V006E0DS") == 0 &&
+		} else if (strcmp (drive->id->model, "V006E0DS") == 0 &&
 		    drive->id->fw_rev[4] == '1' &&
 		    drive->id->fw_rev[6] <= '2') {
 			/* Vertos 600 ESD. */
 			CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd = 1;
-		}
-
-		else if (strcmp (drive->id->model,
+		} else if (strcmp (drive->id->model,
 				 "NEC CD-ROM DRIVE:260") == 0 &&
 			 strncmp (drive->id->fw_rev, "1.01", 4) == 0) { /* FIXME */
 			/* Old NEC260 (not R).
@@ -2782,9 +2817,7 @@
 			CDROM_CONFIG_FLAGS (drive)->playmsf_as_bcd = 1;
 			CDROM_CONFIG_FLAGS (drive)->subchan_as_bcd = 1;
 			CDROM_CONFIG_FLAGS (drive)->nec260         = 1;
-		}
-
-		else if (strcmp (drive->id->model, "WEARNES CDD-120") == 0 &&
+		} else if (strcmp (drive->id->model, "WEARNES CDD-120") == 0 &&
 			 strncmp (drive->id->fw_rev, "A1.1", 4) == 0) { /* FIXME */
 			/* Wearnes */
 			CDROM_CONFIG_FLAGS (drive)->playmsf_as_bcd = 1;
@@ -2929,7 +2962,7 @@
 	owner:			THIS_MODULE,
 	cleanup:		ide_cdrom_cleanup,
 	standby:		NULL,
-	do_request:		ide_do_rw_cdrom,
+	do_request:		ide_cdrom_do_request,
 	end_request:		NULL,
 	ioctl:			ide_cdrom_ioctl,
 	open:			ide_cdrom_open,
@@ -2989,7 +3022,7 @@
 			kfree (info);
 			continue;
 		}
-		memset (info, 0, sizeof (struct cdrom_info));
+		memset(info, 0, sizeof (struct cdrom_info));
 		drive->driver_data = info;
 
 		MOD_INC_USE_COUNT;
diff -Nru a/drivers/ide/ide-cd.h b/drivers/ide/ide-cd.h
--- a/drivers/ide/ide-cd.h	Mon Apr 22 15:32:19 2002
+++ b/drivers/ide/ide-cd.h	Mon Apr 22 15:32:19 2002
@@ -15,7 +15,7 @@
    memory, though. */
 
 #ifndef VERBOSE_IDE_CD_ERRORS
-#define VERBOSE_IDE_CD_ERRORS 1
+# define VERBOSE_IDE_CD_ERRORS 1
 #endif
 
 
@@ -24,7 +24,7 @@
    this will give you a slightly smaller kernel. */
 
 #ifndef STANDARD_ATAPI
-#define STANDARD_ATAPI 0
+# define STANDARD_ATAPI 0
 #endif
 
 
@@ -32,14 +32,14 @@
    This is apparently needed for supermount. */
 
 #ifndef NO_DOOR_LOCKING
-#define NO_DOOR_LOCKING 0
+# define NO_DOOR_LOCKING 0
 #endif
 
 /************************************************************************/
 
-#define SECTOR_BITS 		9
+#define SECTOR_BITS		9
 #ifndef SECTOR_SIZE
-#define SECTOR_SIZE		(1 << SECTOR_BITS)
+# define SECTOR_SIZE		(1 << SECTOR_BITS)
 #endif
 #define SECTORS_PER_FRAME	(CD_FRAMESIZE >> SECTOR_BITS)
 #define SECTOR_BUFFER_SIZE	(CD_FRAMESIZE * 32)
@@ -50,12 +50,6 @@
 
 #define MIN(a,b) ((a) < (b) ? (a) : (b))
 
-/* special command codes for strategy routine. */
-#define PACKET_COMMAND        4315
-#define REQUEST_SENSE_COMMAND 4316
-#define RESET_DRIVE_COMMAND   4317
-
-
 /* Configuration flags.  These describe the capabilities of the drive.
    They generally do not change after initialization, unless we learn
    more about the drive from stuff failing. */
@@ -90,7 +84,6 @@
 };
 #define CDROM_CONFIG_FLAGS(drive) (&(((struct cdrom_info *)(drive->driver_data))->config_flags))
 
- 
 /* State flags.  These give information about the current state of the
    drive, and will change during normal operation. */
 struct ide_cd_state_flags {
@@ -111,24 +104,32 @@
 	int quiet;
 	int timeout;
 	struct request_sense *sense;
-	unsigned char c[12];
+
+	/* This is currently used to pass failed commands through the request
+	 * queue.  Is this for asynchronos error reporting?
+	 *
+	 * Can we always be sure that this didn't valish from stack beneath us
+	 * - we can't!
+	 */
+
+	struct packet_command *failed_command;
 };
 
 /* Structure of a MSF cdrom address. */
 struct atapi_msf {
-	byte reserved;
-	byte minute;
-	byte second;
-	byte frame;
-};
+	u8 __reserved;
+	u8 minute;
+	u8 second;
+	u8 frame;
+} __attribute__((packed));
 
 /* Space to hold the disk TOC. */
 #define MAX_TRACKS 99
 struct atapi_toc_header {
-	unsigned short toc_length;
-	byte first_track;
-	byte last_track;
-};
+	u16 toc_length;
+	u8 first_track;
+	u8 last_track;
+} __attribute__((packed));
 
 struct atapi_toc_entry {
 	byte reserved1;
@@ -162,17 +163,17 @@
 /* This structure is annoyingly close to, but not identical with,
    the cdrom_subchnl structure from cdrom.h. */
 struct atapi_cdrom_subchnl {
- 	u_char  acdsc_reserved;
- 	u_char  acdsc_audiostatus;
- 	u_short acdsc_length;
-	u_char  acdsc_format;
+	u8	acdsc_reserved;
+	u8	acdsc_audiostatus;
+	u16	acdsc_length;
+	u8	acdsc_format;
 
 #if defined(__BIG_ENDIAN_BITFIELD)
-	u_char  acdsc_ctrl:     4;
-	u_char  acdsc_adr:      4;
+	u8	acdsc_ctrl:     4;
+	u8	acdsc_adr:      4;
 #elif defined(__LITTLE_ENDIAN_BITFIELD)
-	u_char  acdsc_adr:	4;
-	u_char  acdsc_ctrl:	4;
+	u8	acdsc_adr:	4;
+	u8	acdsc_ctrl:	4;
 #else
 #error "Please fix <asm/byteorder.h>"
 #endif
diff -Nru a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c
--- a/drivers/ide/ide-disk.c	Mon Apr 22 15:32:18 2002
+++ b/drivers/ide/ide-disk.c	Mon Apr 22 15:32:18 2002
@@ -107,10 +107,7 @@
 	 * 48-bit commands are pretty sanely laid out
 	 */
 	if (lba48bit) {
-		if (cmd == READ)
-			command = WIN_READ_EXT;
-		else
-			command = WIN_WRITE_EXT;
+		command = cmd == READ ? WIN_READ_EXT : WIN_WRITE_EXT;
 
 		if (drive->using_dma) {
 			command++;		/* WIN_*DMA_EXT */
@@ -118,31 +115,33 @@
 				command++;	/* WIN_*DMA_QUEUED_EXT */
 		} else if (drive->mult_count)
 			command += 5;		/* WIN_MULT*_EXT */
-	} else {
-		/*
-		 * 28-bit commands seem not to be, though...
-		 */
-		if (cmd == READ) {
-			if (drive->using_dma) {
-				if (drive->using_tcq)
-					command = WIN_READDMA_QUEUED;
-				else
-					command = WIN_READDMA;
-			} else if (drive->mult_count)
-				command = WIN_MULTREAD;
+
+		return command;
+	}
+
+	/*
+	 * 28-bit commands seem not to be, though...
+	 */
+	if (cmd == READ) {
+		if (drive->using_dma) {
+			if (drive->using_tcq)
+				command = WIN_READDMA_QUEUED;
 			else
-				command = WIN_READ;
-		} else {
-			if (drive->using_dma) {
-				if (drive->using_tcq)
-					command = WIN_WRITEDMA_QUEUED;
-				else
-					command = WIN_WRITEDMA;
-			} else if (drive->mult_count)
-				command = WIN_MULTWRITE;
+				command = WIN_READDMA;
+		} else if (drive->mult_count)
+			command = WIN_MULTREAD;
+		else
+			command = WIN_READ;
+	} else {
+		if (drive->using_dma) {
+			if (drive->using_tcq)
+				command = WIN_WRITEDMA_QUEUED;
 			else
-				command = WIN_WRITE;
-		}
+				command = WIN_WRITEDMA;
+		} else if (drive->mult_count)
+			command = WIN_MULTWRITE;
+		else
+			command = WIN_WRITE;
 	}
 
 	return command;
@@ -756,8 +755,6 @@
 	drive->special.b.recalibrate  = legacy;
 	if (OK_TO_RESET_CONTROLLER)
 		drive->mult_count = 0;
-	if (!drive->keep_settings && !drive->using_dma)
-		drive->mult_req = 0;
 	if (drive->mult_req != drive->mult_count)
 		drive->special.b.set_multmode = 1;
 }
@@ -895,24 +892,24 @@
 
 		__set_bit(i, &tag_mask);
 		len += sprintf(out+len, "%d, ", i);
-		if (ar->ar_time > max_jif)
-			max_jif = ar->ar_time;
+		if (cur_jif - ar->ar_time > max_jif)
+			max_jif = cur_jif - ar->ar_time;
 		cmds++;
 	}
 	len += sprintf(out+len, "]\n");
 
+	len += sprintf(out+len, "Queue:\t\t\treleased [ %d ] - started [ %d ]\n", drive->tcq->immed_rel, drive->tcq->immed_comp);
+
 	if (drive->tcq->queued != cmds)
-		len += sprintf(out+len, "pending request and queue count mismatch (%d)\n", cmds);
+		len += sprintf(out+len, "pending request and queue count mismatch (counted: %d)\n", cmds);
 
 	if (tag_mask != drive->tcq->tag_mask)
 		len += sprintf(out+len, "tag masks differ (counted %lx != %lx\n", tag_mask, drive->tcq->tag_mask);
 
 	len += sprintf(out+len, "DMA status:\t\t%srunning\n", test_bit(IDE_DMA, &HWGROUP(drive)->flags) ? "" : "not ");
 
-	if (max_jif)
-		len += sprintf(out+len, "Oldest command:\t\t%lu\n", cur_jif - max_jif);
-
-	len += sprintf(out+len, "immed rel %d, immed comp %d\n", drive->tcq->immed_rel, drive->tcq->immed_comp);
+	len += sprintf(out+len, "Oldest command:\t\t%lu jiffies\n", max_jif);
+	len += sprintf(out+len, "Oldest command ever:\t%lu\n", drive->tcq->oldest_command);
 
 	drive->tcq->max_last_depth = 0;
 
@@ -1017,8 +1014,10 @@
 		return -EPERM;
 	if (!drive->channel->dmaproc)
 		return -EPERM;
+	if (arg == drive->queue_depth && drive->using_tcq)
+		return 0;
 
-	drive->using_tcq = arg;
+	drive->queue_depth = arg ? arg : 1;
 	if (drive->channel->dmaproc(arg ? ide_dma_queued_on : ide_dma_queued_off, drive))
 		return -EIO;
 
@@ -1050,7 +1049,6 @@
 	ide_add_setting(drive,	"bios_head",		SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	255,				1,	1,	&drive->bios_head,		NULL);
 	ide_add_setting(drive,	"bios_sect",		SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	63,				1,	1,	&drive->bios_sect,		NULL);
 	ide_add_setting(drive,	"address",		SETTING_RW,					HDIO_GET_ADDRESS,	HDIO_SET_ADDRESS,	TYPE_INTA,	0,	2,				1,	1,	&drive->addressing,	set_lba_addressing);
-	ide_add_setting(drive,	"bswap",		SETTING_READ,					-1,			-1,			TYPE_BYTE,	0,	1,				1,	1,	&drive->bswap,			NULL);
 	ide_add_setting(drive,	"multcount",		id ? SETTING_RW : SETTING_READ,			HDIO_GET_MULTCOUNT,	HDIO_SET_MULTCOUNT,	TYPE_BYTE,	0,	id ? id->max_multsect : 0,	1,	1,	&drive->mult_count,		set_multcount);
 	ide_add_setting(drive,	"nowerr",		SETTING_RW,					HDIO_GET_NOWERR,	HDIO_SET_NOWERR,	TYPE_BYTE,	0,	1,				1,	1,	&drive->nowerr,			set_nowerr);
 	ide_add_setting(drive,	"lun",			SETTING_RW,					-1,			-1,			TYPE_INT,	0,	7,				1,	1,	&drive->lun,			NULL);
@@ -1197,11 +1195,11 @@
 	if ((capacity >= (drive->bios_cyl * drive->bios_sect * drive->bios_head)) &&
 	    (!drive->forced_geom) && drive->bios_sect && drive->bios_head)
 		drive->bios_cyl = (capacity / drive->bios_sect) / drive->bios_head;
-	printk (KERN_INFO "%s: %ld sectors", drive->name, capacity);
+	printk(KERN_INFO "%s: %ld sectors", drive->name, capacity);
 
 	/* Give size in megabytes (MB), not mebibytes (MiB). */
 	/* We compute the exact rounded value, avoiding overflow. */
-	printk (" (%ld MB)", (capacity - capacity/625 + 974)/1950);
+	printk(" (%ld MB)", (capacity - capacity/625 + 974)/1950);
 
 	/* Only print cache size when it was specified */
 	if (id->buf_size)
@@ -1212,7 +1210,7 @@
 #ifdef CONFIG_BLK_DEV_IDEDMA
 	if (drive->using_dma)
 		(void) drive->channel->dmaproc(ide_dma_verbose, drive);
-#endif /* CONFIG_BLK_DEV_IDEDMA */
+#endif
 	printk("\n");
 
 	drive->mult_count = 0;
@@ -1222,15 +1220,24 @@
 		id->multsect_valid = id->multsect ? 1 : 0;
 		drive->mult_req = id->multsect_valid ? id->max_multsect : INITIAL_MULT_COUNT;
 		drive->special.b.set_multmode = drive->mult_req ? 1 : 0;
-#else	/* original, pre IDE-NFG, per request of AC */
+#else
+		/* original, pre IDE-NFG, per request of AC */
 		drive->mult_req = INITIAL_MULT_COUNT;
 		if (drive->mult_req > id->max_multsect)
 			drive->mult_req = id->max_multsect;
 		if (drive->mult_req || ((id->multsect_valid & 1) && id->multsect))
 			drive->special.b.set_multmode = 1;
-#endif	/* CONFIG_IDEDISK_MULTI_MODE */
+#endif
 	}
-	drive->no_io_32bit = id->dword_io ? 1 : 0;
+
+	/* FIXME: Nowadays there are many chipsets out there which *require* 32
+	 * bit IO. Those will most propably not work properly with drives not
+	 * supporting this. But right now we don't do anything about this. We
+	 * dont' even *warn* the user!
+	 */
+
+	drive->channel->no_io_32bit = id->dword_io ? 1 : 0;
+
 	if (drive->id->cfs_enable_2 & 0x3000)
 		write_cache(drive, (id->cfs_enable_2 & 0x3000));
 	probe_lba_addressing(drive, 1);
diff -Nru a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c
--- a/drivers/ide/ide-dma.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/ide/ide-dma.c	Mon Apr 22 15:32:19 2002
@@ -372,10 +372,10 @@
 }
 
 /* Teardown mappings after DMA has completed.  */
-void ide_destroy_dmatable (ide_drive_t *drive)
+void ide_destroy_dmatable(struct ata_device *d)
 {
-	struct pci_dev *dev = drive->channel->pci_dev;
-	struct ata_request *ar = IDE_CUR_AR(drive);
+	struct pci_dev *dev = d->channel->pci_dev;
+	struct ata_request *ar = IDE_CUR_AR(d);
 
 	pci_unmap_sg(dev, ar->ar_sg_table, ar->ar_sg_nents, ar->ar_sg_ddir);
 }
@@ -549,8 +549,11 @@
 	/* This can happen with drivers abusing the special request field.
 	 */
 
-	if (!ar)
+	if (!ar) {
+		printk(KERN_ERR "DMA without ATA request\n");
+
 		return 1;
+	}
 
 	if (rq_data_dir(ar->ar_rq) == READ)
 		reading = 1 << 3;
@@ -599,20 +602,23 @@
 			printk("%s: DMA disabled\n", drive->name);
 		case ide_dma_off_quietly:
 			set_high = 0;
-			drive->using_tcq = 0;
 			outb(inb(dma_base+2) & ~(1<<(5+unit)), dma_base+2);
+#ifdef CONFIG_BLK_DEV_IDE_TCQ
+			hwif->dmaproc(ide_dma_queued_off, drive);
+#endif
 		case ide_dma_on:
 			ide_toggle_bounce(drive, set_high);
 			drive->using_dma = (func == ide_dma_on);
-			if (drive->using_dma)
+			if (drive->using_dma) {
 				outb(inb(dma_base+2)|(1<<(5+unit)), dma_base+2);
+#ifdef CONFIG_BLK_DEV_IDE_TCQ_DEFAULT
+				hwif->dmaproc(ide_dma_queued_on, drive);
+#endif
+			}
 			return 0;
 		case ide_dma_check:
 			return config_drive_for_dma (drive);
 		case ide_dma_begin:
-#ifdef DEBUG
-			printk("ide_dma_begin: from %p\n", __builtin_return_address(0));
-#endif
 			if (test_and_set_bit(IDE_DMA, &HWGROUP(drive)->flags))
 				BUG();
 			/* Note that this is done *after* the cmd has
@@ -634,14 +640,13 @@
 		case ide_dma_read:
 			reading = 1 << 3;
 		case ide_dma_write:
-			ar = HWGROUP(drive)->rq->special;
+			ar = IDE_CUR_AR(drive);
 
 			if (ide_start_dma(hwif, drive, func))
 				return 1;
 
 			if (drive->type != ATA_DISK)
 				return 0;
-
 			BUG_ON(HWGROUP(drive)->handler);
 			ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, dma_timer_expiry);	/* issue cmd to drive */
 			if ((ar->ar_rq->flags & REQ_DRIVE_TASKFILE) &&
@@ -655,20 +660,13 @@
 			}
 			return hwif->dmaproc(ide_dma_begin, drive);
 		case ide_dma_end: /* returns 1 on error, 0 otherwise */
-#ifdef DEBUG
-			printk("ide_dma_end: from %p\n", __builtin_return_address(0));
-#endif
-			if (!test_and_clear_bit(IDE_DMA, &HWGROUP(drive)->flags)) {
-				printk("ide_dma_end: dma not going? %p\n", __builtin_return_address(0));
-				return 1;
-			}
+			if (!test_and_clear_bit(IDE_DMA, &HWGROUP(drive)->flags))
+				BUG();
 			drive->waiting_for_dma = 0;
 			outb(inb(dma_base)&~1, dma_base);	/* stop DMA */
 			dma_stat = inb(dma_base+2);	/* get DMA status */
 			outb(dma_stat|6, dma_base+2);	/* clear the INTR & ERROR bits */
 			ide_destroy_dmatable(drive);	/* purge DMA mappings */
-			if (drive->tcq)
-				IDE_SET_CUR_TAG(drive, -1);
 			return (dma_stat & 7) != 4 ? (0x10 | dma_stat) : 0;	/* verify good DMA status */
 		case ide_dma_test_irq: /* returns 1 if dma irq issued, 0 otherwise */
 			dma_stat = inb(dma_base+2);
diff -Nru a/drivers/ide/ide-features.c b/drivers/ide/ide-features.c
--- a/drivers/ide/ide-features.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/ide/ide-features.c	Mon Apr 22 15:32:19 2002
@@ -130,8 +130,8 @@
 		__restore_flags(flags);	/* local CPU only */
 		return 0;
 	}
-	ata_input_data(drive, id, SECTOR_WORDS);
-	(void) GET_STAT();	/* clear drive IRQ */
+	ata_read(drive, id, SECTOR_WORDS);
+	GET_STAT();		/* clear drive IRQ */
 	ide__sti();		/* local CPU only */
 	__restore_flags(flags);	/* local CPU only */
 	ide_fix_driveid(id);
@@ -285,7 +285,7 @@
 	enable_irq(hwif->irq);
 
 	if (error) {
-		(void) ide_dump_status(drive, "set_drive_speed_status", stat);
+		ide_dump_status(drive, "set_drive_speed_status", stat);
 		return error;
 	}
 
diff -Nru a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c
--- a/drivers/ide/ide-floppy.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/ide/ide-floppy.c	Mon Apr 22 15:32:19 2002
@@ -1,8 +1,8 @@
 /*
- * linux/drivers/ide/ide-floppy.c	Version 0.97.sv	Jan 14 2001
+ * linux/drivers/ide/ide-floppy.c	Version 0.99	Feb 24 2002
  *
  * Copyright (C) 1996 - 1999 Gadi Oxman <gadio@netvision.net.il>
- * Copyright (C) 2000 - 2001 Paul Bristow <paul@paulbristow.net>
+ * Copyright (C) 2000 - 2002 Paul Bristow <paul@paulbristow.net>
  */
 
 /*
@@ -13,7 +13,7 @@
  *
  * This driver supports the following IDE floppy drives:
  *
- * LS-120 SuperDisk
+ * LS-120/240 SuperDisk
  * Iomega Zip 100/250
  * Iomega PC Card Clik!/PocketZip
  *
@@ -76,9 +76,11 @@
  *                        bit was being deasserted by my IOMEGA ATAPI ZIP 100
  *                        drive before the drive was actually ready.
  * Ver 0.98a Oct 29 01   Expose delay value so we can play.
+ * Ver 0.99  Feb 24 02   Remove duplicate code, modify clik! detection code
+ *                       to support new PocketZip drives
  */
 
-#define IDEFLOPPY_VERSION "0.98a"
+#define IDEFLOPPY_VERSION "0.99"
 
 #include <linux/config.h>
 #include <linux/module.h>
@@ -717,7 +719,7 @@
 			return;
 		}
 		count = IDEFLOPPY_MIN (bio->bi_size - pc->b_count, bcount);
-		atapi_input_bytes (drive, bio_data(bio) + pc->b_count, count);
+		atapi_read(drive, bio_data(bio) + pc->b_count, count);
 		bcount -= count; pc->b_count += count;
 	}
 }
@@ -744,7 +746,7 @@
 			return;
 		}
 		count = IDEFLOPPY_MIN (pc->b_count, bcount);
-		atapi_output_bytes (drive, pc->b_data, count);
+		atapi_write(drive, pc->b_data, count);
 		bcount -= count; pc->b_data += count; pc->b_count -= count;
 	}
 }
@@ -979,12 +981,12 @@
 	}
 	if (test_bit (PC_WRITING, &pc->flags)) {
 		if (pc->buffer != NULL)
-			atapi_output_bytes (drive,pc->current_position,bcount.all);	/* Write the current buffer */
+			atapi_write(drive,pc->current_position,bcount.all);	/* Write the current buffer */
 		else
 			idefloppy_output_buffers (drive, pc, bcount.all);
 	} else {
 		if (pc->buffer != NULL)
-			atapi_input_bytes (drive,pc->current_position,bcount.all);	/* Read the current buffer */
+			atapi_read(drive,pc->current_position,bcount.all);	/* Read the current buffer */
 		else
 			idefloppy_input_buffers (drive, pc, bcount.all);
 	}
@@ -1020,7 +1022,7 @@
 
 	BUG_ON(HWGROUP(drive)->handler);
 	ide_set_handler (drive, &idefloppy_pc_intr, IDEFLOPPY_WAIT_CMD, NULL);	/* Set the interrupt routine */
-	atapi_output_bytes (drive, floppy->pc->c, 12); /* Send the actual packet */
+	atapi_write(drive, floppy->pc->c, 12); /* Send the actual packet */
 
 	return ide_started;
 }
@@ -1042,7 +1044,7 @@
 {
 	idefloppy_floppy_t *floppy = drive->driver_data;
 
-	atapi_output_bytes (drive, floppy->pc->c, 12); /* Send the actual packet */
+	atapi_write(drive, floppy->pc->c, 12); /* Send the actual packet */
 	return IDEFLOPPY_WAIT_CMD;		/* Timeout for the packet command */
 }
 
@@ -1070,8 +1072,8 @@
 	 */
 	BUG_ON(HWGROUP(drive)->handler);
 	ide_set_handler (drive,
-	  &idefloppy_pc_intr,		/* service routine for packet command */
-	  floppy->ticks,			/* wait this long before "failing" */
+	  &idefloppy_pc_intr, 		/* service routine for packet command */
+	  floppy->ticks,		/* wait this long before "failing" */
 	  &idefloppy_transfer_pc2);	/* fail == transfer_pc2 */
 
 	return ide_started;
@@ -2005,7 +2007,7 @@
 	*      above fix.  It makes nasty clicking noises without
 	*      it, so please don't remove this.
 	*/
-	if (strcmp(drive->id->model, "IOMEGA Clik! 40 CZ ATAPI") == 0) {
+	if (strncmp(drive->id->model, "IOMEGA Clik!", 11) == 0) {
 		blk_queue_max_sectors(&drive->queue, 64);
 		set_bit(IDEFLOPPY_CLIK_DRIVE, &floppy->flags);
 	}
diff -Nru a/drivers/ide/ide-m8xx.c b/drivers/ide/ide-m8xx.c
--- a/drivers/ide/ide-m8xx.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/ide/ide-m8xx.c	Mon Apr 22 15:32:19 2002
@@ -1,8 +1,14 @@
 /*
- *
- *
  *  linux/drivers/ide/ide-m8xx.c
  *
+ *  Copyright (C) 2000, 2001 Wolfgang Denk, wd@denx.de
+ *  Modified for direct IDE interface
+ *	by Thomas Lange, thomas@corelatus.com
+ *  Modified for direct IDE interface on 8xx without using the PCMCIA
+ *  controller
+ *	by Steven.Scholz@imc-berlin.de
+ *  Moved out of arch/ppc/kernel/m8xx_setup.c, other minor cleanups
+ *	by Mathew Locke <mattl@mvista.com>
  */
 
 #include <linux/config.h>
diff -Nru a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c
--- a/drivers/ide/ide-probe.c	Mon Apr 22 15:32:18 2002
+++ b/drivers/ide/ide-probe.c	Mon Apr 22 15:32:18 2002
@@ -57,9 +57,18 @@
 		printk(KERN_WARNING "(ide-probe::do_identify) Out of memory.\n");
 		goto err_kmalloc;
 	}
-	/* read 512 bytes of id info */
+
+	/* Read 512 bytes of id info.
+	 *
+	 * Please note that it is well known that some *very* old drives are
+	 * able to provide only 256 of them, since this was the amount read by
+	 * DOS.
+	 *
+	 * However let's try to get away with this...
+	 */
+
 #if 1
-	ata_input_data(drive, id, SECTOR_WORDS);		/* read 512 bytes of id info */
+	ata_read(drive, id, SECTOR_WORDS);
 #else
         {
                 unsigned long   *ptr = (unsigned long *)id ;
@@ -159,6 +168,9 @@
 		}
 		printk (" drive\n");
 		drive->type = type;
+
+		goto init_queue;
+
 		return;
 	}
 
@@ -189,22 +201,20 @@
 	if (drive->channel->quirkproc)
 		drive->quirk_list = drive->channel->quirkproc(drive);
 
+init_queue:
 	/*
 	 * it's an ata drive, build command list
 	 */
-#ifndef CONFIG_BLK_DEV_IDE_TCQ
 	drive->queue_depth = 1;
+#ifdef CONFIG_BLK_DEV_IDE_TCQ_DEPTH
+	drive->queue_depth = CONFIG_BLK_DEV_IDE_TCQ_DEPTH;
 #else
-# ifndef CONFIG_BLK_DEV_IDE_TCQ_DEPTH
-#  define CONFIG_BLK_DEV_IDE_TCQ_DEPTH 1
-# endif
 	drive->queue_depth = drive->id->queue_depth + 1;
-	if (drive->queue_depth > CONFIG_BLK_DEV_IDE_TCQ_DEPTH)
-		drive->queue_depth = CONFIG_BLK_DEV_IDE_TCQ_DEPTH;
+#endif
 	if (drive->queue_depth < 1 || drive->queue_depth > IDE_MAX_TAG)
 		drive->queue_depth = IDE_MAX_TAG;
-#endif
-	if (ide_build_commandlist(drive))
+
+	if (ide_init_commandlist(drive))
 		goto err_misc;
 
 	return;
@@ -583,10 +593,10 @@
 	__restore_flags(flags);	/* local CPU only */
 	for (unit = 0; unit < MAX_DRIVES; ++unit) {
 		ide_drive_t *drive = &hwif->drives[unit];
-		if (drive->present) {
-			ide_tuneproc_t *tuneproc = drive->channel->tuneproc;
-			if (tuneproc != NULL && drive->autotune == 1)
-				tuneproc(drive, 255);	/* auto-tune PIO mode */
+
+		if (drive->present && (drive->autotune == 1)) {
+			if (drive->channel->tuneproc != NULL)
+				drive->channel->tuneproc(drive, 255);	/* auto-tune PIO mode */
 		}
 	}
 }
diff -Nru a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c
--- a/drivers/ide/ide-proc.c	Mon Apr 22 15:32:18 2002
+++ b/drivers/ide/ide-proc.c	Mon Apr 22 15:32:18 2002
@@ -422,7 +422,6 @@
 static void create_proc_ide_drives(struct ata_channel *hwif)
 {
 	int	d;
-	struct proc_dir_entry *ent;
 	struct proc_dir_entry *parent = hwif->proc;
 	char name[64];
 
diff -Nru a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c
--- a/drivers/ide/ide-tape.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/ide/ide-tape.c	Mon Apr 22 15:32:19 2002
@@ -1503,9 +1503,9 @@
 			idetape_discard_data (drive, bcount);
 			return;
 		}
-#endif /* IDETAPE_DEBUG_BUGS */
+#endif
 		count = min(bio->bi_size - pc->b_count, bcount);
-		atapi_input_bytes (drive, bio_data(bio) + pc->b_count, count);
+		atapi_read(drive, bio_data(bio) + pc->b_count, count);
 		bcount -= count;
 		pc->b_count += bio->bi_size;
 		if (pc->b_count == bio->bi_size) {
@@ -1530,7 +1530,7 @@
 		}
 #endif /* IDETAPE_DEBUG_BUGS */
 		count = min(pc->b_count, bcount);
-		atapi_output_bytes (drive, bio_data(bio), count);
+		atapi_write(drive, bio_data(bio), count);
 		bcount -= count;
 		pc->b_data += count;
 		pc->b_count -= count;
@@ -2169,12 +2169,12 @@
 		if (pc->bio != NULL)
 			idetape_output_buffers (drive, pc, bcount.all);
 		else
-			atapi_output_bytes (drive,pc->current_position,bcount.all);	/* Write the current buffer */
+			atapi_write(drive,pc->current_position,bcount.all);	/* Write the current buffer */
 	} else {
 		if (pc->bio != NULL)
 			idetape_input_buffers (drive, pc, bcount.all);
 		else
-			atapi_input_bytes (drive,pc->current_position,bcount.all);	/* Read the current buffer */
+			atapi_read(drive,pc->current_position,bcount.all);	/* Read the current buffer */
 	}
 	pc->actually_transferred += bcount.all;					/* Update the current position */
 	pc->current_position+=bcount.all;
@@ -2259,7 +2259,7 @@
 	tape->cmd_start_time = jiffies;
 	BUG_ON(HWGROUP(drive)->handler);
 	ide_set_handler(drive, &idetape_pc_intr, IDETAPE_WAIT_CMD, NULL);	/* Set the interrupt routine */
-	atapi_output_bytes (drive,pc->c,12);			/* Send the actual packet */
+	atapi_write(drive,pc->c,12);	/* Send the actual packet */
 	return ide_started;
 }
 
diff -Nru a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c
--- a/drivers/ide/ide-taskfile.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/ide/ide-taskfile.c	Mon Apr 22 15:32:19 2002
@@ -1,4 +1,5 @@
 /*
+ *  Copyright (C) 2002		Marcin Dalecki <martin@dalecki.de>
  *  Copyright (C) 2000		Michael Cornwell <cornwell@acm.org>
  *  Copyright (C) 2000		Andre Hedrick <andre@linux-ide.org>
  *
@@ -33,7 +34,7 @@
 #define DEBUG_TASKFILE	0	/* unset when fixed */
 
 #if DEBUG_TASKFILE
-#define DTF(x...) printk(x)
+#define DTF(x...) printk(##x)
 #else
 #define DTF(x...)
 #endif
@@ -58,15 +59,9 @@
 		bio_kunmap_irq(to, flags);
 }
 
-static void bswap_data (void *buffer, int wcount)
-{
-	u16 *p = buffer;
-
-	while (wcount--) {
-		*p = *p << 8 | *p >> 8; p++;
-		*p = *p << 8 | *p >> 8; p++;
-	}
-}
+/*
+ * Data transfer functions for polled IO.
+ */
 
 #if SUPPORT_VLB_SYNC
 /*
@@ -76,98 +71,138 @@
  * of the sector count register location, with interrupts disabled
  * to ensure that the reads all happen together.
  */
-static inline void task_vlb_sync(ide_drive_t *drive)
+static void ata_read_vlb(struct ata_device *drive, void *buffer, unsigned int wcount)
+{
+	unsigned long flags;
+
+	__save_flags(flags);	/* local CPU only */
+	__cli();		/* local CPU only */
+	IN_BYTE(IDE_NSECTOR_REG);
+	IN_BYTE(IDE_NSECTOR_REG);
+	IN_BYTE(IDE_NSECTOR_REG);
+	insl(IDE_DATA_REG, buffer, wcount);
+	__restore_flags(flags);	/* local CPU only */
+}
+
+static void ata_write_vlb(struct ata_device *drive, void *buffer, unsigned int wcount)
+{
+	unsigned long flags;
+
+	__save_flags(flags);	/* local CPU only */
+	__cli();		/* local CPU only */
+	IN_BYTE(IDE_NSECTOR_REG);
+	IN_BYTE(IDE_NSECTOR_REG);
+	IN_BYTE(IDE_NSECTOR_REG);
+	outsl(IDE_DATA_REG, buffer, wcount);
+	__restore_flags(flags);	/* local CPU only */
+}
+#endif
+
+static void ata_read_32(struct ata_device *drive, void *buffer, unsigned int wcount)
+{
+	insl(IDE_DATA_REG, buffer, wcount);
+}
+
+static void ata_write_32(struct ata_device *drive, void *buffer, unsigned int wcount)
+{
+	outsl(IDE_DATA_REG, buffer, wcount);
+}
+
+#if SUPPORT_SLOW_DATA_PORTS
+static void ata_read_slow(struct ata_device *drive, void *buffer, unsigned int wcount)
+{
+	unsigned short *ptr = (unsigned short *) buffer;
+
+	while (wcount--) {
+		*ptr++ = inw_p(IDE_DATA_REG);
+		*ptr++ = inw_p(IDE_DATA_REG);
+	}
+}
+
+static void ata_write_slow(struct ata_device *drive, void *buffer, unsigned int wcount)
 {
-	ide_ioreg_t port = IDE_NSECTOR_REG;
+	unsigned short *ptr = (unsigned short *) buffer;
 
-	IN_BYTE(port);
-	IN_BYTE(port);
-	IN_BYTE(port);
+	while (wcount--) {
+		outw_p(*ptr++, IDE_DATA_REG);
+		outw_p(*ptr++, IDE_DATA_REG);
+	}
 }
 #endif
 
+static void ata_read_16(ide_drive_t *drive, void *buffer, unsigned int wcount)
+{
+	insw(IDE_DATA_REG, buffer, wcount<<1);
+}
+
+static void ata_write_16(ide_drive_t *drive, void *buffer, unsigned int wcount)
+{
+	outsw(IDE_DATA_REG, buffer, wcount<<1);
+}
+
 /*
- * This is used for most PIO data transfers *from* the IDE interface
+ * This is used for most PIO data transfers *from* the device.
  */
-void ata_input_data(ide_drive_t *drive, void *buffer, unsigned int wcount)
+void ata_read(ide_drive_t *drive, void *buffer, unsigned int wcount)
 {
-	byte io_32bit;
+	int io_32bit;
 
 	/*
-	 * first check if this controller has defined a special function
-	 * for handling polled ide transfers
+	 * First check if this controller has defined a special function
+	 * for handling polled ide transfers.
 	 */
-
-	if (drive->channel->ideproc) {
-		drive->channel->ideproc(ideproc_ide_input_data, drive, buffer, wcount);
+	if (drive->channel->ata_read) {
+		drive->channel->ata_read(drive, buffer, wcount);
 		return;
 	}
 
-	io_32bit = drive->io_32bit;
+	io_32bit = drive->channel->io_32bit;
 
 	if (io_32bit) {
 #if SUPPORT_VLB_SYNC
-		if (io_32bit & 2) {
-			unsigned long flags;
-			__save_flags(flags);	/* local CPU only */
-			__cli();		/* local CPU only */
-			task_vlb_sync(drive);
-			insl(IDE_DATA_REG, buffer, wcount);
-			__restore_flags(flags);	/* local CPU only */
-		} else
+		if (io_32bit & 2)
+			ata_read_vlb(drive, buffer, wcount);
+		else
 #endif
-			insl(IDE_DATA_REG, buffer, wcount);
+			ata_read_32(drive, buffer, wcount);
 	} else {
 #if SUPPORT_SLOW_DATA_PORTS
-		if (drive->slow) {
-			unsigned short *ptr = (unsigned short *) buffer;
-			while (wcount--) {
-				*ptr++ = inw_p(IDE_DATA_REG);
-				*ptr++ = inw_p(IDE_DATA_REG);
-			}
-		} else
+		if (drive->channel->slow)
+			ata_read_slow(drive, buffer, wcount);
+		else
 #endif
-			insw(IDE_DATA_REG, buffer, wcount<<1);
+			ata_read_16(drive, buffer, wcount);
 	}
 }
 
 /*
- * This is used for most PIO data transfers *to* the IDE interface
+ * This is used for most PIO data transfers *to* the device interface.
  */
-void ata_output_data(ide_drive_t *drive, void *buffer, unsigned int wcount)
+void ata_write(ide_drive_t *drive, void *buffer, unsigned int wcount)
 {
-	byte io_32bit;
+	int io_32bit;
 
-	if (drive->channel->ideproc) {
-		drive->channel->ideproc(ideproc_ide_output_data, drive, buffer, wcount);
+	if (drive->channel->ata_write) {
+		drive->channel->ata_write(drive, buffer, wcount);
 		return;
 	}
 
-	io_32bit = drive->io_32bit;
+	io_32bit = drive->channel->io_32bit;
 
 	if (io_32bit) {
 #if SUPPORT_VLB_SYNC
-		if (io_32bit & 2) {
-			unsigned long flags;
-			__save_flags(flags);	/* local CPU only */
-			__cli();		/* local CPU only */
-			task_vlb_sync(drive);
-			outsl(IDE_DATA_REG, buffer, wcount);
-			__restore_flags(flags);	/* local CPU only */
-		} else
+		if (io_32bit & 2)
+			ata_write_vlb(drive, buffer, wcount);
+		else
 #endif
-			outsl(IDE_DATA_REG, buffer, wcount);
+			ata_write_32(drive, buffer, wcount);
 	} else {
 #if SUPPORT_SLOW_DATA_PORTS
-		if (drive->slow) {
-			unsigned short *ptr = (unsigned short *) buffer;
-			while (wcount--) {
-				outw_p(*ptr++, IDE_DATA_REG);
-				outw_p(*ptr++, IDE_DATA_REG);
-			}
-		} else
+		if (drive->channel->slow)
+			ata_write_slow(drive, buffer, wcount);
+		else
 #endif
-			outsw(IDE_DATA_REG, buffer, wcount<<1);
+			ata_write_16(drive, buffer, wcount<<1);
 	}
 }
 
@@ -178,10 +213,10 @@
  * so if an odd bytecount is specified, be sure that there's at least one
  * extra byte allocated for the buffer.
  */
-void atapi_input_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount)
+void atapi_read(ide_drive_t *drive, void *buffer, unsigned int bytecount)
 {
-	if (drive->channel->ideproc) {
-		drive->channel->ideproc(ideproc_atapi_input_bytes, drive, buffer, bytecount);
+	if (drive->channel->atapi_read) {
+		drive->channel->atapi_read(drive, buffer, bytecount);
 		return;
 	}
 
@@ -193,15 +228,15 @@
 		return;
 	}
 #endif
-	ata_input_data (drive, buffer, bytecount / 4);
+	ata_read(drive, buffer, bytecount / 4);
 	if ((bytecount & 0x03) >= 2)
-		insw (IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1);
+		insw(IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1);
 }
 
-void atapi_output_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount)
+void atapi_write(ide_drive_t *drive, void *buffer, unsigned int bytecount)
 {
-	if (drive->channel->ideproc) {
-		drive->channel->ideproc(ideproc_atapi_output_bytes, drive, buffer, bytecount);
+	if (drive->channel->atapi_write) {
+		drive->channel->atapi_write(drive, buffer, bytecount);
 		return;
 	}
 
@@ -213,29 +248,11 @@
 		return;
 	}
 #endif
-	ata_output_data (drive, buffer, bytecount / 4);
+	ata_write(drive, buffer, bytecount / 4);
 	if ((bytecount & 0x03) >= 2)
 		outsw(IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1);
 }
 
-void taskfile_input_data(ide_drive_t *drive, void *buffer, unsigned int wcount)
-{
-	ata_input_data(drive, buffer, wcount);
-	if (drive->bswap)
-		bswap_data(buffer, wcount);
-}
-
-void taskfile_output_data(ide_drive_t *drive, void *buffer, unsigned int wcount)
-{
-	if (drive->bswap) {
-		bswap_data(buffer, wcount);
-		ata_output_data(drive, buffer, wcount);
-		bswap_data(buffer, wcount);
-	} else {
-		ata_output_data(drive, buffer, wcount);
-	}
-}
-
 /*
  * Needed for PCI irq sharing
  */
@@ -311,8 +328,6 @@
 static ide_startstop_t task_mulout_intr (ide_drive_t *drive)
 {
 	byte stat		= GET_STAT();
-	/* FIXME: this should go possible as well */
-	byte io_32bit		= drive->io_32bit;
 	struct request *rq	= &HWGROUP(drive)->wrq;
 	ide_hwgroup_t *hwgroup	= HWGROUP(drive);
 	int mcount		= drive->mult_count;
@@ -378,14 +393,13 @@
 		}
 
 		/*
-		 * Ok, we're all setup for the interrupt
-		 * re-entering us on the last transfer.
+		 * Ok, we're all setup for the interrupt re-entering us on the
+		 * last transfer.
 		 */
-		taskfile_output_data(drive, buffer, nsect * SECTOR_WORDS);
+		ata_write(drive, buffer, nsect * SECTOR_WORDS);
 		bio_kunmap_irq(buffer, &flags);
 	} while (mcount);
 
-	drive->io_32bit = io_32bit;
 	rq->errors = 0;
 	if (hwgroup->handler == NULL)
 		ide_set_handler(drive, task_mulout_intr, WAIT_CMD, NULL);
@@ -399,6 +413,20 @@
 	struct hd_driveid *id = drive->id;
 	u8 HIHI = (drive->addressing) ? 0xE0 : 0xEF;
 
+#if 0
+	printk("ata_taskfile ... %p\n", args->handler);
+
+	printk("   sector feature          %02x\n", args->taskfile.feature);
+	printk("   sector count            %02x\n", args->taskfile.sector_count);
+	printk("   drive/head              %02x\n", args->taskfile.device_head);
+	printk("   command                 %02x\n", args->taskfile.command);
+
+	if (rq)
+		printk("   rq->nr_sectors          %2li\n",  rq->nr_sectors);
+	else
+		printk("   rq->                   = null\n");
+#endif
+ 
 	/* (ks/hs): Moved to start, do not use for multiple out commands */
 	if (args->handler != task_mulout_intr) {
 		if (IDE_CONTROL_REG)
@@ -517,11 +545,28 @@
 }
 
 /*
+ * Quiet handler for commands without a data phase -- handy instead of
+ * task_no_data_intr() for commands we _know_ will fail (such as WIN_NOP)
+ */
+ide_startstop_t task_no_data_quiet_intr(ide_drive_t *drive)
+{
+	struct ata_request *ar = IDE_CUR_AR(drive);
+	struct ata_taskfile *args = &ar->ar_task;
+
+	ide__sti();	/* local CPU only */
+
+	if (args)
+		ide_end_drive_cmd(drive, GET_STAT(), GET_ERR());
+
+	return ide_stopped;
+}
+
+/*
  * Handler for commands without a data phase
  */
 ide_startstop_t task_no_data_intr (ide_drive_t *drive)
 {
-	struct ata_request *ar = HWGROUP(drive)->rq->special;
+	struct ata_request *ar = IDE_CUR_AR(drive);
 	struct ata_taskfile *args = &ar->ar_task;
 	u8 stat = GET_STAT();
 
@@ -542,7 +587,6 @@
 static ide_startstop_t task_in_intr (ide_drive_t *drive)
 {
 	byte stat		= GET_STAT();
-	byte io_32bit		= drive->io_32bit;
 	struct request *rq	= HWGROUP(drive)->rq;
 	char *pBuf		= NULL;
 	unsigned long flags;
@@ -561,23 +605,25 @@
 	pBuf = ide_map_rq(rq, &flags);
 	DTF("Read: %p, rq->current_nr_sectors: %d\n", pBuf, (int) rq->current_nr_sectors);
 
-	drive->io_32bit = 0;
-	taskfile_input_data(drive, pBuf, SECTOR_WORDS);
+	ata_read(drive, pBuf, SECTOR_WORDS);
 	ide_unmap_rq(rq, pBuf, &flags);
-	drive->io_32bit = io_32bit;
 
+	/*
+	 * first segment of the request is complete. note that this does not
+	 * necessarily mean that the entire request is done!! this is only
+	 * true if ide_end_request() returns 0.
+	 */
 	if (--rq->current_nr_sectors <= 0) {
-		/* (hs): swapped next 2 lines */
 		DTF("Request Ended stat: %02x\n", GET_STAT());
-		if (ide_end_request(drive, 1)) {
-			ide_set_handler(drive, &task_in_intr,  WAIT_CMD, NULL);
-			return ide_started;
-		}
-	} else {
-		ide_set_handler(drive, &task_in_intr,  WAIT_CMD, NULL);
-		return ide_started;
+		if (!ide_end_request(drive, 1))
+			return ide_stopped;
 	}
-	return ide_stopped;
+
+	/*
+	 * still data left to transfer
+	 */
+	ide_set_handler(drive, &task_in_intr,  WAIT_CMD, NULL);
+	return ide_started;
 }
 
 static ide_startstop_t pre_task_out_intr(ide_drive_t *drive, struct request *rq)
@@ -597,7 +643,7 @@
 		unsigned long flags;
 		char *buf = ide_map_rq(rq, &flags);
 		/* For Write_sectors we need to stuff the first sector */
-		taskfile_output_data(drive, buf, SECTOR_WORDS);
+		ata_write(drive, buf, SECTOR_WORDS);
 		rq->current_nr_sectors--;
 		ide_unmap_rq(rq, buf, &flags);
 	} else {
@@ -613,8 +659,6 @@
 static ide_startstop_t task_out_intr(ide_drive_t *drive)
 {
 	byte stat		= GET_STAT();
-	/* FIXME: this should go possible as well */
-	byte io_32bit		= drive->io_32bit;
 	struct request *rq	= HWGROUP(drive)->rq;
 	char *pBuf		= NULL;
 	unsigned long flags;
@@ -631,9 +675,8 @@
 		pBuf = ide_map_rq(rq, &flags);
 		DTF("write: %p, rq->current_nr_sectors: %d\n", pBuf, (int) rq->current_nr_sectors);
 
-		taskfile_output_data(drive, pBuf, SECTOR_WORDS);
+		ata_write(drive, pBuf, SECTOR_WORDS);
 		ide_unmap_rq(rq, pBuf, &flags);
-		drive->io_32bit = io_32bit;
 		rq->errors = 0;
 		rq->current_nr_sectors--;
 	}
@@ -649,8 +692,6 @@
 {
 	unsigned int		msect, nsect;
 	byte stat		= GET_STAT();
-	/* FIXME: this should go possible as well */
-	byte io_32bit		= drive->io_32bit;
 	struct request *rq	= HWGROUP(drive)->rq;
 	char *pBuf		= NULL;
 	unsigned long flags;
@@ -676,10 +717,8 @@
 
 		DTF("Multiread: %p, nsect: %d , rq->current_nr_sectors: %d\n",
 			pBuf, nsect, rq->current_nr_sectors);
-		drive->io_32bit = 0;
-		taskfile_input_data(drive, pBuf, nsect * SECTOR_WORDS);
+		ata_read(drive, pBuf, nsect * SECTOR_WORDS);
 		ide_unmap_rq(rq, pBuf, &flags);
-		drive->io_32bit = io_32bit;
 		rq->errors = 0;
 		rq->current_nr_sectors -= nsect;
 		msect -= nsect;
@@ -870,7 +909,7 @@
 			return;
 
 		case WIN_NOP:
-
+			args->handler = task_no_data_quiet_intr;
 			args->command_type = IDE_DRIVE_TASK_NO_DATA;
 			return;
 
@@ -897,22 +936,29 @@
 int ide_raw_taskfile(ide_drive_t *drive, struct ata_taskfile *args, byte *buf)
 {
 	struct request rq;
-	struct ata_request ar;
+	struct ata_request star;
+	int ret;
 
-	ata_ar_init(drive, &ar);
+	ata_ar_init(drive, &star);
 	init_taskfile_request(&rq);
 	rq.buffer = buf;
 
-	memcpy(&ar.ar_task, args, sizeof(*args));
+	memcpy(&star.ar_task, args, sizeof(*args));
 
 	if (args->command_type != IDE_DRIVE_TASK_NO_DATA)
 		rq.current_nr_sectors = rq.nr_sectors
 			= (args->hobfile.sector_count << 8)
 			| args->taskfile.sector_count;
 
-	rq.special = &ar;
+	rq.special = &star;
+
+	ret = ide_do_drive_cmd(drive, &rq, ide_wait);
 
-	return ide_do_drive_cmd(drive, &rq, ide_wait);
+	/*
+	 * copy back status etc
+	 */
+	memcpy(args, &star.ar_task, sizeof(*args));
+	return ret;
 }
 
 /*
@@ -967,6 +1013,7 @@
 		if (argbuf == NULL)
 			return -ENOMEM;
 		memcpy(argbuf, vals, 4);
+		memset(argbuf + 4, 0, argsize - 4);
 	}
 
 	if (set_transfer(drive, &args)) {
@@ -977,14 +1024,8 @@
 
 	/* Issue ATA command and wait for completion.
 	 */
-
-	/* FIXME: Do we really have to zero out the buffer?
-	 */
-	memset(argbuf, 0, 4 + SECTOR_WORDS * 4 * vals[3]);
 	ide_init_drive_cmd(&rq);
 	rq.buffer = argbuf;
-	memcpy(argbuf, vals, 4);
-
 	err = ide_do_drive_cmd(drive, &rq, ide_wait);
 
 	if (!err && xfer_rate) {
@@ -1025,12 +1066,11 @@
 }
 
 EXPORT_SYMBOL(drive_is_ready);
-EXPORT_SYMBOL(ata_input_data);
-EXPORT_SYMBOL(ata_output_data);
-EXPORT_SYMBOL(atapi_input_bytes);
-EXPORT_SYMBOL(atapi_output_bytes);
-EXPORT_SYMBOL(taskfile_input_data);
-EXPORT_SYMBOL(taskfile_output_data);
+
+EXPORT_SYMBOL(ata_read);
+EXPORT_SYMBOL(ata_write);
+EXPORT_SYMBOL(atapi_read);
+EXPORT_SYMBOL(atapi_write);
 
 EXPORT_SYMBOL(ata_taskfile);
 EXPORT_SYMBOL(recal_intr);
diff -Nru a/drivers/ide/ide-tcq.c b/drivers/ide/ide-tcq.c
--- a/drivers/ide/ide-tcq.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/ide/ide-tcq.c	Mon Apr 22 15:32:19 2002
@@ -29,9 +29,11 @@
 #include <asm/delay.h>
 
 /*
- * warning: it will be _very_ verbose if set to 1
+ * warning: it will be _very_ verbose if defined
  */
-#if 0
+#undef IDE_TCQ_DEBUG
+
+#ifdef IDE_TCQ_DEBUG
 #define TCQ_PRINTK printk
 #else
 #define TCQ_PRINTK(x...)
@@ -49,16 +51,17 @@
  */
 #undef IDE_TCQ_FIDDLE_SI
 
-int ide_tcq_end(ide_drive_t *drive);
 ide_startstop_t ide_dmaq_intr(ide_drive_t *drive);
+ide_startstop_t ide_service(ide_drive_t *drive);
 
-static inline void drive_ctl_nien(ide_drive_t *drive, int clear)
+static inline void drive_ctl_nien(ide_drive_t *drive, int set)
 {
 #ifdef IDE_TCQ_NIEN
-	int mask = clear ? 0 : 2;
+	if (IDE_CONTROL_REG) {
+		int mask = set ? 0x02 : 0x00;
 
-	if (IDE_CONTROL_REG)
 		OUT_BYTE(drive->ctl | mask, IDE_CONTROL_REG);
+	}
 #endif
 }
 
@@ -74,12 +77,15 @@
 	struct ata_request *ar;
 	int i;
 
-	printk("%s: invalidating pending queue\n", drive->name);
+	printk("%s: invalidating pending queue (%d)\n", drive->name, drive->tcq->queued);
 
 	spin_lock_irqsave(&ide_lock, flags);
 
 	del_timer(&HWGROUP(drive)->timer);
 
+	if (test_bit(IDE_DMA, &HWGROUP(drive)->flags))
+		drive->channel->dmaproc(ide_dma_end, drive);
+
 	/*
 	 * assume oldest commands have the higher tags... doesn't matter
 	 * much. shove requests back into request queue.
@@ -116,7 +122,6 @@
 	init_taskfile_request(ar->ar_rq);
 	ar->ar_rq->rq_dev = mk_kdev(drive->channel->major, (drive->select.b.unit)<<PARTN_BITS);
 	ar->ar_rq->special = ar;
-	ar->ar_flags |= ATA_AR_RETURN;
 	_elv_add_request(q, ar->ar_rq, 0, 0);
 
 	/*
@@ -142,7 +147,7 @@
 
 	spin_lock_irqsave(&ide_lock, flags);
 
-	if (test_bit(IDE_BUSY, &hwgroup->flags))
+	if (test_and_set_bit(IDE_BUSY, &hwgroup->flags))
 		printk("ide_tcq_intr_timeout: hwgroup not busy\n");
 	if (hwgroup->handler == NULL)
 		printk("ide_tcq_intr_timeout: missing isr!\n");
@@ -151,6 +156,13 @@
 
 	spin_unlock_irqrestore(&ide_lock, flags);
 
+	/*
+	 * if pending commands, try service before giving up
+	 */
+	if (ide_pending_commands(drive) && (GET_STAT() & SERVICE_STAT))
+		if (ide_service(drive) == ide_started)
+			return;
+
 	if (drive)
 		ide_tcq_invalidate_queue(drive);
 }
@@ -179,20 +191,16 @@
 #define IDE_TCQ_WAIT	(10000)
 int ide_tcq_wait_altstat(ide_drive_t *drive, byte *stat, byte busy_mask)
 {
-	int i;
+	int i = 0;
 
-	/*
-	 * one initial udelay(1) should be enough, reading alt stat should
-	 * provide the required delay...
-	 */
-	*stat = 0;
-	i = 0;
-	do {
-		udelay(1);
+	udelay(1);
+
+	while ((*stat = GET_ALTSTAT()) & busy_mask) {
+		udelay(10);
 
 		if (unlikely(i++ > IDE_TCQ_WAIT))
 			return 1;
-	} while ((*stat = GET_ALTSTAT()) & busy_mask);
+	}
 
 	return 0;
 }
@@ -206,10 +214,17 @@
 ide_startstop_t ide_service(ide_drive_t *drive)
 {
 	struct ata_request *ar;
-	byte feat, tag, stat;
+	byte feat, stat;
+	int tag, ret;
+
+	TCQ_PRINTK("%s: started service\n", drive->name);
 
+	/*
+	 * could be called with IDE_DMA in-progress from invalidate
+	 * handler, refuse to do anything
+	 */
 	if (test_bit(IDE_DMA, &HWGROUP(drive)->flags))
-		printk("ide_service: DMA in progress\n");
+		return ide_stopped;
 
 	/*
 	 * need to select the right drive first...
@@ -228,6 +243,7 @@
 
 	if (ide_tcq_wait_altstat(drive, &stat, BUSY_STAT)) {
 		printk("ide_service: BUSY clear took too long\n");
+		ide_dump_status(drive, "ide_service", stat);
 		ide_tcq_invalidate_queue(drive);
 		return ide_stopped;
 	}
@@ -238,9 +254,9 @@
 	 * FIXME, invalidate queue
 	 */
 	if (stat & ERR_STAT) {
-		printk("%s: error SERVICING drive (%x)\n", drive->name, stat);
 		ide_dump_status(drive, "ide_service", stat);
-		return ide_tcq_end(drive);
+		ide_tcq_invalidate_queue(drive);
+		return ide_stopped;
 	}
 
 	/*
@@ -248,13 +264,10 @@
 	 */
 	if ((feat = GET_FEAT()) & NSEC_REL) {
 		printk("%s: release in service\n", drive->name);
-		IDE_SET_CUR_TAG(drive, -1);
+		IDE_SET_CUR_TAG(drive, IDE_INACTIVE_TAG);
 		return ide_stopped;
 	}
 
-	/*
-	 * start dma
-	 */
 	tag = feat >> 3;
 	IDE_SET_CUR_TAG(drive, tag);
 
@@ -265,28 +278,32 @@
 		return ide_stopped;
 	}
 
+	HWGROUP(drive)->rq = ar->ar_rq;
+
 	/*
 	 * we'll start a dma read or write, device will trigger
 	 * interrupt to indicate end of transfer, release is not allowed
 	 */
 	if (rq_data_dir(ar->ar_rq) == READ) {
 		TCQ_PRINTK("ide_service: starting READ %x\n", stat);
-		drive->channel->dmaproc(ide_dma_read_queued, drive);
+		ret = drive->channel->dmaproc(ide_dma_read_queued, drive);
 	} else {
 		TCQ_PRINTK("ide_service: starting WRITE %x\n", stat);
-		drive->channel->dmaproc(ide_dma_write_queued, drive);
+		ret = drive->channel->dmaproc(ide_dma_write_queued, drive);
 	}
 
 	/*
 	 * dmaproc set intr handler
 	 */
-	return ide_started;
+	return !ret ? ide_started : ide_stopped;
 }
 
 ide_startstop_t ide_check_service(ide_drive_t *drive)
 {
 	byte stat;
 
+	TCQ_PRINTK("%s: ide_check_service\n", drive->name);
+
 	if (!ide_pending_commands(drive))
 		return ide_stopped;
 
@@ -300,40 +317,14 @@
 	return ide_started;
 }
 
-int ide_tcq_end(ide_drive_t *drive)
-{
-	byte stat = GET_STAT();
-
-	if (stat & ERR_STAT) {
-		ide_dump_status(drive, "ide_tcq_end", stat);
-		ide_tcq_invalidate_queue(drive);
-		return ide_stopped;
-	} else if (stat & SERVICE_STAT) {
-		TCQ_PRINTK("ide_tcq_end: serv stat=%x\n", stat);
-		return ide_service(drive);
-	}
-
-	TCQ_PRINTK("ide_tcq_end: stat=%x, feat=%x\n", stat, GET_FEAT());
-	return ide_stopped;
-}
-
 ide_startstop_t ide_dmaq_complete(ide_drive_t *drive, byte stat)
 {
-	struct ata_request *ar;
+	struct ata_request *ar = IDE_CUR_TAG(drive);
 	byte dma_stat;
 
-#if 0
-	byte feat = GET_FEAT();
-
-	if ((feat & (NSEC_CD | NSEC_IO)) != (NSEC_CD | NSEC_IO))
-		printk("%s: C/D | I/O not set\n", drive->name);
-#endif
-
 	/*
 	 * transfer was in progress, stop DMA engine
 	 */
-	ar = IDE_CUR_TAG(drive);
-
 	dma_stat = drive->channel->dmaproc(ide_dma_end, drive);
 
 	/*
@@ -352,7 +343,11 @@
 	TCQ_PRINTK("ide_dmaq_intr: ending %p, tag %d\n", ar, ar->ar_tag);
 	ide_end_queued_request(drive, !dma_stat, ar->ar_rq);
 
-	IDE_SET_CUR_TAG(drive, -1);
+	/*
+	 * we completed this command, set tcq inactive and check if we
+	 * can service a new command
+	 */
+	IDE_SET_CUR_TAG(drive, IDE_INACTIVE_TAG);
 	return ide_check_service(drive);
 }
 
@@ -376,7 +371,7 @@
 	 * if a command completion interrupt is pending, do that first and
 	 * check service afterwards
 	 */
-	if (drive->tcq->active_tag != -1)
+	if (drive->tcq->active_tag != IDE_INACTIVE_TAG)
 		return ide_dmaq_complete(drive, stat);
 
 	/*
@@ -392,21 +387,57 @@
 }
 
 /*
+ * check if the ata adapter this drive is attached to supports the
+ * NOP auto-poll for multiple tcq enabled drives on one channel
+ */
+static int ide_tcq_check_autopoll(ide_drive_t *drive)
+{
+	struct ata_channel *ch = HWIF(drive);
+	struct ata_taskfile args;
+	ide_drive_t *next;
+
+	/*
+	 * only need to probe if both drives on a channel support tcq
+	 */
+	next = drive->next;
+	if (next == drive || !next->using_tcq)
+		return 0;
+
+	memset(&args, 0, sizeof(args));
+
+	args.taskfile.feature = 0x01;
+	args.taskfile.command = WIN_NOP;
+	ide_cmd_type_parser(&args);
+
+	/*
+	 * do taskfile and check ABRT bit -- intelligent adapters will not
+	 * pass NOP with sub-code 0x01 to device, so the command will not
+	 * fail there
+	 */
+	ide_raw_taskfile(drive, &args, NULL);
+	if (args.taskfile.feature & ABRT_ERR)
+		return 1;
+
+	ch->auto_poll = 1;
+	printk("%s: NOP Auto-poll enabled\n", ch->name);
+	return 0;
+}
+
+/*
  * configure the drive for tcq
  */
 static int ide_tcq_configure(ide_drive_t *drive)
 {
+	int tcq_mask = 1 << 1 | 1 << 14;
+	int tcq_bits = tcq_mask | 1 << 15;
 	struct ata_taskfile args;
-	int tcq_supp = 1 << 1 | 1 << 14;
 
 	/*
 	 * bit 14 and 1 must be set in word 83 of the device id to indicate
-	 * support for dma queued protocol
+	 * support for dma queued protocol, and bit 15 must be cleared
 	 */
-	if ((drive->id->command_set_2 & tcq_supp) != tcq_supp) {
-		printk("%s: queued feature set not supported\n", drive->name);
-		return 1;
-	}
+	if ((drive->id->command_set_2 & tcq_bits) ^ tcq_mask)
+		return -EIO;
 
 	memset(&args, 0, sizeof(args));
 	args.taskfile.feature = SETFEATURES_EN_WCACHE;
@@ -446,6 +477,16 @@
 		return 1;
 	}
 #endif
+
+	if (!drive->tcq) {
+		drive->tcq = kmalloc(sizeof(ide_tag_info_t), GFP_ATOMIC);
+		if (!drive->tcq)
+			return -ENOMEM;
+
+		memset(drive->tcq, 0, sizeof(ide_tag_info_t));
+		drive->tcq->active_tag = IDE_INACTIVE_TAG;
+	}
+
 	return 0;
 }
 
@@ -455,32 +496,55 @@
  */
 static int ide_enable_queued(ide_drive_t *drive, int on)
 {
+	int depth = drive->using_tcq ? drive->queue_depth : 0;
+
 	/*
 	 * disable or adjust queue depth
 	 */
 	if (!on) {
-		printk("%s: TCQ disabled\n", drive->name);
+		if (drive->using_tcq)
+			printk("%s: TCQ disabled\n", drive->name);
 		drive->using_tcq = 0;
-
 		return 0;
-	} else if (drive->using_tcq) {
-		drive->queue_depth = drive->using_tcq;
-
-		goto out;
 	}
 
 	if (ide_tcq_configure(drive)) {
 		drive->using_tcq = 0;
-
 		return 1;
 	}
 
-out:
-	drive->tcq->max_depth = 0;
+	/*
+	 * possibly expand command list
+	 */
+	if (ide_build_commandlist(drive))
+		return 1;
+
+	/*
+	 * check auto-poll support
+	 */
+	ide_tcq_check_autopoll(drive);
+
+	if (depth != drive->queue_depth)
+		printk("%s: tagged command queueing enabled, command queue depth %d\n", drive->name, drive->queue_depth);
 
-	printk("%s: tagged command queueing enabled, command queue depth %d\n", drive->name, drive->queue_depth);
 	drive->using_tcq = 1;
 
+	/*
+	 * clear stats
+	 */
+	drive->tcq->max_depth = 0;
+	return 0;
+}
+
+int ide_tcq_wait_dataphase(ide_drive_t *drive)
+{
+	ide_startstop_t foo;
+
+	if (ide_wait_stat(&foo, drive, READY_STAT | DRQ_STAT, BUSY_STAT, WAIT_READY)) {
+		printk("%s: timeout waiting for data phase\n", drive->name);
+		return 1;
+	}
+
 	return 0;
 }
 
@@ -488,7 +552,6 @@
 {
 	struct ata_channel *hwif = drive->channel;
 	unsigned int reading = 0, enable_tcq = 1;
-	ide_startstop_t startstop;
 	struct ata_request *ar;
 	byte stat, feat;
 
@@ -501,15 +564,13 @@
 			reading = 1 << 3;
 		case ide_dma_write_queued:
 			TCQ_PRINTK("ide_dma: setting up queued %d\n", drive->tcq->active_tag);
-			BUG_ON(drive->tcq->active_tag == -1);
+			BUG_ON(drive->tcq->active_tag == IDE_INACTIVE_TAG);
 
 			if (!test_bit(IDE_BUSY, &HWGROUP(drive)->flags))
 				printk("queued_rw: IDE_BUSY not set\n");
 
-			if (ide_wait_stat(&startstop, drive, READY_STAT | DRQ_STAT, BUSY_STAT, WAIT_READY)) {
-				printk("%s: timeout waiting for data phase\n", drive->name);
-				return startstop;
-			}
+			if (ide_tcq_wait_dataphase(drive))
+				return ide_stopped;
 
 			if (ide_start_dma(hwif, drive, func))
 				return 1;
@@ -521,7 +582,7 @@
 			 * start a queued command from scratch
 			 */
 		case ide_dma_queued_start:
-			BUG_ON(drive->tcq->active_tag == -1);
+			BUG_ON(drive->tcq->active_tag == IDE_INACTIVE_TAG);
 			ar = IDE_CUR_TAG(drive);
 
 			/*
@@ -533,51 +594,65 @@
 			OUT_BYTE(AR_TASK_CMD(ar), IDE_COMMAND_REG);
 
 			if (ide_tcq_wait_altstat(drive, &stat, BUSY_STAT)) {
-				printk("ide_dma_queued_start: abort (stat=%x)\n", stat);
+				ide_dump_status(drive, "queued start", stat);
+				ide_tcq_invalidate_queue(drive);
 				return ide_stopped;
 			}
 
 			drive_ctl_nien(drive, 0);
 
 			if (stat & ERR_STAT) {
-				printk("ide_dma_queued_start: abort (stat=%x)\n", stat);
+				ide_dump_status(drive, "tcq_start", stat);
 				return ide_stopped;
 			}
 
+			/*
+			 * drive released the bus, clear active tag and
+			 * check for service
+			 */
 			if ((feat = GET_FEAT()) & NSEC_REL) {
+				IDE_SET_CUR_TAG(drive, IDE_INACTIVE_TAG);
 				drive->tcq->immed_rel++;
+
+				ide_tcq_set_intr(HWGROUP(drive), ide_dmaq_intr);
+
 				TCQ_PRINTK("REL in queued_start\n");
-				IDE_SET_CUR_TAG(drive, -1);
 
 				if ((stat = GET_STAT()) & SERVICE_STAT)
 					return ide_service(drive);
 
-				ide_tcq_set_intr(HWGROUP(drive), ide_dmaq_intr);
 				return ide_released;
 			}
 
 			drive->tcq->immed_comp++;
 
-			if (ide_wait_stat(&startstop, drive, READY_STAT | DRQ_STAT, BUSY_STAT, WAIT_READY)) {
-				printk("%s: timeout waiting for data phase\n", drive->name);
-				return startstop;
-			}
+			if (ide_tcq_wait_dataphase(drive))
+				return ide_stopped;
 
 			if (ide_start_dma(hwif, drive, func))
 				return ide_stopped;
 
+			TCQ_PRINTK("IMMED in queued_start\n");
+
+			/*
+			 * need to arm handler before starting dma engine,
+			 * transfer could complete right away
+			 */
+			ide_tcq_set_intr(HWGROUP(drive), ide_dmaq_intr);
+
 			if (hwif->dmaproc(ide_dma_begin, drive))
 				return ide_stopped;
 
 			/*
 			 * wait for SERVICE or completion interrupt
 			 */
-			ide_tcq_set_intr(HWGROUP(drive), ide_dmaq_intr);
 			return ide_started;
 
 		case ide_dma_queued_off:
 			enable_tcq = 0;
 		case ide_dma_queued_on:
+			if (enable_tcq && !drive->using_dma)
+				return 1;
 			return ide_enable_queued(drive, enable_tcq);
 		default:
 			break;
@@ -590,6 +665,10 @@
 ide_startstop_t ide_start_tag(ide_dma_action_t func, ide_drive_t *drive,
 			      struct ata_request *ar)
 {
+	ide_startstop_t startstop;
+
+	TCQ_PRINTK("%s: ide_start_tag: begin tag %p/%d, rq %p\n", drive->name,ar,ar->ar_tag, ar->ar_rq);
+
 	/*
 	 * do this now, no need to run that with interrupts disabled
 	 */
@@ -597,5 +676,14 @@
 		return ide_stopped;
 
 	IDE_SET_CUR_TAG(drive, ar->ar_tag);
-	return ide_tcq_dmaproc(func, drive);
+	HWGROUP(drive)->rq = ar->ar_rq;
+
+	startstop = ide_tcq_dmaproc(func, drive);
+
+	if (unlikely(startstop == ide_stopped)) {
+		IDE_SET_CUR_TAG(drive, IDE_INACTIVE_TAG);
+		HWGROUP(drive)->rq = NULL;
+	}
+
+	return startstop;
 }
diff -Nru a/drivers/ide/ide.c b/drivers/ide/ide.c
--- a/drivers/ide/ide.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/ide/ide.c	Mon Apr 22 15:32:19 2002
@@ -381,8 +381,23 @@
 		add_blkdev_randomness(major(rq->rq_dev));
 
 		spin_lock_irqsave(&ide_lock, flags);
+
+		if ((jiffies - ar->ar_time > ATA_AR_MAX_TURNAROUND) && drive->queue_depth > 1) {
+			printk(KERN_INFO "%s: exceeded max command turn-around time (%d seconds)\n", drive->name, ATA_AR_MAX_TURNAROUND / HZ);
+			drive->queue_depth >>= 1;
+		}
+
+		if (jiffies - ar->ar_time > drive->tcq->oldest_command)
+			drive->tcq->oldest_command = jiffies - ar->ar_time;
+
 		ata_ar_put(drive, ar);
 		end_that_request_last(rq);
+		/*
+		 * IDE_SET_CUR_TAG(drive, IDE_INACTIVE_TAG) will do this
+		 * too, but it really belongs here. assumes that the
+		 * ended request is the active one.
+		 */
+		HWGROUP(drive)->rq = NULL;
 		spin_unlock_irqrestore(&ide_lock, flags);
 	}
 }
@@ -459,27 +474,23 @@
 	spin_unlock_irqrestore(&ide_lock, flags);
 }
 
-static void ata_pre_reset (ide_drive_t *drive)
+static void ata_pre_reset(ide_drive_t *drive)
 {
 	if (ata_ops(drive) && ata_ops(drive)->pre_reset)
 		ata_ops(drive)->pre_reset(drive);
 
-	if (!drive->keep_settings && !drive->using_dma) {
-		drive->unmask = 0;
-		drive->io_32bit = 0;
-	}
+	if (!drive->using_dma)
+	    return;
 
-	if (drive->using_dma) {
-		/* check the DMA crc count */
-		if (drive->crc_count) {
-			drive->channel->dmaproc(ide_dma_off_quietly, drive);
-			if ((drive->channel->speedproc) != NULL)
-				drive->channel->speedproc(drive, ide_auto_reduce_xfer(drive));
-			if (drive->current_speed >= XFER_SW_DMA_0)
-				drive->channel->dmaproc(ide_dma_on, drive);
-		} else
-			drive->channel->dmaproc(ide_dma_off, drive);
-	}
+	/* check the DMA crc count */
+	if (drive->crc_count) {
+		drive->channel->dmaproc(ide_dma_off_quietly, drive);
+		if ((drive->channel->speedproc) != NULL)
+		        drive->channel->speedproc(drive, ide_auto_reduce_xfer(drive));
+		if (drive->current_speed >= XFER_SW_DMA_0)
+			drive->channel->dmaproc(ide_dma_on, drive);
+	} else
+		drive->channel->dmaproc(ide_dma_off, drive);
 }
 
 /*
@@ -513,10 +524,9 @@
 	printk("%s: ata_special: 0x%02x\n", drive->name, s->all);
 #endif
 	if (s->b.set_tune) {
-		ide_tuneproc_t *tuneproc = drive->channel->tuneproc;
 		s->b.set_tune = 0;
-		if (tuneproc != NULL)
-			tuneproc(drive, drive->tune_req);
+		if (drive->channel->tuneproc != NULL)
+			drive->channel->tuneproc(drive, drive->tune_req);
 	} else if (drive->driver != NULL) {
 		if (ata_ops(drive)->special)
 			return ata_ops(drive)->special(drive);
@@ -770,8 +780,7 @@
 		struct ata_taskfile *args = &ar->ar_task;
 
 		rq->errors = !OK_STAT(stat, READY_STAT, BAD_STAT);
-		if (args && args->taskfile.command == WIN_NOP)
-			printk(KERN_INFO "%s: NOP completed\n", __FUNCTION__);
+
 		if (args) {
 			args->taskfile.feature = err;
 			args->taskfile.sector_count = IN_BYTE(IDE_NSECTOR_REG);
@@ -794,8 +803,7 @@
 				args->hobfile.high_cylinder = IN_BYTE(IDE_HCYL_REG);
 			}
 		}
-		if (ar->ar_flags & ATA_AR_RETURN)
-			ata_ar_put(drive, ar);
+		ata_ar_put(drive, ar);
 	}
 
 	blkdev_dequeue_request(rq);
@@ -885,23 +893,22 @@
 }
 
 /*
- * try_to_flush_leftover_data() is invoked in response to a drive
- * unexpectedly having its DRQ_STAT bit set.  As an alternative to
- * resetting the drive, this routine tries to clear the condition
- * by read a sector's worth of data from the drive.  Of course,
+ * This gets invoked in response to a drive unexpectedly having its DRQ_STAT
+ * bit set.  As an alternative to resetting the drive, it tries to clear the
+ * condition by reading a sector's worth of data from the drive.  Of course,
  * this may not help if the drive is *waiting* for data from *us*.
  */
 static void try_to_flush_leftover_data (ide_drive_t *drive)
 {
-	int i = (drive->mult_count ? drive->mult_count : 1) * SECTOR_WORDS;
+	int i;
 
 	if (drive->type != ATA_DISK)
 		return;
-	while (i > 0) {
-		u32 buffer[16];
-		unsigned int wcount = (i > 16) ? 16 : i;
-		i -= wcount;
-		ata_input_data (drive, buffer, wcount);
+
+	for (i = (drive->mult_count ? drive->mult_count : 1); i > 0; --i) {
+		u32 buffer[SECTOR_WORDS];
+
+		ata_read(drive, buffer, SECTOR_WORDS);
 	}
 }
 
@@ -985,19 +992,17 @@
 /*
  * Invoked on completion of a special DRIVE_CMD.
  */
-static ide_startstop_t drive_cmd_intr (ide_drive_t *drive)
+static ide_startstop_t drive_cmd_intr(ide_drive_t *drive)
 {
 	struct request *rq = HWGROUP(drive)->rq;
-	byte *args = (byte *) rq->buffer;
-	byte stat = GET_STAT();
+	u8 *args = rq->buffer;
+	u8 stat = GET_STAT();
 	int retries = 10;
 
 	ide__sti();	/* local CPU only */
 	if ((stat & DRQ_STAT) && args && args[3]) {
-		byte io_32bit = drive->io_32bit;
-		drive->io_32bit = 0;
-		ata_input_data(drive, &args[4], args[3] * SECTOR_WORDS);
-		drive->io_32bit = io_32bit;
+		ata_read(drive, &args[4], args[3] * SECTOR_WORDS);
+
 		while (((stat = GET_STAT()) & BUSY_STAT) && retries--)
 			udelay(100);
 	}
@@ -1005,6 +1010,7 @@
 	if (!OK_STAT(stat, READY_STAT, BAD_STAT))
 		return ide_error(drive, "drive_cmd", stat); /* calls ide_end_drive_cmd */
 	ide_end_drive_cmd (drive, stat, GET_ERR());
+
 	return ide_stopped;
 }
 
@@ -1101,11 +1107,7 @@
 	while ((read_timer() - hwif->last_time) < DISK_RECOVERY_TIME);
 #endif
 
-	if (test_bit(IDE_DMA, &HWGROUP(drive)->flags))
-		printk("start_request: auch, DMA in progress 1\n");
 	SELECT_DRIVE(hwif, drive);
-	if (test_bit(IDE_DMA, &HWGROUP(drive)->flags))
-		printk("start_request: auch, DMA in progress 2\n");
 	if (ide_wait_stat(&startstop, drive, drive->ready_stat,
 			  BUSY_STAT|DRQ_STAT, WAIT_READY)) {
 		printk(KERN_WARNING "%s: drive not ready for command\n", drive->name);
@@ -1256,25 +1258,170 @@
 /*
  * Select the next drive which will be serviced.
  */
-static inline ide_drive_t *choose_drive(ide_hwgroup_t *hwgroup)
+static ide_drive_t *choose_drive(ide_hwgroup_t *hwgroup)
 {
-	ide_drive_t *drive, *best;
+	ide_drive_t *tmp;
+	ide_drive_t *drive = NULL;
+	unsigned long sleep = 0;
 
-	best = NULL;
-	drive = hwgroup->drive;
+	tmp = hwgroup->drive;
 	do {
-		if (!list_empty(&drive->queue.queue_head)
-		&& (!drive->PADAM_sleep	|| time_after_eq(drive->PADAM_sleep, jiffies))) {
-			if (!best
-			 || (drive->PADAM_sleep && (!best->PADAM_sleep || time_after(best->PADAM_sleep, drive->PADAM_sleep)))
-			 || (!best->PADAM_sleep && time_after(best->PADAM_service_start + 2 * best->PADAM_service_time, drive->PADAM_service_start + 2 * drive->PADAM_service_time)))
+		if (!list_empty(&tmp->queue.queue_head)
+		&& (!tmp->PADAM_sleep || time_after_eq(tmp->PADAM_sleep, jiffies))) {
+			if (!drive
+			 || (tmp->PADAM_sleep && (!drive->PADAM_sleep || time_after(drive->PADAM_sleep, tmp->PADAM_sleep)))
+			 || (!drive->PADAM_sleep && time_after(drive->PADAM_service_start + 2 * drive->PADAM_service_time, tmp->PADAM_service_start + 2 * tmp->PADAM_service_time)))
 			{
-				if (!blk_queue_plugged(&drive->queue))
-					best = drive;
+				if (!blk_queue_plugged(&tmp->queue))
+					drive = tmp;
 			}
 		}
+		tmp = tmp->next;
+	} while (tmp != hwgroup->drive);
+
+	if (drive)
+		return drive;
+
+	hwgroup->rq = NULL;
+	drive = hwgroup->drive;
+	do {
+		if (drive->PADAM_sleep && (!sleep || time_after(sleep, drive->PADAM_sleep)))
+			sleep = drive->PADAM_sleep;
 	} while ((drive = drive->next) != hwgroup->drive);
-	return best;
+
+	if (sleep) {
+		/*
+		 * Take a short snooze, and then wake up this hwgroup
+		 * again.  This gives other hwgroups on the same a
+		 * chance to play fairly with us, just in case there
+		 * are big differences in relative throughputs.. don't
+		 * want to hog the cpu too much.
+		 */
+		if (0 < (signed long)(jiffies + WAIT_MIN_SLEEP - sleep))
+			sleep = jiffies + WAIT_MIN_SLEEP;
+
+		if (timer_pending(&hwgroup->timer))
+			printk("ide_set_handler: timer already active\n");
+
+		set_bit(IDE_SLEEP, &hwgroup->flags);
+		mod_timer(&hwgroup->timer, sleep);
+		/* we purposely leave hwgroup busy while
+		 * sleeping */
+	} else {
+		/* Ugly, but how can we sleep for the lock
+		 * otherwise? perhaps from tq_disk? */
+		ide_release_lock(&ide_intr_lock);/* for atari only */
+		clear_bit(IDE_BUSY, &hwgroup->flags);
+	}
+
+	return NULL;
+}
+
+/*
+ * feed commands to a drive until it barfs. used to be part of ide_do_request.
+ * called with ide_lock/DRIVE_LOCK held and busy hwgroup
+ */
+static void ide_queue_commands(ide_drive_t *drive, int masked_irq)
+{
+	ide_hwgroup_t *hwgroup = HWGROUP(drive);
+	ide_startstop_t startstop = -1;
+	struct request *rq;
+
+	do {
+		rq = NULL;
+
+		if (!test_bit(IDE_BUSY, &hwgroup->flags))
+			printk("%s: hwgroup not busy while queueing\n", drive->name);
+
+		/*
+		 * abort early if we can't queue another command. for non
+		 * tcq, ide_can_queue is always 1 since we never get here
+		 * unless the drive is idle.
+		 */
+		if (!ide_can_queue(drive)) {
+			if (!ide_pending_commands(drive))
+				clear_bit(IDE_BUSY, &hwgroup->flags);
+			break;
+		}
+
+		drive->PADAM_sleep = 0;
+		drive->PADAM_service_start = jiffies;
+
+		if (test_bit(IDE_DMA, &hwgroup->flags)) {
+			printk("ide_do_request: DMA in progress...\n");
+			break;
+		}
+
+		/*
+		 * there's a small window between where the queue could be
+		 * replugged while we are in here when using tcq (in which
+		 * case the queue is probably empty anyways...), so check
+		 * and leave if appropriate. When not using tcq, this is
+		 * still a severe BUG!
+		 */
+		if (blk_queue_plugged(&drive->queue)) {
+			BUG_ON(!drive->using_tcq);
+			break;
+		}
+
+		if (!(rq = elv_next_request(&drive->queue))) {
+			if (!ide_pending_commands(drive))
+				clear_bit(IDE_BUSY, &hwgroup->flags);
+			hwgroup->rq = NULL;
+			break;
+		}
+
+		/*
+		 * if there are queued commands, we can't start a non-fs
+		 * request (really, a non-queuable command) until the
+		 * queue is empty
+		 */
+		if (!(rq->flags & REQ_CMD) && ide_pending_commands(drive))
+			break;
+
+		hwgroup->rq = rq;
+
+		/*
+		 * Some systems have trouble with IDE IRQs arriving while
+		 * the driver is still setting things up.  So, here we disable
+		 * the IRQ used by this interface while the request is being
+		 * started.  This may look bad at first, but pretty much the
+		 * same thing happens anyway when any interrupt comes in, IDE
+		 * or otherwise -- the kernel masks the IRQ while it is being
+		 * handled.
+		 */
+		if (masked_irq && HWIF(drive)->irq != masked_irq)
+			disable_irq_nosync(HWIF(drive)->irq);
+
+		spin_unlock(&ide_lock);
+		ide__sti();	/* allow other IRQs while we start this request */
+		startstop = start_request(drive, rq);
+
+		spin_lock_irq(&ide_lock);
+		if (masked_irq && HWIF(drive)->irq != masked_irq)
+			enable_irq(HWIF(drive)->irq);
+
+		/*
+		 * command started, we are busy
+		 */
+		if (startstop == ide_started)
+			break;
+
+		/*
+		 * start_request() can return either ide_stopped (no command
+		 * was started), ide_started (command started, don't queue
+		 * more), or ide_released (command started, try and queue
+		 * more).
+		 */
+#if 0
+		if (startstop == ide_stopped)
+			set_bit(IDE_BUSY, &hwgroup->flags);
+#endif
+
+	} while (1);
+
+	if (startstop == ide_started)
+		return;
 }
 
 /*
@@ -1311,115 +1458,34 @@
 {
 	ide_drive_t *drive;
 	struct ata_channel *hwif;
-	ide_startstop_t	startstop;
-	struct request	*rq;
 
 	ide_get_lock(&ide_intr_lock, ide_intr, hwgroup);/* for atari only: POSSIBLY BROKEN HERE(?) */
 
 	__cli();	/* necessary paranoia: ensure IRQs are masked on local CPU */
 
 	while (!test_and_set_bit(IDE_BUSY, &hwgroup->flags)) {
-		drive = choose_drive(hwgroup);
-		if (drive == NULL) {
-			unsigned long sleep = 0;
-			hwgroup->rq = NULL;
-			drive = hwgroup->drive;
-			do {
-				if (drive->PADAM_sleep && (!sleep || time_after(sleep, drive->PADAM_sleep)))
-					sleep = drive->PADAM_sleep;
-			} while ((drive = drive->next) != hwgroup->drive);
-			if (sleep) {
-				/*
-				 * Take a short snooze, and then wake up this hwgroup again.
-				 * This gives other hwgroups on the same a chance to
-				 * play fairly with us, just in case there are big differences
-				 * in relative throughputs.. don't want to hog the cpu too much.
-				 */
-				if (0 < (signed long)(jiffies + WAIT_MIN_SLEEP - sleep))
-					sleep = jiffies + WAIT_MIN_SLEEP;
-#if 1
-				if (timer_pending(&hwgroup->timer))
-					printk("ide_set_handler: timer already active\n");
-#endif
-				set_bit(IDE_SLEEP, &hwgroup->flags);
-				mod_timer(&hwgroup->timer, sleep);
-				/* we purposely leave hwgroup busy while sleeping */
-			} else {
-				/* Ugly, but how can we sleep for the lock otherwise? perhaps from tq_disk? */
-				ide_release_lock(&ide_intr_lock);/* for atari only */
-				clear_bit(IDE_BUSY, &hwgroup->flags);
-			}
-			return;		/* no more work for this hwgroup (for now) */
-		}
+
+		/*
+		 * will clear IDE_BUSY, if appropriate
+		 */
+		if ((drive = choose_drive(hwgroup)) == NULL)
+			break;
+
 		hwif = drive->channel;
-		if (hwgroup->hwif->sharing_irq && hwif != hwgroup->hwif && hwif->io_ports[IDE_CONTROL_OFFSET]) {
+		if (hwgroup->hwif->sharing_irq && hwif != hwgroup->hwif && IDE_CONTROL_REG) {
 			/* set nIEN for previous hwif */
-
 			if (hwif->intrproc)
 				hwif->intrproc(drive);
 			else
-				OUT_BYTE((drive)->ctl|2, hwif->io_ports[IDE_CONTROL_OFFSET]);
+				OUT_BYTE(drive->ctl|2, IDE_CONTROL_REG);
 		}
 		hwgroup->hwif = hwif;
 		hwgroup->drive = drive;
-		drive->PADAM_sleep = 0;
-queue_next:
-		drive->PADAM_service_start = jiffies;
-
-		if (test_bit(IDE_DMA, &hwgroup->flags)) {
-			printk("ide_do_request: DMA in progress...\n");
-			break;
-		}
 
 		/*
-		 * there's a small window between where the queue could be
-		 * replugged while we are in here when using tcq (in which
-		 * case the queue is probably empty anyways...), so check
-		 * and leave if appropriate. When not using tcq, this is
-		 * still a severe BUG!
+		 * main queueing loop
 		 */
-		if (blk_queue_plugged(&drive->queue)) {
-			BUG_ON(!drive->using_tcq);
-			break;
-		}
-
-		/*
-		 * just continuing an interrupted request maybe
-		 */
-		rq = hwgroup->rq = elv_next_request(&drive->queue);
-
-		if (!rq) {
-			if (!ide_pending_commands(drive))
-				clear_bit(IDE_BUSY, &HWGROUP(drive)->flags);
-			break;
-		}
-
-		/*
-		 * Some systems have trouble with IDE IRQs arriving while
-		 * the driver is still setting things up.  So, here we disable
-		 * the IRQ used by this interface while the request is being started.
-		 * This may look bad at first, but pretty much the same thing
-		 * happens anyway when any interrupt comes in, IDE or otherwise
-		 *  -- the kernel masks the IRQ while it is being handled.
-		 */
-		if (masked_irq && hwif->irq != masked_irq)
-			disable_irq_nosync(hwif->irq);
-
-		spin_unlock(&ide_lock);
-		ide__sti();	/* allow other IRQs while we start this request */
-		startstop = start_request(drive, rq);
-
-		spin_lock_irq(&ide_lock);
-		if (masked_irq && hwif->irq != masked_irq)
-			enable_irq(hwif->irq);
-
-		if (startstop == ide_released)
-			goto queue_next;
-		else if (startstop == ide_stopped) {
-			if (test_bit(IDE_DMA, &hwgroup->flags))
-				printk("2nd illegal clear\n");
-			clear_bit(IDE_BUSY, &hwgroup->flags);
-		}
+		ide_queue_commands(drive, masked_irq);
 	}
 }
 
@@ -1638,20 +1704,23 @@
 	 * handle the unexpected interrupt
 	 */
 	do {
-		if (hwif->irq == irq) {
-			stat = IN_BYTE(hwif->io_ports[IDE_STATUS_OFFSET]);
-			if (!OK_STAT(stat, READY_STAT, BAD_STAT)) {
-				/* Try to not flood the console with msgs */
-				static unsigned long last_msgtime, count;
-				++count;
-				if (0 < (signed long)(jiffies - (last_msgtime + HZ))) {
-					last_msgtime = jiffies;
-					printk("%s%s: unexpected interrupt, status=0x%02x, count=%ld\n",
-					 hwif->name, (hwif->next == hwgroup->hwif) ? "" : "(?)", stat, count);
-				}
+		if (hwif->irq != irq)
+			continue;
+
+		stat = IN_BYTE(hwif->io_ports[IDE_STATUS_OFFSET]);
+		if (!OK_STAT(stat, READY_STAT, BAD_STAT)) {
+			/* Try to not flood the console with msgs */
+			static unsigned long last_msgtime;
+			static int count;
+			++count;
+			if (time_after(jiffies, last_msgtime + HZ)) {
+				last_msgtime = jiffies;
+				printk("%s%s: unexpected interrupt, status=0x%02x, count=%d\n",
+						hwif->name, (hwif->next == hwgroup->hwif) ? "" : "(?)", stat, count);
 			}
 		}
-	} while ((hwif = hwif->next) != hwgroup->hwif);
+		hwif = hwif->next;
+	} while (hwif != hwgroup->hwif);
 }
 
 /*
@@ -1673,6 +1742,8 @@
 		goto out_lock;
 
 	if ((handler = hwgroup->handler) == NULL || hwgroup->poll_timeout != 0) {
+		printk(KERN_INFO "ide: unexpected interrupt %d %d\n", hwif->unit, irq);
+
 		/*
 		 * Not expecting an interrupt from this drive.
 		 * That means this could be:
@@ -1728,7 +1799,7 @@
 	del_timer(&hwgroup->timer);
 	spin_unlock(&ide_lock);
 
-	if (drive->unmask)
+	if (hwif->unmask)
 		ide__sti();	/* local CPU only */
 	startstop = handler(drive);		/* service this interrupt, may set handler for next interrupt */
 	spin_lock_irq(&ide_lock);
@@ -1751,7 +1822,8 @@
 		} else {
 			printk("%s: ide_intr: huh? expected NULL handler on exit\n", drive->name);
 		}
-	}
+	} else if (startstop == ide_released)
+		ide_queue_commands(drive, hwif->irq);
 
 out_lock:
 	spin_unlock_irqrestore(&ide_lock, flags);
@@ -2203,7 +2275,10 @@
 	channel->maskproc = old_hwif.maskproc;
 	channel->quirkproc = old_hwif.quirkproc;
 	channel->rwproc	= old_hwif.rwproc;
-	channel->ideproc = old_hwif.ideproc;
+	channel->ata_read = old_hwif.ata_read;
+	channel->ata_write = old_hwif.ata_write;
+	channel->atapi_read = old_hwif.atapi_read;
+	channel->atapi_write = old_hwif.atapi_write;
 	channel->dmaproc = old_hwif.dmaproc;
 	channel->busproc = old_hwif.busproc;
 	channel->bus_state = old_hwif.bus_state;
@@ -2221,6 +2296,8 @@
 	channel->udma_four = old_hwif.udma_four;
 #ifdef CONFIG_BLK_DEV_IDEPCI
 	channel->pci_dev = old_hwif.pci_dev;
+#else
+	channel->pci_dev = NULL;
 #endif
 	channel->straight8 = old_hwif.straight8;
 abort:
@@ -2468,13 +2545,13 @@
 	return 0;
 }
 
-static int set_io_32bit(ide_drive_t *drive, int arg)
+static int set_io_32bit(struct ata_device *drive, int arg)
 {
-	drive->io_32bit = arg;
-#ifdef CONFIG_BLK_DEV_DTC2278
-	if (drive->channel->chipset == ide_dtc2278)
-		drive->channel->drives[!drive->select.b.unit].io_32bit = arg;
-#endif /* CONFIG_BLK_DEV_DTC2278 */
+	if (drive->channel->no_io_32bit)
+		return -EIO;
+
+	drive->channel->io_32bit = arg;
+
 	return 0;
 }
 
@@ -2507,11 +2584,10 @@
 void ide_add_generic_settings (ide_drive_t *drive)
 {
 /*			drive	setting name		read/write access				read ioctl		write ioctl		data type	min	max				mul_factor	div_factor	data pointer			set function */
-	ide_add_setting(drive,	"io_32bit",		drive->no_io_32bit ? SETTING_READ : SETTING_RW,	HDIO_GET_32BIT,		HDIO_SET_32BIT,		TYPE_BYTE,	0,	1 + (SUPPORT_VLB_SYNC << 1),	1,		1,		&drive->io_32bit,		set_io_32bit);
-	ide_add_setting(drive,	"keepsettings",		SETTING_RW,					HDIO_GET_KEEPSETTINGS,	HDIO_SET_KEEPSETTINGS,	TYPE_BYTE,	0,	1,				1,		1,		&drive->keep_settings,		NULL);
+	ide_add_setting(drive,	"io_32bit",		drive->channel->no_io_32bit ? SETTING_READ : SETTING_RW,	HDIO_GET_32BIT,		HDIO_SET_32BIT,		TYPE_BYTE,	0,	1 + (SUPPORT_VLB_SYNC << 1),	1,		1,		&drive->channel->io_32bit,		set_io_32bit);
 	ide_add_setting(drive,	"pio_mode",		SETTING_WRITE,					-1,			HDIO_SET_PIO_MODE,	TYPE_BYTE,	0,	255,				1,		1,		NULL,				set_pio_mode);
-	ide_add_setting(drive,	"slow",			SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	1,				1,		1,		&drive->slow,			NULL);
-	ide_add_setting(drive,	"unmaskirq",		drive->no_unmask ? SETTING_READ : SETTING_RW,	HDIO_GET_UNMASKINTR,	HDIO_SET_UNMASKINTR,	TYPE_BYTE,	0,	1,				1,		1,		&drive->unmask,			NULL);
+	ide_add_setting(drive,	"slow",			SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	1,				1,		1,		&drive->channel->slow,			NULL);
+	ide_add_setting(drive,	"unmaskirq",		drive->channel->no_unmask ? SETTING_READ : SETTING_RW,	HDIO_GET_UNMASKINTR,	HDIO_SET_UNMASKINTR,	TYPE_BYTE,	0,	1,				1,		1,		&drive->channel->unmask,			NULL);
 	ide_add_setting(drive,	"using_dma",		SETTING_RW,					HDIO_GET_DMA,		HDIO_SET_DMA,		TYPE_BYTE,	0,	1,				1,		1,		&drive->using_dma,		set_using_dma);
 	ide_add_setting(drive,	"ide_scsi",		SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	1,				1,		1,		&drive->scsi,			NULL);
 	ide_add_setting(drive,	"init_speed",		SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	69,				1,		1,		&drive->init_speed,		NULL);
@@ -2699,42 +2775,33 @@
 	}
 }
 
-void ide_teardown_commandlist(ide_drive_t *drive)
-{
-	struct pci_dev *pdev= drive->channel->pci_dev;
-	struct list_head *entry;
-
-	list_for_each(entry, &drive->free_req) {
-		struct ata_request *ar = list_ata_entry(entry);
-
-		list_del(&ar->ar_queue);
-		kfree(ar->ar_sg_table);
-		pci_free_consistent(pdev, PRD_SEGMENTS * PRD_BYTES, ar->ar_dmatable_cpu, ar->ar_dmatable);
-		kfree(ar);
-	}
-}
-
 int ide_build_commandlist(ide_drive_t *drive)
 {
-	struct pci_dev *pdev= drive->channel->pci_dev;
+#ifdef CONFIG_BLK_DEV_IDEPCI
+	struct pci_dev *pdev = drive->channel->pci_dev;
+#else
+	struct pci_dev *pdev = NULL;
+#endif
+	struct list_head *p;
+	unsigned long flags;
 	struct ata_request *ar;
-	ide_tag_info_t *tcq;
-	int i, err;
+	int i, cur;
 
-	tcq = kmalloc(sizeof(ide_tag_info_t), GFP_ATOMIC);
-	if (!tcq)
-		return -ENOMEM;
+	spin_lock_irqsave(&ide_lock, flags);
 
-	drive->tcq = tcq;
-	memset(drive->tcq, 0, sizeof(ide_tag_info_t));
+	cur = 0;
+	list_for_each(p, &drive->free_req)
+		cur++;
 
-	INIT_LIST_HEAD(&drive->free_req);
-	drive->using_tcq = 0;
+	/*
+	 * for now, just don't shrink it...
+	 */
+	if (drive->queue_depth <= cur) {
+		spin_unlock_irqrestore(&ide_lock, flags);
+		return 0;
+	}
 
-	err = -ENOMEM;
-	for (i = 0; i < drive->queue_depth; i++) {
-		/* Having kzmalloc would help reduce code size at quite
-		 * many places in kernel. */
+	for (i = cur; i < drive->queue_depth; i++) {
 		ar = kmalloc(sizeof(*ar), GFP_ATOMIC);
 		if (!ar)
 			break;
@@ -2759,25 +2826,33 @@
 		 * pheew, all done, add to list
 		 */
 		list_add_tail(&ar->ar_queue, &drive->free_req);
+		++cur;
 	}
+	drive->queue_depth = cur;
+	spin_unlock_irqrestore(&ide_lock, flags);
+	return 0;
+}
 
-	if (i) {
-		drive->queue_depth = i;
-		if (i >= 1) {
-			drive->using_tcq = 1;
-			drive->tcq->queued = 0;
-			drive->tcq->active_tag = -1;
-			return 0;
-		}
+int ide_init_commandlist(ide_drive_t *drive)
+{
+	INIT_LIST_HEAD(&drive->free_req);
 
-		kfree(drive->tcq);
-		drive->tcq = NULL;
-		err = 0;
-	}
+	return ide_build_commandlist(drive);
+}
 
-	kfree(drive->tcq);
-	drive->tcq = NULL;
-	return err;
+void ide_teardown_commandlist(ide_drive_t *drive)
+{
+	struct pci_dev *pdev= drive->channel->pci_dev;
+	struct list_head *entry;
+
+	list_for_each(entry, &drive->free_req) {
+		struct ata_request *ar = list_ata_entry(entry);
+
+		list_del(&ar->ar_queue);
+		kfree(ar->ar_sg_table);
+		pci_free_consistent(pdev, PRD_SEGMENTS * PRD_BYTES, ar->ar_dmatable_cpu, ar->ar_dmatable);
+		kfree(ar);
+	}
 }
 
 static int ide_check_media_change (kdev_t i_rdev)
@@ -2922,8 +2997,6 @@
  * "hdx=slow"		: insert a huge pause after each access to the data
  *				port. Should be used only as a last resort.
  *
- * "hdx=swapdata"	: when the drive is a disk, byte swap all data
- * "hdx=bswap"		: same as above..........
  * "hdxlun=xx"          : set the drive last logical unit.
  * "hdx=flash"		: allows for more than one ata_flash disk to be
  *				registered. In most cases, only one device
@@ -3031,14 +3104,13 @@
 	if (!strncmp(s, "hd", 2) && s[2] >= 'a' && s[2] <= max_drive) {
 		const char *hd_words[] = {"none", "noprobe", "nowerr", "cdrom",
 				"serialize", "autotune", "noautotune",
-				"slow", "swapdata", "bswap", "flash",
-				"remap", "noremap", "scsi", NULL};
+				"slow", "flash", "remap", "noremap", "scsi", NULL};
 		unit = s[2] - 'a';
 		hw   = unit / MAX_DRIVES;
 		unit = unit % MAX_DRIVES;
 		hwif = &ide_hwifs[hw];
 		drive = &hwif->drives[unit];
-		if (strncmp(s + 4, "ide-", 4) == 0) {
+		if (!strncmp(s + 4, "ide-", 4)) {
 			strncpy(drive->driver_req, s + 4, 9);
 			goto done;
 		}
@@ -3080,22 +3152,18 @@
 				drive->autotune = 2;
 				goto done;
 			case -8: /* "slow" */
-				drive->slow = 1;
-				goto done;
-			case -9: /* "swapdata" or "bswap" */
-			case -10:
-				drive->bswap = 1;
+				hwif->slow = 1;
 				goto done;
-			case -11: /* "flash" */
+			case -9: /* "flash" */
 				drive->ata_flash = 1;
 				goto done;
-			case -12: /* "remap" */
+			case -10: /* "remap" */
 				drive->remap_0_to_1 = 1;
 				goto done;
-			case -13: /* "noremap" */
+			case -11: /* "noremap" */
 				drive->remap_0_to_1 = 2;
 				goto done;
-			case -14: /* "scsi" */
+			case -12: /* "scsi" */
 #if defined(CONFIG_BLK_DEV_IDESCSI) && defined(CONFIG_SCSI)
 				drive->scsi = 1;
 				goto done;
@@ -3120,7 +3188,7 @@
 	/*
 	 * Look for bus speed option:  "idebus="
 	 */
-	if (strncmp(s, "idebus", 6)) {
+	if (!strncmp(s, "idebus", 6)) {
 		if (match_parm(&s[6], NULL, vals, 1) != 1)
 			goto bad_option;
 		if (vals[0] >= 20 && vals[0] <= 66) {
diff -Nru a/drivers/ide/it8172.c b/drivers/ide/it8172.c
--- a/drivers/ide/it8172.c	Mon Apr 22 15:32:18 2002
+++ b/drivers/ide/it8172.c	Mon Apr 22 15:32:18 2002
@@ -50,7 +50,7 @@
 #if defined(CONFIG_BLK_DEV_IDEDMA) && defined(CONFIG_IT8172_TUNING)
 static byte it8172_dma_2_pio (byte xfer_rate);
 static int it8172_tune_chipset (ide_drive_t *drive, byte speed);
-static int it8172_config_drive_for_dma (ide_drive_t *drive);
+static int it8172_config_chipset_for_dma (ide_drive_t *drive);
 static int it8172_dmaproc(ide_dma_action_t func, ide_drive_t *drive);
 #endif
 void __init ide_init_it8172(struct ata_channel *channel);
@@ -59,16 +59,14 @@
 static void it8172_tune_drive (ide_drive_t *drive, byte pio)
 {
     unsigned long flags;
-    u16 master_data;
-    u32 slave_data;
+    u16 drive_enables;
+    u32 drive_timing;
     int is_slave	= (&drive->channel->drives[1] == drive);
-    int master_port	= 0x40;
-    int slave_port      = 0x44;
-
-    if (pio == 255)
-	pio = ata_timing_mode(drive, XFER_PIO | XFER_EPIO) - XFER_PIO_0;
-    else
-       pio = min_t(byte, pio, 4);
+    
+	if (pio == 255)
+		pio = ata_timing_mode(drive, XFER_PIO | XFER_EPIO) - XFER_PIO_0;
+	else
+		pio = min_t(byte, pio, 4);
 
     pci_read_config_word(drive->channel->pci_dev, master_port, &master_data);
     pci_read_config_dword(drive->channel->pci_dev, slave_port, &slave_data);
@@ -77,19 +75,27 @@
      * FIX! The DIOR/DIOW pulse width and recovery times in port 0x44
      * are being left at the default values of 8 PCI clocks (242 nsec
      * for a 33 MHz clock). These can be safely shortened at higher
-     * PIO modes.
+     * PIO modes. The DIOR/DIOW pulse width and recovery times only
+     * apply to PIO modes, not to the DMA modes.
      */
     
+    /*
+     * Enable port 0x44. The IT8172G spec is confused; it calls
+     * this register the "Slave IDE Timing Register", but in fact,
+     * it controls timing for both master and slave drives.
+     */
+    drive_enables |= 0x4000;
+
     if (is_slave) {
-	master_data |= 0x4000;
+	drive_enables &= 0xc006;
 	if (pio > 1)
-	    /* enable PPE and IE */
-	    master_data |= 0x0060;
+	    /* enable prefetch and IORDY sample-point */
+	    drive_enables |= 0x0060;
     } else {
-	master_data &= 0xc060;
+	drive_enables &= 0xc060;
 	if (pio > 1)
-	    /* enable PPE and IE */
-	    master_data |= 0x0006;
+	    /* enable prefetch and IORDY sample-point */
+	    drive_enables |= 0x0006;
     }
 
     save_flags(flags);
@@ -163,6 +169,7 @@
     case XFER_UDMA_0:	u_speed = 0 << (drive->dn * 4); break;
     case XFER_MW_DMA_2:
     case XFER_MW_DMA_1:
+    case XFER_MW_DMA_0:
     case XFER_SW_DMA_2:	break;
     default:		return -1;
     }
@@ -185,7 +192,7 @@
     return err;
 }
 
-static int it8172_config_drive_for_dma(ide_drive_t *drive)
+static int it8172_config_chipset_for_dma(ide_drive_t *drive)
 {
     struct hd_driveid *id = drive->id;
     byte speed;
@@ -205,7 +212,7 @@
 {
     switch (func) {
     case ide_dma_check:
-	return ide_dmaproc((ide_dma_action_t)it8172_config_drive_for_dma(drive),
+	return ide_dmaproc((ide_dma_action_t)it8172_config_chipset_for_dma(drive),
 			   drive);
     default :
 	break;
diff -Nru a/drivers/ide/pdc202xx.c b/drivers/ide/pdc202xx.c
--- a/drivers/ide/pdc202xx.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/ide/pdc202xx.c	Mon Apr 22 15:32:19 2002
@@ -1268,11 +1268,9 @@
 
 #undef CONFIG_PDC202XX_32_UNMASK
 #ifdef CONFIG_PDC202XX_32_UNMASK
-	hwif->drives[0].io_32bit = 1;
-	hwif->drives[1].io_32bit = 1;
-	hwif->drives[0].unmask = 1;
-	hwif->drives[1].unmask = 1;
-#endif /* CONFIG_PDC202XX_32_UNMASK */
+	hwif->io_32bit = 1;
+	hwif->unmask = 1;
+#endif
 
 #ifdef CONFIG_BLK_DEV_IDEDMA
 	if (hwif->dma_base) {
@@ -1285,9 +1283,9 @@
 		hwif->drives[1].autotune = 1;
 		hwif->autodma = 0;
 	}
-#else /* !CONFIG_BLK_DEV_IDEDMA */
+#else
 	hwif->drives[0].autotune = 1;
 	hwif->drives[1].autotune = 1;
 	hwif->autodma = 0;
-#endif /* CONFIG_BLK_DEV_IDEDMA */
+#endif
 }
diff -Nru a/drivers/ide/pdc4030.c b/drivers/ide/pdc4030.c
--- a/drivers/ide/pdc4030.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/ide/pdc4030.c	Mon Apr 22 15:32:19 2002
@@ -1,7 +1,7 @@
 /*  -*- linux-c -*-
- *  linux/drivers/ide/pdc4030.c		Version 0.90  May 27, 1999
+ *  linux/drivers/ide/pdc4030.c		Version 0.92  Jan 15, 2002
  *
- *  Copyright (C) 1995-1999  Linus Torvalds & authors (see below)
+ *  Copyright (C) 1995-2002  Linus Torvalds & authors (see below)
  */
 
 /*
@@ -37,6 +37,8 @@
  *			Autodetection code added.
  *
  *  Version 0.90	Transition to BETA code. No lost/unexpected interrupts
+ *  Version 0.91	Bring in line with new bio code in 2.5.1
+ *  Version 0.92	Update for IDE driver taskfile changes
  */
 
 /*
@@ -72,8 +74,8 @@
  * effects.
  */
 
-#define DEBUG_READ
-#define DEBUG_WRITE
+#undef DEBUG_READ
+#undef DEBUG_WRITE
 
 #include <linux/types.h>
 #include <linux/kernel.h>
@@ -91,6 +93,10 @@
 
 #include "pdc4030.h"
 
+#if SUPPORT_VLB_SYNC != 1
+#error This driver will not work unless SUPPORT_VLB_SYNC is 1
+#endif
+
 /*
  * promise_selectproc() is invoked by ide.c
  * in preparation for access to the specified drive.
@@ -183,7 +189,7 @@
 			"%s: Failed Promise read config!\n",hwif->name);
 		return 0;
 	}
-	ata_input_data(drive, &ident, SECTOR_WORDS);
+	ata_read(drive, &ident, SECTOR_WORDS);
 	if (ident.id[1] != 'P' || ident.id[0] != 'T') {
 		return 0;
 	}
@@ -229,12 +235,12 @@
 	hwif->selectproc = hwif2->selectproc = &promise_selectproc;
 	hwif->serialized = hwif2->serialized = 1;
 
-/* Shift the remaining interfaces down by one */
+/* Shift the remaining interfaces up by one */
 	for (i=MAX_HWIFS-1 ; i > hwif->index+1 ; i--) {
 		struct ata_channel *h = &ide_hwifs[i];
 
 #ifdef DEBUG
-		printk(KERN_DEBUG "Shifting i/f %d values to i/f %d\n",i-1,i);
+		printk(KERN_DEBUG "pdc4030: Shifting i/f %d values to i/f %d\n",i-1,i);
 #endif
 		ide_init_hwif_ports(&h->hw, (h-1)->io_ports[IDE_DATA_OFFSET], 0, NULL);
 		memcpy(h->io_ports, h->hw.io_ports, sizeof(h->io_ports));
@@ -244,11 +250,9 @@
 	memcpy(hwif2->io_ports, hwif->hw.io_ports, sizeof(hwif2->io_ports));
 	hwif2->irq = hwif->irq;
 	hwif2->hw.irq = hwif->hw.irq = hwif->irq;
+	hwif->io_32bit = 3;
+	hwif2->io_32bit = 3;
 	for (i=0; i<2 ; i++) {
-		hwif->drives[i].io_32bit = 3;
-		hwif2->drives[i].io_32bit = 3;
-		hwif->drives[i].keep_settings = 1;
-		hwif2->drives[i].keep_settings = 1;
 		if (!ident.current_tm[i].cyl)
 			hwif->drives[i].noprobe = 1;
 		if (!ident.current_tm[i+2].cyl)
@@ -332,7 +336,7 @@
 		nsect = sectors_avail;
 	sectors_avail -= nsect;
 	to = bio_kmap_irq(rq->bio, &flags) + ide_rq_offset(rq);
-	ata_input_data(drive, to, nsect * SECTOR_WORDS);
+	ata_read(drive, to, nsect * SECTOR_WORDS);
 #ifdef DEBUG_READ
 	printk(KERN_DEBUG "%s:  promise_read: sectors(%ld-%ld), "
 	       "buf=0x%08lx, rem=%ld\n", drive->name, rq->sector,
@@ -460,7 +464,7 @@
 		 * Ok, we're all setup for the interrupt
 		 * re-entering us on the last transfer.
 		 */
-		taskfile_output_data(drive, buffer, nsect<<7);
+		ata_write(drive, buffer, nsect << 7);
 		bio_kunmap_irq(buffer, &flags);
 	} while (mcount);
 
@@ -628,7 +632,7 @@
 			       "PROMISE_WRITE\n", drive->name);
 			return startstop;
 		}
-		if (!drive->unmask)
+		if (!drive->channel->unmask)
 			__cli();	/* local CPU only */
 		HWGROUP(drive)->wrq = *rq; /* scratchpad */
 		return promise_write(drive);
@@ -648,6 +652,12 @@
 
 	memset(&taskfile, 0, sizeof(struct hd_drive_task_hdr));
 
+	/* The four drives on the two logical (one physical) interfaces
+	   are distinguished by writing the drive number (0-3) to the
+	   Feature register.
+	   FIXME: Is promise_selectproc now redundant??
+	*/
+	taskfile.feature	= (drive->channel->unit << 1) + drive->select.b.unit;
 	taskfile.sector_count	= rq->nr_sectors;
 	taskfile.sector_number	= block;
 	taskfile.low_cylinder	= (block>>=8);
diff -Nru a/drivers/ide/piix.c b/drivers/ide/piix.c
--- a/drivers/ide/piix.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/ide/piix.c	Mon Apr 22 15:32:19 2002
@@ -545,10 +545,9 @@
 	hwif->tuneproc = &piix_tune_drive;
 	hwif->speedproc = &piix_set_drive;
 	hwif->autodma = 0;
-
+	hwif->io_32bit = 1;
+	hwif->unmask = 1;
 	for (i = 0; i < 2; i++) {
-		hwif->drives[i].io_32bit = 1;
-		hwif->drives[i].unmask = 1;
 		hwif->drives[i].autotune = 1;
 		hwif->drives[i].dn = hwif->unit * 2 + i;
 	}
diff -Nru a/drivers/ide/qd65xx.c b/drivers/ide/qd65xx.c
--- a/drivers/ide/qd65xx.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/ide/qd65xx.c	Mon Apr 22 15:32:19 2002
@@ -370,8 +370,7 @@
 		hwif->config_data = config;
 		hwif->drives[0].drive_data =
 		hwif->drives[1].drive_data = QD6500_DEF_DATA;
-		hwif->drives[0].io_32bit =
-		hwif->drives[1].io_32bit = 1;
+		hwif->io_32bit = 1;
 		hwif->tuneproc = &qd6500_tune_drive;
 		return 1;
 	}
@@ -403,8 +402,7 @@
 			hwif->config_data = config | (control <<8);
 			hwif->drives[0].drive_data =
 			hwif->drives[1].drive_data = QD6580_DEF_DATA;
-			hwif->drives[0].io_32bit =
-			hwif->drives[1].io_32bit = 1;
+			hwif->io_32bit = 1;
 			hwif->tuneproc = &qd6580_tune_drive;
 
 			qd_write_reg(QD_DEF_CONTR,QD_CONTROL_PORT);
@@ -426,11 +424,11 @@
 				ide_hwifs[i].select_data = base;
 				ide_hwifs[i].config_data = config | (control <<8);
 				ide_hwifs[i].tuneproc = &qd6580_tune_drive;
+				ide_hwifs[i].io_32bit = 1;
 
 				for (j = 0; j < 2; j++) {
 					ide_hwifs[i].drives[j].drive_data =
 					       i?QD6580_DEF_DATA2:QD6580_DEF_DATA;
-					ide_hwifs[i].drives[j].io_32bit = 1;
 				}
 			}
 
diff -Nru a/drivers/ide/rz1000.c b/drivers/ide/rz1000.c
--- a/drivers/ide/rz1000.c	Mon Apr 22 15:32:18 2002
+++ b/drivers/ide/rz1000.c	Mon Apr 22 15:32:18 2002
@@ -40,8 +40,7 @@
 		printk("%s: disabled chipset read-ahead (buggy RZ1000/RZ1001)\n", hwif->name);
 	} else {
 		hwif->serialized = 1;
-		hwif->drives[0].no_unmask = 1;
-		hwif->drives[1].no_unmask = 1;
+		hwif->no_unmask = 1;
 		printk("%s: serialized, disabled unmasking (buggy RZ1000/RZ1001)\n", hwif->name);
 	}
 }
diff -Nru a/drivers/ide/via82cxxx.c b/drivers/ide/via82cxxx.c
--- a/drivers/ide/via82cxxx.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/ide/via82cxxx.c	Mon Apr 22 15:32:19 2002
@@ -535,10 +535,10 @@
 	hwif->tuneproc = &via82cxxx_tune_drive;
 	hwif->speedproc = &via_set_drive;
 	hwif->autodma = 0;
+	hwif->io_32bit = 1;
 
+	hwif->unmask = (via_config->flags & VIA_NO_UNMASK) ? 0 : 1;
 	for (i = 0; i < 2; i++) {
-		hwif->drives[i].io_32bit = 1;
-		hwif->drives[i].unmask = (via_config->flags & VIA_NO_UNMASK) ? 0 : 1;
 		hwif->drives[i].autotune = 1;
 		hwif->drives[i].dn = hwif->unit * 2 + i;
 	}
diff -Nru a/drivers/ieee1394/Makefile b/drivers/ieee1394/Makefile
--- a/drivers/ieee1394/Makefile	Mon Apr 22 15:32:19 2002
+++ b/drivers/ieee1394/Makefile	Mon Apr 22 15:32:19 2002
@@ -6,7 +6,6 @@
 
 export-objs := ieee1394_core.o ohci1394.o cmp.o
 
-list-multi := ieee1394.o
 ieee1394-objs := ieee1394_core.o ieee1394_transactions.o hosts.o \
 		 highlevel.o csr.o nodemgr.o
 
@@ -22,6 +21,3 @@
 obj-$(CONFIG_IEEE1394_CMP) += cmp.o
 
 include $(TOPDIR)/Rules.make
-
-ieee1394.o: $(ieee1394-objs)
-	$(LD) -r -o $@ $(ieee1394-objs)
diff -Nru a/drivers/isdn/Config.help b/drivers/isdn/Config.help
--- a/drivers/isdn/Config.help	Mon Apr 22 15:32:19 2002
+++ b/drivers/isdn/Config.help	Mon Apr 22 15:32:19 2002
@@ -1,4 +1,4 @@
-CONFIG_ISDN
+CONFIG_ISDN_BOOL
   ISDN ("Integrated Services Digital Networks", called RNIS in France)
   is a special type of fully digital telephone service; it's mostly
   used to connect to your Internet service provider (with SLIP or
@@ -9,528 +9,5 @@
   provider purchased an ISDN line from the phone company.  For
   details, read <http://alumni.caltech.edu/~dank/isdn/> on the WWW.
 
-  This driver allows you to use an ISDN-card for networking
-  connections and as dialin/out device.  The isdn-tty's have a built
-  in AT-compatible modem emulator.  Network devices support autodial,
-  channel-bundling, callback and caller-authentication without having
-  a daemon running.  A reduced T.70 protocol is supported with tty's
-  suitable for German BTX.  On D-Channel, the protocols EDSS1
-  (Euro-ISDN) and 1TR6 (German style) are supported.  See
-  <file:Documentation/isdn/README> for more information.
-
-  If you want to compile the ISDN code as a module ( = code which can
-  be inserted in and removed from the running kernel whenever you
-  want), say M here and read <file:Documentation/modules.txt>.  The
-  module will be called isdn.o. If unsure, say N.
-
-CONFIG_ISDN_PPP
-  Over digital connections such as ISDN, there is no need to
-  synchronize sender and recipient's clocks with start and stop bits
-  as is done over analog telephone lines. Instead, one can use
-  "synchronous PPP". Saying Y here will include this protocol. This
-  protocol is used by Cisco and Sun for example. So you want to say Y
-  here if the other end of your ISDN connection supports it. You will
-  need a special version of pppd (called ipppd) for using this
-  feature. See <file:Documentation/isdn/README.syncppp> and
-  <file:Documentation/isdn/syncPPP.FAQ> for more information.
-
-CONFIG_ISDN_MPP
-  With synchronous PPP enabled, it is possible to increase throughput
-  by bundling several ISDN-connections, using this protocol. See
-  <file:Documentation/isdn/README.syncppp> for more information.
-
-CONFIG_ISDN_PPP_VJ
-  This enables Van Jacobson header compression for synchronous PPP.
-  Say Y if the other end of the connection supports it.
-
-CONFIG_ISDN_PPP_BSDCOMP
-  Support for the BSD-Compress compression method for PPP, which uses
-  the LZW compression method to compress each PPP packet before it is
-  sent over the wire. The machine at the other end of the PPP link
-  (usually your ISP) has to support the BSD-Compress compression
-  method as well for this to be useful. Even if they don't support it,
-  it is safe to say Y here.
-
-CONFIG_ISDN_AUDIO
-  If you say Y here, the modem-emulator will support a subset of the
-  EIA Class 8 Voice commands. Using a getty with voice-support
-  (mgetty+sendfax by gert@greenie.muc.de with an extension, available
-  with the ISDN utility package for example), you will be able to use
-  your Linux box as an ISDN-answering machine. Of course, this must be
-  supported by the lowlevel driver also. Currently, the HiSax driver
-  is the only voice-supporting driver. See
-  <file:Documentation/isdn/README.audio> for more information.
-
-CONFIG_ISDN_X25
-  This feature provides the X.25 protocol over ISDN connections.
-  See <file:Documentation/isdn/README.x25> for more information
-  if you are thinking about using this.
-
-CONFIG_ISDN_DIVERSION
-  This option allows you to use some supplementary diversion
-  services in conjunction with the HiSax driver on an EURO/DSS1
-  line.
-
-  Supported options are CD (call deflection), CFU (Call forward
-  unconditional), CFB (Call forward when busy) and CFNR (call forward
-  not reachable). Additionally the actual CFU, CFB and CFNR state may
-  be interrogated.
-
-  The use of CFU, CFB, CFNR and interrogation may be limited to some
-  countries. The keypad protocol is still not implemented. CD should
-  work in all countries if the service has been subscribed to.
-
-  Please read the file <file:Documentation/isdn/README.diversion>.
-
-CONFIG_ISDN_DRV_ICN
-  This enables support for two kinds of ISDN-cards made by a German
-  company called ICN.  2B is the standard version for a single ISDN
-  line with two B-channels, 4B supports two ISDN lines.  For running
-  this card, additional firmware is necessary, which has to be
-  downloaded into the card using a utility which is distributed
-  separately.  See <file:Documentation/isdn/README> and
-  <file:Documentation/isdn/README.icn> for more
-  information.
-
-  If you want to compile this as a module ( = code which can be
-  inserted in and removed from the running kernel whenever you want),
-  say M here and read <file:Documentation/modules.txt>.  The module
-  will be called icn.o.
-
-CONFIG_ISDN_DRV_LOOP
-  This driver provides a virtual ISDN card. Its primary purpose is
-  testing of linklevel features or configuration without getting
-  charged by your service-provider for lots of phone calls.
-  You need will need the loopctrl utility from the latest isdn4k-utils
-  package to set up this driver.
-
-CONFIG_ISDN_DRV_HISAX
-  This is a driver supporting the Siemens chipset on various
-  ISDN-cards (like AVM A1, Elsa ISDN cards, Teles S0-16.0, Teles
-  S0-16.3, Teles S0-8, Teles/Creatix PnP, ITK micro ix1 and many
-  compatibles).
-
-  HiSax is just the name of this driver, not the name of any hardware.
-
-  If you have a card with such a chipset, you should say Y here and
-  also to the configuration option of the driver for your particular
-  card, below.
-
-  If you want to compile this as a module ( = code which can be
-  inserted in and removed from the running kernel whenever you want),
-  say M here and read <file:Documentation/modules.txt>.  The module
-  will be called hisax.o.  See <file:Documentation/isdn/README.HiSax>
-  for more information on using this driver.
-
-CONFIG_HISAX_EURO
-  Say Y or N according to the D-channel protocol which your local
-  telephone service company provides.
-
-  The call control protocol E-DSS1 is used in most European countries.
-  If unsure, say Y.
-
-CONFIG_DE_AOC
-  If you want that the HiSax hardware driver sends messages to the
-  upper level of the isdn code on each AOCD (Advice Of Charge, During
-  the call -- transmission of the fee information during a call) and
-  on each AOCE (Advice Of Charge, at the End of the call --
-  transmission of fee information at the end of the call), say Y here.
-  This works only in Germany.
-
-CONFIG_HISAX_NO_SENDCOMPLETE
-  If you have trouble with some ugly exchanges or you live in
-  Australia select this option.
-
-CONFIG_HISAX_NO_LLC
-  If you have trouble with some ugly exchanges try to select this
-  option.
-
-CONFIG_HISAX_NO_KEYPAD
-  If you like to send special dial strings including * or # without
-  using the keypad protocol, select this option.
-
-CONFIG_HISAX_1TR6
-  Say Y or N according to the D-channel protocol which your local
-  telephone service company provides.
-
-  1TR6 is an old call control protocol which was used in Germany
-  before E-DSS1 was established. Nowadays, all new lines in Germany
-  use E-DSS1.
-
-CONFIG_HISAX_NI1
-  Enable this if you like to use ISDN in US on a NI1 basic rate
-  interface.
-
-CONFIG_HISAX_MAX_CARDS
-  This option allows you to specify the maximum number of cards which
-  the HiSax driver will be able to handle.  
-
-CONFIG_HISAX_16_0
-  This enables HiSax support for the Teles ISDN-cards S0-16.0, S0-8
-  and many compatibles.
-
-  See <file:Documentation/isdn/README.HiSax> on how to configure it
-  using the different cards, a different D-channel protocol, or
-  non-standard IRQ/port/shmem settings.
-
-CONFIG_HISAX_16_3
-  This enables HiSax support for the Teles ISDN-cards S0-16.3 the
-  Teles/Creatix PnP and the Teles PCMCIA.
-
-  See <file:Documentation/isdn/README.HiSax> on how to configure it
-  using the different cards, a different D-channel protocol, or
-  non-standard IRQ/port settings.
-
-CONFIG_HISAX_TELESPCI
-  This enables HiSax support for the Teles PCI.
-  See <file:Documentation/isdn/README.HiSax> on how to configure it.
-
-CONFIG_HISAX_S0BOX
-  This enables HiSax support for the Teles/Creatix parallel port
-  S0BOX.  See <file:Documentation/isdn/README.HiSax> on how to
-  configure it.
-
-CONFIG_HISAX_AVM_A1
-  This enables HiSax support for the AVM A1 (aka "Fritz").
-
-  See <file:Documentation/isdn/README.HiSax> on how to configure it
-  using the different cards, a different D-channel protocol, or
-  non-standard IRQ/port settings.
-
-CONFIG_HISAX_FRITZPCI
-  This enables HiSax support for the AVM "Fritz!PnP" and "Fritz!PCI".
-  See <file:Documentation/isdn/README.HiSax> on how to configure it.
-
-CONFIG_HISAX_AVM_A1_PCMCIA
-  This enables HiSax support for the AVM A1 "Fritz!PCMCIA").
-  See <file:Documentation/isdn/README.HiSax> on how to configure it.
-
-CONFIG_HISAX_ELSA
-  This enables HiSax support for the Elsa Mircolink ISA cards, for the
-  Elsa Quickstep series cards and Elsa PCMCIA.
-
-  See <file:Documentation/isdn/README.HiSax> on how to configure it
-  using the different cards, a different D-channel protocol, or
-  non-standard IRQ/port settings.
-
-CONFIG_HISAX_IX1MICROR2
-  This enables HiSax support for the ITK ix1-micro Revision 2 card.
-
-  See <file:Documentation/isdn/README.HiSax> on how to configure it
-  using the different cards, a different D-channel protocol, or
-  non-standard IRQ/port settings.
-
-CONFIG_HISAX_DIEHLDIVA
-  This enables HiSax support for the Eicon.Diehl Diva none PRO
-  versions passive ISDN cards.
-
-  See <file:Documentation/isdn/README.HiSax> on how to configure it
-  using the different cards, a different D-channel protocol, or
-  non-standard IRQ/port settings.
-
-CONFIG_HISAX_ASUSCOM
-  This enables HiSax support for the AsusCom and their OEM versions
-  passive ISDN ISA cards.
-
-  See <file:Documentation/isdn/README.HiSax> on how to configure it
-  using the different cards, a different D-channel protocol, or
-  non-standard IRQ/port settings.
-
-CONFIG_HISAX_TELEINT
-  This enables HiSax support for the TELEINT SA1 semiactiv ISDN card.
-
-  See <file:Documentation/isdn/README.HiSax> on how to configure it
-  using the different cards, a different D-channel protocol, or
-  non-standard IRQ/port settings.
-
-CONFIG_HISAX_HFCS
-  This enables HiSax support for the HFC-S 2BDS0 based cards, like
-  teles 16.3c.
-
-  See <file:Documentation/isdn/README.HiSax> on how to configure it
-  using the different cards, a different D-channel protocol, or
-  non-standard IRQ/port settings.
-
-CONFIG_HISAX_SEDLBAUER
-  This enables HiSax support for the Sedlbauer passive ISDN cards.
-
-  See <file:Documentation/isdn/README.HiSax> on how to configure it
-  using the different cards, a different D-channel protocol, or
-  non-standard IRQ/port settings.
-
-CONFIG_HISAX_SPORTSTER
-  This enables HiSax support for the USR Sportster internal TA card.
-
-  See <file:Documentation/isdn/README.HiSax> on how to configure it
-  using a different D-channel protocol, or non-standard IRQ/port
-  settings.
-
-CONFIG_HISAX_MIC
-  This enables HiSax support for the ITH MIC card.
-
-  See <file:Documentation/isdn/README.HiSax> on how to configure it
-  using a different D-channel protocol, or non-standard IRQ/port
-  settings.
-
-CONFIG_HISAX_NETJET
-  This enables HiSax support for the NetJet from Traverse
-  Technologies.
-
-  See <file:Documentation/isdn/README.HiSax> on how to configure it
-  using a different D-channel protocol, or non-standard IRQ/port
-  settings.
-
-CONFIG_HISAX_NETJET_U
-  This enables HiSax support for the Netspider U interface ISDN card
-  from Traverse Technologies.
-  See <file:Documentation/isdn/README.HiSax> on how to configure it
-  using a different D-channel protocol, or non-standard IRQ/port
-  settings.
-
-CONFIG_HISAX_NICCY
-  This enables HiSax support for the Dr. Neuhaus Niccy PnP or PCI.
-
-  See <file:Documentation/isdn/README.HiSax> on how to configure it
-  using a different D-channel protocol, or non-standard IRQ/port
-  settings.
-
-CONFIG_HISAX_ISURF
-  This enables HiSax support for the Siemens I-Talk/I-Surf card with
-  ISAR chip.
-  See <file:Documentation/isdn/README.HiSax> on how to configure it
-  using a different D-channel protocol, or non-standard IRQ/port
-  settings.
-
-CONFIG_HISAX_HSTSAPHIR
-  This enables HiSax support for the HST Saphir card.
-
-  See <file:Documentation/isdn/README.HiSax> on how to configure it
-  using a different D-channel protocol, or non-standard IRQ/port
-  settings.
-
-CONFIG_HISAX_BKM_A4T
-  This enables HiSax support for the Telekom A4T card.
-
-  See <file:Documentation/isdn/README.HiSax> on how to configure it
-  using a different D-channel protocol, or non-standard IRQ/port
-  settings.
-
-CONFIG_HISAX_SCT_QUADRO
-  This enables HiSax support for the Scitel Quadro card.
-
-  See <file:Documentation/isdn/README.HiSax> on how to configure it
-  using a different D-channel protocol, or non-standard IRQ/port
-  settings.
-
-CONFIG_HISAX_GAZEL
-  This enables HiSax support for the Gazel cards.
-
-  See <file:Documentation/isdn/README.HiSax> on how to configure it
-  using a different D-channel protocol, or non-standard IRQ/port
-  settings.
-
-CONFIG_HISAX_HFC_PCI
-  This enables HiSax support for the HFC-S PCI 2BDS0 based cards.
-
-  For more informations see under
-  <file:Documentation/isdn/README.hfc-pci>.
-
-CONFIG_HISAX_W6692
-  This enables HiSax support for Winbond W6692 based PCI ISDN cards.
-
-  See <file:Documentation/isdn/README.HiSax> on how to configure it
-  using a different D-channel protocol, or non-standard IRQ/port
-  settings.
-
-CONFIG_HISAX_HFC_SX
-  This enables HiSax support for the HFC-S+, HFC-SP and HFC-PCMCIA
-  cards. This code is not finished yet.
-
-CONFIG_HISAX_AMD7930
-  This enables HiSax support for the AMD7930 chips on some SPARCs.
-  This code is not finished yet.
-
-CONFIG_HISAX_DEBUG
-  This enables debugging code in the new-style HiSax drivers, i.e.
-  the ST5481 USB driver currently. 
-  If in doubt, say yes.
-
-CONFIG_HISAX_ELSA_CS
-  This enables the PCMCIA client driver for the Elsa PCMCIA MicroLink
-  card.
-
-CONFIG_HISAX_SEDLBAUER_CS
-  This enables the PCMCIA client driver for the Sedlbauer Speed Star
-  and Speed Star II cards.
-
-CONFIG_HISAX_ST5481
-  This enables the driver for ST5481 based USB ISDN adapters,
-  e.g. the BeWan Gazel 128 USB
-
-CONFIG_HISAX_FRITZ_PCIPNP
-  This enables the driver for the AVM Fritz!Card PCI, Fritz!Card PCI v2 
-  and Fritz!Card PnP.
-  (the latter also needs you to select "ISA Plug and Play support"
-  from the menu "Plug and Play configuration")
-
-CONFIG_HISAX_FRITZ_CLASSIC
-  This enables the driver for the AVM Fritz!Card classic, formerly
-  known as AVM A1.
-
-CONFIG_ISDN_DRV_PCBIT
-  This enables support for the PCBIT ISDN-card.  This card is
-  manufactured in Portugal by Octal.  For running this card,
-  additional firmware is necessary, which has to be downloaded into
-  the card using a utility which is distributed separately.  See
-  <file:Documentation/isdn/README> and
-  <file:Documentation/isdn/README.pcbit> for more information.
-
-  If you want to compile this as a module ( = code which can be
-  inserted in and removed from the running kernel whenever you want),
-  say M here and read <file:Documentation/modules.txt>.  The module
-  will be called pcbit.o.
-
-CONFIG_ISDN_DRV_SC
-  This enables support for the Spellcaster BRI ISDN boards.  This
-  driver currently builds only in a modularized version ( = code which
-  can be inserted in and removed from the running kernel whenever you
-  want, details in <file:Documentation/modules.txt>); the module will
-  be called sc.o.  See <file:Documentation/isdn/README.sc> and
-  <http://www.spellcast.com/> for more information.
-
-CONFIG_ISDN_DRV_EICON
-  Say Y here if you have an Eicon active ISDN card. In order to use
-  this card, additional firmware is necessary, which has to be loaded
-  into the card using the eiconctrl utility which is part of the
-  latest isdn4k-utils package. Please read the file
-  <file:Documentation/isdn/README.eicon> for more information.
-
-CONFIG_ISDN_DRV_EICON_OLD
-  Say Y here to use your Eicon active ISDN card with ISDN4Linux
-  isdn module.
-
-CONFIG_ISDN_DRV_EICON_PCI
-  Say Y here if you have an Eicon Diva Server (BRI/PRI/4BRI) ISDN
-  card.  Please read <file:Documentation/isdn/README.eicon> for more
-  information.
-
-CONFIG_ISDN_DRV_EICON_ISA
-  Say Y here if you have an old-type Eicon active ISDN card. In order
-  to use this card, additional firmware is necessary, which has to be
-  loaded into the card using the eiconctrl utility which is part of
-  the latest isdn4k-utils package. Please read the file
-  <file:Documentation/isdn/README.eicon> for more information.
-
-CONFIG_ISDN_DRV_EICON_DIVAS
-  Enable this option if you want the eicon driver as standalone
-  version with no interface to the ISDN4Linux isdn module. If you
-  say Y here, the eicon module only supports the Diva Server PCI
-  cards and will provide its own IDI interface. You should say N
-  here.
-
-CONFIG_ISDN_TTY_FAX
-  If you say Y here, the modem-emulator will support a subset of the
-  Fax Class 1 and 2 commands. Using a getty with fax-support
-  (mgetty+sendfax, hylafax), you will be able to use your Linux box as
-  an ISDN-fax-machine. This must be supported by the lowlevel driver
-  also. See <file:Documentation/isdn/README.fax> for more information.
-
-CONFIG_ISDN_CAPI
-  This provides the CAPI (Common ISDN Application Programming
-  Interface, a standard making it easy for programs to access ISDN
-  hardware, see <http://www.capi.org/>.  This is needed for AVM's set
-  of active ISDN controllers like B1, T1, M1.
-
-  This code is also available as a module ( = code which can be
-  inserted in and removed from the running kernel whenever you want).
-  The modules will be called capi.o and kernelcapi.o. If you want to
-  compile it as a module, say M here and read
-  <file:Documentation/modules.txt>.
-
-CONFIG_ISDN_CAPI_CAPI20
-  This option will provide the CAPI 2.0 interface to userspace
-  applications via /dev/capi20. Applications should use the
-  standardized libcapi20 to access this functionality.  You should say
-  Y/M here.
-
-CONFIG_ISDN_CAPI_MIDDLEWARE
-  This option will enhance the capabilities of the /dev/capi20
-  interface.  It will provide a means of moving a data connection,
-  established via the usual /dev/capi20 interface to a special tty
-  device.  If you want to use pppd with pppdcapiplugin to dial up to
-  your ISP, say Y here.
-
-CONFIG_ISDN_CAPI_CAPIFS
-  This option provides a special file system, similar to /dev/pts with
-  device nodes for the special ttys established by using the
-  middleware extension above. If you want to use pppd with
-  pppdcapiplugin to dial up to your ISP, say Y here.
-
-CONFIG_ISDN_CAPI_CAPIDRV
-  This option provides the glue code to hook up CAPI driven cards to
-  the legacy isdn4linux link layer.  If you have a card which is
-  supported by a CAPI driver, but still want to use old features like
-  ippp interfaces or ttyI emulation, say Y/M here.
-
-CONFIG_ISDN_DRV_AVMB1_B1ISA
-  Enable support for the ISA version of the AVM B1 card.
-
-CONFIG_ISDN_DRV_AVMB1_B1PCI
-  Enable support for the PCI version of the AVM B1 card.
-
-CONFIG_ISDN_DRV_AVMB1_B1PCIV4
-  Enable support for the V4 version of AVM B1 PCI card.
-
-CONFIG_ISDN_DRV_AVMB1_T1ISA
-  Enable support for the AVM T1 T1B card.
-  Note: This is a PRI card and handle 30 B-channels.
-
-CONFIG_ISDN_DRV_AVMB1_B1PCMCIA
-  Enable support for the PCMCIA version of the AVM B1 card.
-
-CONFIG_ISDN_DRV_AVMB1_AVM_CS
-  Enable the PCMCIA client driver for the AVM B1/M1/M2
-  PCMCIA cards.
-
-CONFIG_ISDN_DRV_AVMB1_T1PCI
-  Enable support for the AVM T1 T1B card.
-  Note: This is a PRI card and handle 30 B-channels.
-
-CONFIG_ISDN_DRV_AVMB1_C4
-  Enable support for the AVM C4/C2 PCI cards.
-  These cards handle 4/2 BRI ISDN lines (8/4 channels).
-
-CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON
-  If you say Y here, the AVM B1 driver will give verbose reasons for
-  disconnecting. This will increase the size of the kernel by 7 KB. If
-  unsure, say Y.
-
-CONFIG_ISDN_DRV_ACT2000
-  Say Y here if you have an IBM Active 2000 ISDN card. In order to use
-  this card, additional firmware is necessary, which has to be loaded
-  into the card using a utility which is part of the latest
-  isdn4k-utils package. Please read the file
-  <file:Documentation/isdn/README.act2000> for more information.
-
-CONFIG_ISDN_DRV_TPAM
-  This enables support for the Auvertech TurboPAM ISDN-card.
-  For running this card, additional firmware is necessary, which has
-  to be downloaded into the card using a utility which is distributed
-  separately from the Auvertech's web site: <http://www.auvertech.fr/>.
-
-  Please redirect all support questions to support@auvertech.fr.
-
-  If you want to compile this as a module ( = code which can be
-  inserted in and removed from the running kernel whenever you want),
-  say M here and read <file:Documentation/modules.txt>.  The module
-  will be called tpam.o.
-
-CONFIG_HYSDN
-  Say Y here if you have one of Hypercope's active PCI ISDN cards
-  Champ, Ergo and Metro. You will then get a module called hysdn.o.
-  Please read the file <file:Documentation/isdn/README.hysdn> for more
-  information.
-
-CONFIG_HYSDN_CAPI
-  Say Y here if you like to use Hypercope's CAPI 2.0 interface.
+  Select this option if you want your kernel to support ISDN.
 
diff -Nru a/drivers/isdn/Config.in b/drivers/isdn/Config.in
--- a/drivers/isdn/Config.in	Mon Apr 22 15:32:19 2002
+++ b/drivers/isdn/Config.in	Mon Apr 22 15:32:19 2002
@@ -2,153 +2,21 @@
 # ISDN device configuration
 #
 
-# only included if CONFIG_ISDN != n
-
-if [ "$CONFIG_INET" != "n" ]; then
-   bool '  Support synchronous PPP' CONFIG_ISDN_PPP
-   if [ "$CONFIG_ISDN_PPP" != "n" ]; then
-      bool         '    Use VJ-compression with synchronous PPP' CONFIG_ISDN_PPP_VJ
-      bool         '    Support generic MP (RFC 1717)' CONFIG_ISDN_MPP
-      dep_tristate '    Support BSD compression' CONFIG_ISDN_PPP_BSDCOMP $CONFIG_ISDN
-   fi
-fi
-bool '  Support audio via ISDN' CONFIG_ISDN_AUDIO
-if [ "$CONFIG_ISDN_AUDIO" != "n" ]; then
-   bool '    Support AT-Fax Class 1 and 2 commands' CONFIG_ISDN_TTY_FAX
-fi
-if [ "$CONFIG_X25" != "n" ]; then
-   bool '  X.25 PLP on top of ISDN' CONFIG_ISDN_X25
-fi
-
 mainmenu_option next_comment
-comment 'ISDN feature submodules'
-   dep_tristate 'isdnloop support' CONFIG_ISDN_DRV_LOOP $CONFIG_ISDN
-   dep_tristate 'Support isdn diversion services' CONFIG_ISDN_DIVERSION $CONFIG_ISDN
-endmenu
-
-comment 'low-level hardware drivers'
-
-mainmenu_option next_comment
-comment 'Passive ISDN cards'
-dep_tristate 'HiSax SiemensChipSet driver support' CONFIG_ISDN_DRV_HISAX $CONFIG_ISDN
-if [ "$CONFIG_ISDN_DRV_HISAX" != "n" ]; then
-   comment '  D-channel protocol features'
-   bool '  HiSax Support for EURO/DSS1' CONFIG_HISAX_EURO
-   if [ "$CONFIG_HISAX_EURO" != "n" ]; then
-      bool '    Support for german chargeinfo' CONFIG_DE_AOC
-      bool '    Disable sending complete' CONFIG_HISAX_NO_SENDCOMPLETE
-      bool '    Disable sending low layer compatibility' CONFIG_HISAX_NO_LLC
-      bool '    Disable keypad protocol option' CONFIG_HISAX_NO_KEYPAD
-   fi
-   bool '  HiSax Support for german 1TR6' CONFIG_HISAX_1TR6
-   bool '  HiSax Support for US NI1' CONFIG_HISAX_NI1
-   int  '  Maximum number of cards supported by HiSax' CONFIG_HISAX_MAX_CARDS 8
-   comment '  HiSax supported cards'
-   bool '  Teles 16.0/8.0' CONFIG_HISAX_16_0
-   bool '  Teles 16.3 or PNP or PCMCIA' CONFIG_HISAX_16_3
-   bool '  Teles PCI' CONFIG_HISAX_TELESPCI 
-   bool '  Teles S0Box' CONFIG_HISAX_S0BOX 
-   bool '  AVM A1 (Fritz)' CONFIG_HISAX_AVM_A1
-   bool '  AVM PnP/PCI (Fritz!PnP/PCI)' CONFIG_HISAX_FRITZPCI
-   bool '  AVM A1 PCMCIA (Fritz)' CONFIG_HISAX_AVM_A1_PCMCIA
-   bool '  Elsa cards' CONFIG_HISAX_ELSA
-   bool '  ITK ix1-micro Revision 2' CONFIG_HISAX_IX1MICROR2
-   bool '  Eicon.Diehl Diva cards' CONFIG_HISAX_DIEHLDIVA
-   bool '  ASUSCOM ISA cards' CONFIG_HISAX_ASUSCOM
-   bool '  TELEINT cards' CONFIG_HISAX_TELEINT
-   bool '  HFC-S based cards' CONFIG_HISAX_HFCS
-   bool '  Sedlbauer cards' CONFIG_HISAX_SEDLBAUER
-   bool '  USR Sportster internal TA' CONFIG_HISAX_SPORTSTER
-   bool '  MIC card' CONFIG_HISAX_MIC
-   bool '  NETjet card' CONFIG_HISAX_NETJET
-   bool '  NETspider U card' CONFIG_HISAX_NETJET_U
-   bool '  Niccy PnP/PCI card' CONFIG_HISAX_NICCY
-   bool '  Siemens I-Surf card' CONFIG_HISAX_ISURF
-   bool '  HST Saphir card' CONFIG_HISAX_HSTSAPHIR
-   bool '  Telekom A4T card' CONFIG_HISAX_BKM_A4T
-   bool '  Scitel Quadro card' CONFIG_HISAX_SCT_QUADRO
-   bool '  Gazel cards' CONFIG_HISAX_GAZEL
-   bool '  HFC PCI-Bus cards' CONFIG_HISAX_HFC_PCI
-   bool '  Winbond W6692 based cards' CONFIG_HISAX_W6692
-   bool '  HFC-S+, HFC-SP, HFC-PCMCIA cards' CONFIG_HISAX_HFC_SX
-   if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then
-#      bool '  TESTEMULATOR (EXPERIMENTAL)' CONFIG_HISAX_TESTEMU
-      if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then
-	 bool '  Am7930' CONFIG_HISAX_AMD7930
+comment 'ISDN subsystem'
+if [ "$CONFIG_NET" != "n" ]; then
+   bool 'ISDN support' CONFIG_ISDN_BOOL
+
+   if [ "$CONFIG_ISDN_BOOL" == "y" ]; then
+      mainmenu_option next_comment
+      comment 'Old ISDN4Linux'
+
+      tristate 'Old ISDN4Linux (obsolete)' CONFIG_ISDN
+      if [ "$CONFIG_ISDN" != "n" ]; then
+         source drivers/isdn/i4l/Config.in
       fi
+      endmenu
    fi
-   bool '  HiSax debugging' CONFIG_HISAX_DEBUG
-
-   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 'AVM Fritz!Card PCI/PCIv2/PnP support (EXPERIMENTAL)' CONFIG_HISAX_FRITZ_PCIPNP $CONFIG_HISAX $CONFIG_EXPERIMENTAL
-   dep_tristate 'AVM Fritz!Card classic support (EXPERIMENTAL)' CONFIG_HISAX_FRITZ_CLASSIC $CONFIG_HISAX $CONFIG_EXPERIMENTAL
-   dep_tristate 'HFC PCI support (EXPERIMENTAL)' CONFIG_HISAX_HFCPCI $CONFIG_HISAX $CONFIG_EXPERIMENTAL
 fi
 endmenu
 
-### Active ISDN cards
-
-mainmenu_option next_comment
-comment 'Active ISDN cards'
-
-dep_tristate       'ICN 2B and 4B support' CONFIG_ISDN_DRV_ICN $CONFIG_ISDN
-dep_tristate       'PCBIT-D support' CONFIG_ISDN_DRV_PCBIT $CONFIG_ISDN
-dep_tristate       'Spellcaster support' CONFIG_ISDN_DRV_SC $CONFIG_ISDN
-dep_tristate       'IBM Active 2000 support' CONFIG_ISDN_DRV_ACT2000 $CONFIG_ISDN
-
-bool               'Eicon active card support' CONFIG_ISDN_DRV_EICON
-if [ "$CONFIG_ISDN_DRV_EICON" != "n" ]; then
-   if [ "$CONFIG_ISDN_DRV_EICON_OLD" != "y" ]; then
-      dep_tristate '  Build Eicon driver type standalone' CONFIG_ISDN_DRV_EICON_DIVAS $CONFIG_ISDN $CONFIG_PCI
-   fi
-   if [ "$CONFIG_ISDN_DRV_EICON_DIVAS" != "y" ]; then
-      dep_tristate '  Legacy Eicon driver' CONFIG_ISDN_DRV_EICON_OLD $CONFIG_ISDN
-      if [ "$CONFIG_ISDN_DRV_EICON_OLD" != "n" ]; then
-         dep_bool  '    Eicon PCI DIVA Server BRI/PRI/4BRI support' CONFIG_ISDN_DRV_EICON_PCI $CONFIG_PCI
-         bool      '    Eicon S,SX,SCOM,Quadro,S2M support' CONFIG_ISDN_DRV_EICON_ISA
-      fi
-   fi
-fi
-
-if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then
-   dep_tristate    'Auvertech TurboPAM support' CONFIG_ISDN_DRV_TPAM $CONFIG_ISDN $CONFIG_PCI
-fi
-
-# CAPI subsystem
-
-tristate           'CAPI2.0 support' CONFIG_ISDN_CAPI
-if [ "$CONFIG_ISDN_CAPI" != "n" ]; then
-   bool            '  Verbose reason code reporting (kernel size +=7K)' CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON
-   dep_bool        '  CAPI2.0 Middleware support (EXPERIMENTAL)' CONFIG_ISDN_CAPI_MIDDLEWARE $CONFIG_EXPERIMENTAL
-   dep_tristate    '  CAPI2.0 /dev/capi support' CONFIG_ISDN_CAPI_CAPI20 $CONFIG_ISDN_CAPI
-   if [ "$CONFIG_ISDN_CAPI_MIDDLEWARE" = "y" ]; then
-      dep_mbool    '    CAPI2.0 filesystem support' CONFIG_ISDN_CAPI_CAPIFS_BOOL $CONFIG_ISDN_CAPI_CAPI20
-      if [ "$CONFIG_ISDN_CAPI_CAPIFS_BOOL" = "y" ]; then
-	 define_tristate CONFIG_ISDN_CAPI_CAPIFS $CONFIG_ISDN_CAPI_CAPI20
-      else
-	 define_tristate CONFIG_ISDN_CAPI_CAPIFS n
-      fi
-   fi
-   dep_tristate    '  CAPI2.0 capidrv interface support' CONFIG_ISDN_CAPI_CAPIDRV $CONFIG_ISDN_CAPI $CONFIG_ISDN
-fi
-
-# CAPI drivers
-
-if [ "$CONFIG_ISDN_CAPI" != "n" ]; then
-   dep_tristate '  AVM B1 ISA support' CONFIG_ISDN_DRV_AVMB1_B1ISA $CONFIG_ISDN_CAPI
-   dep_tristate '  AVM B1 PCI support' CONFIG_ISDN_DRV_AVMB1_B1PCI $CONFIG_ISDN_CAPI $CONFIG_PCI
-   dep_mbool    '    AVM B1 PCI V4 support' CONFIG_ISDN_DRV_AVMB1_B1PCIV4 $CONFIG_ISDN_DRV_AVMB1_B1PCI
-   dep_tristate '  AVM T1/T1-B ISA support' CONFIG_ISDN_DRV_AVMB1_T1ISA $CONFIG_ISDN_CAPI
-   dep_tristate '  AVM B1/M1/M2 PCMCIA support' CONFIG_ISDN_DRV_AVMB1_B1PCMCIA $CONFIG_ISDN_CAPI
-   dep_tristate '  AVM B1/M1/M2 PCMCIA cs module' CONFIG_ISDN_DRV_AVMB1_AVM_CS $CONFIG_ISDN_DRV_AVMB1_B1PCMCIA $CONFIG_PCMCIA
-   dep_tristate '  AVM T1/T1-B PCI support' CONFIG_ISDN_DRV_AVMB1_T1PCI $CONFIG_ISDN_CAPI $CONFIG_PCI
-   dep_tristate '  AVM C4/C2 support' CONFIG_ISDN_DRV_AVMB1_C4 $CONFIG_ISDN_CAPI $CONFIG_PCI
-fi
-
-# HYSDN
-
-dep_tristate '  Hypercope HYSDN cards (Champ, Ergo, Metro) support (module only)' CONFIG_HYSDN m $CONFIG_PROC_FS
-dep_mbool    '    HYSDN CAPI 2.0 support' CONFIG_HYSDN_CAPI $CONFIG_HYSDN $CONFIG_ISDN_CAPI
-endmenu
diff -Nru a/drivers/isdn/Makefile b/drivers/isdn/Makefile
--- a/drivers/isdn/Makefile	Mon Apr 22 15:32:19 2002
+++ b/drivers/isdn/Makefile	Mon Apr 22 15:32:19 2002
@@ -4,34 +4,10 @@
 
 O_TARGET	:= vmlinux-obj.o
 
-# Objects that export symbols.
-
-export-objs	:= isdn_common.o
-
-# Multipart objects.
-
-isdn-objs	:= isdn_net.o isdn_tty.o isdn_v110.o isdn_common.o
-
-# Optional parts of multipart objects.
-
-isdn-objs-$(CONFIG_ISDN_PPP)		+= isdn_ppp.o
-isdn-objs-$(CONFIG_ISDN_X25)		+= isdn_concap.o isdn_x25iface.o
-isdn-objs-$(CONFIG_ISDN_AUDIO)		+= isdn_audio.o
-isdn-objs-$(CONFIG_ISDN_TTY_FAX)	+= isdn_ttyfax.o
-isdn-objs-$(CONFIG_ISDN_WITH_ABC)	+= isdn_dwabc.o
-
-isdn-objs				+= $(isdn-objs-y)
-
-# Ordering constraints: isdn.o first, rest doesn't matter
-
-# Each configuration option enables a list of files.
-
-obj-$(CONFIG_ISDN)			+= isdn.o
-obj-$(CONFIG_ISDN_PPP_BSDCOMP)		+= isdn_bsdcomp.o
-
 # Object files in subdirectories
 
-mod-subdirs				:= avmb1 eicon
+mod-subdirs				:= i4l hisax avmb1 eicon
+subdir-$(CONFIG_ISDN)			+= i4l
 subdir-$(CONFIG_ISDN_DIVERSION)		+= divert
 subdir-$(CONFIG_ISDN_DRV_HISAX)		+= hisax
 subdir-$(CONFIG_ISDN_DRV_ICN)		+= icn
diff -Nru a/drivers/isdn/act2000/Config.help b/drivers/isdn/act2000/Config.help
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/act2000/Config.help	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,7 @@
+CONFIG_ISDN_DRV_ACT2000
+  Say Y here if you have an IBM Active 2000 ISDN card. In order to use
+  this card, additional firmware is necessary, which has to be loaded
+  into the card using a utility which is part of the latest
+  isdn4k-utils package. Please read the file
+  <file:Documentation/isdn/README.act2000> for more information.
+
diff -Nru a/drivers/isdn/act2000/Config.in b/drivers/isdn/act2000/Config.in
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/act2000/Config.in	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,6 @@
+#
+# Config.in for IBM Active 2000 ISDN driver
+#
+
+dep_tristate       'IBM Active 2000 support' CONFIG_ISDN_DRV_ACT2000 $CONFIG_ISDN
+
diff -Nru a/drivers/isdn/avmb1/Config.help b/drivers/isdn/avmb1/Config.help
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/avmb1/Config.help	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,64 @@
+CONFIG_ISDN_CAPI
+  This provides the CAPI (Common ISDN Application Programming
+  Interface, a standard making it easy for programs to access ISDN
+  hardware, see <http://www.capi.org/>.  This is needed for AVM's set
+  of active ISDN controllers like B1, T1, M1.
+
+CONFIG_ISDN_CAPI_CAPI20
+  This option will provide the CAPI 2.0 interface to userspace
+  applications via /dev/capi20. Applications should use the
+  standardized libcapi20 to access this functionality.  You should say
+  Y/M here.
+
+CONFIG_ISDN_CAPI_MIDDLEWARE
+  This option will enhance the capabilities of the /dev/capi20
+  interface.  It will provide a means of moving a data connection,
+  established via the usual /dev/capi20 interface to a special tty
+  device.  If you want to use pppd with pppdcapiplugin to dial up to
+  your ISP, say Y here.
+
+CONFIG_ISDN_CAPI_CAPIFS
+  This option provides a special file system, similar to /dev/pts with
+  device nodes for the special ttys established by using the
+  middleware extension above. If you want to use pppd with
+  pppdcapiplugin to dial up to your ISP, say Y here.
+
+CONFIG_ISDN_CAPI_CAPIDRV
+  This option provides the glue code to hook up CAPI driven cards to
+  the legacy isdn4linux link layer.  If you have a card which is
+  supported by a CAPI driver, but still want to use old features like
+  ippp interfaces or ttyI emulation, say Y/M here.
+
+CONFIG_ISDN_DRV_AVMB1_B1ISA
+  Enable support for the ISA version of the AVM B1 card.
+
+CONFIG_ISDN_DRV_AVMB1_B1PCI
+  Enable support for the PCI version of the AVM B1 card.
+
+CONFIG_ISDN_DRV_AVMB1_B1PCIV4
+  Enable support for the V4 version of AVM B1 PCI card.
+
+CONFIG_ISDN_DRV_AVMB1_T1ISA
+  Enable support for the AVM T1 T1B card.
+  Note: This is a PRI card and handle 30 B-channels.
+
+CONFIG_ISDN_DRV_AVMB1_B1PCMCIA
+  Enable support for the PCMCIA version of the AVM B1 card.
+
+CONFIG_ISDN_DRV_AVMB1_AVM_CS
+  Enable the PCMCIA client driver for the AVM B1/M1/M2
+  PCMCIA cards.
+
+CONFIG_ISDN_DRV_AVMB1_T1PCI
+  Enable support for the AVM T1 T1B card.
+  Note: This is a PRI card and handle 30 B-channels.
+
+CONFIG_ISDN_DRV_AVMB1_C4
+  Enable support for the AVM C4/C2 PCI cards.
+  These cards handle 4/2 BRI ISDN lines (8/4 channels).
+
+CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON
+  If you say Y here, the AVM B1 driver will give verbose reasons for
+  disconnecting. This will increase the size of the kernel by 7 KB. If
+  unsure, say Y.
+
diff -Nru a/drivers/isdn/avmb1/Config.in b/drivers/isdn/avmb1/Config.in
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/avmb1/Config.in	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,33 @@
+#
+# Config.in for the CAPI subsystem
+#
+
+tristate           'CAPI2.0 support' CONFIG_ISDN_CAPI
+if [ "$CONFIG_ISDN_CAPI" != "n" ]; then
+   bool            '  Verbose reason code reporting (kernel size +=7K)' CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON
+   dep_bool        '  CAPI2.0 Middleware support (EXPERIMENTAL)' CONFIG_ISDN_CAPI_MIDDLEWARE $CONFIG_EXPERIMENTAL
+   dep_tristate    '  CAPI2.0 /dev/capi support' CONFIG_ISDN_CAPI_CAPI20 $CONFIG_ISDN_CAPI
+   if [ "$CONFIG_ISDN_CAPI_MIDDLEWARE" = "y" ]; then
+      dep_mbool    '    CAPI2.0 filesystem support' CONFIG_ISDN_CAPI_CAPIFS_BOOL $CONFIG_ISDN_CAPI_CAPI20
+      if [ "$CONFIG_ISDN_CAPI_CAPIFS_BOOL" = "y" ]; then
+	 define_tristate CONFIG_ISDN_CAPI_CAPIFS $CONFIG_ISDN_CAPI_CAPI20
+      else
+	 define_tristate CONFIG_ISDN_CAPI_CAPIFS n
+      fi
+   fi
+   dep_tristate    '  CAPI2.0 capidrv interface support' CONFIG_ISDN_CAPI_CAPIDRV $CONFIG_ISDN_CAPI $CONFIG_ISDN
+fi
+
+# CAPI drivers
+
+if [ "$CONFIG_ISDN_CAPI" != "n" ]; then
+   dep_tristate '  AVM B1 ISA support' CONFIG_ISDN_DRV_AVMB1_B1ISA $CONFIG_ISDN_CAPI
+   dep_tristate '  AVM B1 PCI support' CONFIG_ISDN_DRV_AVMB1_B1PCI $CONFIG_ISDN_CAPI $CONFIG_PCI
+   dep_mbool    '    AVM B1 PCI V4 support' CONFIG_ISDN_DRV_AVMB1_B1PCIV4 $CONFIG_ISDN_DRV_AVMB1_B1PCI
+   dep_tristate '  AVM T1/T1-B ISA support' CONFIG_ISDN_DRV_AVMB1_T1ISA $CONFIG_ISDN_CAPI
+   dep_tristate '  AVM B1/M1/M2 PCMCIA support' CONFIG_ISDN_DRV_AVMB1_B1PCMCIA $CONFIG_ISDN_CAPI
+   dep_tristate '  AVM B1/M1/M2 PCMCIA cs module' CONFIG_ISDN_DRV_AVMB1_AVM_CS $CONFIG_ISDN_DRV_AVMB1_B1PCMCIA $CONFIG_PCMCIA
+   dep_tristate '  AVM T1/T1-B PCI support' CONFIG_ISDN_DRV_AVMB1_T1PCI $CONFIG_ISDN_CAPI $CONFIG_PCI
+   dep_tristate '  AVM C4/C2 support' CONFIG_ISDN_DRV_AVMB1_C4 $CONFIG_ISDN_CAPI $CONFIG_PCI
+fi
+
diff -Nru a/drivers/isdn/eicon/Config.help b/drivers/isdn/eicon/Config.help
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/eicon/Config.help	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,30 @@
+CONFIG_ISDN_DRV_EICON
+  Say Y here if you have an Eicon active ISDN card. In order to use
+  this card, additional firmware is necessary, which has to be loaded
+  into the card using the eiconctrl utility which is part of the
+  latest isdn4k-utils package. Please read the file
+  <file:Documentation/isdn/README.eicon> for more information.
+
+CONFIG_ISDN_DRV_EICON_OLD
+  Say Y here to use your Eicon active ISDN card with ISDN4Linux
+  isdn module.
+
+CONFIG_ISDN_DRV_EICON_PCI
+  Say Y here if you have an Eicon Diva Server (BRI/PRI/4BRI) ISDN
+  card.  Please read <file:Documentation/isdn/README.eicon> for more
+  information.
+
+CONFIG_ISDN_DRV_EICON_ISA
+  Say Y here if you have an old-type Eicon active ISDN card. In order
+  to use this card, additional firmware is necessary, which has to be
+  loaded into the card using the eiconctrl utility which is part of
+  the latest isdn4k-utils package. Please read the file
+  <file:Documentation/isdn/README.eicon> for more information.
+
+CONFIG_ISDN_DRV_EICON_DIVAS
+  Enable this option if you want the eicon driver as standalone
+  version with no interface to the ISDN4Linux isdn module. If you
+  say Y here, the eicon module only supports the Diva Server PCI
+  cards and will provide its own IDI interface. You should say N
+  here.
+
diff -Nru a/drivers/isdn/eicon/Config.in b/drivers/isdn/eicon/Config.in
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/eicon/Config.in	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,20 @@
+#
+# Config.in for Eicon active ISDN support
+#
+
+bool               'Eicon active card support' CONFIG_ISDN_DRV_EICON
+if [ "$CONFIG_ISDN_DRV_EICON" != "n" ]; then
+
+   if [ "$CONFIG_ISDN_DRV_EICON_OLD" != "y" ]; then
+      dep_tristate '  Build Eicon driver type standalone' CONFIG_ISDN_DRV_EICON_DIVAS $CONFIG_ISDN $CONFIG_PCI
+   fi
+   if [ "$CONFIG_ISDN_DRV_EICON_DIVAS" != "y" ]; then
+      dep_tristate '  Legacy Eicon driver' CONFIG_ISDN_DRV_EICON_OLD $CONFIG_ISDN
+      if [ "$CONFIG_ISDN_DRV_EICON_OLD" != "n" ]; then
+         dep_bool  '    Eicon PCI DIVA Server BRI/PRI/4BRI support' CONFIG_ISDN_DRV_EICON_PCI $CONFIG_PCI
+         bool      '    Eicon S,SX,SCOM,Quadro,S2M support' CONFIG_ISDN_DRV_EICON_ISA
+      fi
+
+   fi
+fi
+
diff -Nru a/drivers/isdn/hisax/Config.help b/drivers/isdn/hisax/Config.help
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/hisax/Config.help	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,267 @@
+CONFIG_ISDN_DRV_HISAX
+  This is a driver supporting the Siemens chipset on various
+  ISDN-cards (like AVM A1, Elsa ISDN cards, Teles S0-16.0, Teles
+  S0-16.3, Teles S0-8, Teles/Creatix PnP, ITK micro ix1 and many
+  compatibles).
+
+  HiSax is just the name of this driver, not the name of any hardware.
+
+  If you have a card with such a chipset, you should say Y here and
+  also to the configuration option of the driver for your particular
+  card, below.
+
+CONFIG_HISAX_EURO
+  Say Y or N according to the D-channel protocol which your local
+  telephone service company provides.
+
+  The call control protocol E-DSS1 is used in most European countries.
+  If unsure, say Y.
+
+CONFIG_DE_AOC
+  If you want that the HiSax hardware driver sends messages to the
+  upper level of the isdn code on each AOCD (Advice Of Charge, During
+  the call -- transmission of the fee information during a call) and
+  on each AOCE (Advice Of Charge, at the End of the call --
+  transmission of fee information at the end of the call), say Y here.
+  This works only in Germany.
+
+CONFIG_HISAX_NO_SENDCOMPLETE
+  If you have trouble with some ugly exchanges or you live in
+  Australia select this option.
+
+CONFIG_HISAX_NO_LLC
+  If you have trouble with some ugly exchanges try to select this
+  option.
+
+CONFIG_HISAX_NO_KEYPAD
+  If you like to send special dial strings including * or # without
+  using the keypad protocol, select this option.
+
+CONFIG_HISAX_1TR6
+  Say Y or N according to the D-channel protocol which your local
+  telephone service company provides.
+
+  1TR6 is an old call control protocol which was used in Germany
+  before E-DSS1 was established. Nowadays, all new lines in Germany
+  use E-DSS1.
+
+CONFIG_HISAX_NI1
+  Enable this if you like to use ISDN in US on a NI1 basic rate
+  interface.
+
+CONFIG_HISAX_MAX_CARDS
+  This option allows you to specify the maximum number of cards which
+  the HiSax driver will be able to handle.  
+
+CONFIG_HISAX_16_0
+  This enables HiSax support for the Teles ISDN-cards S0-16.0, S0-8
+  and many compatibles.
+
+  See <file:Documentation/isdn/README.HiSax> on how to configure it
+  using the different cards, a different D-channel protocol, or
+  non-standard IRQ/port/shmem settings.
+
+CONFIG_HISAX_16_3
+  This enables HiSax support for the Teles ISDN-cards S0-16.3 the
+  Teles/Creatix PnP and the Teles PCMCIA.
+
+  See <file:Documentation/isdn/README.HiSax> on how to configure it
+  using the different cards, a different D-channel protocol, or
+  non-standard IRQ/port settings.
+
+CONFIG_HISAX_TELESPCI
+  This enables HiSax support for the Teles PCI.
+  See <file:Documentation/isdn/README.HiSax> on how to configure it.
+
+CONFIG_HISAX_S0BOX
+  This enables HiSax support for the Teles/Creatix parallel port
+  S0BOX.  See <file:Documentation/isdn/README.HiSax> on how to
+  configure it.
+
+CONFIG_HISAX_AVM_A1
+  This enables HiSax support for the AVM A1 (aka "Fritz").
+
+  See <file:Documentation/isdn/README.HiSax> on how to configure it
+  using the different cards, a different D-channel protocol, or
+  non-standard IRQ/port settings.
+
+CONFIG_HISAX_FRITZPCI
+  This enables HiSax support for the AVM "Fritz!PnP" and "Fritz!PCI".
+  See <file:Documentation/isdn/README.HiSax> on how to configure it.
+
+CONFIG_HISAX_AVM_A1_PCMCIA
+  This enables HiSax support for the AVM A1 "Fritz!PCMCIA").
+  See <file:Documentation/isdn/README.HiSax> on how to configure it.
+
+CONFIG_HISAX_ELSA
+  This enables HiSax support for the Elsa Mircolink ISA cards, for the
+  Elsa Quickstep series cards and Elsa PCMCIA.
+
+  See <file:Documentation/isdn/README.HiSax> on how to configure it
+  using the different cards, a different D-channel protocol, or
+  non-standard IRQ/port settings.
+
+CONFIG_HISAX_IX1MICROR2
+  This enables HiSax support for the ITK ix1-micro Revision 2 card.
+
+  See <file:Documentation/isdn/README.HiSax> on how to configure it
+  using the different cards, a different D-channel protocol, or
+  non-standard IRQ/port settings.
+
+CONFIG_HISAX_DIEHLDIVA
+  This enables HiSax support for the Eicon.Diehl Diva none PRO
+  versions passive ISDN cards.
+
+  See <file:Documentation/isdn/README.HiSax> on how to configure it
+  using the different cards, a different D-channel protocol, or
+  non-standard IRQ/port settings.
+
+CONFIG_HISAX_ASUSCOM
+  This enables HiSax support for the AsusCom and their OEM versions
+  passive ISDN ISA cards.
+
+  See <file:Documentation/isdn/README.HiSax> on how to configure it
+  using the different cards, a different D-channel protocol, or
+  non-standard IRQ/port settings.
+
+CONFIG_HISAX_TELEINT
+  This enables HiSax support for the TELEINT SA1 semiactiv ISDN card.
+
+  See <file:Documentation/isdn/README.HiSax> on how to configure it
+  using the different cards, a different D-channel protocol, or
+  non-standard IRQ/port settings.
+
+CONFIG_HISAX_HFCS
+  This enables HiSax support for the HFC-S 2BDS0 based cards, like
+  teles 16.3c.
+
+  See <file:Documentation/isdn/README.HiSax> on how to configure it
+  using the different cards, a different D-channel protocol, or
+  non-standard IRQ/port settings.
+
+CONFIG_HISAX_SEDLBAUER
+  This enables HiSax support for the Sedlbauer passive ISDN cards.
+
+  See <file:Documentation/isdn/README.HiSax> on how to configure it
+  using the different cards, a different D-channel protocol, or
+  non-standard IRQ/port settings.
+
+CONFIG_HISAX_SPORTSTER
+  This enables HiSax support for the USR Sportster internal TA card.
+
+  See <file:Documentation/isdn/README.HiSax> on how to configure it
+  using a different D-channel protocol, or non-standard IRQ/port
+  settings.
+
+CONFIG_HISAX_MIC
+  This enables HiSax support for the ITH MIC card.
+
+  See <file:Documentation/isdn/README.HiSax> on how to configure it
+  using a different D-channel protocol, or non-standard IRQ/port
+  settings.
+
+CONFIG_HISAX_NETJET
+  This enables HiSax support for the NetJet from Traverse
+  Technologies.
+
+  See <file:Documentation/isdn/README.HiSax> on how to configure it
+  using a different D-channel protocol, or non-standard IRQ/port
+  settings.
+
+CONFIG_HISAX_NETJET_U
+  This enables HiSax support for the Netspider U interface ISDN card
+  from Traverse Technologies.
+  See <file:Documentation/isdn/README.HiSax> on how to configure it
+  using a different D-channel protocol, or non-standard IRQ/port
+  settings.
+
+CONFIG_HISAX_NICCY
+  This enables HiSax support for the Dr. Neuhaus Niccy PnP or PCI.
+
+  See <file:Documentation/isdn/README.HiSax> on how to configure it
+  using a different D-channel protocol, or non-standard IRQ/port
+  settings.
+
+CONFIG_HISAX_ISURF
+  This enables HiSax support for the Siemens I-Talk/I-Surf card with
+  ISAR chip.
+  See <file:Documentation/isdn/README.HiSax> on how to configure it
+  using a different D-channel protocol, or non-standard IRQ/port
+  settings.
+
+CONFIG_HISAX_HSTSAPHIR
+  This enables HiSax support for the HST Saphir card.
+
+  See <file:Documentation/isdn/README.HiSax> on how to configure it
+  using a different D-channel protocol, or non-standard IRQ/port
+  settings.
+
+CONFIG_HISAX_BKM_A4T
+  This enables HiSax support for the Telekom A4T card.
+
+  See <file:Documentation/isdn/README.HiSax> on how to configure it
+  using a different D-channel protocol, or non-standard IRQ/port
+  settings.
+
+CONFIG_HISAX_SCT_QUADRO
+  This enables HiSax support for the Scitel Quadro card.
+
+  See <file:Documentation/isdn/README.HiSax> on how to configure it
+  using a different D-channel protocol, or non-standard IRQ/port
+  settings.
+
+CONFIG_HISAX_GAZEL
+  This enables HiSax support for the Gazel cards.
+
+  See <file:Documentation/isdn/README.HiSax> on how to configure it
+  using a different D-channel protocol, or non-standard IRQ/port
+  settings.
+
+CONFIG_HISAX_HFC_PCI
+  This enables HiSax support for the HFC-S PCI 2BDS0 based cards.
+
+  For more informations see under
+  <file:Documentation/isdn/README.hfc-pci>.
+
+CONFIG_HISAX_W6692
+  This enables HiSax support for Winbond W6692 based PCI ISDN cards.
+
+  See <file:Documentation/isdn/README.HiSax> on how to configure it
+  using a different D-channel protocol, or non-standard IRQ/port
+  settings.
+
+CONFIG_HISAX_HFC_SX
+  This enables HiSax support for the HFC-S+, HFC-SP and HFC-PCMCIA
+  cards. This code is not finished yet.
+
+CONFIG_HISAX_AMD7930
+  This enables HiSax support for the AMD7930 chips on some SPARCs.
+  This code is not finished yet.
+
+CONFIG_HISAX_DEBUG
+  This enables debugging code in the new-style HiSax drivers, i.e.
+  the ST5481 USB driver currently. 
+  If in doubt, say yes.
+
+CONFIG_HISAX_ELSA_CS
+  This enables the PCMCIA client driver for the Elsa PCMCIA MicroLink
+  card.
+
+CONFIG_HISAX_SEDLBAUER_CS
+  This enables the PCMCIA client driver for the Sedlbauer Speed Star
+  and Speed Star II cards.
+
+CONFIG_HISAX_ST5481
+  This enables the driver for ST5481 based USB ISDN adapters,
+  e.g. the BeWan Gazel 128 USB
+
+CONFIG_HISAX_FRITZ_PCIPNP
+  This enables the driver for the AVM Fritz!Card PCI, Fritz!Card PCI v2 
+  and Fritz!Card PnP.
+  (the latter also needs you to select "ISA Plug and Play support"
+  from the menu "Plug and Play configuration")
+
+CONFIG_HISAX_FRITZ_CLASSIC
+  This enables the driver for the AVM Fritz!Card classic, formerly
+  known as AVM A1.
+
diff -Nru a/drivers/isdn/hisax/Config.in b/drivers/isdn/hisax/Config.in
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/hisax/Config.in	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,60 @@
+mainmenu_option next_comment
+comment 'Passive ISDN cards'
+dep_tristate 'HiSax SiemensChipSet driver support' CONFIG_ISDN_DRV_HISAX $CONFIG_ISDN
+if [ "$CONFIG_ISDN_DRV_HISAX" != "n" ]; then
+   comment '  D-channel protocol features'
+   bool '  HiSax Support for EURO/DSS1' CONFIG_HISAX_EURO
+   if [ "$CONFIG_HISAX_EURO" != "n" ]; then
+      bool '    Support for german chargeinfo' CONFIG_DE_AOC
+      bool '    Disable sending complete' CONFIG_HISAX_NO_SENDCOMPLETE
+      bool '    Disable sending low layer compatibility' CONFIG_HISAX_NO_LLC
+      bool '    Disable keypad protocol option' CONFIG_HISAX_NO_KEYPAD
+   fi
+   bool '  HiSax Support for german 1TR6' CONFIG_HISAX_1TR6
+   bool '  HiSax Support for US NI1' CONFIG_HISAX_NI1
+   int  '  Maximum number of cards supported by HiSax' CONFIG_HISAX_MAX_CARDS 8
+   comment '  HiSax supported cards'
+   bool '  Teles 16.0/8.0' CONFIG_HISAX_16_0
+   bool '  Teles 16.3 or PNP or PCMCIA' CONFIG_HISAX_16_3
+   bool '  Teles PCI' CONFIG_HISAX_TELESPCI 
+   bool '  Teles S0Box' CONFIG_HISAX_S0BOX 
+   bool '  AVM A1 (Fritz)' CONFIG_HISAX_AVM_A1
+   bool '  AVM PnP/PCI (Fritz!PnP/PCI)' CONFIG_HISAX_FRITZPCI
+   bool '  AVM A1 PCMCIA (Fritz)' CONFIG_HISAX_AVM_A1_PCMCIA
+   bool '  Elsa cards' CONFIG_HISAX_ELSA
+   bool '  ITK ix1-micro Revision 2' CONFIG_HISAX_IX1MICROR2
+   bool '  Eicon.Diehl Diva cards' CONFIG_HISAX_DIEHLDIVA
+   bool '  ASUSCOM ISA cards' CONFIG_HISAX_ASUSCOM
+   bool '  TELEINT cards' CONFIG_HISAX_TELEINT
+   bool '  HFC-S based cards' CONFIG_HISAX_HFCS
+   bool '  Sedlbauer cards' CONFIG_HISAX_SEDLBAUER
+   bool '  USR Sportster internal TA' CONFIG_HISAX_SPORTSTER
+   bool '  MIC card' CONFIG_HISAX_MIC
+   bool '  NETjet card' CONFIG_HISAX_NETJET
+   bool '  NETspider U card' CONFIG_HISAX_NETJET_U
+   bool '  Niccy PnP/PCI card' CONFIG_HISAX_NICCY
+   bool '  Siemens I-Surf card' CONFIG_HISAX_ISURF
+   bool '  HST Saphir card' CONFIG_HISAX_HSTSAPHIR
+   bool '  Telekom A4T card' CONFIG_HISAX_BKM_A4T
+   bool '  Scitel Quadro card' CONFIG_HISAX_SCT_QUADRO
+   bool '  Gazel cards' CONFIG_HISAX_GAZEL
+   bool '  HFC PCI-Bus cards' CONFIG_HISAX_HFC_PCI
+   bool '  Winbond W6692 based cards' CONFIG_HISAX_W6692
+   bool '  HFC-S+, HFC-SP, HFC-PCMCIA cards' CONFIG_HISAX_HFC_SX
+   if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then
+#      bool '  TESTEMULATOR (EXPERIMENTAL)' CONFIG_HISAX_TESTEMU
+      if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then
+	 bool '  Am7930' CONFIG_HISAX_AMD7930
+      fi
+   fi
+   bool '  HiSax debugging' CONFIG_HISAX_DEBUG
+
+   dep_tristate 'Sedlbauer PCMCIA cards'                              CONFIG_HISAX_SEDLBAUER_CS  $CONFIG_DRV_ISDN_HISAX $CONFIG_PCMCIA
+   dep_tristate 'ELSA PCMCIA MicroLink cards'                         CONFIG_HISAX_ELSA_CS       $CONFIG_DRV_ISDN_HISAX $CONFIG_PCMCIA
+   dep_tristate 'ST5481 USB ISDN modem (EXPERIMENTAL)'                CONFIG_HISAX_ST5481        $CONFIG_DRV_ISDN_HISAX $CONFIG_USB $CONFIG_EXPERIMENTAL
+   dep_tristate 'AVM Fritz!Card PCI/PCIv2/PnP support (EXPERIMENTAL)' CONFIG_HISAX_FRITZ_PCIPNP  $CONFIG_DRV_ISDN_HISAX $CONFIG_EXPERIMENTAL
+   dep_tristate 'AVM Fritz!Card classic support (EXPERIMENTAL)'       CONFIG_HISAX_FRITZ_CLASSIC $CONFIG_DRV_ISDN_HISAX  $CONFIG_EXPERIMENTAL
+   dep_tristate 'HFC PCI support (EXPERIMENTAL)'                      CONFIG_HISAX_HFCPCI        $CONFIG_DRV_ISDN_HISAX $CONFIG_EXPERIMENTAL
+fi
+endmenu
+
diff -Nru a/drivers/isdn/hysdn/Config.help b/drivers/isdn/hysdn/Config.help
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/hysdn/Config.help	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,9 @@
+CONFIG_HYSDN
+  Say Y here if you have one of Hypercope's active PCI ISDN cards
+  Champ, Ergo and Metro. You will then get a module called hysdn.o.
+  Please read the file <file:Documentation/isdn/README.hysdn> for more
+  information.
+
+CONFIG_HYSDN_CAPI
+  Say Y here if you like to use Hypercope's CAPI 2.0 interface.
+
diff -Nru a/drivers/isdn/hysdn/Config.in b/drivers/isdn/hysdn/Config.in
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/hysdn/Config.in	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,6 @@
+#
+# Config.in for HYSDN ISDN driver
+#
+
+dep_tristate '  Hypercope HYSDN cards (Champ, Ergo, Metro) support (module only)' CONFIG_HYSDN m $CONFIG_PROC_FS
+dep_mbool    '    HYSDN CAPI 2.0 support' CONFIG_HYSDN_CAPI $CONFIG_HYSDN $CONFIG_ISDN_CAPI
diff -Nru a/drivers/isdn/i4l/Config.help b/drivers/isdn/i4l/Config.help
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/i4l/Config.help	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,88 @@
+CONFIG_ISDN
+  This driver allows you to use an ISDN-card for networking
+  connections and as dialin/out device.  The isdn-tty's have a built
+  in AT-compatible modem emulator.  Network devices support autodial,
+  channel-bundling, callback and caller-authentication without having
+  a daemon running.  A reduced T.70 protocol is supported with tty's
+  suitable for German BTX.  On D-Channel, the protocols EDSS1
+  (Euro-ISDN) and 1TR6 (German style) are supported.  See
+  <file:Documentation/isdn/README> for more information.
+
+  ISDN support in the linux kernel is moving towards a new API,
+  called CAPI (Common ISDN Application Programming Interface).
+  Therefore the old ISDN4Linux layer is becoming obsolete. It is 
+  still usable, though, if you select this option.
+
+CONFIG_ISDN_PPP
+  Over digital connections such as ISDN, there is no need to
+  synchronize sender and recipient's clocks with start and stop bits
+  as is done over analog telephone lines. Instead, one can use
+  "synchronous PPP". Saying Y here will include this protocol. This
+  protocol is used by Cisco and Sun for example. So you want to say Y
+  here if the other end of your ISDN connection supports it. You will
+  need a special version of pppd (called ipppd) for using this
+  feature. See <file:Documentation/isdn/README.syncppp> and
+  <file:Documentation/isdn/syncPPP.FAQ> for more information.
+
+CONFIG_ISDN_MPP
+  With synchronous PPP enabled, it is possible to increase throughput
+  by bundling several ISDN-connections, using this protocol. See
+  <file:Documentation/isdn/README.syncppp> for more information.
+
+CONFIG_ISDN_PPP_VJ
+  This enables Van Jacobson header compression for synchronous PPP.
+  Say Y if the other end of the connection supports it.
+
+CONFIG_ISDN_PPP_BSDCOMP
+  Support for the BSD-Compress compression method for PPP, which uses
+  the LZW compression method to compress each PPP packet before it is
+  sent over the wire. The machine at the other end of the PPP link
+  (usually your ISP) has to support the BSD-Compress compression
+  method as well for this to be useful. Even if they don't support it,
+  it is safe to say Y here.
+
+CONFIG_ISDN_AUDIO
+  If you say Y here, the modem-emulator will support a subset of the
+  EIA Class 8 Voice commands. Using a getty with voice-support
+  (mgetty+sendfax by gert@greenie.muc.de with an extension, available
+  with the ISDN utility package for example), you will be able to use
+  your Linux box as an ISDN-answering machine. Of course, this must be
+  supported by the lowlevel driver also. Currently, the HiSax driver
+  is the only voice-supporting driver. See
+  <file:Documentation/isdn/README.audio> for more information.
+
+CONFIG_ISDN_X25
+  This feature provides the X.25 protocol over ISDN connections.
+  See <file:Documentation/isdn/README.x25> for more information
+  if you are thinking about using this.
+
+CONFIG_ISDN_DIVERSION
+  This option allows you to use some supplementary diversion
+  services in conjunction with the HiSax driver on an EURO/DSS1
+  line.
+
+  Supported options are CD (call deflection), CFU (Call forward
+  unconditional), CFB (Call forward when busy) and CFNR (call forward
+  not reachable). Additionally the actual CFU, CFB and CFNR state may
+  be interrogated.
+
+  The use of CFU, CFB, CFNR and interrogation may be limited to some
+  countries. The keypad protocol is still not implemented. CD should
+  work in all countries if the service has been subscribed to.
+
+  Please read the file <file:Documentation/isdn/README.diversion>.
+
+CONFIG_ISDN_TTY_FAX
+  If you say Y here, the modem-emulator will support a subset of the
+  Fax Class 1 and 2 commands. Using a getty with fax-support
+  (mgetty+sendfax, hylafax), you will be able to use your Linux box as
+  an ISDN-fax-machine. This must be supported by the lowlevel driver
+  also. See <file:Documentation/isdn/README.fax> for more information.
+
+CONFIG_ISDN_DRV_LOOP
+  This driver provides a virtual ISDN card. Its primary purpose is
+  testing of linklevel features or configuration without getting
+  charged by your service-provider for lots of phone calls.
+  You need will need the loopctrl utility from the latest isdn4k-utils
+  package to set up this driver.
+
diff -Nru a/drivers/isdn/i4l/Config.in b/drivers/isdn/i4l/Config.in
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/i4l/Config.in	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,46 @@
+#
+# Old ISDN4Linux config
+#
+
+if [ "$CONFIG_INET" != "n" ]; then
+   bool '  Support synchronous PPP' CONFIG_ISDN_PPP
+   if [ "$CONFIG_ISDN_PPP" != "n" ]; then
+      bool         '    Use VJ-compression with synchronous PPP' CONFIG_ISDN_PPP_VJ
+      bool         '    Support generic MP (RFC 1717)' CONFIG_ISDN_MPP
+      dep_tristate '    Support BSD compression' CONFIG_ISDN_PPP_BSDCOMP $CONFIG_ISDN
+   fi
+fi
+bool '  Support audio via ISDN' CONFIG_ISDN_AUDIO
+if [ "$CONFIG_ISDN_AUDIO" != "n" ]; then
+   bool '    Support AT-Fax Class 1 and 2 commands' CONFIG_ISDN_TTY_FAX
+fi
+if [ "$CONFIG_X25" != "n" ]; then
+   bool '  X.25 PLP on top of ISDN' CONFIG_ISDN_X25
+fi
+
+mainmenu_option next_comment
+comment 'ISDN feature submodules'
+   dep_tristate 'isdnloop support' CONFIG_ISDN_DRV_LOOP $CONFIG_ISDN
+   dep_tristate 'Support isdn diversion services' CONFIG_ISDN_DIVERSION $CONFIG_ISDN
+endmenu
+
+comment 'low-level hardware drivers'
+
+source drivers/isdn/hisax/Config.in
+
+### Active ISDN cards
+
+mainmenu_option next_comment
+comment 'Active ISDN cards'
+
+source drivers/isdn/icn/Config.in
+source drivers/isdn/pcbit/Config.in
+source drivers/isdn/sc/Config.in
+source drivers/isdn/act2000/Config.in
+source drivers/isdn/eicon/Config.in
+source drivers/isdn/tpam/Config.in
+source drivers/isdn/avmb1/Config.in
+source drivers/isdn/hysdn/Config.in
+
+endmenu
+
diff -Nru a/drivers/isdn/i4l/Makefile b/drivers/isdn/i4l/Makefile
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/i4l/Makefile	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,34 @@
+# Makefile for the kernel ISDN subsystem and device drivers.
+
+# The target object and module list name.
+
+O_TARGET	:= vmlinux-obj.o
+
+# Objects that export symbols.
+
+export-objs	:= isdn_common.o
+
+# Multipart objects.
+
+isdn-objs	:= isdn_net.o isdn_tty.o isdn_v110.o isdn_common.o
+
+# Optional parts of multipart objects.
+
+isdn-objs-$(CONFIG_ISDN_PPP)		+= isdn_ppp.o
+isdn-objs-$(CONFIG_ISDN_X25)		+= isdn_concap.o isdn_x25iface.o
+isdn-objs-$(CONFIG_ISDN_AUDIO)		+= isdn_audio.o
+isdn-objs-$(CONFIG_ISDN_TTY_FAX)	+= isdn_ttyfax.o
+isdn-objs-$(CONFIG_ISDN_WITH_ABC)	+= isdn_dwabc.o
+
+isdn-objs				+= $(isdn-objs-y)
+
+# Ordering constraints: isdn.o first, rest doesn't matter
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_ISDN)			+= isdn.o
+obj-$(CONFIG_ISDN_PPP_BSDCOMP)		+= isdn_bsdcomp.o
+
+# The global Rules.make.
+
+include $(TOPDIR)/Rules.make
diff -Nru a/drivers/isdn/i4l/isdn_audio.c b/drivers/isdn/i4l/isdn_audio.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/i4l/isdn_audio.c	Mon Apr 22 15:32:18 2002
@@ -0,0 +1,720 @@
+/* $Id: isdn_audio.c,v 1.21.6.2 2001/09/23 22:24:31 kai Exp $
+ *
+ * Linux ISDN subsystem, audio conversion and compression (linklevel).
+ *
+ * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de)
+ * DTMF code (c) 1996 by Christian Mock (cm@kukuruz.ping.at)
+ * Silence detection (c) 1998 by Armin Schindler (mac@gismo.telekom.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/isdn.h>
+#include "isdn_audio.h"
+#include "isdn_common.h"
+
+char *isdn_audio_revision = "$Revision: 1.21.6.2 $";
+
+/*
+ * Misc. lookup-tables.
+ */
+
+/* ulaw -> signed 16-bit */
+static short isdn_audio_ulaw_to_s16[] =
+{
+	0x8284, 0x8684, 0x8a84, 0x8e84, 0x9284, 0x9684, 0x9a84, 0x9e84,
+	0xa284, 0xa684, 0xaa84, 0xae84, 0xb284, 0xb684, 0xba84, 0xbe84,
+	0xc184, 0xc384, 0xc584, 0xc784, 0xc984, 0xcb84, 0xcd84, 0xcf84,
+	0xd184, 0xd384, 0xd584, 0xd784, 0xd984, 0xdb84, 0xdd84, 0xdf84,
+	0xe104, 0xe204, 0xe304, 0xe404, 0xe504, 0xe604, 0xe704, 0xe804,
+	0xe904, 0xea04, 0xeb04, 0xec04, 0xed04, 0xee04, 0xef04, 0xf004,
+	0xf0c4, 0xf144, 0xf1c4, 0xf244, 0xf2c4, 0xf344, 0xf3c4, 0xf444,
+	0xf4c4, 0xf544, 0xf5c4, 0xf644, 0xf6c4, 0xf744, 0xf7c4, 0xf844,
+	0xf8a4, 0xf8e4, 0xf924, 0xf964, 0xf9a4, 0xf9e4, 0xfa24, 0xfa64,
+	0xfaa4, 0xfae4, 0xfb24, 0xfb64, 0xfba4, 0xfbe4, 0xfc24, 0xfc64,
+	0xfc94, 0xfcb4, 0xfcd4, 0xfcf4, 0xfd14, 0xfd34, 0xfd54, 0xfd74,
+	0xfd94, 0xfdb4, 0xfdd4, 0xfdf4, 0xfe14, 0xfe34, 0xfe54, 0xfe74,
+	0xfe8c, 0xfe9c, 0xfeac, 0xfebc, 0xfecc, 0xfedc, 0xfeec, 0xfefc,
+	0xff0c, 0xff1c, 0xff2c, 0xff3c, 0xff4c, 0xff5c, 0xff6c, 0xff7c,
+	0xff88, 0xff90, 0xff98, 0xffa0, 0xffa8, 0xffb0, 0xffb8, 0xffc0,
+	0xffc8, 0xffd0, 0xffd8, 0xffe0, 0xffe8, 0xfff0, 0xfff8, 0x0000,
+	0x7d7c, 0x797c, 0x757c, 0x717c, 0x6d7c, 0x697c, 0x657c, 0x617c,
+	0x5d7c, 0x597c, 0x557c, 0x517c, 0x4d7c, 0x497c, 0x457c, 0x417c,
+	0x3e7c, 0x3c7c, 0x3a7c, 0x387c, 0x367c, 0x347c, 0x327c, 0x307c,
+	0x2e7c, 0x2c7c, 0x2a7c, 0x287c, 0x267c, 0x247c, 0x227c, 0x207c,
+	0x1efc, 0x1dfc, 0x1cfc, 0x1bfc, 0x1afc, 0x19fc, 0x18fc, 0x17fc,
+	0x16fc, 0x15fc, 0x14fc, 0x13fc, 0x12fc, 0x11fc, 0x10fc, 0x0ffc,
+	0x0f3c, 0x0ebc, 0x0e3c, 0x0dbc, 0x0d3c, 0x0cbc, 0x0c3c, 0x0bbc,
+	0x0b3c, 0x0abc, 0x0a3c, 0x09bc, 0x093c, 0x08bc, 0x083c, 0x07bc,
+	0x075c, 0x071c, 0x06dc, 0x069c, 0x065c, 0x061c, 0x05dc, 0x059c,
+	0x055c, 0x051c, 0x04dc, 0x049c, 0x045c, 0x041c, 0x03dc, 0x039c,
+	0x036c, 0x034c, 0x032c, 0x030c, 0x02ec, 0x02cc, 0x02ac, 0x028c,
+	0x026c, 0x024c, 0x022c, 0x020c, 0x01ec, 0x01cc, 0x01ac, 0x018c,
+	0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104,
+	0x00f4, 0x00e4, 0x00d4, 0x00c4, 0x00b4, 0x00a4, 0x0094, 0x0084,
+	0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040,
+	0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000
+};
+
+/* alaw -> signed 16-bit */
+static short isdn_audio_alaw_to_s16[] =
+{
+	0x13fc, 0xec04, 0x0144, 0xfebc, 0x517c, 0xae84, 0x051c, 0xfae4,
+	0x0a3c, 0xf5c4, 0x0048, 0xffb8, 0x287c, 0xd784, 0x028c, 0xfd74,
+	0x1bfc, 0xe404, 0x01cc, 0xfe34, 0x717c, 0x8e84, 0x071c, 0xf8e4,
+	0x0e3c, 0xf1c4, 0x00c4, 0xff3c, 0x387c, 0xc784, 0x039c, 0xfc64,
+	0x0ffc, 0xf004, 0x0104, 0xfefc, 0x417c, 0xbe84, 0x041c, 0xfbe4,
+	0x083c, 0xf7c4, 0x0008, 0xfff8, 0x207c, 0xdf84, 0x020c, 0xfdf4,
+	0x17fc, 0xe804, 0x018c, 0xfe74, 0x617c, 0x9e84, 0x061c, 0xf9e4,
+	0x0c3c, 0xf3c4, 0x0084, 0xff7c, 0x307c, 0xcf84, 0x030c, 0xfcf4,
+	0x15fc, 0xea04, 0x0164, 0xfe9c, 0x597c, 0xa684, 0x059c, 0xfa64,
+	0x0b3c, 0xf4c4, 0x0068, 0xff98, 0x2c7c, 0xd384, 0x02cc, 0xfd34,
+	0x1dfc, 0xe204, 0x01ec, 0xfe14, 0x797c, 0x8684, 0x07bc, 0xf844,
+	0x0f3c, 0xf0c4, 0x00e4, 0xff1c, 0x3c7c, 0xc384, 0x03dc, 0xfc24,
+	0x11fc, 0xee04, 0x0124, 0xfedc, 0x497c, 0xb684, 0x049c, 0xfb64,
+	0x093c, 0xf6c4, 0x0028, 0xffd8, 0x247c, 0xdb84, 0x024c, 0xfdb4,
+	0x19fc, 0xe604, 0x01ac, 0xfe54, 0x697c, 0x9684, 0x069c, 0xf964,
+	0x0d3c, 0xf2c4, 0x00a4, 0xff5c, 0x347c, 0xcb84, 0x034c, 0xfcb4,
+	0x12fc, 0xed04, 0x0134, 0xfecc, 0x4d7c, 0xb284, 0x04dc, 0xfb24,
+	0x09bc, 0xf644, 0x0038, 0xffc8, 0x267c, 0xd984, 0x026c, 0xfd94,
+	0x1afc, 0xe504, 0x01ac, 0xfe54, 0x6d7c, 0x9284, 0x06dc, 0xf924,
+	0x0dbc, 0xf244, 0x00b4, 0xff4c, 0x367c, 0xc984, 0x036c, 0xfc94,
+	0x0f3c, 0xf0c4, 0x00f4, 0xff0c, 0x3e7c, 0xc184, 0x03dc, 0xfc24,
+	0x07bc, 0xf844, 0x0008, 0xfff8, 0x1efc, 0xe104, 0x01ec, 0xfe14,
+	0x16fc, 0xe904, 0x0174, 0xfe8c, 0x5d7c, 0xa284, 0x05dc, 0xfa24,
+	0x0bbc, 0xf444, 0x0078, 0xff88, 0x2e7c, 0xd184, 0x02ec, 0xfd14,
+	0x14fc, 0xeb04, 0x0154, 0xfeac, 0x557c, 0xaa84, 0x055c, 0xfaa4,
+	0x0abc, 0xf544, 0x0058, 0xffa8, 0x2a7c, 0xd584, 0x02ac, 0xfd54,
+	0x1cfc, 0xe304, 0x01cc, 0xfe34, 0x757c, 0x8a84, 0x075c, 0xf8a4,
+	0x0ebc, 0xf144, 0x00d4, 0xff2c, 0x3a7c, 0xc584, 0x039c, 0xfc64,
+	0x10fc, 0xef04, 0x0114, 0xfeec, 0x457c, 0xba84, 0x045c, 0xfba4,
+	0x08bc, 0xf744, 0x0018, 0xffe8, 0x227c, 0xdd84, 0x022c, 0xfdd4,
+	0x18fc, 0xe704, 0x018c, 0xfe74, 0x657c, 0x9a84, 0x065c, 0xf9a4,
+	0x0cbc, 0xf344, 0x0094, 0xff6c, 0x327c, 0xcd84, 0x032c, 0xfcd4
+};
+
+/* alaw -> ulaw */
+static char isdn_audio_alaw_to_ulaw[] =
+{
+	0xab, 0x2b, 0xe3, 0x63, 0x8b, 0x0b, 0xc9, 0x49,
+	0xba, 0x3a, 0xf6, 0x76, 0x9b, 0x1b, 0xd7, 0x57,
+	0xa3, 0x23, 0xdd, 0x5d, 0x83, 0x03, 0xc1, 0x41,
+	0xb2, 0x32, 0xeb, 0x6b, 0x93, 0x13, 0xcf, 0x4f,
+	0xaf, 0x2f, 0xe7, 0x67, 0x8f, 0x0f, 0xcd, 0x4d,
+	0xbe, 0x3e, 0xfe, 0x7e, 0x9f, 0x1f, 0xdb, 0x5b,
+	0xa7, 0x27, 0xdf, 0x5f, 0x87, 0x07, 0xc5, 0x45,
+	0xb6, 0x36, 0xef, 0x6f, 0x97, 0x17, 0xd3, 0x53,
+	0xa9, 0x29, 0xe1, 0x61, 0x89, 0x09, 0xc7, 0x47,
+	0xb8, 0x38, 0xf2, 0x72, 0x99, 0x19, 0xd5, 0x55,
+	0xa1, 0x21, 0xdc, 0x5c, 0x81, 0x01, 0xbf, 0x3f,
+	0xb0, 0x30, 0xe9, 0x69, 0x91, 0x11, 0xce, 0x4e,
+	0xad, 0x2d, 0xe5, 0x65, 0x8d, 0x0d, 0xcb, 0x4b,
+	0xbc, 0x3c, 0xfa, 0x7a, 0x9d, 0x1d, 0xd9, 0x59,
+	0xa5, 0x25, 0xde, 0x5e, 0x85, 0x05, 0xc3, 0x43,
+	0xb4, 0x34, 0xed, 0x6d, 0x95, 0x15, 0xd1, 0x51,
+	0xac, 0x2c, 0xe4, 0x64, 0x8c, 0x0c, 0xca, 0x4a,
+	0xbb, 0x3b, 0xf8, 0x78, 0x9c, 0x1c, 0xd8, 0x58,
+	0xa4, 0x24, 0xde, 0x5e, 0x84, 0x04, 0xc2, 0x42,
+	0xb3, 0x33, 0xec, 0x6c, 0x94, 0x14, 0xd0, 0x50,
+	0xb0, 0x30, 0xe8, 0x68, 0x90, 0x10, 0xce, 0x4e,
+	0xbf, 0x3f, 0xfe, 0x7e, 0xa0, 0x20, 0xdc, 0x5c,
+	0xa8, 0x28, 0xe0, 0x60, 0x88, 0x08, 0xc6, 0x46,
+	0xb7, 0x37, 0xf0, 0x70, 0x98, 0x18, 0xd4, 0x54,
+	0xaa, 0x2a, 0xe2, 0x62, 0x8a, 0x0a, 0xc8, 0x48,
+	0xb9, 0x39, 0xf4, 0x74, 0x9a, 0x1a, 0xd6, 0x56,
+	0xa2, 0x22, 0xdd, 0x5d, 0x82, 0x02, 0xc0, 0x40,
+	0xb1, 0x31, 0xea, 0x6a, 0x92, 0x12, 0xcf, 0x4f,
+	0xae, 0x2e, 0xe6, 0x66, 0x8e, 0x0e, 0xcc, 0x4c,
+	0xbd, 0x3d, 0xfc, 0x7c, 0x9e, 0x1e, 0xda, 0x5a,
+	0xa6, 0x26, 0xdf, 0x5f, 0x86, 0x06, 0xc4, 0x44,
+	0xb5, 0x35, 0xee, 0x6e, 0x96, 0x16, 0xd2, 0x52
+};
+
+/* ulaw -> alaw */
+static char isdn_audio_ulaw_to_alaw[] =
+{
+	0xab, 0x55, 0xd5, 0x15, 0x95, 0x75, 0xf5, 0x35,
+	0xb5, 0x45, 0xc5, 0x05, 0x85, 0x65, 0xe5, 0x25,
+	0xa5, 0x5d, 0xdd, 0x1d, 0x9d, 0x7d, 0xfd, 0x3d,
+	0xbd, 0x4d, 0xcd, 0x0d, 0x8d, 0x6d, 0xed, 0x2d,
+	0xad, 0x51, 0xd1, 0x11, 0x91, 0x71, 0xf1, 0x31,
+	0xb1, 0x41, 0xc1, 0x01, 0x81, 0x61, 0xe1, 0x21,
+	0x59, 0xd9, 0x19, 0x99, 0x79, 0xf9, 0x39, 0xb9,
+	0x49, 0xc9, 0x09, 0x89, 0x69, 0xe9, 0x29, 0xa9,
+	0xd7, 0x17, 0x97, 0x77, 0xf7, 0x37, 0xb7, 0x47,
+	0xc7, 0x07, 0x87, 0x67, 0xe7, 0x27, 0xa7, 0xdf,
+	0x9f, 0x7f, 0xff, 0x3f, 0xbf, 0x4f, 0xcf, 0x0f,
+	0x8f, 0x6f, 0xef, 0x2f, 0x53, 0x13, 0x73, 0x33,
+	0xb3, 0x43, 0xc3, 0x03, 0x83, 0x63, 0xe3, 0x23,
+	0xa3, 0x5b, 0xdb, 0x1b, 0x9b, 0x7b, 0xfb, 0x3b,
+	0xbb, 0xbb, 0x4b, 0x4b, 0xcb, 0xcb, 0x0b, 0x0b,
+	0x8b, 0x8b, 0x6b, 0x6b, 0xeb, 0xeb, 0x2b, 0x2b,
+	0xab, 0x54, 0xd4, 0x14, 0x94, 0x74, 0xf4, 0x34,
+	0xb4, 0x44, 0xc4, 0x04, 0x84, 0x64, 0xe4, 0x24,
+	0xa4, 0x5c, 0xdc, 0x1c, 0x9c, 0x7c, 0xfc, 0x3c,
+	0xbc, 0x4c, 0xcc, 0x0c, 0x8c, 0x6c, 0xec, 0x2c,
+	0xac, 0x50, 0xd0, 0x10, 0x90, 0x70, 0xf0, 0x30,
+	0xb0, 0x40, 0xc0, 0x00, 0x80, 0x60, 0xe0, 0x20,
+	0x58, 0xd8, 0x18, 0x98, 0x78, 0xf8, 0x38, 0xb8,
+	0x48, 0xc8, 0x08, 0x88, 0x68, 0xe8, 0x28, 0xa8,
+	0xd6, 0x16, 0x96, 0x76, 0xf6, 0x36, 0xb6, 0x46,
+	0xc6, 0x06, 0x86, 0x66, 0xe6, 0x26, 0xa6, 0xde,
+	0x9e, 0x7e, 0xfe, 0x3e, 0xbe, 0x4e, 0xce, 0x0e,
+	0x8e, 0x6e, 0xee, 0x2e, 0x52, 0x12, 0x72, 0x32,
+	0xb2, 0x42, 0xc2, 0x02, 0x82, 0x62, 0xe2, 0x22,
+	0xa2, 0x5a, 0xda, 0x1a, 0x9a, 0x7a, 0xfa, 0x3a,
+	0xba, 0xba, 0x4a, 0x4a, 0xca, 0xca, 0x0a, 0x0a,
+	0x8a, 0x8a, 0x6a, 0x6a, 0xea, 0xea, 0x2a, 0x2a
+};
+
+#define NCOEFF           16     /* number of frequencies to be analyzed       */
+#define DTMF_TRESH    25000     /* above this is dtmf                         */
+#define SILENCE_TRESH   200     /* below this is silence                      */
+#define H2_TRESH      20000     /* 2nd harmonic                               */
+#define AMP_BITS          9     /* bits per sample, reduced to avoid overflow */
+#define LOGRP             0
+#define HIGRP             1
+
+typedef struct {
+	int grp;                /* low/high group     */
+	int k;                  /* k                  */
+	int k2;                 /* k fuer 2. harmonic */
+} dtmf_t;
+
+/* For DTMF recognition:
+ * 2 * cos(2 * PI * k / N) precalculated for all k
+ */
+static int cos2pik[NCOEFF] =
+{
+	55812, 29528, 53603, 24032, 51193, 14443, 48590, 6517,
+	38113, -21204, 33057, -32186, 25889, -45081, 18332, -55279
+};
+
+static dtmf_t dtmf_tones[8] =
+{
+	{LOGRP, 0, 1},          /*  697 Hz */
+	{LOGRP, 2, 3},          /*  770 Hz */
+	{LOGRP, 4, 5},          /*  852 Hz */
+	{LOGRP, 6, 7},          /*  941 Hz */
+	{HIGRP, 8, 9},          /* 1209 Hz */
+	{HIGRP, 10, 11},        /* 1336 Hz */
+	{HIGRP, 12, 13},        /* 1477 Hz */
+	{HIGRP, 14, 15}         /* 1633 Hz */
+};
+
+static char dtmf_matrix[4][4] =
+{
+	{'1', '2', '3', 'A'},
+	{'4', '5', '6', 'B'},
+	{'7', '8', '9', 'C'},
+	{'*', '0', '#', 'D'}
+};
+
+static inline void
+isdn_audio_tlookup(const u_char *table, u_char *buff, unsigned long n)
+{
+#ifdef __i386__
+	unsigned long d0, d1, d2, d3;
+	__asm__ __volatile__(
+		"cld\n"
+		"1:\tlodsb\n\t"
+		"xlatb\n\t"
+		"stosb\n\t"
+		"loop 1b\n\t"
+	:	"=&b"(d0), "=&c"(d1), "=&D"(d2), "=&S"(d3)
+	:	"0"((long) table), "1"(n), "2"((long) buff), "3"((long) buff)
+	:	"memory", "ax");
+#else
+	while (n--) {
+		*buff = table[*buff];
+		buff++;
+	}
+#endif
+}
+
+void
+isdn_audio_ulaw2alaw(unsigned char *buff, unsigned long len)
+{
+	isdn_audio_tlookup(isdn_audio_ulaw_to_alaw, buff, len);
+}
+
+void
+isdn_audio_alaw2ulaw(unsigned char *buff, unsigned long len)
+{
+	isdn_audio_tlookup(isdn_audio_alaw_to_ulaw, buff, len);
+}
+
+/*
+ * linear <-> adpcm conversion stuff
+ * Most parts from the mgetty-package.
+ * (C) by Gert Doering and Klaus Weidner
+ * Used by permission of Gert Doering
+ */
+
+
+#define ZEROTRAP                /* turn on the trap as per the MIL-STD */
+#undef ZEROTRAP
+#define BIAS 0x84               /* define the add-in bias for 16 bit samples */
+#define CLIP 32635
+
+static unsigned char
+isdn_audio_linear2ulaw(int sample)
+{
+	static int exp_lut[256] =
+	{
+		0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
+		4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+		5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+		5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+		6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+		6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+		6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+		6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+	};
+	int sign,
+	 exponent,
+	 mantissa;
+	unsigned char ulawbyte;
+
+	/* Get the sample into sign-magnitude. */
+	sign = (sample >> 8) & 0x80;	/* set aside the sign  */
+	if (sign != 0)
+		sample = -sample;	/* get magnitude       */
+	if (sample > CLIP)
+		sample = CLIP;  /* clip the magnitude  */
+
+	/* Convert from 16 bit linear to ulaw. */
+	sample = sample + BIAS;
+	exponent = exp_lut[(sample >> 7) & 0xFF];
+	mantissa = (sample >> (exponent + 3)) & 0x0F;
+	ulawbyte = ~(sign | (exponent << 4) | mantissa);
+#ifdef ZEROTRAP
+	/* optional CCITT trap */
+	if (ulawbyte == 0)
+		ulawbyte = 0x02;
+#endif
+	return (ulawbyte);
+}
+
+
+static int Mx[3][8] =
+{
+	{0x3800, 0x5600, 0, 0, 0, 0, 0, 0},
+	{0x399a, 0x3a9f, 0x4d14, 0x6607, 0, 0, 0, 0},
+	{0x3556, 0x3556, 0x399A, 0x3A9F, 0x4200, 0x4D14, 0x6607, 0x6607},
+};
+
+static int bitmask[9] =
+{
+	0, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff
+};
+
+static int
+isdn_audio_get_bits(adpcm_state * s, unsigned char **in, int *len)
+{
+	while (s->nleft < s->nbits) {
+		int d = *((*in)++);
+		(*len)--;
+		s->word = (s->word << 8) | d;
+		s->nleft += 8;
+	}
+	s->nleft -= s->nbits;
+	return (s->word >> s->nleft) & bitmask[s->nbits];
+}
+
+static void
+isdn_audio_put_bits(int data, int nbits, adpcm_state * s,
+		    unsigned char **out, int *len)
+{
+	s->word = (s->word << nbits) | (data & bitmask[nbits]);
+	s->nleft += nbits;
+	while (s->nleft >= 8) {
+		int d = (s->word >> (s->nleft - 8));
+		*(out[0]++) = d & 255;
+		(*len)++;
+		s->nleft -= 8;
+	}
+}
+
+adpcm_state *
+isdn_audio_adpcm_init(adpcm_state * s, int nbits)
+{
+	if (!s)
+		s = (adpcm_state *) kmalloc(sizeof(adpcm_state), GFP_ATOMIC);
+	if (s) {
+		s->a = 0;
+		s->d = 5;
+		s->word = 0;
+		s->nleft = 0;
+		s->nbits = nbits;
+	}
+	return s;
+}
+
+dtmf_state *
+isdn_audio_dtmf_init(dtmf_state * s)
+{
+	if (!s)
+		s = (dtmf_state *) kmalloc(sizeof(dtmf_state), GFP_ATOMIC);
+	if (s) {
+		s->idx = 0;
+		s->last = ' ';
+	}
+	return s;
+}
+
+/*
+ * Decompression of adpcm data to a/u-law
+ *
+ */
+
+int
+isdn_audio_adpcm2xlaw(adpcm_state * s, int fmt, unsigned char *in,
+		      unsigned char *out, int len)
+{
+	int a = s->a;
+	int d = s->d;
+	int nbits = s->nbits;
+	int olen = 0;
+
+	while (len) {
+		int e = isdn_audio_get_bits(s, &in, &len);
+		int sign;
+
+		if (nbits == 4 && e == 0)
+			d = 4;
+		sign = (e >> (nbits - 1)) ? -1 : 1;
+		e &= bitmask[nbits - 1];
+		a += sign * ((e << 1) + 1) * d >> 1;
+		if (d & 1)
+			a++;
+		if (fmt)
+			*out++ = isdn_audio_ulaw_to_alaw[
+					 isdn_audio_linear2ulaw(a << 2)];
+		else
+			*out++ = isdn_audio_linear2ulaw(a << 2);
+		olen++;
+		d = (d * Mx[nbits - 2][e] + 0x2000) >> 14;
+		if (d < 5)
+			d = 5;
+	}
+	s->a = a;
+	s->d = d;
+	return olen;
+}
+
+int
+isdn_audio_2adpcm_flush(adpcm_state * s, unsigned char *out)
+{
+	int olen = 0;
+
+	if (s->nleft)
+		isdn_audio_put_bits(0, 8 - s->nleft, s, &out, &olen);
+	return olen;
+}
+
+int
+isdn_audio_xlaw2adpcm(adpcm_state * s, int fmt, unsigned char *in,
+		      unsigned char *out, int len)
+{
+	int a = s->a;
+	int d = s->d;
+	int nbits = s->nbits;
+	int olen = 0;
+
+	while (len--) {
+		int e = 0,
+		 nmax = 1 << (nbits - 1);
+		int sign,
+		 delta;
+
+		if (fmt)
+			delta = (isdn_audio_alaw_to_s16[*in++] >> 2) - a;
+		else
+			delta = (isdn_audio_ulaw_to_s16[*in++] >> 2) - a;
+		if (delta < 0) {
+			e = nmax;
+			delta = -delta;
+		}
+		while (--nmax && delta > d) {
+			delta -= d;
+			e++;
+		}
+		if (nbits == 4 && ((e & 0x0f) == 0))
+			e = 8;
+		isdn_audio_put_bits(e, nbits, s, &out, &olen);
+		sign = (e >> (nbits - 1)) ? -1 : 1;
+		e &= bitmask[nbits - 1];
+
+		a += sign * ((e << 1) + 1) * d >> 1;
+		if (d & 1)
+			a++;
+		d = (d * Mx[nbits - 2][e] + 0x2000) >> 14;
+		if (d < 5)
+			d = 5;
+	}
+	s->a = a;
+	s->d = d;
+	return olen;
+}
+
+/*
+ * Goertzel algorithm.
+ * See http://ptolemy.eecs.berkeley.edu/~pino/Ptolemy/papers/96/dtmf_ict/
+ * for more info.
+ * Result is stored into an sk_buff and queued up for later
+ * evaluation.
+ */
+static void
+isdn_audio_goertzel(int *sample, modem_info * info)
+{
+	int sk,
+	 sk1,
+	 sk2;
+	int k,
+	 n;
+	struct sk_buff *skb;
+	int *result;
+
+	skb = dev_alloc_skb(sizeof(int) * NCOEFF);
+	if (!skb) {
+		printk(KERN_WARNING
+		  "isdn_audio: Could not alloc DTMF result for ttyI%d\n",
+		       info->line);
+		return;
+	}
+	result = (int *) skb_put(skb, sizeof(int) * NCOEFF);
+	for (k = 0; k < NCOEFF; k++) {
+		sk = sk1 = sk2 = 0;
+		for (n = 0; n < DTMF_NPOINTS; n++) {
+			sk = sample[n] + ((cos2pik[k] * sk1) >> 15) - sk2;
+			sk2 = sk1;
+			sk1 = sk;
+		}
+		result[k] =
+		    ((sk * sk) >> AMP_BITS) -
+		    ((((cos2pik[k] * sk) >> 15) * sk2) >> AMP_BITS) +
+		    ((sk2 * sk2) >> AMP_BITS);
+	}
+	skb_queue_tail(&info->dtmf_queue, skb);
+	isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
+}
+
+void
+isdn_audio_eval_dtmf(modem_info * info)
+{
+	struct sk_buff *skb;
+	int *result;
+	dtmf_state *s;
+	int silence;
+	int i;
+	int di;
+	int ch;
+	unsigned long flags;
+	int grp[2];
+	char what;
+	char *p;
+
+	while ((skb = skb_dequeue(&info->dtmf_queue))) {
+		result = (int *) skb->data;
+		s = info->dtmf_state;
+		grp[LOGRP] = grp[HIGRP] = -2;
+		silence = 0;
+		for (i = 0; i < 8; i++) {
+			if ((result[dtmf_tones[i].k] > DTMF_TRESH) &&
+			    (result[dtmf_tones[i].k2] < H2_TRESH))
+				grp[dtmf_tones[i].grp] = (grp[dtmf_tones[i].grp] == -2) ? i : -1;
+			else if ((result[dtmf_tones[i].k] < SILENCE_TRESH) &&
+			      (result[dtmf_tones[i].k2] < SILENCE_TRESH))
+				silence++;
+		}
+		if (silence == 8)
+			what = ' ';
+		else {
+			if ((grp[LOGRP] >= 0) && (grp[HIGRP] >= 0)) {
+				what = dtmf_matrix[grp[LOGRP]][grp[HIGRP] - 4];
+				if (s->last != ' ' && s->last != '.')
+					s->last = what;	/* min. 1 non-DTMF between DTMF */
+			} else
+				what = '.';
+		}
+		if ((what != s->last) && (what != ' ') && (what != '.')) {
+			printk(KERN_DEBUG "dtmf: tt='%c'\n", what);
+			p = skb->data;
+			*p++ = 0x10;
+			*p = what;
+			skb_trim(skb, 2);
+			if (skb_headroom(skb) < sizeof(isdn_audio_skb)) {
+				printk(KERN_WARNING
+				       "isdn_audio: insufficient skb_headroom, dropping\n");
+				kfree_skb(skb);
+				return;
+			}
+			ISDN_AUDIO_SKB_DLECOUNT(skb) = 0;
+			ISDN_AUDIO_SKB_LOCK(skb) = 0;
+			save_flags(flags);
+			cli();
+			di = info->isdn_driver;
+			ch = info->isdn_channel;
+			__skb_queue_tail(&dev->drv[di]->rpqueue[ch], skb);
+			dev->drv[di]->rcvcount[ch] += 2;
+			restore_flags(flags);
+			/* Schedule dequeuing */
+			if ((dev->modempoll) && (info->rcvsched))
+				isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
+			wake_up_interruptible(&dev->drv[di]->rcv_waitq[ch]);
+		} else
+			kfree_skb(skb);
+		s->last = what;
+	}
+}
+
+/*
+ * Decode DTMF tones, queue result in separate sk_buf for
+ * later examination.
+ * Parameters:
+ *   s    = pointer to state-struct.
+ *   buf  = input audio data
+ *   len  = size of audio data.
+ *   fmt  = audio data format (0 = ulaw, 1 = alaw)
+ */
+void
+isdn_audio_calc_dtmf(modem_info * info, unsigned char *buf, int len, int fmt)
+{
+	dtmf_state *s = info->dtmf_state;
+	int i;
+	int c;
+
+	while (len) {
+		c = DTMF_NPOINTS - s->idx;
+		if (c > len)
+			c = len;
+		if (c <= 0)
+			break;
+		for (i = 0; i < c; i++) {
+			if (fmt)
+				s->buf[s->idx++] =
+				    isdn_audio_alaw_to_s16[*buf++] >> (15 - AMP_BITS);
+			else
+				s->buf[s->idx++] =
+				    isdn_audio_ulaw_to_s16[*buf++] >> (15 - AMP_BITS);
+		}
+		if (s->idx == DTMF_NPOINTS) {
+			isdn_audio_goertzel(s->buf, info);
+			s->idx = 0;
+		}
+		len -= c;
+	}
+}
+
+silence_state *
+isdn_audio_silence_init(silence_state * s)
+{
+	if (!s)
+		s = (silence_state *) kmalloc(sizeof(silence_state), GFP_ATOMIC);
+	if (s) {
+		s->idx = 0;
+		s->state = 0;
+	}
+	return s;
+}
+
+void
+isdn_audio_calc_silence(modem_info * info, unsigned char *buf, int len, int fmt)
+{
+	silence_state *s = info->silence_state;
+	int i;
+	signed char c;
+
+	if (!info->emu.vpar[1]) return;
+
+	for (i = 0; i < len; i++) {
+		if (fmt)
+		    c = isdn_audio_alaw_to_ulaw[*buf++];
+			else
+		    c = *buf++;
+
+		if (c > 0) c -= 128;
+		c = abs(c);
+
+		if (c > (info->emu.vpar[1] * 4)) { 
+			s->idx = 0;
+			s->state = 1; 
+		} else {
+			if (s->idx < 210000) s->idx++; 
+		}
+	}
+}
+
+void
+isdn_audio_put_dle_code(modem_info * info, u_char code)
+{
+	struct sk_buff *skb;
+	unsigned long flags;
+	int di;
+	int ch;
+	char *p;
+
+	skb = dev_alloc_skb(2);
+	if (!skb) {
+		printk(KERN_WARNING
+		  "isdn_audio: Could not alloc skb for ttyI%d\n",
+		       info->line);
+		return;
+	}
+	p = (char *) skb_put(skb, 2);
+	p[0] = 0x10;
+	p[1] = code;
+	if (skb_headroom(skb) < sizeof(isdn_audio_skb)) {
+		printk(KERN_WARNING
+		       "isdn_audio: insufficient skb_headroom, dropping\n");
+		kfree_skb(skb);
+		return;
+	}
+	ISDN_AUDIO_SKB_DLECOUNT(skb) = 0;
+	ISDN_AUDIO_SKB_LOCK(skb) = 0;
+	save_flags(flags);
+	cli();
+	di = info->isdn_driver;
+	ch = info->isdn_channel;
+	__skb_queue_tail(&dev->drv[di]->rpqueue[ch], skb);
+	dev->drv[di]->rcvcount[ch] += 2;
+	restore_flags(flags);
+	/* Schedule dequeuing */
+	if ((dev->modempoll) && (info->rcvsched))
+		isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
+	wake_up_interruptible(&dev->drv[di]->rcv_waitq[ch]);
+}
+
+void
+isdn_audio_eval_silence(modem_info * info)
+{
+	silence_state *s = info->silence_state;
+	char what;
+
+	what = ' ';
+
+	if (s->idx > (info->emu.vpar[2] * 800)) { 
+		s->idx = 0;
+		if (!s->state) {	/* silence from beginning of rec */ 
+			what = 's';
+		} else {
+			what = 'q';
+		}
+	}
+		if ((what == 's') || (what == 'q')) {
+			printk(KERN_DEBUG "ttyI%d: %s\n", info->line,
+				(what=='s') ? "silence":"quiet");
+			isdn_audio_put_dle_code(info, what);
+		} 
+}
diff -Nru a/drivers/isdn/i4l/isdn_audio.h b/drivers/isdn/i4l/isdn_audio.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/i4l/isdn_audio.h	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,44 @@
+/* $Id: isdn_audio.h,v 1.9.6.1 2001/09/23 22:24:31 kai Exp $
+ *
+ * Linux ISDN subsystem, audio conversion and compression (linklevel).
+ *
+ * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#define DTMF_NPOINTS 205        /* Number of samples for DTMF recognition */
+typedef struct adpcm_state {
+	int a;
+	int d;
+	int word;
+	int nleft;
+	int nbits;
+} adpcm_state;
+
+typedef struct dtmf_state {
+	char last;
+	int idx;
+	int buf[DTMF_NPOINTS];
+} dtmf_state;
+
+typedef struct silence_state {
+	int state;
+	unsigned int idx;
+} silence_state;
+
+extern void isdn_audio_ulaw2alaw(unsigned char *, unsigned long);
+extern void isdn_audio_alaw2ulaw(unsigned char *, unsigned long);
+extern adpcm_state *isdn_audio_adpcm_init(adpcm_state *, int);
+extern int isdn_audio_adpcm2xlaw(adpcm_state *, int, unsigned char *, unsigned char *, int);
+extern int isdn_audio_xlaw2adpcm(adpcm_state *, int, unsigned char *, unsigned char *, int);
+extern int isdn_audio_2adpcm_flush(adpcm_state * s, unsigned char *out);
+extern void isdn_audio_calc_dtmf(modem_info *, unsigned char *, int, int);
+extern void isdn_audio_eval_dtmf(modem_info *);
+dtmf_state *isdn_audio_dtmf_init(dtmf_state *);
+extern void isdn_audio_calc_silence(modem_info *, unsigned char *, int, int);
+extern void isdn_audio_eval_silence(modem_info *);
+silence_state *isdn_audio_silence_init(silence_state *);
+extern void isdn_audio_put_dle_code(modem_info *, u_char);
diff -Nru a/drivers/isdn/i4l/isdn_bsdcomp.c b/drivers/isdn/i4l/isdn_bsdcomp.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/i4l/isdn_bsdcomp.c	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,940 @@
+/*
+ * BSD compression module
+ *
+ * Patched version for ISDN syncPPP written 1997/1998 by Michael Hipp
+ * The whole module is now SKB based.
+ *
+ */
+
+/*
+ * Update: The Berkeley copyright was changed, and the change 
+ * is retroactive to all "true" BSD software (ie everything
+ * from UCB as opposed to other peoples code that just carried
+ * the same license). The new copyright doesn't clash with the
+ * GPL, so the module-only restriction has been removed..
+ */
+
+/*
+ * Original copyright notice:
+ *
+ * Copyright (c) 1985, 1986 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * James A. Woods, derived from original work by Spencer Thomas
+ * and Joseph Orost.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/string.h>	/* used in new tty drivers */
+#include <linux/signal.h>	/* used in new tty drivers */
+
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/byteorder.h>
+#include <asm/types.h>
+
+#include <linux/if.h>
+
+#include <linux/if_ether.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/inet.h>
+#include <linux/ioctl.h>
+#include <linux/vmalloc.h>
+
+#include <linux/ppp_defs.h>
+
+#include <linux/isdn.h>
+#include <linux/isdn_ppp.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/if_arp.h>
+#include <linux/ppp-comp.h>
+
+#include "isdn_ppp.h"
+
+MODULE_DESCRIPTION("ISDN4Linux: BSD Compression for PPP over ISDN");
+MODULE_LICENSE("Dual BSD/GPL");
+
+#define BSD_VERSION(x)	((x) >> 5)
+#define BSD_NBITS(x)	((x) & 0x1F)
+
+#define BSD_CURRENT_VERSION	1
+
+#define DEBUG 1
+
+/*
+ * A dictionary for doing BSD compress.
+ */
+
+struct bsd_dict {
+	u32 fcode;
+	u16 codem1;		/* output of hash table -1 */
+	u16 cptr;		/* map code to hash table entry */
+};
+
+struct bsd_db {
+	int            totlen;		/* length of this structure */
+	unsigned int   hsize;		/* size of the hash table */
+	unsigned char  hshift;		/* used in hash function */
+	unsigned char  n_bits;		/* current bits/code */
+	unsigned char  maxbits;		/* maximum bits/code */
+	unsigned char  debug;		/* non-zero if debug desired */
+	unsigned char  unit;		/* ppp unit number */
+	u16 seqno;          		/* sequence # of next packet */
+	unsigned int   mru;		/* size of receive (decompress) bufr */
+	unsigned int   maxmaxcode;	/* largest valid code */
+	unsigned int   max_ent;		/* largest code in use */
+	unsigned int   in_count;	/* uncompressed bytes, aged */
+	unsigned int   bytes_out;	/* compressed bytes, aged */
+	unsigned int   ratio;		/* recent compression ratio */
+	unsigned int   checkpoint;	/* when to next check the ratio */
+	unsigned int   clear_count;	/* times dictionary cleared */
+	unsigned int   incomp_count;	/* incompressible packets */
+	unsigned int   incomp_bytes;	/* incompressible bytes */
+	unsigned int   uncomp_count;	/* uncompressed packets */
+	unsigned int   uncomp_bytes;	/* uncompressed bytes */
+	unsigned int   comp_count;	/* compressed packets */
+	unsigned int   comp_bytes;	/* compressed bytes */
+	unsigned short  *lens;		/* array of lengths of codes */
+	struct bsd_dict *dict;		/* dictionary */
+	int xmit;
+};
+
+#define BSD_OVHD	2		/* BSD compress overhead/packet */
+#define MIN_BSD_BITS	9
+#define BSD_INIT_BITS	MIN_BSD_BITS
+#define MAX_BSD_BITS	15
+
+/*
+ * the next two codes should not be changed lightly, as they must not
+ * lie within the contiguous general code space.
+ */
+#define CLEAR	256			/* table clear output code */
+#define FIRST	257			/* first free entry */
+#define LAST	255
+
+#define MAXCODE(b)	((1 << (b)) - 1)
+#define BADCODEM1	MAXCODE(MAX_BSD_BITS);
+
+#define BSD_HASH(prefix,suffix,hshift) ((((unsigned long)(suffix))<<(hshift)) \
+					 ^ (unsigned long)(prefix))
+#define BSD_KEY(prefix,suffix)		((((unsigned long)(suffix)) << 16) \
+					 + (unsigned long)(prefix))
+
+#define CHECK_GAP	10000		/* Ratio check interval */
+
+#define RATIO_SCALE_LOG	8
+#define RATIO_SCALE	(1<<RATIO_SCALE_LOG)
+#define RATIO_MAX	(0x7fffffff>>RATIO_SCALE_LOG)
+
+/*
+ * clear the dictionary
+ */
+
+static void bsd_clear(struct bsd_db *db)
+{
+	db->clear_count++;
+	db->max_ent      = FIRST-1;
+	db->n_bits       = BSD_INIT_BITS;
+	db->bytes_out    = 0;
+	db->in_count     = 0;
+	db->incomp_count = 0;
+	db->ratio	     = 0;
+	db->checkpoint   = CHECK_GAP;
+}
+
+/*
+ * If the dictionary is full, then see if it is time to reset it.
+ *
+ * Compute the compression ratio using fixed-point arithmetic
+ * with 8 fractional bits.
+ *
+ * Since we have an infinite stream instead of a single file,
+ * watch only the local compression ratio.
+ *
+ * Since both peers must reset the dictionary at the same time even in
+ * the absence of CLEAR codes (while packets are incompressible), they
+ * must compute the same ratio.
+ */
+static int bsd_check (struct bsd_db *db)	/* 1=output CLEAR */
+{
+    unsigned int new_ratio;
+
+    if (db->in_count >= db->checkpoint)
+      {
+	/* age the ratio by limiting the size of the counts */
+	if (db->in_count >= RATIO_MAX || db->bytes_out >= RATIO_MAX)
+	  {
+	    db->in_count  -= (db->in_count  >> 2);
+	    db->bytes_out -= (db->bytes_out >> 2);
+	  }
+	
+	db->checkpoint = db->in_count + CHECK_GAP;
+	
+	if (db->max_ent >= db->maxmaxcode)
+	  {
+	    /* Reset the dictionary only if the ratio is worse,
+	     * or if it looks as if it has been poisoned
+	     * by incompressible data.
+	     *
+	     * This does not overflow, because
+	     *	db->in_count <= RATIO_MAX.
+	     */
+
+	    new_ratio = db->in_count << RATIO_SCALE_LOG;
+	    if (db->bytes_out != 0)
+	      {
+		new_ratio /= db->bytes_out;
+	      }
+	    
+	    if (new_ratio < db->ratio || new_ratio < 1 * RATIO_SCALE)
+	      {
+		bsd_clear (db);
+		return 1;
+	      }
+	    db->ratio = new_ratio;
+	  }
+      }
+    return 0;
+}
+
+/*
+ * Return statistics.
+ */
+
+static void bsd_stats (void *state, struct compstat *stats)
+{
+	struct bsd_db *db = (struct bsd_db *) state;
+    
+	stats->unc_bytes    = db->uncomp_bytes;
+	stats->unc_packets  = db->uncomp_count;
+	stats->comp_bytes   = db->comp_bytes;
+	stats->comp_packets = db->comp_count;
+	stats->inc_bytes    = db->incomp_bytes;
+	stats->inc_packets  = db->incomp_count;
+	stats->in_count     = db->in_count;
+	stats->bytes_out    = db->bytes_out;
+}
+
+/*
+ * Reset state, as on a CCP ResetReq.
+ */
+static void bsd_reset (void *state,unsigned char code, unsigned char id,
+			unsigned char *data, unsigned len,
+			struct isdn_ppp_resetparams *rsparm)
+{
+	struct bsd_db *db = (struct bsd_db *) state;
+
+	bsd_clear(db);
+	db->seqno       = 0;
+	db->clear_count = 0;
+}
+
+/*
+ * Release the compression structure
+ */
+static void bsd_free (void *state)
+{
+	struct bsd_db *db = (struct bsd_db *) state;
+
+	if (db) {
+		/*
+		 * Release the dictionary
+		 */
+		if (db->dict) {
+			vfree (db->dict);
+			db->dict = NULL;
+		}
+
+		/*
+		 * Release the string buffer
+		 */
+		if (db->lens) {
+			vfree (db->lens);
+			db->lens = NULL;
+		}
+
+		/*
+		 * Finally release the structure itself.
+		 */
+		kfree (db);
+		MOD_DEC_USE_COUNT;
+	}
+}
+
+
+/*
+ * Allocate space for a (de) compressor.
+ */
+static void *bsd_alloc (struct isdn_ppp_comp_data *data)
+{
+	int bits;
+	unsigned int hsize, hshift, maxmaxcode;
+	struct bsd_db *db;
+	int decomp;
+
+	static unsigned int htab[][2] = {
+		{ 5003 , 4 } , { 5003 , 4 } , { 5003 , 4 } , { 5003 , 4 } , 
+		{ 9001 , 5 } , { 18013 , 6 } , { 35023 , 7 } , { 69001 , 8 } 
+	};
+		
+	if (data->optlen != 1 || data->num != CI_BSD_COMPRESS
+		|| BSD_VERSION(data->options[0]) != BSD_CURRENT_VERSION)
+		return NULL;
+
+	bits = BSD_NBITS(data->options[0]);
+
+	if(bits < 9 || bits > 15)
+		return NULL;
+
+	hsize = htab[bits-9][0];
+	hshift = htab[bits-9][1];
+	
+	/*
+	 * Allocate the main control structure for this instance.
+	 */
+	maxmaxcode = MAXCODE(bits);
+	db = (struct bsd_db *) kmalloc (sizeof (struct bsd_db),GFP_KERNEL);
+	if (!db)
+		return NULL;
+
+	memset (db, 0, sizeof(struct bsd_db));
+
+	db->xmit = data->flags & IPPP_COMP_FLAG_XMIT;
+	decomp = db->xmit ? 0 : 1;
+
+	/*
+	 * Allocate space for the dictionary. This may be more than one page in
+	 * length.
+	 */
+	db->dict = (struct bsd_dict *) vmalloc (hsize * sizeof (struct bsd_dict));
+	if (!db->dict) {
+		bsd_free (db);
+		return NULL;
+	}
+
+	MOD_INC_USE_COUNT;
+
+	/*
+	 * If this is the compression buffer then there is no length data.
+	 * For decompression, the length information is needed as well.
+	 */
+	if (!decomp)
+		db->lens = NULL;
+	else {
+		db->lens = (unsigned short *) vmalloc ((maxmaxcode + 1) *
+			sizeof (db->lens[0]));
+		if (!db->lens) {
+			bsd_free (db); /* calls MOD_DEC_USE_COUNT; */
+			return (NULL);
+		}
+	}
+
+	/*
+	 * Initialize the data information for the compression code
+	 */
+	db->totlen     = sizeof (struct bsd_db) + (sizeof (struct bsd_dict) * hsize);
+	db->hsize      = hsize;
+	db->hshift     = hshift;
+	db->maxmaxcode = maxmaxcode;
+	db->maxbits    = bits;
+
+	return (void *) db;
+}
+
+/*
+ * Initialize the database.
+ */
+static int bsd_init (void *state, struct isdn_ppp_comp_data *data, int unit, int debug)
+{
+	struct bsd_db *db = state;
+	int indx;
+	int decomp;
+
+	if(!state || !data) {
+		printk(KERN_ERR "isdn_bsd_init: [%d] ERR, state %lx data %lx\n",unit,(long)state,(long)data);
+		return 0;
+	}
+
+	decomp = db->xmit ? 0 : 1;
+    
+	if (data->optlen != 1 || data->num != CI_BSD_COMPRESS
+		|| (BSD_VERSION(data->options[0]) != BSD_CURRENT_VERSION)
+		|| (BSD_NBITS(data->options[0]) != db->maxbits)
+		|| (decomp && db->lens == NULL)) {
+		printk(KERN_ERR "isdn_bsd: %d %d %d %d %lx\n",data->optlen,data->num,data->options[0],decomp,(unsigned long)db->lens);
+		return 0;
+	}
+
+	if (decomp)
+		for(indx=LAST;indx>=0;indx--)
+			db->lens[indx] = 1;
+
+	indx = db->hsize;
+	while (indx-- != 0) {
+		db->dict[indx].codem1 = BADCODEM1;
+		db->dict[indx].cptr   = 0;
+	}
+
+	db->unit = unit;
+	db->mru  = 0;
+
+	db->debug = 1;
+    
+	bsd_reset(db,0,0,NULL,0,NULL);
+    
+	return 1;
+}
+
+/*
+ * Obtain pointers to the various structures in the compression tables
+ */
+
+#define dict_ptrx(p,idx) &(p->dict[idx])
+#define lens_ptrx(p,idx) &(p->lens[idx])
+
+#ifdef DEBUG
+static unsigned short *lens_ptr(struct bsd_db *db, int idx)
+{
+	if ((unsigned int) idx > (unsigned int) db->maxmaxcode) {
+		printk (KERN_DEBUG "<9>ppp: lens_ptr(%d) > max\n", idx);
+		idx = 0;
+	}
+	return lens_ptrx (db, idx);
+}
+
+static struct bsd_dict *dict_ptr(struct bsd_db *db, int idx)
+{
+	if ((unsigned int) idx >= (unsigned int) db->hsize) {
+		printk (KERN_DEBUG "<9>ppp: dict_ptr(%d) > max\n", idx);
+		idx = 0;
+	}
+	return dict_ptrx (db, idx);
+}
+
+#else
+#define lens_ptr(db,idx) lens_ptrx(db,idx)
+#define dict_ptr(db,idx) dict_ptrx(db,idx)
+#endif
+
+/*
+ * compress a packet
+ */
+static int bsd_compress (void *state, struct sk_buff *skb_in, struct sk_buff *skb_out,int proto)
+{
+	struct bsd_db *db;
+	int hshift;
+	unsigned int max_ent;
+	unsigned int n_bits;
+	unsigned int bitno;
+	unsigned long accm;
+	int ent;
+	unsigned long fcode;
+	struct bsd_dict *dictp;
+	unsigned char c;
+	int hval,disp,ilen,mxcode;
+	unsigned char *rptr = skb_in->data;
+	int isize = skb_in->len;
+
+#define OUTPUT(ent)			\
+  {					\
+    bitno -= n_bits;			\
+    accm |= ((ent) << bitno);		\
+    do	{				\
+        if(skb_out && skb_tailroom(skb_out) > 0) 	\
+      		*(skb_put(skb_out,1)) = (unsigned char) (accm>>24); \
+	accm <<= 8;			\
+	bitno += 8;			\
+    } while (bitno <= 24);		\
+  }
+
+	/*
+	 * If the protocol is not in the range we're interested in,
+	 * just return without compressing the packet.  If it is,
+	 * the protocol becomes the first byte to compress.
+	 */
+	printk(KERN_DEBUG "bsd_compress called with %x\n",proto);
+	
+	ent = proto;
+	if (proto < 0x21 || proto > 0xf9 || !(proto & 0x1) )
+		return 0;
+
+	db      = (struct bsd_db *) state;
+	hshift  = db->hshift;
+	max_ent = db->max_ent;
+	n_bits  = db->n_bits;
+	bitno   = 32;
+	accm    = 0;
+	mxcode  = MAXCODE (n_bits);
+	
+	/* This is the PPP header information */
+	if(skb_out && skb_tailroom(skb_out) >= 2) {
+		char *v = skb_put(skb_out,2);
+		/* we only push our own data on the header,
+		  AC,PC and protos is pushed by caller  */
+		v[0] = db->seqno >> 8;
+		v[1] = db->seqno;
+	}
+
+	ilen   = ++isize; /* This is off by one, but that is what is in draft! */
+
+	while (--ilen > 0) {
+		c     = *rptr++;
+		fcode = BSD_KEY  (ent, c);
+		hval  = BSD_HASH (ent, c, hshift);
+		dictp = dict_ptr (db, hval);
+	
+		/* Validate and then check the entry. */
+		if (dictp->codem1 >= max_ent)
+			goto nomatch;
+
+		if (dictp->fcode == fcode) {
+			ent = dictp->codem1 + 1;
+			continue;	/* found (prefix,suffix) */
+		}
+	
+		/* continue probing until a match or invalid entry */
+		disp = (hval == 0) ? 1 : hval;
+
+		do {
+			hval += disp;
+			if (hval >= db->hsize)
+				hval -= db->hsize;
+			dictp = dict_ptr (db, hval);
+			if (dictp->codem1 >= max_ent)
+				goto nomatch;
+		} while (dictp->fcode != fcode);
+
+		ent = dictp->codem1 + 1;	/* finally found (prefix,suffix) */
+		continue;
+	
+nomatch:
+		OUTPUT(ent);		/* output the prefix */
+	
+		/* code -> hashtable */
+		if (max_ent < db->maxmaxcode) {
+			struct bsd_dict *dictp2;
+			struct bsd_dict *dictp3;
+			int indx;
+
+			/* expand code size if needed */
+			if (max_ent >= mxcode) {
+				db->n_bits = ++n_bits;
+				mxcode = MAXCODE (n_bits);
+			}
+	    
+			/* 
+			 * Invalidate old hash table entry using
+			 * this code, and then take it over.
+			 */
+			dictp2 = dict_ptr (db, max_ent + 1);
+			indx   = dictp2->cptr;
+			dictp3 = dict_ptr (db, indx);
+
+			if (dictp3->codem1 == max_ent)
+				dictp3->codem1 = BADCODEM1;
+
+			dictp2->cptr   = hval;
+			dictp->codem1  = max_ent;
+			dictp->fcode = fcode;
+			db->max_ent    = ++max_ent;
+
+			if (db->lens) {
+				unsigned short *len1 = lens_ptr (db, max_ent);
+				unsigned short *len2 = lens_ptr (db, ent);
+				*len1 = *len2 + 1;
+			}
+		}
+		ent = c;
+	}
+    
+	OUTPUT(ent);		/* output the last code */
+
+	if(skb_out)
+		db->bytes_out    += skb_out->len; /* Do not count bytes from here */
+	db->uncomp_bytes += isize;
+	db->in_count     += isize;
+	++db->uncomp_count;
+	++db->seqno;
+
+	if (bitno < 32)
+		++db->bytes_out; /* must be set before calling bsd_check */
+
+	/*
+	 * Generate the clear command if needed
+	 */
+
+	if (bsd_check(db))
+		OUTPUT (CLEAR);
+
+	/*
+	 * Pad dribble bits of last code with ones.
+	 * Do not emit a completely useless byte of ones.
+	 */
+	if (bitno < 32 && skb_out && skb_tailroom(skb_out) > 0) 
+		*(skb_put(skb_out,1)) = (unsigned char) ((accm | (0xff << (bitno-8))) >> 24);
+    
+	/*
+	 * Increase code size if we would have without the packet
+	 * boundary because the decompressor will do so.
+	 */
+	if (max_ent >= mxcode && max_ent < db->maxmaxcode)
+		db->n_bits++;
+
+	/* If output length is too large then this is an incompressible frame. */
+	if (!skb_out || (skb_out && skb_out->len >= skb_in->len) ) {
+		++db->incomp_count;
+		db->incomp_bytes += isize;
+		return 0;
+	}
+
+	/* Count the number of compressed frames */
+	++db->comp_count;
+	db->comp_bytes += skb_out->len;
+	return skb_out->len;
+
+#undef OUTPUT
+}
+
+/*
+ * Update the "BSD Compress" dictionary on the receiver for
+ * incompressible data by pretending to compress the incoming data.
+ */
+static void bsd_incomp (void *state, struct sk_buff *skb_in,int proto)
+{
+	bsd_compress (state, skb_in, NULL, proto);
+}
+
+/*
+ * Decompress "BSD Compress".
+ */
+static int bsd_decompress (void *state, struct sk_buff *skb_in, struct sk_buff *skb_out,
+			   struct isdn_ppp_resetparams *rsparm)
+{
+	struct bsd_db *db;
+	unsigned int max_ent;
+	unsigned long accm;
+	unsigned int bitno;		/* 1st valid bit in accm */
+	unsigned int n_bits;
+	unsigned int tgtbitno;	/* bitno when we have a code */
+	struct bsd_dict *dictp;
+	int seq;
+	unsigned int incode;
+	unsigned int oldcode;
+	unsigned int finchar;
+	unsigned char *p,*ibuf;
+	int ilen;
+	int codelen;
+	int extra;
+
+	db       = (struct bsd_db *) state;
+	max_ent  = db->max_ent;
+	accm     = 0;
+	bitno    = 32;		/* 1st valid bit in accm */
+	n_bits   = db->n_bits;
+	tgtbitno = 32 - n_bits;	/* bitno when we have a code */
+
+	printk(KERN_DEBUG "bsd_decompress called\n");
+
+	if(!skb_in || !skb_out) {
+		printk(KERN_ERR "bsd_decompress called with NULL parameter\n");
+		return DECOMP_ERROR;
+	}
+    
+	/*
+	 * Get the sequence number.
+	 */
+	if( (p = skb_pull(skb_in,2)) == NULL) {
+		return DECOMP_ERROR;
+	}
+	p-=2;
+	seq   = (p[0] << 8) + p[1];
+	ilen  = skb_in->len;
+	ibuf = skb_in->data;
+
+	/*
+	 * Check the sequence number and give up if it differs from
+	 * the value we're expecting.
+	 */
+	if (seq != db->seqno) {
+		if (db->debug) {
+			printk(KERN_DEBUG "bsd_decomp%d: bad sequence # %d, expected %d\n",
+				db->unit, seq, db->seqno - 1);
+		}
+		return DECOMP_ERROR;
+	}
+
+	++db->seqno;
+	db->bytes_out += ilen;
+
+	if(skb_tailroom(skb_out) > 0)
+		*(skb_put(skb_out,1)) = 0;
+	else
+		return DECOMP_ERR_NOMEM;
+    
+	oldcode = CLEAR;
+
+	/*
+	 * Keep the checkpoint correctly so that incompressible packets
+	 * clear the dictionary at the proper times.
+	 */
+
+	for (;;) {
+		if (ilen-- <= 0) {
+			db->in_count += (skb_out->len - 1); /* don't count the header */
+			break;
+		}
+
+		/*
+		 * Accumulate bytes until we have a complete code.
+		 * Then get the next code, relying on the 32-bit,
+		 * unsigned accm to mask the result.
+		 */
+
+		bitno -= 8;
+		accm  |= *ibuf++ << bitno;
+		if (tgtbitno < bitno)
+			continue;
+
+		incode = accm >> tgtbitno;
+		accm <<= n_bits;
+		bitno += n_bits;
+
+		/*
+		 * The dictionary must only be cleared at the end of a packet.
+		 */
+	
+		if (incode == CLEAR) {
+			if (ilen > 0) {
+				if (db->debug)
+					printk(KERN_DEBUG "bsd_decomp%d: bad CLEAR\n", db->unit);
+				return DECOMP_FATALERROR;	/* probably a bug */
+			}
+			bsd_clear(db);
+			break;
+		}
+
+		if ((incode > max_ent + 2) || (incode > db->maxmaxcode)
+			|| (incode > max_ent && oldcode == CLEAR)) {
+			if (db->debug) {
+				printk(KERN_DEBUG "bsd_decomp%d: bad code 0x%x oldcode=0x%x ",
+					db->unit, incode, oldcode);
+				printk(KERN_DEBUG "max_ent=0x%x skb->Len=%d seqno=%d\n",
+					max_ent, skb_out->len, db->seqno);
+			}
+			return DECOMP_FATALERROR;	/* probably a bug */
+		}
+	
+		/* Special case for KwKwK string. */
+		if (incode > max_ent) {
+			finchar = oldcode;
+			extra   = 1;
+		} else {
+			finchar = incode;
+			extra   = 0;
+		}
+
+		codelen = *(lens_ptr (db, finchar));
+		if( skb_tailroom(skb_out) < codelen + extra) {
+			if (db->debug) {
+				printk(KERN_DEBUG "bsd_decomp%d: ran out of mru\n", db->unit);
+#ifdef DEBUG
+				printk(KERN_DEBUG "  len=%d, finchar=0x%x, codelen=%d,skblen=%d\n",
+					ilen, finchar, codelen, skb_out->len);
+#endif
+			}
+			return DECOMP_FATALERROR;
+		}
+
+		/*
+		 * Decode this code and install it in the decompressed buffer.
+		 */
+
+		p     = skb_put(skb_out,codelen);
+		p += codelen;
+		while (finchar > LAST) {
+			struct bsd_dict *dictp2 = dict_ptr (db, finchar);
+	    
+			dictp = dict_ptr (db, dictp2->cptr);
+
+#ifdef DEBUG
+			if (--codelen <= 0 || dictp->codem1 != finchar-1) {
+				if (codelen <= 0) {
+					printk(KERN_ERR "bsd_decomp%d: fell off end of chain ", db->unit);
+					printk(KERN_ERR "0x%x at 0x%x by 0x%x, max_ent=0x%x\n", incode, finchar, dictp2->cptr, max_ent);
+				} else {
+					if (dictp->codem1 != finchar-1) {
+						printk(KERN_ERR "bsd_decomp%d: bad code chain 0x%x finchar=0x%x ",db->unit, incode, finchar);
+						printk(KERN_ERR "oldcode=0x%x cptr=0x%x codem1=0x%x\n", oldcode, dictp2->cptr, dictp->codem1);
+					}
+				}
+				return DECOMP_FATALERROR;
+			}
+#endif
+
+			{
+				u32 fcode = dictp->fcode;
+				*--p    = (fcode >> 16) & 0xff;
+				finchar = fcode & 0xffff;
+			}
+		}
+		*--p = finchar;
+	
+#ifdef DEBUG
+		if (--codelen != 0)
+			printk(KERN_ERR "bsd_decomp%d: short by %d after code 0x%x, max_ent=0x%x\n", db->unit, codelen, incode, max_ent);
+#endif
+	
+		if (extra)		/* the KwKwK case again */
+			*(skb_put(skb_out,1)) = finchar;
+	
+		/*
+		 * If not first code in a packet, and
+		 * if not out of code space, then allocate a new code.
+		 *
+		 * Keep the hash table correct so it can be used
+		 * with uncompressed packets.
+		 */
+		if (oldcode != CLEAR && max_ent < db->maxmaxcode) {
+			struct bsd_dict *dictp2, *dictp3;
+			u16  *lens1,  *lens2;
+			unsigned long fcode;
+			int hval, disp, indx;
+	    
+			fcode = BSD_KEY(oldcode,finchar);
+			hval  = BSD_HASH(oldcode,finchar,db->hshift);
+			dictp = dict_ptr (db, hval);
+	    
+			/* look for a free hash table entry */
+			if (dictp->codem1 < max_ent) {
+				disp = (hval == 0) ? 1 : hval;
+				do {
+					hval += disp;
+					if (hval >= db->hsize)
+						hval -= db->hsize;
+					dictp = dict_ptr (db, hval);
+				} while (dictp->codem1 < max_ent);
+			}
+	    
+			/*
+			 * Invalidate previous hash table entry
+			 * assigned this code, and then take it over
+			 */
+
+			dictp2 = dict_ptr (db, max_ent + 1);
+			indx   = dictp2->cptr;
+			dictp3 = dict_ptr (db, indx);
+
+			if (dictp3->codem1 == max_ent)
+				dictp3->codem1 = BADCODEM1;
+
+			dictp2->cptr   = hval;
+			dictp->codem1  = max_ent;
+			dictp->fcode = fcode;
+			db->max_ent    = ++max_ent;
+
+			/* Update the length of this string. */
+			lens1  = lens_ptr (db, max_ent);
+			lens2  = lens_ptr (db, oldcode);
+			*lens1 = *lens2 + 1;
+	    
+			/* Expand code size if needed. */
+			if (max_ent >= MAXCODE(n_bits) && max_ent < db->maxmaxcode) {
+				db->n_bits = ++n_bits;
+				tgtbitno   = 32-n_bits;
+			}
+		}
+		oldcode = incode;
+	}
+
+	++db->comp_count;
+	++db->uncomp_count;
+	db->comp_bytes   += skb_in->len - BSD_OVHD;
+	db->uncomp_bytes += skb_out->len;
+
+	if (bsd_check(db)) {
+		if (db->debug)
+			printk(KERN_DEBUG "bsd_decomp%d: peer should have cleared dictionary on %d\n",
+				db->unit, db->seqno - 1);
+	}
+	return skb_out->len;
+}
+
+/*************************************************************
+ * Table of addresses for the BSD compression module
+ *************************************************************/
+
+static struct isdn_ppp_compressor ippp_bsd_compress = {
+	NULL,NULL,		/* prev,next: overwritten by isdn_ppp */
+	CI_BSD_COMPRESS,	/* compress_proto */
+	bsd_alloc,		/* alloc */
+	bsd_free,		/* free */
+	bsd_init,		/* init */
+	bsd_reset,		/* reset */
+	bsd_compress,		/* compress */
+	bsd_decompress,		/* decompress */
+	bsd_incomp,		/* incomp */
+	bsd_stats		/* comp_stat */
+};
+
+/*************************************************************
+ * Module support routines
+ *************************************************************/
+
+static int __init isdn_bsdcomp_init(void)
+{
+	int answer = isdn_ppp_register_compressor (&ippp_bsd_compress);
+	if (answer == 0)
+		printk (KERN_INFO "PPP BSD Compression module registered\n");
+	return answer;
+}
+
+static void __exit isdn_bsdcomp_exit(void)
+{
+	isdn_ppp_unregister_compressor (&ippp_bsd_compress);
+}
+
+module_init(isdn_bsdcomp_init);
+module_exit(isdn_bsdcomp_exit);
diff -Nru a/drivers/isdn/i4l/isdn_common.c b/drivers/isdn/i4l/isdn_common.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/i4l/isdn_common.c	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,2371 @@
+/* $Id: isdn_common.c,v 1.114.6.16 2001/11/06 20:58:28 kai Exp $
+ *
+ * Linux ISDN subsystem, common used functions (linklevel).
+ *
+ * Copyright 1994-1999  by Fritz Elfert (fritz@isdn4linux.de)
+ * Copyright 1995,96    Thinking Objects Software GmbH Wuerzburg
+ * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/poll.h>
+#include <linux/vmalloc.h>
+#include <linux/isdn.h>
+#include <linux/smp_lock.h>
+#include "isdn_common.h"
+#include "isdn_tty.h"
+#include "isdn_net.h"
+#include "isdn_ppp.h"
+#ifdef CONFIG_ISDN_AUDIO
+#include "isdn_audio.h"
+#endif
+#ifdef CONFIG_ISDN_DIVERSION_MODULE
+#define CONFIG_ISDN_DIVERSION
+#endif
+#ifdef CONFIG_ISDN_DIVERSION
+#include <linux/isdn_divertif.h>
+#endif /* CONFIG_ISDN_DIVERSION */
+#include "isdn_v110.h"
+#include <linux/devfs_fs_kernel.h>
+
+/* Debugflags */
+#undef ISDN_DEBUG_STATCALLB
+
+MODULE_DESCRIPTION("ISDN4Linux: link layer");
+MODULE_AUTHOR("Fritz Elfert");
+MODULE_LICENSE("GPL");
+
+isdn_dev *dev;
+
+static char *isdn_revision = "$Revision: 1.114.6.16 $";
+
+extern char *isdn_net_revision;
+extern char *isdn_tty_revision;
+#ifdef CONFIG_ISDN_PPP
+extern char *isdn_ppp_revision;
+#else
+static char *isdn_ppp_revision = ": none $";
+#endif
+#ifdef CONFIG_ISDN_AUDIO
+extern char *isdn_audio_revision;
+#else
+static char *isdn_audio_revision = ": none $";
+#endif
+extern char *isdn_v110_revision;
+
+#ifdef CONFIG_ISDN_DIVERSION
+static isdn_divert_if *divert_if; /* = NULL */
+#endif /* CONFIG_ISDN_DIVERSION */
+
+
+static void set_global_features(void);
+static void isdn_register_devfs(int);
+static void isdn_unregister_devfs(int);
+static int isdn_wildmat(char *s, char *p);
+
+void
+isdn_lock_drivers(void)
+{
+	int i;
+
+	for (i = 0; i < dev->drivers; i++) {
+		isdn_ctrl cmd;
+
+		cmd.driver = i;
+		cmd.arg = 0;
+		cmd.command = ISDN_CMD_LOCK;
+		isdn_command(&cmd);
+		dev->drv[i]->locks++;
+	}
+}
+
+void
+isdn_MOD_INC_USE_COUNT(void)
+{
+	MOD_INC_USE_COUNT;
+	isdn_lock_drivers();
+}
+
+void
+isdn_unlock_drivers(void)
+{
+	int i;
+
+	for (i = 0; i < dev->drivers; i++)
+		if (dev->drv[i]->locks > 0) {
+			isdn_ctrl cmd;
+
+			cmd.driver = i;
+			cmd.arg = 0;
+			cmd.command = ISDN_CMD_UNLOCK;
+			isdn_command(&cmd);
+			dev->drv[i]->locks--;
+		}
+}
+
+void
+isdn_MOD_DEC_USE_COUNT(void)
+{
+	MOD_DEC_USE_COUNT;
+	isdn_unlock_drivers();
+}
+
+#if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP)
+void
+isdn_dumppkt(char *s, u_char * p, int len, int dumplen)
+{
+	int dumpc;
+
+	printk(KERN_DEBUG "%s(%d) ", s, len);
+	for (dumpc = 0; (dumpc < dumplen) && (len); len--, dumpc++)
+		printk(" %02x", *p++);
+	printk("\n");
+}
+#endif
+
+/*
+ * I picked the pattern-matching-functions from an old GNU-tar version (1.10)
+ * It was originally written and put to PD by rs@mirror.TMC.COM (Rich Salz)
+ */
+static int
+isdn_star(char *s, char *p)
+{
+	while (isdn_wildmat(s, p)) {
+		if (*++s == '\0')
+			return (2);
+	}
+	return (0);
+}
+
+/*
+ * Shell-type Pattern-matching for incoming caller-Ids
+ * This function gets a string in s and checks, if it matches the pattern
+ * given in p.
+ *
+ * Return:
+ *   0 = match.
+ *   1 = no match.
+ *   2 = no match. Would eventually match, if s would be longer.
+ *
+ * Possible Patterns:
+ *
+ * '?'     matches one character
+ * '*'     matches zero or more characters
+ * [xyz]   matches the set of characters in brackets.
+ * [^xyz]  matches any single character not in the set of characters
+ */
+
+static int
+isdn_wildmat(char *s, char *p)
+{
+	register int last;
+	register int matched;
+	register int reverse;
+	register int nostar = 1;
+
+	if (!(*s) && !(*p))
+		return(1);
+	for (; *p; s++, p++)
+		switch (*p) {
+			case '\\':
+				/*
+				 * Literal match with following character,
+				 * fall through.
+				 */
+				p++;
+			default:
+				if (*s != *p)
+					return (*s == '\0')?2:1;
+				continue;
+			case '?':
+				/* Match anything. */
+				if (*s == '\0')
+					return (2);
+				continue;
+			case '*':
+				nostar = 0;	
+				/* Trailing star matches everything. */
+				return (*++p ? isdn_star(s, p) : 0);
+			case '[':
+				/* [^....] means inverse character class. */
+				if ((reverse = (p[1] == '^')))
+					p++;
+				for (last = 0, matched = 0; *++p && (*p != ']'); last = *p)
+					/* This next line requires a good C compiler. */
+					if (*p == '-' ? *s <= *++p && *s >= last : *s == *p)
+						matched = 1;
+				if (matched == reverse)
+					return (1);
+				continue;
+		}
+	return (*s == '\0')?0:nostar;
+}
+
+int isdn_msncmp( const char * msn1, const char * msn2 )
+{
+	char TmpMsn1[ ISDN_MSNLEN ];
+	char TmpMsn2[ ISDN_MSNLEN ];
+	char *p;
+
+	for ( p = TmpMsn1; *msn1 && *msn1 != ':'; )  // Strip off a SPID
+		*p++ = *msn1++;
+	*p = '\0';
+
+	for ( p = TmpMsn2; *msn2 && *msn2 != ':'; )  // Strip off a SPID
+		*p++ = *msn2++;
+	*p = '\0';
+
+	return isdn_wildmat( TmpMsn1, TmpMsn2 );
+}
+
+int
+isdn_dc2minor(int di, int ch)
+{
+	int i;
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+		if (dev->chanmap[i] == ch && dev->drvmap[i] == di)
+			return i;
+	return -1;
+}
+
+static int isdn_timer_cnt1 = 0;
+static int isdn_timer_cnt2 = 0;
+static int isdn_timer_cnt3 = 0;
+
+static void
+isdn_timer_funct(ulong dummy)
+{
+	int tf = dev->tflags;
+	if (tf & ISDN_TIMER_FAST) {
+		if (tf & ISDN_TIMER_MODEMREAD)
+			isdn_tty_readmodem();
+		if (tf & ISDN_TIMER_MODEMPLUS)
+			isdn_tty_modem_escape();
+		if (tf & ISDN_TIMER_MODEMXMIT)
+			isdn_tty_modem_xmit();
+	}
+	if (tf & ISDN_TIMER_SLOW) {
+		if (++isdn_timer_cnt1 >= ISDN_TIMER_02SEC) {
+			isdn_timer_cnt1 = 0;
+			if (tf & ISDN_TIMER_NETDIAL)
+				isdn_net_dial();
+		}
+		if (++isdn_timer_cnt2 >= ISDN_TIMER_1SEC) {
+			isdn_timer_cnt2 = 0;
+			if (tf & ISDN_TIMER_NETHANGUP)
+				isdn_net_autohup();
+			if (++isdn_timer_cnt3 >= ISDN_TIMER_RINGING) {
+				isdn_timer_cnt3 = 0;
+				if (tf & ISDN_TIMER_MODEMRING)
+					isdn_tty_modem_ring();
+			}
+			if (tf & ISDN_TIMER_CARRIER)
+				isdn_tty_carrier_timeout();
+		}
+	}
+	if (tf) 
+	{
+		unsigned long flags;
+
+		save_flags(flags);
+		cli();
+		mod_timer(&dev->timer, jiffies+ISDN_TIMER_RES);
+		restore_flags(flags);
+	}
+}
+
+void
+isdn_timer_ctrl(int tf, int onoff)
+{
+	unsigned long flags;
+	int old_tflags;
+
+	save_flags(flags);
+	cli();
+	if ((tf & ISDN_TIMER_SLOW) && (!(dev->tflags & ISDN_TIMER_SLOW))) {
+		/* If the slow-timer wasn't activated until now */
+		isdn_timer_cnt1 = 0;
+		isdn_timer_cnt2 = 0;
+	}
+	old_tflags = dev->tflags;
+	if (onoff)
+		dev->tflags |= tf;
+	else
+		dev->tflags &= ~tf;
+	if (dev->tflags && !old_tflags)
+		mod_timer(&dev->timer, jiffies+ISDN_TIMER_RES);
+	restore_flags(flags);
+}
+
+/*
+ * Receive a packet from B-Channel. (Called from low-level-module)
+ */
+static void
+isdn_receive_skb_callback(int di, int channel, struct sk_buff *skb)
+{
+	int i;
+
+	if ((i = isdn_dc2minor(di, channel)) == -1) {
+		dev_kfree_skb(skb);
+		return;
+	}
+	/* Update statistics */
+	dev->ibytes[i] += skb->len;
+	
+	/* First, try to deliver data to network-device */
+	if (isdn_net_rcv_skb(i, skb))
+		return;
+
+	/* V.110 handling
+	 * makes sense for async streams only, so it is
+	 * called after possible net-device delivery.
+	 */
+	if (dev->v110[i]) {
+		atomic_inc(&dev->v110use[i]);
+		skb = isdn_v110_decode(dev->v110[i], skb);
+		atomic_dec(&dev->v110use[i]);
+		if (!skb)
+			return;
+	}
+
+	/* No network-device found, deliver to tty or raw-channel */
+	if (skb->len) {
+		if (isdn_tty_rcv_skb(i, di, channel, skb))
+			return;
+		wake_up_interruptible(&dev->drv[di]->rcv_waitq[channel]);
+	} else
+		dev_kfree_skb(skb);
+}
+
+/*
+ * Intercept command from Linklevel to Lowlevel.
+ * If layer 2 protocol is V.110 and this is not supported by current
+ * lowlevel-driver, use driver's transparent mode and handle V.110 in
+ * linklevel instead.
+ */
+int
+isdn_command(isdn_ctrl *cmd)
+{
+	if (cmd->driver == -1) {
+		printk(KERN_WARNING "isdn_command command(%x) driver -1\n", cmd->command);
+		return(1);
+	}
+	if (cmd->command == ISDN_CMD_SETL2) {
+		int idx = isdn_dc2minor(cmd->driver, cmd->arg & 255);
+		unsigned long l2prot = (cmd->arg >> 8) & 255;
+		unsigned long features = (dev->drv[cmd->driver]->interface->features
+						>> ISDN_FEATURE_L2_SHIFT) &
+						ISDN_FEATURE_L2_MASK;
+		unsigned long l2_feature = (1 << l2prot);
+
+		switch (l2prot) {
+			case ISDN_PROTO_L2_V11096:
+			case ISDN_PROTO_L2_V11019:
+			case ISDN_PROTO_L2_V11038:
+			/* If V.110 requested, but not supported by
+			 * HL-driver, set emulator-flag and change
+			 * Layer-2 to transparent
+			 */
+				if (!(features & l2_feature)) {
+					dev->v110emu[idx] = l2prot;
+					cmd->arg = (cmd->arg & 255) |
+						(ISDN_PROTO_L2_TRANS << 8);
+				} else
+					dev->v110emu[idx] = 0;
+		}
+	}
+	return dev->drv[cmd->driver]->interface->command(cmd);
+}
+
+void
+isdn_all_eaz(int di, int ch)
+{
+	isdn_ctrl cmd;
+
+	if (di < 0)
+		return;
+	cmd.driver = di;
+	cmd.arg = ch;
+	cmd.command = ISDN_CMD_SETEAZ;
+	cmd.parm.num[0] = '\0';
+	isdn_command(&cmd);
+}
+
+/*
+ * Begin of a CAPI like LL<->HL interface, currently used only for 
+ * supplementary service (CAPI 2.0 part III)
+ */
+#include "avmb1/capicmd.h"  /* this should be moved in a common place */
+
+int
+isdn_capi_rec_hl_msg(capi_msg *cm) {
+	
+	int di;
+	int ch;
+	
+	di = (cm->adr.Controller & 0x7f) -1;
+	ch = isdn_dc2minor(di, (cm->adr.Controller>>8)& 0x7f);
+	switch(cm->Command) {
+		case CAPI_FACILITY:
+			/* in the moment only handled in tty */
+			return(isdn_tty_capi_facility(cm));
+		default:
+			return(-1);
+	}
+}
+
+static int
+isdn_status_callback(isdn_ctrl * c)
+{
+	int di;
+	ulong flags;
+	int i;
+	int r;
+	int retval = 0;
+	isdn_ctrl cmd;
+	isdn_net_dev *p;
+
+	di = c->driver;
+	i = isdn_dc2minor(di, c->arg);
+	switch (c->command) {
+		case ISDN_STAT_BSENT:
+			if (i < 0)
+				return -1;
+			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+				return 0;
+			if (isdn_net_stat_callback(i, c))
+				return 0;
+			if (isdn_v110_stat_callback(i, c))
+				return 0;
+			if (isdn_tty_stat_callback(i, c))
+				return 0;
+			wake_up_interruptible(&dev->drv[di]->snd_waitq[c->arg]);
+			break;
+		case ISDN_STAT_STAVAIL:
+			save_flags(flags);
+			cli();
+			dev->drv[di]->stavail += c->arg;
+			restore_flags(flags);
+			wake_up_interruptible(&dev->drv[di]->st_waitq);
+			break;
+		case ISDN_STAT_RUN:
+			dev->drv[di]->flags |= DRV_FLAG_RUNNING;
+			for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+				if (dev->drvmap[i] == di)
+					isdn_all_eaz(di, dev->chanmap[i]);
+			set_global_features();
+			break;
+		case ISDN_STAT_STOP:
+			dev->drv[di]->flags &= ~DRV_FLAG_RUNNING;
+			break;
+		case ISDN_STAT_ICALL:
+			if (i < 0)
+				return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+			printk(KERN_DEBUG "ICALL (net): %d %ld %s\n", di, c->arg, c->parm.num);
+#endif
+			if (dev->global_flags & ISDN_GLOBAL_STOPPED) {
+				cmd.driver = di;
+				cmd.arg = c->arg;
+				cmd.command = ISDN_CMD_HANGUP;
+				isdn_command(&cmd);
+				return 0;
+			}
+			/* Try to find a network-interface which will accept incoming call */
+			r = ((c->command == ISDN_STAT_ICALLW) ? 0 : isdn_net_find_icall(di, c->arg, i, &c->parm.setup));
+			switch (r) {
+				case 0:
+					/* No network-device replies.
+					 * Try ttyI's.
+					 * These return 0 on no match, 1 on match and
+					 * 3 on eventually match, if CID is longer.
+					 */
+                                        if (c->command == ISDN_STAT_ICALL)
+					  if ((retval = isdn_tty_find_icall(di, c->arg, &c->parm.setup))) return(retval);
+#ifdef CONFIG_ISDN_DIVERSION 
+                                         if (divert_if)
+                 	                  if ((retval = divert_if->stat_callback(c))) 
+					    return(retval); /* processed */
+#endif /* CONFIG_ISDN_DIVERSION */                       
+					if ((!retval) && (dev->drv[di]->flags & DRV_FLAG_REJBUS)) {
+						/* No tty responding */
+						cmd.driver = di;
+						cmd.arg = c->arg;
+						cmd.command = ISDN_CMD_HANGUP;
+						isdn_command(&cmd);
+						retval = 2;
+					}
+					break;
+				case 1:
+					/* Schedule connection-setup */
+					isdn_net_dial();
+					cmd.driver = di;
+					cmd.arg = c->arg;
+					cmd.command = ISDN_CMD_ACCEPTD;
+					for ( p = dev->netdev; p; p = p->next )
+						if ( p->local->isdn_channel == cmd.arg )
+						{
+							strcpy( cmd.parm.setup.eazmsn, p->local->msn );
+							isdn_command(&cmd);
+							retval = 1;
+							break;
+						}
+					break;
+
+				case 2:	/* For calling back, first reject incoming call ... */
+				case 3:	/* Interface found, but down, reject call actively  */
+					retval = 2;
+					printk(KERN_INFO "isdn: Rejecting Call\n");
+					cmd.driver = di;
+					cmd.arg = c->arg;
+					cmd.command = ISDN_CMD_HANGUP;
+					isdn_command(&cmd);
+					if (r == 3)
+						break;
+					/* Fall through */
+				case 4:
+					/* ... then start callback. */
+					isdn_net_dial();
+					break;
+				case 5:
+					/* Number would eventually match, if longer */
+					retval = 3;
+					break;
+			}
+#ifdef ISDN_DEBUG_STATCALLB
+			printk(KERN_DEBUG "ICALL: ret=%d\n", retval);
+#endif
+			return retval;
+			break;
+		case ISDN_STAT_CINF:
+			if (i < 0)
+				return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+			printk(KERN_DEBUG "CINF: %ld %s\n", c->arg, c->parm.num);
+#endif
+			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+				return 0;
+			if (strcmp(c->parm.num, "0"))
+				isdn_net_stat_callback(i, c);
+			isdn_tty_stat_callback(i, c);
+			break;
+		case ISDN_STAT_CAUSE:
+#ifdef ISDN_DEBUG_STATCALLB
+			printk(KERN_DEBUG "CAUSE: %ld %s\n", c->arg, c->parm.num);
+#endif
+			printk(KERN_INFO "isdn: %s,ch%ld cause: %s\n",
+			       dev->drvid[di], c->arg, c->parm.num);
+			isdn_tty_stat_callback(i, c);
+#ifdef CONFIG_ISDN_DIVERSION
+                        if (divert_if)
+                         divert_if->stat_callback(c); 
+#endif /* CONFIG_ISDN_DIVERSION */
+			break;
+		case ISDN_STAT_DISPLAY:
+#ifdef ISDN_DEBUG_STATCALLB
+			printk(KERN_DEBUG "DISPLAY: %ld %s\n", c->arg, c->parm.display);
+#endif
+			isdn_tty_stat_callback(i, c);
+#ifdef CONFIG_ISDN_DIVERSION
+                        if (divert_if)
+                         divert_if->stat_callback(c); 
+#endif /* CONFIG_ISDN_DIVERSION */
+			break;
+		case ISDN_STAT_DCONN:
+			if (i < 0)
+				return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+			printk(KERN_DEBUG "DCONN: %ld\n", c->arg);
+#endif
+			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+				return 0;
+			/* Find any net-device, waiting for D-channel setup */
+			if (isdn_net_stat_callback(i, c))
+				break;
+			isdn_v110_stat_callback(i, c);
+			/* Find any ttyI, waiting for D-channel setup */
+			if (isdn_tty_stat_callback(i, c)) {
+				cmd.driver = di;
+				cmd.arg = c->arg;
+				cmd.command = ISDN_CMD_ACCEPTB;
+				isdn_command(&cmd);
+				break;
+			}
+			break;
+		case ISDN_STAT_DHUP:
+			if (i < 0)
+				return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+			printk(KERN_DEBUG "DHUP: %ld\n", c->arg);
+#endif
+			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+				return 0;
+			dev->drv[di]->online &= ~(1 << (c->arg));
+			isdn_info_update();
+			/* Signal hangup to network-devices */
+			if (isdn_net_stat_callback(i, c))
+				break;
+			isdn_v110_stat_callback(i, c);
+			if (isdn_tty_stat_callback(i, c))
+				break;
+#ifdef CONFIG_ISDN_DIVERSION
+                        if (divert_if)
+                         divert_if->stat_callback(c); 
+#endif /* CONFIG_ISDN_DIVERSION */
+			break;
+			break;
+		case ISDN_STAT_BCONN:
+			if (i < 0)
+				return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+			printk(KERN_DEBUG "BCONN: %ld\n", c->arg);
+#endif
+			/* Signal B-channel-connect to network-devices */
+			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+				return 0;
+			dev->drv[di]->online |= (1 << (c->arg));
+			isdn_info_update();
+			if (isdn_net_stat_callback(i, c))
+				break;
+			isdn_v110_stat_callback(i, c);
+			if (isdn_tty_stat_callback(i, c))
+				break;
+			break;
+		case ISDN_STAT_BHUP:
+			if (i < 0)
+				return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+			printk(KERN_DEBUG "BHUP: %ld\n", c->arg);
+#endif
+			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+				return 0;
+			dev->drv[di]->online &= ~(1 << (c->arg));
+			isdn_info_update();
+#ifdef CONFIG_ISDN_X25
+			/* Signal hangup to network-devices */
+			if (isdn_net_stat_callback(i, c))
+				break;
+#endif
+			isdn_v110_stat_callback(i, c);
+			if (isdn_tty_stat_callback(i, c))
+				break;
+			break;
+		case ISDN_STAT_NODCH:
+			if (i < 0)
+				return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+			printk(KERN_DEBUG "NODCH: %ld\n", c->arg);
+#endif
+			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+				return 0;
+			if (isdn_net_stat_callback(i, c))
+				break;
+			if (isdn_tty_stat_callback(i, c))
+				break;
+			break;
+		case ISDN_STAT_ADDCH:
+			if (isdn_add_channels(dev->drv[di], di, c->arg, 1))
+				return -1;
+			isdn_info_update();
+			break;
+		case ISDN_STAT_DISCH:
+			save_flags(flags);
+			cli();
+			for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+				if ((dev->drvmap[i] == di) &&
+				    (dev->chanmap[i] == c->arg)) {
+				    if (c->parm.num[0])
+				      dev->usage[i] &= ~ISDN_USAGE_DISABLED;
+				    else
+				      if (USG_NONE(dev->usage[i])) {
+					dev->usage[i] |= ISDN_USAGE_DISABLED;
+				      }
+				      else 
+					retval = -1;
+				    break;
+				}
+			restore_flags(flags);
+			isdn_info_update();
+			break;
+		case ISDN_STAT_UNLOAD:
+			while (dev->drv[di]->locks > 0) {
+				isdn_ctrl cmd;
+				cmd.driver = di;
+				cmd.arg = 0;
+				cmd.command = ISDN_CMD_UNLOCK;
+				isdn_command(&cmd);
+				dev->drv[di]->locks--;
+			}
+			save_flags(flags);
+			cli();
+			isdn_tty_stat_callback(i, c);
+			for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+				if (dev->drvmap[i] == di) {
+					dev->drvmap[i] = -1;
+					dev->chanmap[i] = -1;
+					dev->usage[i] &= ~ISDN_USAGE_DISABLED;
+					isdn_unregister_devfs(i);
+				}
+			dev->drivers--;
+			dev->channels -= dev->drv[di]->channels;
+			kfree(dev->drv[di]->rcverr);
+			kfree(dev->drv[di]->rcvcount);
+			for (i = 0; i < dev->drv[di]->channels; i++)
+				skb_queue_purge(&dev->drv[di]->rpqueue[i]);
+			kfree(dev->drv[di]->rpqueue);
+			kfree(dev->drv[di]->rcv_waitq);
+			kfree(dev->drv[di]);
+			dev->drv[di] = NULL;
+			dev->drvid[di][0] = '\0';
+			isdn_info_update();
+			set_global_features();
+			restore_flags(flags);
+			return 0;
+		case ISDN_STAT_L1ERR:
+			break;
+		case CAPI_PUT_MESSAGE:
+			return(isdn_capi_rec_hl_msg(&c->parm.cmsg));
+#ifdef CONFIG_ISDN_TTY_FAX
+		case ISDN_STAT_FAXIND:
+			isdn_tty_stat_callback(i, c);
+			break;
+#endif
+#ifdef CONFIG_ISDN_AUDIO
+		case ISDN_STAT_AUDIO:
+			isdn_tty_stat_callback(i, c);
+			break;
+#endif
+#ifdef CONFIG_ISDN_DIVERSION
+	        case ISDN_STAT_PROT:
+	        case ISDN_STAT_REDIR:
+                        if (divert_if)
+                          return(divert_if->stat_callback(c));
+#endif /* CONFIG_ISDN_DIVERSION */
+		default:
+			return -1;
+	}
+	return 0;
+}
+
+/*
+ * Get integer from char-pointer, set pointer to end of number
+ */
+int
+isdn_getnum(char **p)
+{
+	int v = -1;
+
+	while (*p[0] >= '0' && *p[0] <= '9')
+		v = ((v < 0) ? 0 : (v * 10)) + (int) ((*p[0]++) - '0');
+	return v;
+}
+
+#define DLE 0x10
+
+/*
+ * isdn_readbchan() tries to get data from the read-queue.
+ * It MUST be called with interrupts off.
+ *
+ * Be aware that this is not an atomic operation when sleep != 0, even though 
+ * interrupts are turned off! Well, like that we are currently only called
+ * on behalf of a read system call on raw device files (which are documented
+ * to be dangerous and for for debugging purpose only). The inode semaphore
+ * takes care that this is not called for the same minor device number while
+ * we are sleeping, but access is not serialized against simultaneous read()
+ * from the corresponding ttyI device. Can other ugly events, like changes
+ * of the mapping (di,ch)<->minor, happen during the sleep? --he 
+ */
+int
+isdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, wait_queue_head_t *sleep)
+{
+	int count;
+	int count_pull;
+	int count_put;
+	int dflag;
+	struct sk_buff *skb;
+	u_char *cp;
+
+	if (!dev->drv[di])
+		return 0;
+	if (skb_queue_empty(&dev->drv[di]->rpqueue[channel])) {
+		if (sleep)
+			interruptible_sleep_on(sleep);
+		else
+			return 0;
+	}
+	if (len > dev->drv[di]->rcvcount[channel])
+		len = dev->drv[di]->rcvcount[channel];
+	cp = buf;
+	count = 0;
+	while (len) {
+		if (!(skb = skb_peek(&dev->drv[di]->rpqueue[channel])))
+			break;
+#ifdef CONFIG_ISDN_AUDIO
+		if (ISDN_AUDIO_SKB_LOCK(skb))
+			break;
+		ISDN_AUDIO_SKB_LOCK(skb) = 1;
+		if ((ISDN_AUDIO_SKB_DLECOUNT(skb)) || (dev->drv[di]->DLEflag & (1 << channel))) {
+			char *p = skb->data;
+			unsigned long DLEmask = (1 << channel);
+
+			dflag = 0;
+			count_pull = count_put = 0;
+			while ((count_pull < skb->len) && (len > 0)) {
+				len--;
+				if (dev->drv[di]->DLEflag & DLEmask) {
+					*cp++ = DLE;
+					dev->drv[di]->DLEflag &= ~DLEmask;
+				} else {
+					*cp++ = *p;
+					if (*p == DLE) {
+						dev->drv[di]->DLEflag |= DLEmask;
+						(ISDN_AUDIO_SKB_DLECOUNT(skb))--;
+					}
+					p++;
+					count_pull++;
+				}
+				count_put++;
+			}
+			if (count_pull >= skb->len)
+				dflag = 1;
+		} else {
+#endif
+			/* No DLE's in buff, so simply copy it */
+			dflag = 1;
+			if ((count_pull = skb->len) > len) {
+				count_pull = len;
+				dflag = 0;
+			}
+			count_put = count_pull;
+			memcpy(cp, skb->data, count_put);
+			cp += count_put;
+			len -= count_put;
+#ifdef CONFIG_ISDN_AUDIO
+		}
+#endif
+		count += count_put;
+		if (fp) {
+			memset(fp, 0, count_put);
+			fp += count_put;
+		}
+		if (dflag) {
+			/* We got all the data in this buff.
+			 * Now we can dequeue it.
+			 */
+			if (fp)
+				*(fp - 1) = 0xff;
+#ifdef CONFIG_ISDN_AUDIO
+			ISDN_AUDIO_SKB_LOCK(skb) = 0;
+#endif
+			skb = skb_dequeue(&dev->drv[di]->rpqueue[channel]);
+			dev_kfree_skb(skb);
+		} else {
+			/* Not yet emptied this buff, so it
+			 * must stay in the queue, for further calls
+			 * but we pull off the data we got until now.
+			 */
+			skb_pull(skb, count_pull);
+#ifdef CONFIG_ISDN_AUDIO
+			ISDN_AUDIO_SKB_LOCK(skb) = 0;
+#endif
+		}
+		dev->drv[di]->rcvcount[channel] -= count_put;
+	}
+	return count;
+}
+
+static __inline int
+isdn_minor2drv(int minor)
+{
+	return (dev->drvmap[minor]);
+}
+
+static __inline int
+isdn_minor2chan(int minor)
+{
+	return (dev->chanmap[minor]);
+}
+
+static char *
+isdn_statstr(void)
+{
+	static char istatbuf[2048];
+	char *p;
+	int i;
+
+	sprintf(istatbuf, "idmap:\t");
+	p = istatbuf + strlen(istatbuf);
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		sprintf(p, "%s ", (dev->drvmap[i] < 0) ? "-" : dev->drvid[dev->drvmap[i]]);
+		p = istatbuf + strlen(istatbuf);
+	}
+	sprintf(p, "\nchmap:\t");
+	p = istatbuf + strlen(istatbuf);
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		sprintf(p, "%d ", dev->chanmap[i]);
+		p = istatbuf + strlen(istatbuf);
+	}
+	sprintf(p, "\ndrmap:\t");
+	p = istatbuf + strlen(istatbuf);
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		sprintf(p, "%d ", dev->drvmap[i]);
+		p = istatbuf + strlen(istatbuf);
+	}
+	sprintf(p, "\nusage:\t");
+	p = istatbuf + strlen(istatbuf);
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		sprintf(p, "%d ", dev->usage[i]);
+		p = istatbuf + strlen(istatbuf);
+	}
+	sprintf(p, "\nflags:\t");
+	p = istatbuf + strlen(istatbuf);
+	for (i = 0; i < ISDN_MAX_DRIVERS; i++) {
+		if (dev->drv[i]) {
+			sprintf(p, "%ld ", dev->drv[i]->online);
+			p = istatbuf + strlen(istatbuf);
+		} else {
+			sprintf(p, "? ");
+			p = istatbuf + strlen(istatbuf);
+		}
+	}
+	sprintf(p, "\nphone:\t");
+	p = istatbuf + strlen(istatbuf);
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		sprintf(p, "%s ", dev->num[i]);
+		p = istatbuf + strlen(istatbuf);
+	}
+	sprintf(p, "\n");
+	return istatbuf;
+}
+
+/* 
+ * /dev/isdninfo
+ */
+
+void
+isdn_info_update(void)
+{
+	infostruct *p = dev->infochain;
+
+	while (p) {
+		*(p->private) = 1;
+		p = (infostruct *) p->next;
+	}
+	wake_up_interruptible(&(dev->info_waitq));
+}
+
+static int
+isdn_status_open(struct inode *ino, struct file *filep)
+{
+	infostruct *p;
+	
+	p = kmalloc(sizeof(infostruct), GFP_KERNEL);
+	if (!p)
+		return -ENOMEM;
+
+	p->next = (char *) dev->infochain;
+	p->private = (char *) &(filep->private_data);
+	dev->infochain = p;
+	/* At opening we allow a single update */
+	filep->private_data = (char *) 1;
+
+	return 0;
+}
+
+static int
+isdn_status_release(struct inode *ino, struct file *filep)
+{
+	infostruct *p = dev->infochain;
+	infostruct *q = NULL;
+	
+	lock_kernel();
+
+	while (p) {
+		if (p->private == (char *) &(filep->private_data)) {
+			if (q)
+				q->next = p->next;
+			else
+				dev->infochain = (infostruct *) (p->next);
+			kfree(p);
+			goto out;
+		}
+		q = p;
+		p = (infostruct *) (p->next);
+	}
+	printk(KERN_WARNING "isdn: No private data while closing isdnctrl\n");
+
+ out:
+	unlock_kernel();
+	return 0;
+}
+
+static ssize_t
+isdn_status_read(struct file *file, char *buf, size_t count, loff_t * off)
+{
+	int retval;
+	int len = 0;
+	char *p;
+
+	if (off != &file->f_pos)
+		return -ESPIPE;
+
+	lock_kernel();
+	if (!file->private_data) {
+		if (file->f_flags & O_NONBLOCK) {
+			retval = -EAGAIN;
+			goto out;
+		}
+		interruptible_sleep_on(&(dev->info_waitq));
+	}
+	p = isdn_statstr();
+	file->private_data = 0;
+	if ((len = strlen(p)) <= count) {
+		if (copy_to_user(buf, p, len)) {
+			retval = -EFAULT;
+			goto out;
+		}
+		*off += len;
+		retval = len;
+		goto out;
+	}
+	retval = 0;
+	goto out;
+
+ out:
+	unlock_kernel();
+	return retval;
+}
+
+static ssize_t
+isdn_status_write(struct file *file, const char *buf, size_t count, loff_t *off)
+{
+	return -EPERM;
+}
+
+static unsigned int
+isdn_status_poll(struct file *file, poll_table *wait)
+{
+	unsigned int mask = 0;
+
+	lock_kernel();
+
+	poll_wait(file, &(dev->info_waitq), wait);
+	if (file->private_data)
+		mask |= POLLIN | POLLRDNORM;
+
+	unlock_kernel();
+	return mask;
+}
+
+static int
+isdn_status_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
+{
+	int ret;
+	union iocpar {
+		char name[10];
+		char bname[22];
+		isdn_ioctl_struct iocts;
+		isdn_net_ioctl_phone phone;
+		isdn_net_ioctl_cfg cfg;
+	} iocpar;
+
+#define name  iocpar.name
+#define bname iocpar.bname
+#define iocts iocpar.iocts
+#define phone iocpar.phone
+#define cfg   iocpar.cfg
+
+	switch (cmd) {
+	case IIOCGETDVR:
+		return (TTY_DV +
+			(NET_DV << 8) +
+			(INF_DV << 16));
+	case IIOCGETCPS:
+		if (arg) {
+			ulong *p = (ulong *) arg;
+			int i;
+			if ((ret = verify_area(VERIFY_WRITE, (void *) arg,
+					       sizeof(ulong) * ISDN_MAX_CHANNELS * 2)))
+				return ret;
+			for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+				put_user(dev->ibytes[i], p++);
+				put_user(dev->obytes[i], p++);
+			}
+			return 0;
+		} else
+			return -EINVAL;
+		break;
+#ifdef CONFIG_NETDEVICES
+	case IIOCNETGPN:
+		/* Get peer phone number of a connected 
+		 * isdn network interface */
+		if (arg) {
+			if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone)))
+				return -EFAULT;
+			return isdn_net_getpeer(&phone, (isdn_net_ioctl_phone *) arg);
+		} else
+			return -EINVAL;
+#endif
+	default:
+		return -EINVAL;
+	}
+
+#undef name
+#undef bname
+#undef iocts
+#undef phone
+#undef cfg
+}
+
+static struct file_operations isdn_status_fops =
+{
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	read:		isdn_status_read,
+	write:		isdn_status_write,
+	poll:		isdn_status_poll,
+	ioctl:		isdn_status_ioctl,
+	open:		isdn_status_open,
+	release:	isdn_status_release,
+};
+
+/*
+ * /dev/isdnctrlX
+ */
+
+static int
+isdn_ctrl_open(struct inode *ino, struct file *filep)
+{
+	uint minor = minor(ino->i_rdev);
+	int drvidx;
+
+	drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
+	if (drvidx < 0)
+		return -ENODEV;
+
+	isdn_lock_drivers();
+	return 0;
+}
+
+static int
+isdn_ctrl_release(struct inode *ino, struct file *filep)
+{
+	lock_kernel();
+
+	if (dev->profd == current)
+		dev->profd = NULL;
+
+	isdn_unlock_drivers();
+
+	unlock_kernel();
+	return 0;
+}
+
+static ssize_t
+isdn_ctrl_read(struct file *file, char *buf, size_t count, loff_t * off)
+{
+	uint minor = minor(file->f_dentry->d_inode->i_rdev);
+	ulong flags;
+	int len = 0;
+	int drvidx;
+	int retval;
+
+	if (off != &file->f_pos)
+		return -ESPIPE;
+
+	lock_kernel();
+
+	drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
+	if (drvidx < 0) {
+		retval = -ENODEV;
+		goto out;
+	}
+	if (!dev->drv[drvidx]->stavail) {
+		if (file->f_flags & O_NONBLOCK) {
+			retval = -EAGAIN;
+			goto out;
+		}
+		interruptible_sleep_on(&(dev->drv[drvidx]->st_waitq));
+	}
+	if (dev->drv[drvidx]->interface->readstat) {
+		if (count > dev->drv[drvidx]->stavail)
+			count = dev->drv[drvidx]->stavail;
+		len = dev->drv[drvidx]->interface->
+			readstat(buf, count, 1, drvidx,
+				 isdn_minor2chan(minor));
+	} else {
+		len = 0;
+	}
+	save_flags(flags);
+	cli();
+	if (len)
+		dev->drv[drvidx]->stavail -= len;
+	else
+		dev->drv[drvidx]->stavail = 0;
+	restore_flags(flags);
+	*off += len;
+	retval = len;
+	
+ out:
+	unlock_kernel();
+	return retval;
+}
+
+static ssize_t
+isdn_ctrl_write(struct file *file, const char *buf, size_t count, loff_t *off)
+{
+	uint minor = minor(file->f_dentry->d_inode->i_rdev);
+	int drvidx;
+	int retval;
+
+	if (off != &file->f_pos)
+		return -ESPIPE;
+
+	lock_kernel();
+
+	drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
+	if (drvidx < 0) {
+		retval = -ENODEV;
+		goto out;
+	}
+	if (!dev->drv[drvidx]->interface->writecmd) {
+		retval = -EINVAL;
+		goto out;
+	}
+	retval = dev->drv[drvidx]->interface->
+		writecmd(buf, count, 1, drvidx, isdn_minor2chan(minor - ISDN_MINOR_CTRL));
+
+ out:
+	unlock_kernel();
+	return retval;
+}
+
+static unsigned int
+isdn_ctrl_poll(struct file *file, poll_table *wait)
+{
+	unsigned int mask = 0;
+	unsigned int minor = minor(file->f_dentry->d_inode->i_rdev);
+	int drvidx;
+
+	drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
+	if (drvidx < 0)
+		/* driver deregistered while file open */
+		return POLLHUP;
+
+	lock_kernel();
+
+	poll_wait(file, &(dev->drv[drvidx]->st_waitq), wait);
+	mask = POLLOUT | POLLWRNORM;
+	if (dev->drv[drvidx]->stavail)
+		mask |= POLLIN | POLLRDNORM;
+
+	unlock_kernel();
+	return mask;
+}
+
+
+static int
+isdn_ctrl_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
+{
+	isdn_ctrl c;
+	int drvidx;
+	int ret;
+	int i;
+	char *p;
+	char *s;
+	union iocpar {
+		char name[10];
+		char bname[22];
+		isdn_ioctl_struct iocts;
+		isdn_net_ioctl_phone phone;
+		isdn_net_ioctl_cfg cfg;
+	} iocpar;
+
+#define name  iocpar.name
+#define bname iocpar.bname
+#define iocts iocpar.iocts
+#define phone iocpar.phone
+#define cfg   iocpar.cfg
+/*
+ * isdn net devices manage lots of configuration variables as linked lists.
+ * Those lists must only be manipulated from user space. Some of the ioctl's
+ * service routines access user space and are not atomic. Therefor, ioctl's
+ * manipulating the lists and ioctl's sleeping while accessing the lists
+ * are serialized by means of a semaphore.
+ */
+	switch (cmd) {
+	case IIOCNETDWRSET:
+		printk(KERN_INFO "INFO: ISDN_DW_ABC_EXTENSION not enabled\n");
+		return(-EINVAL);
+	case IIOCNETLCR:
+		printk(KERN_INFO "INFO: ISDN_ABC_LCR_SUPPORT not enabled\n");
+		return -ENODEV;
+#ifdef CONFIG_NETDEVICES
+	case IIOCNETAIF:
+		/* Add a network-interface */
+		if (arg) {
+			if (copy_from_user(name, (char *) arg, sizeof(name)))
+				return -EFAULT;
+			s = name;
+		} else {
+			s = NULL;
+		}
+		ret = down_interruptible(&dev->sem);
+		if( ret ) return ret;
+		if ((s = isdn_net_new(s, NULL))) {
+			if (copy_to_user((char *) arg, s, strlen(s) + 1)){
+				ret = -EFAULT;
+			} else {
+				ret = 0;
+			}
+		} else
+			ret = -ENODEV;
+		up(&dev->sem);
+		return ret;
+	case IIOCNETASL:
+		/* Add a slave to a network-interface */
+		if (arg) {
+			if (copy_from_user(bname, (char *) arg, sizeof(bname) - 1))
+				return -EFAULT;
+		} else
+			return -EINVAL;
+		ret = down_interruptible(&dev->sem);
+		if( ret ) return ret;
+		if ((s = isdn_net_newslave(bname))) {
+			if (copy_to_user((char *) arg, s, strlen(s) + 1)){
+				ret = -EFAULT;
+			} else {
+				ret = 0;
+			}
+		} else
+			ret = -ENODEV;
+		up(&dev->sem);
+		return ret;
+	case IIOCNETDIF:
+		/* Delete a network-interface */
+		if (arg) {
+			if (copy_from_user(name, (char *) arg, sizeof(name)))
+				return -EFAULT;
+			ret = down_interruptible(&dev->sem);
+			if( ret ) return ret;
+			ret = isdn_net_rm(name);
+			up(&dev->sem);
+			return ret;
+		} else
+			return -EINVAL;
+	case IIOCNETSCF:
+		/* Set configurable parameters of a network-interface */
+		if (arg) {
+			if (copy_from_user((char *) &cfg, (char *) arg, sizeof(cfg)))
+				return -EFAULT;
+			return isdn_net_setcfg(&cfg);
+		} else
+			return -EINVAL;
+	case IIOCNETGCF:
+		/* Get configurable parameters of a network-interface */
+		if (arg) {
+			if (copy_from_user((char *) &cfg, (char *) arg, sizeof(cfg)))
+				return -EFAULT;
+			if (!(ret = isdn_net_getcfg(&cfg))) {
+				if (copy_to_user((char *) arg, (char *) &cfg, sizeof(cfg)))
+					return -EFAULT;
+			}
+			return ret;
+		} else
+			return -EINVAL;
+	case IIOCNETANM:
+		/* Add a phone-number to a network-interface */
+		if (arg) {
+			if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone)))
+				return -EFAULT;
+			ret = down_interruptible(&dev->sem);
+			if( ret ) return ret;
+			ret = isdn_net_addphone(&phone);
+			up(&dev->sem);
+			return ret;
+		} else
+			return -EINVAL;
+	case IIOCNETGNM:
+		/* Get list of phone-numbers of a network-interface */
+		if (arg) {
+			if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone)))
+				return -EFAULT;
+			ret = down_interruptible(&dev->sem);
+			if( ret ) return ret;
+			ret = isdn_net_getphones(&phone, (char *) arg);
+			up(&dev->sem);
+			return ret;
+		} else
+			return -EINVAL;
+	case IIOCNETDNM:
+		/* Delete a phone-number of a network-interface */
+		if (arg) {
+			if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone)))
+				return -EFAULT;
+			ret = down_interruptible(&dev->sem);
+			if( ret ) return ret;
+			ret = isdn_net_delphone(&phone);
+			up(&dev->sem);
+			return ret;
+		} else
+			return -EINVAL;
+	case IIOCNETDIL:
+		/* Force dialing of a network-interface */
+		if (arg) {
+			if (copy_from_user(name, (char *) arg, sizeof(name)))
+				return -EFAULT;
+			return isdn_net_force_dial(name);
+		} else
+			return -EINVAL;
+#ifdef CONFIG_ISDN_PPP
+	case IIOCNETALN:
+		if (!arg)
+			return -EINVAL;
+		if (copy_from_user(name, (char *) arg, sizeof(name)))
+			return -EFAULT;
+		return isdn_ppp_dial_slave(name);
+	case IIOCNETDLN:
+		if (!arg)
+			return -EINVAL;
+		if (copy_from_user(name, (char *) arg, sizeof(name)))
+			return -EFAULT;
+		return isdn_ppp_hangup_slave(name);
+#endif
+	case IIOCNETHUP:
+		/* Force hangup of a network-interface */
+		if (!arg)
+			return -EINVAL;
+		if (copy_from_user(name, (char *) arg, sizeof(name)))
+			return -EFAULT;
+		return isdn_net_force_hangup(name);
+		break;
+#endif                          /* CONFIG_NETDEVICES */
+	case IIOCSETVER:
+		dev->net_verbose = arg;
+		printk(KERN_INFO "isdn: Verbose-Level is %d\n", dev->net_verbose);
+		return 0;
+	case IIOCSETGST:
+		if (arg)
+			dev->global_flags |= ISDN_GLOBAL_STOPPED;
+		else
+			dev->global_flags &= ~ISDN_GLOBAL_STOPPED;
+		printk(KERN_INFO "isdn: Global Mode %s\n",
+		       (dev->global_flags & ISDN_GLOBAL_STOPPED) ? "stopped" : "running");
+		return 0;
+	case IIOCSETBRJ:
+		drvidx = -1;
+		if (arg) {
+			int i;
+			char *p;
+			if (copy_from_user((char *) &iocts, (char *) arg,
+					   sizeof(isdn_ioctl_struct)))
+				return -EFAULT;
+			if (strlen(iocts.drvid)) {
+				if ((p = strchr(iocts.drvid, ',')))
+					*p = 0;
+				drvidx = -1;
+				for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+					if (!(strcmp(dev->drvid[i], iocts.drvid))) {
+						drvidx = i;
+						break;
+					}
+			}
+		}
+		if (drvidx == -1)
+			return -ENODEV;
+		if (iocts.arg)
+			dev->drv[drvidx]->flags |= DRV_FLAG_REJBUS;
+		else
+			dev->drv[drvidx]->flags &= ~DRV_FLAG_REJBUS;
+		return 0;
+	case IIOCSIGPRF:
+		dev->profd = current;
+		return 0;
+		break;
+	case IIOCGETPRF:
+		/* Get all Modem-Profiles */
+		if (arg) {
+			char *p = (char *) arg;
+			int i;
+
+			if ((ret = verify_area(VERIFY_WRITE, (void *) arg,
+					       (ISDN_MODEM_NUMREG + ISDN_MSNLEN + ISDN_LMSNLEN)
+					       * ISDN_MAX_CHANNELS)))
+				return ret;
+
+			for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+				if (copy_to_user(p, dev->mdm.info[i].emu.profile,
+						 ISDN_MODEM_NUMREG))
+					return -EFAULT;
+				p += ISDN_MODEM_NUMREG;
+				if (copy_to_user(p, dev->mdm.info[i].emu.pmsn, ISDN_MSNLEN))
+					return -EFAULT;
+				p += ISDN_MSNLEN;
+				if (copy_to_user(p, dev->mdm.info[i].emu.plmsn, ISDN_LMSNLEN))
+					return -EFAULT;
+				p += ISDN_LMSNLEN;
+			}
+			return (ISDN_MODEM_NUMREG + ISDN_MSNLEN + ISDN_LMSNLEN) * ISDN_MAX_CHANNELS;
+		} else
+			return -EINVAL;
+		break;
+	case IIOCSETPRF:
+		/* Set all Modem-Profiles */
+		if (arg) {
+			char *p = (char *) arg;
+			int i;
+
+			if ((ret = verify_area(VERIFY_READ, (void *) arg,
+					       (ISDN_MODEM_NUMREG + ISDN_MSNLEN + ISDN_LMSNLEN)
+					       * ISDN_MAX_CHANNELS)))
+				return ret;
+
+			for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+				if (copy_from_user(dev->mdm.info[i].emu.profile, p,
+						   ISDN_MODEM_NUMREG))
+					return -EFAULT;
+				p += ISDN_MODEM_NUMREG;
+				if (copy_from_user(dev->mdm.info[i].emu.plmsn, p, ISDN_LMSNLEN))
+					return -EFAULT;
+				p += ISDN_LMSNLEN;
+				if (copy_from_user(dev->mdm.info[i].emu.pmsn, p, ISDN_MSNLEN))
+					return -EFAULT;
+				p += ISDN_MSNLEN;
+			}
+			return 0;
+		} else
+			return -EINVAL;
+		break;
+	case IIOCSETMAP:
+	case IIOCGETMAP:
+		/* Set/Get MSN->EAZ-Mapping for a driver */
+		if (arg) {
+
+			if (copy_from_user((char *) &iocts,
+					   (char *) arg,
+					   sizeof(isdn_ioctl_struct)))
+				return -EFAULT;
+			if (strlen(iocts.drvid)) {
+				drvidx = -1;
+				for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+					if (!(strcmp(dev->drvid[i], iocts.drvid))) {
+						drvidx = i;
+						break;
+					}
+			} else
+				drvidx = 0;
+			if (drvidx == -1)
+				return -ENODEV;
+			if (cmd == IIOCSETMAP) {
+				int loop = 1;
+
+				p = (char *) iocts.arg;
+				i = 0;
+				while (loop) {
+					int j = 0;
+
+					while (1) {
+						if ((ret = verify_area(VERIFY_READ, p, 1)))
+							return ret;
+						get_user(bname[j], p++);
+						switch (bname[j]) {
+						case '\0':
+							loop = 0;
+							/* Fall through */
+						case ',':
+							bname[j] = '\0';
+							strcpy(dev->drv[drvidx]->msn2eaz[i], bname);
+							j = ISDN_MSNLEN;
+							break;
+						default:
+							j++;
+						}
+						if (j >= ISDN_MSNLEN)
+							break;
+					}
+					if (++i > 9)
+						break;
+				}
+			} else {
+				p = (char *) iocts.arg;
+				for (i = 0; i < 10; i++) {
+					sprintf(bname, "%s%s",
+						strlen(dev->drv[drvidx]->msn2eaz[i]) ?
+						dev->drv[drvidx]->msn2eaz[i] : "_",
+						(i < 9) ? "," : "\0");
+					if (copy_to_user(p, bname, strlen(bname) + 1))
+						return -EFAULT;
+					p += strlen(bname);
+				}
+			}
+			return 0;
+		} else
+			return -EINVAL;
+	case IIOCDBGVAR:
+		if (arg) {
+			if (copy_to_user((char *) arg, (char *) &dev, sizeof(ulong)))
+				return -EFAULT;
+			return 0;
+		} else
+			return -EINVAL;
+		break;
+	default:
+		if ((cmd & IIOCDRVCTL) == IIOCDRVCTL)
+			cmd = ((cmd >> _IOC_NRSHIFT) & _IOC_NRMASK) & ISDN_DRVIOCTL_MASK;
+		else
+			return -EINVAL;
+		if (arg) {
+			int i;
+			char *p;
+			if (copy_from_user((char *) &iocts, (char *) arg, sizeof(isdn_ioctl_struct)))
+				return -EFAULT;
+			if (strlen(iocts.drvid)) {
+				if ((p = strchr(iocts.drvid, ',')))
+					*p = 0;
+				drvidx = -1;
+				for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+					if (!(strcmp(dev->drvid[i], iocts.drvid))) {
+						drvidx = i;
+						break;
+					}
+			} else
+				drvidx = 0;
+			if (drvidx == -1)
+				return -ENODEV;
+			if ((ret = verify_area(VERIFY_WRITE, (void *) arg,
+					       sizeof(isdn_ioctl_struct))))
+				return ret;
+			c.driver = drvidx;
+			c.command = ISDN_CMD_IOCTL;
+			c.arg = cmd;
+			memcpy(c.parm.num, (char *) &iocts.arg, sizeof(ulong));
+			ret = isdn_command(&c);
+			memcpy((char *) &iocts.arg, c.parm.num, sizeof(ulong));
+			if (copy_to_user((char *) arg, &iocts, sizeof(isdn_ioctl_struct)))
+				return -EFAULT;
+			return ret;
+		} else
+			return -EINVAL;
+	}
+
+#undef name
+#undef bname
+#undef iocts
+#undef phone
+#undef cfg
+}
+
+static struct file_operations isdn_ctrl_fops =
+{
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	read:		isdn_ctrl_read,
+	write:		isdn_ctrl_write,
+	poll:		isdn_ctrl_poll,
+	ioctl:		isdn_ctrl_ioctl,
+	open:		isdn_ctrl_open,
+	release:	isdn_ctrl_release,
+};
+
+/*
+ * file_operations for major 43, /dev/isdn*
+ * stolen from drivers/char/misc.c
+ */
+
+static int
+isdn_open(struct inode * inode, struct file * file)
+{
+	int minor = minor(inode->i_rdev);
+	int err = -ENODEV;
+	struct file_operations *old_fops, *new_fops = NULL;
+	
+	if (minor >= ISDN_MINOR_CTRL && minor <= ISDN_MINOR_CTRLMAX)
+		new_fops = fops_get(&isdn_ctrl_fops);
+#ifdef CONFIG_ISDN_PPP
+	else if (minor >= ISDN_MINOR_PPP && minor <= ISDN_MINOR_PPPMAX)
+		new_fops = fops_get(&isdn_ppp_fops);
+#endif
+	else if (minor == ISDN_MINOR_STATUS)
+		new_fops = fops_get(&isdn_status_fops);
+
+	if (!new_fops)
+		goto out;
+
+	err = 0;
+	old_fops = file->f_op;
+	file->f_op = new_fops;
+	if (file->f_op->open) {
+		err = file->f_op->open(inode,file);
+		if (err) {
+			fops_put(file->f_op);
+			file->f_op = fops_get(old_fops);
+		}
+	}
+	fops_put(old_fops);
+	
+ out:
+	return err;
+}
+
+static struct file_operations isdn_fops =
+{
+	owner:		THIS_MODULE,
+	open:		isdn_open,
+};
+
+char *
+isdn_map_eaz2msn(char *msn, int di)
+{
+	driver *this = dev->drv[di];
+	int i;
+
+	if (strlen(msn) == 1) {
+		i = msn[0] - '0';
+		if ((i >= 0) && (i <= 9))
+			if (strlen(this->msn2eaz[i]))
+				return (this->msn2eaz[i]);
+	}
+	return (msn);
+}
+
+/*
+ * Find an unused ISDN-channel, whose feature-flags match the
+ * given L2- and L3-protocols.
+ */
+#define L2V (~(ISDN_FEATURE_L2_V11096|ISDN_FEATURE_L2_V11019|ISDN_FEATURE_L2_V11038))
+
+int
+isdn_get_free_channel(int usage, int l2_proto, int l3_proto, int pre_dev
+		      ,int pre_chan, char *msn)
+{
+	int i;
+	ulong flags;
+	ulong features;
+	ulong vfeatures;
+
+	save_flags(flags);
+	cli();
+	features = ((1 << l2_proto) | (0x10000 << l3_proto));
+	vfeatures = (((1 << l2_proto) | (0x10000 << l3_proto)) &
+		     ~(ISDN_FEATURE_L2_V11096|ISDN_FEATURE_L2_V11019|ISDN_FEATURE_L2_V11038));
+	/* If Layer-2 protocol is V.110, accept drivers with
+	 * transparent feature even if these don't support V.110
+	 * because we can emulate this in linklevel.
+	 */
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+		if (USG_NONE(dev->usage[i]) &&
+		    (dev->drvmap[i] != -1)) {
+			int d = dev->drvmap[i];
+			if ((dev->usage[i] & ISDN_USAGE_EXCLUSIVE) &&
+			((pre_dev != d) || (pre_chan != dev->chanmap[i])))
+				continue;
+			if (!strcmp(isdn_map_eaz2msn(msn, d), "-"))
+				continue;
+			if (dev->usage[i] & ISDN_USAGE_DISABLED)
+			        continue; /* usage not allowed */
+			if (dev->drv[d]->flags & DRV_FLAG_RUNNING) {
+				if (((dev->drv[d]->interface->features & features) == features) ||
+				    (((dev->drv[d]->interface->features & vfeatures) == vfeatures) &&
+				     (dev->drv[d]->interface->features & ISDN_FEATURE_L2_TRANS))) {
+					if ((pre_dev < 0) || (pre_chan < 0)) {
+						dev->usage[i] &= ISDN_USAGE_EXCLUSIVE;
+						dev->usage[i] |= usage;
+						isdn_info_update();
+						restore_flags(flags);
+						return i;
+					} else {
+						if ((pre_dev == d) && (pre_chan == dev->chanmap[i])) {
+							dev->usage[i] &= ISDN_USAGE_EXCLUSIVE;
+							dev->usage[i] |= usage;
+							isdn_info_update();
+							restore_flags(flags);
+							return i;
+						}
+					}
+				}
+			}
+		}
+	restore_flags(flags);
+	return -1;
+}
+
+/*
+ * Set state of ISDN-channel to 'unused'
+ */
+void
+isdn_free_channel(int di, int ch, int usage)
+{
+	int i;
+	ulong flags;
+
+	save_flags(flags);
+	cli();
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+		if (((!usage) || ((dev->usage[i] & ISDN_USAGE_MASK) == usage)) &&
+		    (dev->drvmap[i] == di) &&
+		    (dev->chanmap[i] == ch)) {
+			dev->usage[i] &= (ISDN_USAGE_NONE | ISDN_USAGE_EXCLUSIVE);
+			strcpy(dev->num[i], "???");
+			dev->ibytes[i] = 0;
+			dev->obytes[i] = 0;
+// 20.10.99 JIM, try to reinitialize v110 !
+			dev->v110emu[i] = 0;
+			atomic_set(&(dev->v110use[i]), 0);
+			isdn_v110_close(dev->v110[i]);
+			dev->v110[i] = NULL;
+// 20.10.99 JIM, try to reinitialize v110 !
+			isdn_info_update();
+			skb_queue_purge(&dev->drv[di]->rpqueue[ch]);
+		}
+	restore_flags(flags);
+}
+
+/*
+ * Cancel Exclusive-Flag for ISDN-channel
+ */
+void
+isdn_unexclusive_channel(int di, int ch)
+{
+	int i;
+	ulong flags;
+
+	save_flags(flags);
+	cli();
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+		if ((dev->drvmap[i] == di) &&
+		    (dev->chanmap[i] == ch)) {
+			dev->usage[i] &= ~ISDN_USAGE_EXCLUSIVE;
+			isdn_info_update();
+			restore_flags(flags);
+			return;
+		}
+	restore_flags(flags);
+}
+
+/*
+ * Return: length of data on success, -ERRcode on failure.
+ */
+int
+isdn_writebuf_skb_stub(int drvidx, int chan, int ack, struct sk_buff *skb)
+{
+	int ret;
+	struct sk_buff *nskb = NULL;
+	int v110_ret = skb->len;
+	int idx = isdn_dc2minor(drvidx, chan);
+
+	if (dev->v110[idx]) {
+		atomic_inc(&dev->v110use[idx]);
+		nskb = isdn_v110_encode(dev->v110[idx], skb);
+		atomic_dec(&dev->v110use[idx]);
+		if (!nskb)
+			return 0;
+		v110_ret = *((int *)nskb->data);
+		skb_pull(nskb, sizeof(int));
+		if (!nskb->len) {
+			dev_kfree_skb(nskb);
+			return v110_ret;
+		}
+		/* V.110 must always be acknowledged */
+		ack = 1;
+		ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, ack, nskb);
+	} else {
+		int hl = dev->drv[drvidx]->interface->hl_hdrlen;
+
+		if( skb_headroom(skb) < hl ){
+			/* 
+			 * This should only occur when new HL driver with
+			 * increased hl_hdrlen was loaded after netdevice
+			 * was created and connected to the new driver.
+			 *
+			 * The V.110 branch (re-allocates on its own) does
+			 * not need this
+			 */
+			struct sk_buff * skb_tmp;
+
+			skb_tmp = skb_realloc_headroom(skb, hl);
+			printk(KERN_DEBUG "isdn_writebuf_skb_stub: reallocating headroom%s\n", skb_tmp ? "" : " failed");
+			if (!skb_tmp) return -ENOMEM; /* 0 better? */
+			ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, ack, skb_tmp);
+			if( ret > 0 ){
+				dev_kfree_skb(skb);
+			} else {
+				dev_kfree_skb(skb_tmp);
+			}
+		} else {
+			ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, ack, skb);
+		}
+	}
+	if (ret > 0) {
+		dev->obytes[idx] += ret;
+		if (dev->v110[idx]) {
+			atomic_inc(&dev->v110use[idx]);
+			dev->v110[idx]->skbuser++;
+			atomic_dec(&dev->v110use[idx]);
+			/* For V.110 return unencoded data length */
+			ret = v110_ret;
+			/* if the complete frame was send we free the skb;
+			   if not upper function will requeue the skb */ 
+			if (ret == skb->len)
+				dev_kfree_skb(skb);
+		}
+	} else
+		if (dev->v110[idx])
+			dev_kfree_skb(nskb);
+	return ret;
+}
+
+int
+isdn_add_channels(driver *d, int drvidx, int n, int adding)
+{
+	int j, k, m;
+	ulong flags;
+
+	init_waitqueue_head(&d->st_waitq);
+	if (d->flags & DRV_FLAG_RUNNING)
+		return -1;
+       	if (n < 1) return 0;
+
+	m = (adding) ? d->channels + n : n;
+
+	if (dev->channels + n > ISDN_MAX_CHANNELS) {
+		printk(KERN_WARNING "register_isdn: Max. %d channels supported\n",
+		       ISDN_MAX_CHANNELS);
+		return -1;
+	}
+
+	if ((adding) && (d->rcverr))
+		kfree(d->rcverr);
+	if (!(d->rcverr = kmalloc(sizeof(int) * m, GFP_KERNEL))) {
+		printk(KERN_WARNING "register_isdn: Could not alloc rcverr\n");
+		return -1;
+	}
+	memset((char *) d->rcverr, 0, sizeof(int) * m);
+
+	if ((adding) && (d->rcvcount))
+		kfree(d->rcvcount);
+	if (!(d->rcvcount = kmalloc(sizeof(int) * m, GFP_KERNEL))) {
+		printk(KERN_WARNING "register_isdn: Could not alloc rcvcount\n");
+		if (!adding) kfree(d->rcverr);
+		return -1;
+	}
+	memset((char *) d->rcvcount, 0, sizeof(int) * m);
+
+	if ((adding) && (d->rpqueue)) {
+		for (j = 0; j < d->channels; j++)
+			skb_queue_purge(&d->rpqueue[j]);
+		kfree(d->rpqueue);
+	}
+	if (!(d->rpqueue = kmalloc(sizeof(struct sk_buff_head) * m, GFP_KERNEL))) {
+		printk(KERN_WARNING "register_isdn: Could not alloc rpqueue\n");
+		if (!adding) {
+			kfree(d->rcvcount);
+			kfree(d->rcverr);
+		}
+		return -1; 
+	}
+	for (j = 0; j < m; j++) {
+		skb_queue_head_init(&d->rpqueue[j]);
+	}
+
+	if ((adding) && (d->rcv_waitq))
+		kfree(d->rcv_waitq);
+	d->rcv_waitq = kmalloc(sizeof(wait_queue_head_t) * 2 * m, GFP_KERNEL);
+	if (!d->rcv_waitq) {
+		printk(KERN_WARNING "register_isdn: Could not alloc rcv_waitq\n");
+		if (!adding) {
+			kfree(d->rpqueue);
+			kfree(d->rcvcount);
+			kfree(d->rcverr);
+		}
+		return -1;
+	}
+	d->snd_waitq = d->rcv_waitq + m;
+	for (j = 0; j < m; j++) {
+		init_waitqueue_head(&d->rcv_waitq[j]);
+		init_waitqueue_head(&d->snd_waitq[j]);
+	}
+
+	dev->channels += n;
+	save_flags(flags);
+	cli();
+	for (j = d->channels; j < m; j++)
+		for (k = 0; k < ISDN_MAX_CHANNELS; k++)
+			if (dev->chanmap[k] < 0) {
+				dev->chanmap[k] = j;
+				dev->drvmap[k] = drvidx;
+				isdn_register_devfs(k);
+				break;
+			}
+	restore_flags(flags);
+	d->channels = m;
+	return 0;
+}
+
+/*
+ * Low-level-driver registration
+ */
+
+static void
+set_global_features(void)
+{
+	int drvidx;
+
+	dev->global_features = 0;
+	for (drvidx = 0; drvidx < ISDN_MAX_DRIVERS; drvidx++) {
+		if (!dev->drv[drvidx])
+			continue;
+		if (dev->drv[drvidx]->interface)
+			dev->global_features |= dev->drv[drvidx]->interface->features;
+	}
+}
+
+#ifdef CONFIG_ISDN_DIVERSION
+
+static char *map_drvname(int di)
+{
+  if ((di < 0) || (di >= ISDN_MAX_DRIVERS)) 
+    return(NULL);
+  return(dev->drvid[di]); /* driver name */
+} /* map_drvname */
+
+static int map_namedrv(char *id)
+{  int i;
+
+   for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+    { if (!strcmp(dev->drvid[i],id)) 
+        return(i);
+    }
+   return(-1);
+} /* map_namedrv */
+
+int DIVERT_REG_NAME(isdn_divert_if *i_div)
+{
+  if (i_div->if_magic != DIVERT_IF_MAGIC) 
+    return(DIVERT_VER_ERR);
+  switch (i_div->cmd)
+    {
+      case DIVERT_CMD_REL:
+        if (divert_if != i_div) 
+          return(DIVERT_REL_ERR);
+        divert_if = NULL; /* free interface */
+        MOD_DEC_USE_COUNT;
+        return(DIVERT_NO_ERR);
+
+      case DIVERT_CMD_REG:
+        if (divert_if) 
+          return(DIVERT_REG_ERR);
+        i_div->ll_cmd = isdn_command; /* set command function */
+        i_div->drv_to_name = map_drvname; 
+        i_div->name_to_drv = map_namedrv; 
+        MOD_INC_USE_COUNT;
+        divert_if = i_div; /* remember interface */
+        return(DIVERT_NO_ERR);
+
+      default:
+        return(DIVERT_CMD_ERR);   
+    }
+} /* DIVERT_REG_NAME */
+
+EXPORT_SYMBOL(DIVERT_REG_NAME);
+
+#endif /* CONFIG_ISDN_DIVERSION */
+
+
+EXPORT_SYMBOL(register_isdn);
+#ifdef CONFIG_ISDN_PPP
+EXPORT_SYMBOL(isdn_ppp_register_compressor);
+EXPORT_SYMBOL(isdn_ppp_unregister_compressor);
+#endif
+
+int
+register_isdn(isdn_if * i)
+{
+	driver *d;
+	int j;
+	ulong flags;
+	int drvidx;
+
+	if (dev->drivers >= ISDN_MAX_DRIVERS) {
+		printk(KERN_WARNING "register_isdn: Max. %d drivers supported\n",
+		       ISDN_MAX_DRIVERS);
+		return 0;
+	}
+	if (!i->writebuf_skb) {
+		printk(KERN_WARNING "register_isdn: No write routine given.\n");
+		return 0;
+	}
+	if (!(d = kmalloc(sizeof(driver), GFP_KERNEL))) {
+		printk(KERN_WARNING "register_isdn: Could not alloc driver-struct\n");
+		return 0;
+	}
+	memset((char *) d, 0, sizeof(driver));
+
+	d->maxbufsize = i->maxbufsize;
+	d->pktcount = 0;
+	d->stavail = 0;
+	d->flags = DRV_FLAG_LOADED;
+	d->online = 0;
+	d->interface = i;
+	d->channels = 0;
+	for (drvidx = 0; drvidx < ISDN_MAX_DRIVERS; drvidx++)
+		if (!dev->drv[drvidx])
+			break;
+	if (isdn_add_channels(d, drvidx, i->channels, 0)) {
+		kfree(d);
+		return 0;
+	}
+	i->channels = drvidx;
+	i->rcvcallb_skb = isdn_receive_skb_callback;
+	i->statcallb = isdn_status_callback;
+	if (!strlen(i->id))
+		sprintf(i->id, "line%d", drvidx);
+	save_flags(flags);
+	cli();
+	for (j = 0; j < drvidx; j++)
+		if (!strcmp(i->id, dev->drvid[j]))
+			sprintf(i->id, "line%d", drvidx);
+	dev->drv[drvidx] = d;
+	strcpy(dev->drvid[drvidx], i->id);
+	isdn_info_update();
+	dev->drivers++;
+	set_global_features();
+	restore_flags(flags);
+	return 1;
+}
+
+/*
+ *****************************************************************************
+ * And now the modules code.
+ *****************************************************************************
+ */
+
+static char *
+isdn_getrev(const char *revision)
+{
+	char *rev;
+	char *p;
+
+	if ((p = strchr(revision, ':'))) {
+		rev = p + 2;
+		p = strchr(rev, '$');
+		*--p = 0;
+	} else
+		rev = "???";
+	return rev;
+}
+
+#ifdef CONFIG_DEVFS_FS
+
+static devfs_handle_t devfs_handle;
+
+static void isdn_register_devfs(int k)
+{
+	char buf[11];
+
+	sprintf (buf, "isdnctrl%d", k);
+	dev->devfs_handle_isdnctrlX[k] =
+	    devfs_register (devfs_handle, buf, DEVFS_FL_DEFAULT,
+			    ISDN_MAJOR, ISDN_MINOR_CTRL + k, 0600 | S_IFCHR,
+			    &isdn_fops, NULL);
+}
+
+static void isdn_unregister_devfs(int k)
+{
+	devfs_unregister (dev->devfs_handle_isdnctrlX[k]);
+}
+
+static void isdn_init_devfs(void)
+{
+#  ifdef CONFIG_ISDN_PPP
+	int i;
+#  endif
+
+	devfs_handle = devfs_mk_dir (NULL, "isdn", NULL);
+#  ifdef CONFIG_ISDN_PPP
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		char buf[8];
+
+		sprintf (buf, "ippp%d", i);
+		dev->devfs_handle_ipppX[i] =
+		    devfs_register (devfs_handle, buf, DEVFS_FL_DEFAULT,
+				    ISDN_MAJOR, ISDN_MINOR_PPP + i,
+				    0600 | S_IFCHR, &isdn_fops, NULL);
+	}
+#  endif
+
+	dev->devfs_handle_isdninfo =
+	    devfs_register (devfs_handle, "isdninfo", DEVFS_FL_DEFAULT,
+			    ISDN_MAJOR, ISDN_MINOR_STATUS, 0600 | S_IFCHR,
+			    &isdn_fops, NULL);
+	dev->devfs_handle_isdnctrl =
+	    devfs_register (devfs_handle, "isdnctrl", DEVFS_FL_DEFAULT,
+			    ISDN_MAJOR, ISDN_MINOR_CTRL, 0600 | S_IFCHR, 
+			    &isdn_fops, NULL);
+}
+
+static void isdn_cleanup_devfs(void)
+{
+#  ifdef CONFIG_ISDN_PPP
+	int i;
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) 
+		devfs_unregister (dev->devfs_handle_ipppX[i]);
+#  endif
+	devfs_unregister (dev->devfs_handle_isdninfo);
+	devfs_unregister (dev->devfs_handle_isdnctrl);
+	devfs_unregister (devfs_handle);
+}
+
+#else   /* CONFIG_DEVFS_FS */
+static void isdn_register_devfs(int dummy)
+{
+}
+
+static void isdn_unregister_devfs(int dummy)
+{
+}
+
+static void isdn_init_devfs(void)
+{
+}
+
+static void isdn_cleanup_devfs(void)
+{
+}
+
+#endif  /* CONFIG_DEVFS_FS */
+
+/*
+ * Allocate and initialize all data, register modem-devices
+ */
+static int __init isdn_init(void)
+{
+	int i;
+	char tmprev[50];
+
+	if (!(dev = (isdn_dev *) vmalloc(sizeof(isdn_dev)))) {
+		printk(KERN_WARNING "isdn: Could not allocate device-struct.\n");
+		return -EIO;
+	}
+	memset((char *) dev, 0, sizeof(isdn_dev));
+	init_timer(&dev->timer);
+	dev->timer.function = isdn_timer_funct;
+	init_MUTEX(&dev->sem);
+	init_waitqueue_head(&dev->info_waitq);
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		dev->drvmap[i] = -1;
+		dev->chanmap[i] = -1;
+		dev->m_idx[i] = -1;
+		strcpy(dev->num[i], "???");
+		init_waitqueue_head(&dev->mdm.info[i].open_wait);
+		init_waitqueue_head(&dev->mdm.info[i].close_wait);
+	}
+	if (devfs_register_chrdev(ISDN_MAJOR, "isdn", &isdn_fops)) {
+		printk(KERN_WARNING "isdn: Could not register control devices\n");
+		vfree(dev);
+		return -EIO;
+	}
+	isdn_init_devfs();
+	if ((i = isdn_tty_modem_init()) < 0) {
+		printk(KERN_WARNING "isdn: Could not register tty devices\n");
+		if (i == -3)
+			tty_unregister_driver(&dev->mdm.cua_modem);
+		if (i <= -2)
+			tty_unregister_driver(&dev->mdm.tty_modem);
+		vfree(dev);
+		isdn_cleanup_devfs();
+		devfs_unregister_chrdev(ISDN_MAJOR, "isdn");
+		return -EIO;
+	}
+#ifdef CONFIG_ISDN_PPP
+	if (isdn_ppp_init() < 0) {
+		printk(KERN_WARNING "isdn: Could not create PPP-device-structs\n");
+		tty_unregister_driver(&dev->mdm.tty_modem);
+		tty_unregister_driver(&dev->mdm.cua_modem);
+		for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+			kfree(dev->mdm.info[i].xmit_buf - 4);
+		isdn_cleanup_devfs();
+		devfs_unregister_chrdev(ISDN_MAJOR, "isdn");
+		vfree(dev);
+		return -EIO;
+	}
+#endif                          /* CONFIG_ISDN_PPP */
+
+	strcpy(tmprev, isdn_revision);
+	printk(KERN_NOTICE "ISDN subsystem Rev: %s/", isdn_getrev(tmprev));
+	strcpy(tmprev, isdn_tty_revision);
+	printk("%s/", isdn_getrev(tmprev));
+	strcpy(tmprev, isdn_net_revision);
+	printk("%s/", isdn_getrev(tmprev));
+	strcpy(tmprev, isdn_ppp_revision);
+	printk("%s/", isdn_getrev(tmprev));
+	strcpy(tmprev, isdn_audio_revision);
+	printk("%s/", isdn_getrev(tmprev));
+	strcpy(tmprev, isdn_v110_revision);
+	printk("%s", isdn_getrev(tmprev));
+
+#ifdef MODULE
+	printk(" loaded\n");
+#else
+	printk("\n");
+#endif
+	isdn_info_update();
+	return 0;
+}
+
+/*
+ * Unload module
+ */
+static void __exit isdn_exit(void)
+{
+	unsigned long flags;
+	int i;
+
+#ifdef CONFIG_ISDN_PPP
+	isdn_ppp_cleanup();
+#endif
+	save_flags(flags);
+	cli();
+	if (isdn_net_rmall() < 0) {
+		printk(KERN_WARNING "isdn: net-device busy, remove cancelled\n");
+		restore_flags(flags);
+		return;
+	}
+	if (tty_unregister_driver(&dev->mdm.tty_modem)) {
+		printk(KERN_WARNING "isdn: ttyI-device busy, remove cancelled\n");
+		restore_flags(flags);
+		return;
+	}
+	if (tty_unregister_driver(&dev->mdm.cua_modem)) {
+		printk(KERN_WARNING "isdn: cui-device busy, remove cancelled\n");
+		restore_flags(flags);
+		return;
+	}
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		isdn_tty_cleanup_xmit(&dev->mdm.info[i]);
+		kfree(dev->mdm.info[i].xmit_buf - 4);
+#ifdef CONFIG_ISDN_TTY_FAX
+		kfree(dev->mdm.info[i].fax);
+#endif
+	}
+	if (devfs_unregister_chrdev(ISDN_MAJOR, "isdn") != 0) {
+		printk(KERN_WARNING "isdn: controldevice busy, remove cancelled\n");
+		restore_flags(flags);
+	} else {
+		isdn_cleanup_devfs();
+		del_timer(&dev->timer);
+		restore_flags(flags);
+		/* call vfree with interrupts enabled, else it will hang */
+		vfree(dev);
+		printk(KERN_NOTICE "ISDN-subsystem unloaded\n");
+	}
+}
+
+module_init(isdn_init);
+module_exit(isdn_exit);
diff -Nru a/drivers/isdn/i4l/isdn_common.h b/drivers/isdn/i4l/isdn_common.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/i4l/isdn_common.h	Mon Apr 22 15:32:18 2002
@@ -0,0 +1,49 @@
+/* $Id: isdn_common.h,v 1.21.6.1 2001/09/23 22:24:31 kai Exp $
+ *
+ * header for Linux ISDN subsystem
+ * common used functions and debugging-switches (linklevel).
+ *
+ * Copyright 1994-1999  by Fritz Elfert (fritz@isdn4linux.de)
+ * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
+ * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#undef  ISDN_DEBUG_MODEM_OPEN
+#undef  ISDN_DEBUG_MODEM_IOCTL
+#undef  ISDN_DEBUG_MODEM_WAITSENT
+#undef  ISDN_DEBUG_MODEM_HUP
+#undef  ISDN_DEBUG_MODEM_ICALL
+#undef  ISDN_DEBUG_MODEM_DUMP
+#undef  ISDN_DEBUG_MODEM_VOICE
+#undef  ISDN_DEBUG_AT
+#undef  ISDN_DEBUG_NET_DUMP
+#undef  ISDN_DEBUG_NET_DIAL
+#undef  ISDN_DEBUG_NET_ICALL
+
+/* Prototypes */
+extern void isdn_MOD_INC_USE_COUNT(void);
+extern void isdn_MOD_DEC_USE_COUNT(void);
+extern void isdn_lock_drivers(void);
+extern void isdn_unlock_drivers(void);
+extern void isdn_free_channel(int di, int ch, int usage);
+extern void isdn_all_eaz(int di, int ch);
+extern int isdn_command(isdn_ctrl *);
+extern int isdn_dc2minor(int di, int ch);
+extern void isdn_info_update(void);
+extern char *isdn_map_eaz2msn(char *msn, int di);
+extern void isdn_timer_ctrl(int tf, int onoff);
+extern void isdn_unexclusive_channel(int di, int ch);
+extern int isdn_getnum(char **);
+extern int isdn_readbchan(int, int, u_char *, u_char *, int, wait_queue_head_t *);
+extern int isdn_get_free_channel(int, int, int, int, int, char *);
+extern int isdn_writebuf_skb_stub(int, int, int, struct sk_buff *);
+extern int register_isdn(isdn_if * i);
+extern int isdn_msncmp( const char *,  const char *);
+extern int isdn_add_channels(driver *, int, int, int);
+#if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP)
+extern void isdn_dumppkt(char *, u_char *, int, int);
+#endif
diff -Nru a/drivers/isdn/i4l/isdn_concap.c b/drivers/isdn/i4l/isdn_concap.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/i4l/isdn_concap.c	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,108 @@
+/* $Id: isdn_concap.c,v 1.8.6.1 2001/09/23 22:24:31 kai Exp $
+ * 
+ * Linux ISDN subsystem, protocol encapsulation
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+/* Stuff to support the concap_proto by isdn4linux. isdn4linux - specific
+ * stuff goes here. Stuff that depends only on the concap protocol goes to
+ * another -- protocol specific -- source file.
+ *
+ */
+
+
+#include <linux/isdn.h>
+#include "isdn_x25iface.h"
+#include "isdn_net.h"
+#include <linux/concap.h>
+#include "isdn_concap.h"
+
+
+/* The following set of device service operations are for encapsulation
+   protocols that require for reliable datalink semantics. That means:
+
+   - before any data is to be submitted the connection must explicitly
+     be set up.
+   - after the successful set up of the connection is signalled the
+     connection is considered to be reliably up.
+
+   Auto-dialing ist not compatible with this requirements. Thus, auto-dialing 
+   is completely bypassed.
+
+   It might be possible to implement a (non standardized) datalink protocol
+   that provides a reliable data link service while using some auto dialing
+   mechanism. Such a protocol would need an auxiliary channel (i.e. user-user-
+   signaling on the D-channel) while the B-channel is down.
+   */
+
+
+int isdn_concap_dl_data_req(struct concap_proto *concap, struct sk_buff *skb)
+{
+	struct net_device *ndev = concap -> net_dev;
+	isdn_net_dev *nd = ((isdn_net_local *) ndev->priv)->netdev;
+	isdn_net_local *lp = isdn_net_get_locked_lp(nd);
+
+	IX25DEBUG( "isdn_concap_dl_data_req: %s \n", concap->net_dev->name);
+	if (!lp) {
+		IX25DEBUG( "isdn_concap_dl_data_req: %s : isdn_net_send_skb returned %d\n", concap -> net_dev -> name, 1);
+		return 1;
+	}
+	lp->huptimer = 0;
+	isdn_net_writebuf_skb(lp, skb);
+	spin_unlock_bh(&lp->xmit_lock);
+	IX25DEBUG( "isdn_concap_dl_data_req: %s : isdn_net_send_skb returned %d\n", concap -> net_dev -> name, 0);
+	return 0;
+}
+
+
+int isdn_concap_dl_connect_req(struct concap_proto *concap)
+{
+	struct net_device *ndev = concap -> net_dev;
+	isdn_net_local *lp = (isdn_net_local *) ndev->priv;
+	int ret;
+	IX25DEBUG( "isdn_concap_dl_connect_req: %s \n", ndev -> name);
+
+	/* dial ... */
+	ret = isdn_net_dial_req( lp );
+	if ( ret ) IX25DEBUG("dialing failed\n");
+	return ret;
+}
+
+int isdn_concap_dl_disconn_req(struct concap_proto *concap)
+{
+	IX25DEBUG( "isdn_concap_dl_disconn_req: %s \n", concap -> net_dev -> name);
+
+	isdn_net_hangup( concap -> net_dev );
+	return 0;
+}
+
+struct concap_device_ops isdn_concap_reliable_dl_dops = {
+	&isdn_concap_dl_data_req,
+	&isdn_concap_dl_connect_req,
+	&isdn_concap_dl_disconn_req
+};
+
+struct concap_device_ops isdn_concap_demand_dial_dops = {
+	NULL, /* set this first entry to something like &isdn_net_start_xmit,
+		 but the entry part of the current isdn_net_start_xmit must be
+		 separated first. */
+	/* no connection control for demand dial semantics */
+	NULL,
+	NULL,
+};
+
+/* The following should better go into a dedicated source file such that
+   this sourcefile does not need to include any protocol specific header
+   files. For now:
+   */
+struct concap_proto * isdn_concap_new( int encap )
+{
+	switch ( encap ) {
+	case ISDN_NET_ENCAP_X25IFACE:
+		return isdn_x25iface_proto_new();
+	}
+	return NULL;
+}
diff -Nru a/drivers/isdn/i4l/isdn_concap.h b/drivers/isdn/i4l/isdn_concap.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/i4l/isdn_concap.h	Mon Apr 22 15:32:18 2002
@@ -0,0 +1,14 @@
+/* $Id: isdn_concap.h,v 1.3.6.1 2001/09/23 22:24:31 kai Exp $
+ *
+ * Linux ISDN subsystem, protocol encapsulation
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+extern struct concap_device_ops isdn_concap_reliable_dl_dops;
+extern struct concap_device_ops isdn_concap_demand_dial_dops;
+extern struct concap_proto * isdn_concap_new( int );
+
+
diff -Nru a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/i4l/isdn_net.c	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,3238 @@
+/* $Id: isdn_net.c,v 1.140.6.11 2001/11/06 20:58:28 kai Exp $
+ *
+ * Linux ISDN subsystem, network interfaces and related functions (linklevel).
+ *
+ * Copyright 1994-1998  by Fritz Elfert (fritz@isdn4linux.de)
+ * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
+ * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Jan 2001: fix CISCO HDLC      Bjoern A. Zeeb <i4l@zabbadoz.net>
+ *           for info on the protocol, see 
+ *           http://i4l.zabbadoz.net/i4l/cisco-hdlc.txt
+ */
+
+#include <linux/config.h>
+#include <linux/isdn.h>
+#include <net/arp.h>
+#include <net/dst.h>
+#include <net/pkt_sched.h>
+#include <linux/inetdevice.h>
+#include "isdn_common.h"
+#include "isdn_net.h"
+#ifdef CONFIG_ISDN_PPP
+#include "isdn_ppp.h"
+#endif
+#ifdef CONFIG_ISDN_X25
+#include <linux/concap.h>
+#include "isdn_concap.h"
+#endif
+
+
+/*
+ * Outline of new tbusy handling: 
+ *
+ * Old method, roughly spoken, consisted of setting tbusy when entering
+ * isdn_net_start_xmit() and at several other locations and clearing
+ * it from isdn_net_start_xmit() thread when sending was successful.
+ *
+ * With 2.3.x multithreaded network core, to prevent problems, tbusy should
+ * only be set by the isdn_net_start_xmit() thread and only when a tx-busy
+ * condition is detected. Other threads (in particular isdn_net_stat_callb())
+ * are only allowed to clear tbusy.
+ *
+ * -HE
+ */
+
+/*
+ * About SOFTNET:
+ * Most of the changes were pretty obvious and basically done by HE already.
+ *
+ * One problem of the isdn net device code is that is uses struct net_device
+ * for masters and slaves. However, only master interface are registered to 
+ * the network layer, and therefore, it only makes sense to call netif_* 
+ * functions on them.
+ *
+ * --KG
+ */
+
+/* 
+ * Find out if the netdevice has been ifup-ed yet.
+ * For slaves, look at the corresponding master.
+ */
+static __inline__ int isdn_net_device_started(isdn_net_dev *n)
+{
+	isdn_net_local *lp = n->local;
+	struct net_device *dev;
+	
+	if (lp->master) 
+		dev = lp->master;
+	else
+		dev = &n->dev;
+	return netif_running(dev);
+}
+
+/*
+ * wake up the network -> net_device queue.
+ * For slaves, wake the corresponding master interface.
+ */
+static __inline__ void isdn_net_device_wake_queue(isdn_net_local *lp)
+{
+	if (lp->master) 
+		netif_wake_queue(lp->master);
+	else
+		netif_wake_queue(&lp->netdev->dev);
+}
+
+/*
+ * stop the network -> net_device queue.
+ * For slaves, stop the corresponding master interface.
+ */
+static __inline__ void isdn_net_device_stop_queue(isdn_net_local *lp)
+{
+	if (lp->master)
+		netif_stop_queue(lp->master);
+	else
+		netif_stop_queue(&lp->netdev->dev);
+}
+
+/*
+ * find out if the net_device which this lp belongs to (lp can be
+ * master or slave) is busy. It's busy iff all (master and slave) 
+ * queues are busy
+ */
+static __inline__ int isdn_net_device_busy(isdn_net_local *lp)
+{
+	isdn_net_local *nlp;
+	isdn_net_dev *nd;
+	unsigned long flags;
+
+	if (!isdn_net_lp_busy(lp))
+		return 0;
+
+	if (lp->master)
+		nd = ((isdn_net_local *) lp->master->priv)->netdev;
+	else
+		nd = lp->netdev;
+	
+	spin_lock_irqsave(&nd->queue_lock, flags);
+	nlp = lp->next;
+	while (nlp != lp) {
+		if (!isdn_net_lp_busy(nlp)) {
+			spin_unlock_irqrestore(&nd->queue_lock, flags);
+			return 0;
+		}
+		nlp = nlp->next;
+	}
+	spin_unlock_irqrestore(&nd->queue_lock, flags);
+	return 1;
+}
+
+static __inline__ void isdn_net_inc_frame_cnt(isdn_net_local *lp)
+{
+	atomic_inc(&lp->frame_cnt);
+	if (isdn_net_device_busy(lp))
+		isdn_net_device_stop_queue(lp);
+}
+
+static __inline__ void isdn_net_dec_frame_cnt(isdn_net_local *lp)
+{
+	atomic_dec(&lp->frame_cnt);
+
+	if (!(isdn_net_device_busy(lp))) {
+		if (!skb_queue_empty(&lp->super_tx_queue)) {
+			queue_task(&lp->tqueue, &tq_immediate);
+			mark_bh(IMMEDIATE_BH);
+		} else {
+			isdn_net_device_wake_queue(lp);
+		}
+       }                                                                      
+}
+
+static __inline__ void isdn_net_zero_frame_cnt(isdn_net_local *lp)
+{
+	atomic_set(&lp->frame_cnt, 0);
+}
+
+/* For 2.2.x we leave the transmitter busy timeout at 2 secs, just 
+ * to be safe.
+ * For 2.3.x we push it up to 20 secs, because call establishment
+ * (in particular callback) may take such a long time, and we 
+ * don't want confusing messages in the log. However, there is a slight
+ * possibility that this large timeout will break other things like MPPP,
+ * which might rely on the tx timeout. If so, we'll find out this way...
+ */
+
+#define ISDN_NET_TX_TIMEOUT (20*HZ) 
+
+/* Prototypes */
+
+int isdn_net_force_dial_lp(isdn_net_local *);
+static int isdn_net_start_xmit(struct sk_buff *, struct net_device *);
+
+static void isdn_net_ciscohdlck_connected(isdn_net_local *lp);
+static void isdn_net_ciscohdlck_disconnected(isdn_net_local *lp);
+
+char *isdn_net_revision = "$Revision: 1.140.6.11 $";
+
+ /*
+  * Code for raw-networking over ISDN
+  */
+
+static void
+isdn_net_unreachable(struct net_device *dev, struct sk_buff *skb, char *reason)
+{
+	if(skb) {
+
+		u_short proto = ntohs(skb->protocol);
+
+		printk(KERN_DEBUG "isdn_net: %s: %s, signalling dst_link_failure %s\n",
+		       dev->name,
+		       (reason != NULL) ? reason : "unknown",
+		       (proto != ETH_P_IP) ? "Protocol != ETH_P_IP" : "");
+		
+		dst_link_failure(skb);
+	}
+	else {  /* dial not triggered by rawIP packet */
+		printk(KERN_DEBUG "isdn_net: %s: %s\n",
+			   dev->name,
+			   (reason != NULL) ? reason : "reason unknown");
+	}
+}
+
+static void
+isdn_net_reset(struct net_device *dev)
+{
+#ifdef CONFIG_ISDN_X25
+	struct concap_device_ops * dops =
+		( (isdn_net_local *) dev->priv ) -> dops;
+	struct concap_proto * cprot =
+		( (isdn_net_local *) dev->priv ) -> netdev -> cprot;
+#endif
+	ulong flags;
+
+	/* not sure if the cli() is needed at all --KG */
+	save_flags(flags);
+	cli();                  /* Avoid glitch on writes to CMD regs */
+#ifdef CONFIG_ISDN_X25
+	if( cprot && cprot -> pops && dops )
+		cprot -> pops -> restart ( cprot, dev, dops );
+#endif
+	restore_flags(flags);
+}
+
+/* Open/initialize the board. */
+static int
+isdn_net_open(struct net_device *dev)
+{
+	int i;
+	struct net_device *p;
+	struct in_device *in_dev;
+
+	/* moved here from isdn_net_reset, because only the master has an
+	   interface associated which is supposed to be started. BTW:
+	   we need to call netif_start_queue, not netif_wake_queue here */
+	netif_start_queue(dev);
+
+	isdn_net_reset(dev);
+	/* Fill in the MAC-level header (not needed, but for compatibility... */
+	for (i = 0; i < ETH_ALEN - sizeof(u32); i++)
+		dev->dev_addr[i] = 0xfc;
+	if ((in_dev = dev->ip_ptr) != NULL) {
+		/*
+		 *      Any address will do - we take the first
+		 */
+		struct in_ifaddr *ifa = in_dev->ifa_list;
+		if (ifa != NULL)
+			memcpy(dev->dev_addr+2, &ifa->ifa_local, 4);
+	}
+
+	/* If this interface has slaves, start them also */
+
+	if ((p = (((isdn_net_local *) dev->priv)->slave))) {
+		while (p) {
+			isdn_net_reset(p);
+			p = (((isdn_net_local *) p->priv)->slave);
+		}
+	}
+	isdn_MOD_INC_USE_COUNT();
+	return 0;
+}
+
+/*
+ * Assign an ISDN-channel to a net-interface
+ */
+static void
+isdn_net_bind_channel(isdn_net_local * lp, int idx)
+{
+	ulong flags;
+
+	save_flags(flags);
+	cli();
+	lp->flags |= ISDN_NET_CONNECTED;
+	lp->isdn_device = dev->drvmap[idx];
+	lp->isdn_channel = dev->chanmap[idx];
+	dev->rx_netdev[idx] = lp->netdev;
+	dev->st_netdev[idx] = lp->netdev;
+	restore_flags(flags);
+}
+
+/*
+ * unbind a net-interface (resets interface after an error)
+ */
+static void
+isdn_net_unbind_channel(isdn_net_local * lp)
+{
+	ulong flags;
+
+	save_flags(flags);
+	cli();
+	skb_queue_purge(&lp->super_tx_queue);
+
+	if (!lp->master) {	/* reset only master device */
+		/* Moral equivalent of dev_purge_queues():
+		   BEWARE! This chunk of code cannot be called from hardware
+		   interrupt handler. I hope it is true. --ANK
+		 */
+		qdisc_reset(lp->netdev->dev.qdisc);
+	}
+	lp->dialstate = 0;
+	dev->rx_netdev[isdn_dc2minor(lp->isdn_device, lp->isdn_channel)] = NULL;
+	dev->st_netdev[isdn_dc2minor(lp->isdn_device, lp->isdn_channel)] = NULL;
+	isdn_free_channel(lp->isdn_device, lp->isdn_channel, ISDN_USAGE_NET);
+	lp->flags &= ~ISDN_NET_CONNECTED;
+	lp->isdn_device = -1;
+	lp->isdn_channel = -1;
+
+	restore_flags(flags);
+}
+
+/*
+ * Perform auto-hangup and cps-calculation for net-interfaces.
+ *
+ * auto-hangup:
+ * Increment idle-counter (this counter is reset on any incoming or
+ * outgoing packet), if counter exceeds configured limit either do a
+ * hangup immediately or - if configured - wait until just before the next
+ * charge-info.
+ *
+ * cps-calculation (needed for dynamic channel-bundling):
+ * Since this function is called every second, simply reset the
+ * byte-counter of the interface after copying it to the cps-variable.
+ */
+unsigned long last_jiffies = -HZ;
+
+void
+isdn_net_autohup()
+{
+	isdn_net_dev *p = dev->netdev;
+	int anymore;
+
+	anymore = 0;
+	while (p) {
+		isdn_net_local *l = p->local;
+		if (jiffies == last_jiffies)
+			l->cps = l->transcount;
+		else
+			l->cps = (l->transcount * HZ) / (jiffies - last_jiffies);
+		l->transcount = 0;
+		if (dev->net_verbose > 3)
+			printk(KERN_DEBUG "%s: %d bogocps\n", l->name, l->cps);
+		if ((l->flags & ISDN_NET_CONNECTED) && (!l->dialstate)) {
+			anymore = 1;
+			l->huptimer++;
+			/*
+			 * if there is some dialmode where timeout-hangup
+			 * should _not_ be done, check for that here
+			 */
+			if ((l->onhtime) &&
+			    (l->huptimer > l->onhtime))
+			{
+				if (l->hupflags & ISDN_MANCHARGE &&
+				    l->hupflags & ISDN_CHARGEHUP) {
+					while (time_after(jiffies, l->chargetime + l->chargeint))
+						l->chargetime += l->chargeint;
+					if (time_after(jiffies, l->chargetime + l->chargeint - 2 * HZ))
+						if (l->outgoing || l->hupflags & ISDN_INHUP)
+							isdn_net_hangup(&p->dev);
+				} else if (l->outgoing) {
+					if (l->hupflags & ISDN_CHARGEHUP) {
+						if (l->hupflags & ISDN_WAITCHARGE) {
+							printk(KERN_DEBUG "isdn_net: Hupflags of %s are %X\n",
+							       l->name, l->hupflags);
+							isdn_net_hangup(&p->dev);
+						} else if (time_after(jiffies, l->chargetime + l->chargeint)) {
+							printk(KERN_DEBUG
+							       "isdn_net: %s: chtime = %lu, chint = %d\n",
+							       l->name, l->chargetime, l->chargeint);
+							isdn_net_hangup(&p->dev);
+						}
+					}
+				} else if (l->hupflags & ISDN_INHUP)
+					isdn_net_hangup(&p->dev);
+			}
+
+			if(dev->global_flags & ISDN_GLOBAL_STOPPED || (ISDN_NET_DIALMODE(*l) == ISDN_NET_DM_OFF)) {
+				isdn_net_hangup(&p->dev);
+				break;
+			}
+		}
+		p = (isdn_net_dev *) p->next;
+	}
+	last_jiffies = jiffies;
+	isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, anymore);
+}
+
+static void isdn_net_lp_disconnected(isdn_net_local *lp)
+{
+	isdn_net_rm_from_bundle(lp);
+}
+
+/*
+ * Handle status-messages from ISDN-interfacecard.
+ * This function is called from within the main-status-dispatcher
+ * isdn_status_callback, which itself is called from the low-level driver.
+ * Return: 1 = Event handled, 0 = not for us or unknown Event.
+ */
+int
+isdn_net_stat_callback(int idx, isdn_ctrl *c)
+{
+	isdn_net_dev *p = dev->st_netdev[idx];
+	int cmd = c->command;
+
+	if (p) {
+		isdn_net_local *lp = p->local;
+#ifdef CONFIG_ISDN_X25
+		struct concap_proto *cprot = lp -> netdev -> cprot;
+		struct concap_proto_ops *pops = cprot ? cprot -> pops : 0;
+#endif
+		switch (cmd) {
+			case ISDN_STAT_BSENT:
+				/* A packet has successfully been sent out */
+				if ((lp->flags & ISDN_NET_CONNECTED) &&
+				    (!lp->dialstate)) {
+					isdn_net_dec_frame_cnt(lp);
+					lp->stats.tx_packets++;
+					lp->stats.tx_bytes += c->parm.length;
+				}
+				return 1;
+			case ISDN_STAT_DCONN:
+				/* D-Channel is up */
+				switch (lp->dialstate) {
+					case 4:
+					case 7:
+					case 8:
+						lp->dialstate++;
+						return 1;
+					case 12:
+						lp->dialstate = 5;
+						return 1;
+				}
+				break;
+			case ISDN_STAT_DHUP:
+				/* Either D-Channel-hangup or error during dialout */
+#ifdef CONFIG_ISDN_X25
+				/* If we are not connencted then dialing had
+				   failed. If there are generic encap protocol
+				   receiver routines signal the closure of
+				   the link*/
+
+				if( !(lp->flags & ISDN_NET_CONNECTED)
+				    && pops && pops -> disconn_ind )
+					pops -> disconn_ind(cprot);
+#endif /* CONFIG_ISDN_X25 */
+				if ((!lp->dialstate) && (lp->flags & ISDN_NET_CONNECTED)) {
+					if (lp->p_encap == ISDN_NET_ENCAP_CISCOHDLCK)
+						isdn_net_ciscohdlck_disconnected(lp);
+#ifdef CONFIG_ISDN_PPP
+					if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+						isdn_ppp_free(lp);
+#endif
+					isdn_net_lp_disconnected(lp);
+					isdn_all_eaz(lp->isdn_device, lp->isdn_channel);
+					printk(KERN_INFO "%s: remote hangup\n", lp->name);
+					printk(KERN_INFO "%s: Chargesum is %d\n", lp->name,
+					       lp->charge);
+					isdn_net_unbind_channel(lp);
+					return 1;
+				}
+				break;
+#ifdef CONFIG_ISDN_X25
+			case ISDN_STAT_BHUP:
+				/* B-Channel-hangup */
+				/* try if there are generic encap protocol
+				   receiver routines and signal the closure of
+				   the link */
+				if( pops  &&  pops -> disconn_ind ){
+						pops -> disconn_ind(cprot);
+						return 1;
+					}
+				break;
+#endif /* CONFIG_ISDN_X25 */
+			case ISDN_STAT_BCONN:
+				/* B-Channel is up */
+				isdn_net_zero_frame_cnt(lp);
+				switch (lp->dialstate) {
+					case 5:
+					case 6:
+					case 7:
+					case 8:
+					case 9:
+					case 10:
+					case 12:
+						if (lp->dialstate <= 6) {
+							dev->usage[idx] |= ISDN_USAGE_OUTGOING;
+							isdn_info_update();
+						} else
+							dev->rx_netdev[idx] = p;
+						lp->dialstate = 0;
+						isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 1);
+						if (lp->p_encap == ISDN_NET_ENCAP_CISCOHDLCK)
+							isdn_net_ciscohdlck_connected(lp);
+						if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP) {
+							if (lp->master) { /* is lp a slave? */
+								isdn_net_dev *nd = ((isdn_net_local *)lp->master->priv)->netdev;
+								isdn_net_add_to_bundle(nd, lp);
+							}
+						}
+						printk(KERN_INFO "isdn_net: %s connected\n", lp->name);
+						/* If first Chargeinfo comes before B-Channel connect,
+						 * we correct the timestamp here.
+						 */
+						lp->chargetime = jiffies;
+
+						/* reset dial-timeout */
+						lp->dialstarted = 0;
+						lp->dialwait_timer = 0;
+
+#ifdef CONFIG_ISDN_PPP
+						if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+							isdn_ppp_wakeup_daemon(lp);
+#endif
+#ifdef CONFIG_ISDN_X25
+						/* try if there are generic concap receiver routines */
+						if( pops )
+							if( pops->connect_ind)
+								pops->connect_ind(cprot);
+#endif /* CONFIG_ISDN_X25 */
+						/* ppp needs to do negotiations first */
+						if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP)
+							isdn_net_device_wake_queue(lp);
+						return 1;
+				}
+				break;
+			case ISDN_STAT_NODCH:
+				/* No D-Channel avail. */
+				if (lp->dialstate == 4) {
+					lp->dialstate--;
+					return 1;
+				}
+				break;
+			case ISDN_STAT_CINF:
+				/* Charge-info from TelCo. Calculate interval between
+				 * charge-infos and set timestamp for last info for
+				 * usage by isdn_net_autohup()
+				 */
+				lp->charge++;
+				if (lp->hupflags & ISDN_HAVECHARGE) {
+					lp->hupflags &= ~ISDN_WAITCHARGE;
+					lp->chargeint = jiffies - lp->chargetime - (2 * HZ);
+				}
+				if (lp->hupflags & ISDN_WAITCHARGE)
+					lp->hupflags |= ISDN_HAVECHARGE;
+				lp->chargetime = jiffies;
+				printk(KERN_DEBUG "isdn_net: Got CINF chargetime of %s now %lu\n",
+				       lp->name, lp->chargetime);
+				return 1;
+		}
+	}
+	return 0;
+}
+
+/*
+ * Perform dialout for net-interfaces and timeout-handling for
+ * D-Channel-up and B-Channel-up Messages.
+ * This function is initially called from within isdn_net_start_xmit() or
+ * or isdn_net_find_icall() after initializing the dialstate for an
+ * interface. If further calls are needed, the function schedules itself
+ * for a timer-callback via isdn_timer_function().
+ * The dialstate is also affected by incoming status-messages from
+ * the ISDN-Channel which are handled in isdn_net_stat_callback() above.
+ */
+void
+isdn_net_dial(void)
+{
+	isdn_net_dev *p = dev->netdev;
+	int anymore = 0;
+	int i;
+	unsigned long flags;
+	isdn_ctrl cmd;
+
+	while (p) {
+		isdn_net_local *lp = p->local;
+
+#ifdef ISDN_DEBUG_NET_DIAL
+		if (lp->dialstate)
+			printk(KERN_DEBUG "%s: dialstate=%d\n", lp->name, lp->dialstate);
+#endif
+		switch (lp->dialstate) {
+			case 0:
+				/* Nothing to do for this interface */
+				break;
+			case 1:
+				/* Initiate dialout. Set phone-number-pointer to first number
+				 * of interface.
+				 */
+				save_flags(flags);
+				cli();
+				lp->dial = lp->phone[1];
+				restore_flags(flags);
+				if (!lp->dial) {
+					printk(KERN_WARNING "%s: phone number deleted?\n",
+					       lp->name);
+					isdn_net_hangup(&p->dev);
+					break;
+				}
+				anymore = 1;
+
+				if(lp->dialtimeout > 0)
+					if(lp->dialstarted == 0 || time_after(jiffies, lp->dialstarted + lp->dialtimeout + lp->dialwait)) {
+						lp->dialstarted = jiffies;
+						lp->dialwait_timer = 0;
+					}
+
+				lp->dialstate++;
+				/* Fall through */
+			case 2:
+				/* Prepare dialing. Clear EAZ, then set EAZ. */
+				cmd.driver = lp->isdn_device;
+				cmd.arg = lp->isdn_channel;
+				cmd.command = ISDN_CMD_CLREAZ;
+				isdn_command(&cmd);
+				sprintf(cmd.parm.num, "%s", isdn_map_eaz2msn(lp->msn, cmd.driver));
+				cmd.command = ISDN_CMD_SETEAZ;
+				isdn_command(&cmd);
+				lp->dialretry = 0;
+				anymore = 1;
+				lp->dialstate++;
+				/* Fall through */
+			case 3:
+				/* Setup interface, dial current phone-number, switch to next number.
+				 * If list of phone-numbers is exhausted, increment
+				 * retry-counter.
+				 */
+				if(dev->global_flags & ISDN_GLOBAL_STOPPED || (ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_OFF)) {
+					char *s;
+					if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+						s = "dial suppressed: isdn system stopped";
+					else
+						s = "dial suppressed: dialmode `off'";
+					isdn_net_unreachable(&p->dev, 0, s);
+					isdn_net_hangup(&p->dev);
+					break;
+				}
+				cmd.driver = lp->isdn_device;
+				cmd.command = ISDN_CMD_SETL2;
+				cmd.arg = lp->isdn_channel + (lp->l2_proto << 8);
+				isdn_command(&cmd);
+				cmd.driver = lp->isdn_device;
+				cmd.command = ISDN_CMD_SETL3;
+				cmd.arg = lp->isdn_channel + (lp->l3_proto << 8);
+				isdn_command(&cmd);
+				cmd.driver = lp->isdn_device;
+				cmd.arg = lp->isdn_channel;
+				save_flags(flags);
+				cli();
+				if (!lp->dial) {
+					restore_flags(flags);
+					printk(KERN_WARNING "%s: phone number deleted?\n",
+					       lp->name);
+					isdn_net_hangup(&p->dev);
+					break;
+				}
+				if (!strncmp(lp->dial->num, "LEASED", strlen("LEASED"))) {
+					restore_flags(flags);
+					lp->dialstate = 4;
+					printk(KERN_INFO "%s: Open leased line ...\n", lp->name);
+				} else {
+					if(lp->dialtimeout > 0)
+						if (time_after(jiffies, lp->dialstarted + lp->dialtimeout)) {
+							restore_flags(flags);
+							lp->dialwait_timer = jiffies + lp->dialwait;
+							lp->dialstarted = 0;
+							isdn_net_unreachable(&p->dev, 0, "dial: timed out");
+							isdn_net_hangup(&p->dev);
+							break;
+						}
+
+					sprintf(cmd.parm.setup.phone, "%s", lp->dial->num);
+					/*
+					 * Switch to next number or back to start if at end of list.
+					 */
+					if (!(lp->dial = (isdn_net_phone *) lp->dial->next)) {
+						lp->dial = lp->phone[1];
+						lp->dialretry++;
+
+						if (lp->dialretry > lp->dialmax) {
+							restore_flags(flags);
+							if (lp->dialtimeout == 0) {
+								lp->dialwait_timer = jiffies + lp->dialwait;
+								lp->dialstarted = 0;
+								isdn_net_unreachable(&p->dev, 0, "dial: tried all numbers dialmax times");
+							}
+							isdn_net_hangup(&p->dev);
+							break;
+						}
+					}
+					restore_flags(flags);
+					cmd.driver = lp->isdn_device;
+					cmd.command = ISDN_CMD_DIAL;
+					cmd.parm.setup.si1 = 7;
+					cmd.parm.setup.si2 = 0;
+					sprintf(cmd.parm.setup.eazmsn, "%s",
+						isdn_map_eaz2msn(lp->msn, cmd.driver));
+					i = isdn_dc2minor(lp->isdn_device, lp->isdn_channel);
+					if (i >= 0) {
+						strcpy(dev->num[i], cmd.parm.setup.phone);
+						dev->usage[i] |= ISDN_USAGE_OUTGOING;
+						isdn_info_update();
+					}
+					printk(KERN_INFO "%s: dialing %d %s...\n", lp->name,
+					       lp->dialretry, cmd.parm.setup.phone);
+					lp->dtimer = 0;
+#ifdef ISDN_DEBUG_NET_DIAL
+					printk(KERN_DEBUG "dial: d=%d c=%d\n", lp->isdn_device,
+					       lp->isdn_channel);
+#endif
+					isdn_command(&cmd);
+				}
+				lp->huptimer = 0;
+				lp->outgoing = 1;
+				if (lp->chargeint) {
+					lp->hupflags |= ISDN_HAVECHARGE;
+					lp->hupflags &= ~ISDN_WAITCHARGE;
+				} else {
+					lp->hupflags |= ISDN_WAITCHARGE;
+					lp->hupflags &= ~ISDN_HAVECHARGE;
+				}
+				anymore = 1;
+				lp->dialstate =
+				    (lp->cbdelay &&
+				     (lp->flags & ISDN_NET_CBOUT)) ? 12 : 4;
+				break;
+			case 4:
+				/* Wait for D-Channel-connect.
+				 * If timeout, switch back to state 3.
+				 * Dialmax-handling moved to state 3.
+				 */
+				if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT10)
+					lp->dialstate = 3;
+				anymore = 1;
+				break;
+			case 5:
+				/* Got D-Channel-Connect, send B-Channel-request */
+				cmd.driver = lp->isdn_device;
+				cmd.arg = lp->isdn_channel;
+				cmd.command = ISDN_CMD_ACCEPTB;
+				anymore = 1;
+				lp->dtimer = 0;
+				lp->dialstate++;
+				isdn_command(&cmd);
+				break;
+			case 6:
+				/* Wait for B- or D-Channel-connect. If timeout,
+				 * switch back to state 3.
+				 */
+#ifdef ISDN_DEBUG_NET_DIAL
+				printk(KERN_DEBUG "dialtimer2: %d\n", lp->dtimer);
+#endif
+				if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT10)
+					lp->dialstate = 3;
+				anymore = 1;
+				break;
+			case 7:
+				/* Got incoming Call, setup L2 and L3 protocols,
+				 * then wait for D-Channel-connect
+				 */
+#ifdef ISDN_DEBUG_NET_DIAL
+				printk(KERN_DEBUG "dialtimer4: %d\n", lp->dtimer);
+#endif
+				cmd.driver = lp->isdn_device;
+				cmd.command = ISDN_CMD_SETL2;
+				cmd.arg = lp->isdn_channel + (lp->l2_proto << 8);
+				isdn_command(&cmd);
+				cmd.driver = lp->isdn_device;
+				cmd.command = ISDN_CMD_SETL3;
+				cmd.arg = lp->isdn_channel + (lp->l3_proto << 8);
+				isdn_command(&cmd);
+				if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT15)
+					isdn_net_hangup(&p->dev);
+				else {
+					anymore = 1;
+					lp->dialstate++;
+				}
+				break;
+			case 9:
+				/* Got incoming D-Channel-Connect, send B-Channel-request */
+				cmd.driver = lp->isdn_device;
+				cmd.arg = lp->isdn_channel;
+				cmd.command = ISDN_CMD_ACCEPTB;
+				isdn_command(&cmd);
+				anymore = 1;
+				lp->dtimer = 0;
+				lp->dialstate++;
+				break;
+			case 8:
+			case 10:
+				/*  Wait for B- or D-channel-connect */
+#ifdef ISDN_DEBUG_NET_DIAL
+				printk(KERN_DEBUG "dialtimer4: %d\n", lp->dtimer);
+#endif
+				if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT10)
+					isdn_net_hangup(&p->dev);
+				else
+					anymore = 1;
+				break;
+			case 11:
+				/* Callback Delay */
+				if (lp->dtimer++ > lp->cbdelay)
+					lp->dialstate = 1;
+				anymore = 1;
+				break;
+			case 12:
+				/* Remote does callback. Hangup after cbdelay, then wait for incoming
+				 * call (in state 4).
+				 */
+				if (lp->dtimer++ > lp->cbdelay)
+				{
+					printk(KERN_INFO "%s: hangup waiting for callback ...\n", lp->name);
+					lp->dtimer = 0;
+					lp->dialstate = 4;
+					cmd.driver = lp->isdn_device;
+					cmd.command = ISDN_CMD_HANGUP;
+					cmd.arg = lp->isdn_channel;
+					isdn_command(&cmd);
+					isdn_all_eaz(lp->isdn_device, lp->isdn_channel);
+				}
+				anymore = 1;
+				break;
+			default:
+				printk(KERN_WARNING "isdn_net: Illegal dialstate %d for device %s\n",
+				       lp->dialstate, lp->name);
+		}
+		p = (isdn_net_dev *) p->next;
+	}
+	isdn_timer_ctrl(ISDN_TIMER_NETDIAL, anymore);
+}
+
+/*
+ * Perform hangup for a net-interface.
+ */
+void
+isdn_net_hangup(struct net_device *d)
+{
+	isdn_net_local *lp = (isdn_net_local *) d->priv;
+	isdn_ctrl cmd;
+#ifdef CONFIG_ISDN_X25
+	struct concap_proto *cprot = lp -> netdev -> cprot;
+	struct concap_proto_ops *pops = cprot ? cprot -> pops : 0;
+#endif
+
+	if (lp->flags & ISDN_NET_CONNECTED) {
+		if (lp->slave != NULL) {
+			isdn_net_local *slp = (isdn_net_local *)lp->slave->priv;
+			if (slp->flags & ISDN_NET_CONNECTED) {
+				printk(KERN_INFO
+					"isdn_net: hang up slave %s before %s\n",
+					slp->name, lp->name);
+				isdn_net_hangup(lp->slave);
+			}
+		}
+		printk(KERN_INFO "isdn_net: local hangup %s\n", lp->name);
+#ifdef CONFIG_ISDN_PPP
+		if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+			isdn_ppp_free(lp);
+#endif
+		isdn_net_lp_disconnected(lp);
+#ifdef CONFIG_ISDN_X25
+		/* try if there are generic encap protocol
+		   receiver routines and signal the closure of
+		   the link */
+		if( pops && pops -> disconn_ind )
+		  pops -> disconn_ind(cprot);
+#endif /* CONFIG_ISDN_X25 */
+
+		cmd.driver = lp->isdn_device;
+		cmd.command = ISDN_CMD_HANGUP;
+		cmd.arg = lp->isdn_channel;
+		isdn_command(&cmd);
+		printk(KERN_INFO "%s: Chargesum is %d\n", lp->name, lp->charge);
+		isdn_all_eaz(lp->isdn_device, lp->isdn_channel);
+	}
+	isdn_net_unbind_channel(lp);
+}
+
+typedef struct {
+	unsigned short source;
+	unsigned short dest;
+} ip_ports;
+
+static void
+isdn_net_log_skb(struct sk_buff * skb, isdn_net_local * lp)
+{
+	u_char *p = skb->nh.raw; /* hopefully, this was set correctly */
+	unsigned short proto = ntohs(skb->protocol);
+	int data_ofs;
+	ip_ports *ipp;
+	char addinfo[100];
+
+	addinfo[0] = '\0';
+	/* This check stolen from 2.1.72 dev_queue_xmit_nit() */
+	if (skb->nh.raw < skb->data || skb->nh.raw >= skb->tail) {
+		/* fall back to old isdn_net_log_packet method() */
+		char * buf = skb->data;
+
+		printk(KERN_DEBUG "isdn_net: protocol %04x is buggy, dev %s\n", skb->protocol, lp->name);
+		p = buf;
+		proto = ETH_P_IP;
+		switch (lp->p_encap) {
+			case ISDN_NET_ENCAP_IPTYP:
+				proto = ntohs(*(unsigned short *) &buf[0]);
+				p = &buf[2];
+				break;
+			case ISDN_NET_ENCAP_ETHER:
+				proto = ntohs(*(unsigned short *) &buf[12]);
+				p = &buf[14];
+				break;
+			case ISDN_NET_ENCAP_CISCOHDLC:
+				proto = ntohs(*(unsigned short *) &buf[2]);
+				p = &buf[4];
+				break;
+#ifdef CONFIG_ISDN_PPP
+			case ISDN_NET_ENCAP_SYNCPPP:
+				proto = ntohs(skb->protocol);
+				p = &buf[IPPP_MAX_HEADER];
+				break;
+#endif
+		}
+	}
+	data_ofs = ((p[0] & 15) * 4);
+	switch (proto) {
+		case ETH_P_IP:
+			switch (p[9]) {
+				case 1:
+					strcpy(addinfo, " ICMP");
+					break;
+				case 2:
+					strcpy(addinfo, " IGMP");
+					break;
+				case 4:
+					strcpy(addinfo, " IPIP");
+					break;
+				case 6:
+					ipp = (ip_ports *) (&p[data_ofs]);
+					sprintf(addinfo, " TCP, port: %d -> %d", ntohs(ipp->source),
+						ntohs(ipp->dest));
+					break;
+				case 8:
+					strcpy(addinfo, " EGP");
+					break;
+				case 12:
+					strcpy(addinfo, " PUP");
+					break;
+				case 17:
+					ipp = (ip_ports *) (&p[data_ofs]);
+					sprintf(addinfo, " UDP, port: %d -> %d", ntohs(ipp->source),
+						ntohs(ipp->dest));
+					break;
+				case 22:
+					strcpy(addinfo, " IDP");
+					break;
+			}
+			printk(KERN_INFO
+				"OPEN: %d.%d.%d.%d -> %d.%d.%d.%d%s\n",
+
+			       p[12], p[13], p[14], p[15],
+			       p[16], p[17], p[18], p[19],
+			       addinfo);
+			break;
+		case ETH_P_ARP:
+			printk(KERN_INFO
+				"OPEN: ARP %d.%d.%d.%d -> *.*.*.* ?%d.%d.%d.%d\n",
+			       p[14], p[15], p[16], p[17],
+			       p[24], p[25], p[26], p[27]);
+			break;
+	}
+}
+
+/*
+ * this function is used to send supervisory data, i.e. data which was
+ * not received from the network layer, but e.g. frames from ipppd, CCP
+ * reset frames etc.
+ */
+void isdn_net_write_super(isdn_net_local *lp, struct sk_buff *skb)
+{
+	if (in_irq()) {
+		// we can't grab the lock from irq context, 
+		// so we just queue the packet
+		skb_queue_tail(&lp->super_tx_queue, skb); 
+		queue_task(&lp->tqueue, &tq_immediate);
+		mark_bh(IMMEDIATE_BH);
+		return;
+	}
+
+	spin_lock_bh(&lp->xmit_lock);
+	if (!isdn_net_lp_busy(lp)) {
+		isdn_net_writebuf_skb(lp, skb);
+	} else {
+		skb_queue_tail(&lp->super_tx_queue, skb);
+	}
+	spin_unlock_bh(&lp->xmit_lock);
+}
+
+/*
+ * called from tq_immediate
+ */
+static void isdn_net_softint(void *private)
+{
+	isdn_net_local *lp = private;
+	struct sk_buff *skb;
+
+	spin_lock_bh(&lp->xmit_lock);
+	while (!isdn_net_lp_busy(lp)) {
+		skb = skb_dequeue(&lp->super_tx_queue);
+		if (!skb)
+			break;
+		isdn_net_writebuf_skb(lp, skb);                                
+	}
+	spin_unlock_bh(&lp->xmit_lock);
+}
+
+/* 
+ * all frames sent from the (net) LL to a HL driver should go via this function
+ * it's serialized by the caller holding the lp->xmit_lock spinlock
+ */
+void isdn_net_writebuf_skb(isdn_net_local *lp, struct sk_buff *skb)
+{
+	int ret;
+	int len = skb->len;     /* save len */
+
+	/* before obtaining the lock the caller should have checked that
+	   the lp isn't busy */
+	if (isdn_net_lp_busy(lp)) {
+		printk("isdn BUG at %s:%d!\n", __FILE__, __LINE__);
+		goto error;
+	}
+
+	if (!(lp->flags & ISDN_NET_CONNECTED)) {
+		printk("isdn BUG at %s:%d!\n", __FILE__, __LINE__);
+		goto error;
+	}
+	ret = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, 1, skb);
+	if (ret != len) {
+		/* we should never get here */
+		printk(KERN_WARNING "%s: HL driver queue full\n", lp->name);
+		goto error;
+	}
+	
+	lp->transcount += len;
+	isdn_net_inc_frame_cnt(lp);
+	return;
+
+ error:
+	dev_kfree_skb(skb);
+	lp->stats.tx_errors++;
+
+}
+
+
+/*
+ *  Helper function for isdn_net_start_xmit.
+ *  When called, the connection is already established.
+ *  Based on cps-calculation, check if device is overloaded.
+ *  If so, and if a slave exists, trigger dialing for it.
+ *  If any slave is online, deliver packets using a simple round robin
+ *  scheme.
+ *
+ *  Return: 0 on success, !0 on failure.
+ */
+
+static int
+isdn_net_xmit(struct net_device *ndev, struct sk_buff *skb)
+{
+	isdn_net_dev *nd;
+	isdn_net_local *slp;
+	isdn_net_local *lp = (isdn_net_local *) ndev->priv;
+	int retv = 0;
+
+	if (((isdn_net_local *) (ndev->priv))->master) {
+		printk("isdn BUG at %s:%d!\n", __FILE__, __LINE__);
+		dev_kfree_skb(skb);
+		return 0;
+	}
+
+	/* For the other encaps the header has already been built */
+#ifdef CONFIG_ISDN_PPP
+	if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) {
+		return isdn_ppp_xmit(skb, ndev);
+	}
+#endif
+	nd = ((isdn_net_local *) ndev->priv)->netdev;
+	lp = isdn_net_get_locked_lp(nd);
+	if (!lp) {
+		printk(KERN_WARNING "%s: all channels busy - requeuing!\n", ndev->name);
+		return 1;
+	}
+	/* we have our lp locked from now on */
+
+	/* Reset hangup-timeout */
+	lp->huptimer = 0; // FIXME?
+	isdn_net_writebuf_skb(lp, skb);
+	spin_unlock_bh(&lp->xmit_lock);
+
+	/* the following stuff is here for backwards compatibility.
+	 * in future, start-up and hangup of slaves (based on current load)
+	 * should move to userspace and get based on an overall cps
+	 * calculation
+	 */
+	if (lp->cps > lp->triggercps) {
+		if (lp->slave) {
+			if (!lp->sqfull) {
+				/* First time overload: set timestamp only */
+				lp->sqfull = 1;
+				lp->sqfull_stamp = jiffies;
+			} else {
+				/* subsequent overload: if slavedelay exceeded, start dialing */
+				if (time_after(jiffies, lp->sqfull_stamp + lp->slavedelay)) {
+					slp = lp->slave->priv;
+					if (!(slp->flags & ISDN_NET_CONNECTED)) {
+						isdn_net_force_dial_lp((isdn_net_local *) lp->slave->priv);
+					}
+				}
+			}
+		}
+	} else {
+		if (lp->sqfull && time_after(jiffies, lp->sqfull_stamp + lp->slavedelay + (10 * HZ))) {
+			lp->sqfull = 0;
+		}
+		/* this is a hack to allow auto-hangup for slaves on moderate loads */
+		nd->queue = nd->local;
+	}
+
+	return retv;
+
+}
+
+static void
+isdn_net_adjust_hdr(struct sk_buff *skb, struct net_device *dev)
+{
+	isdn_net_local *lp = (isdn_net_local *) dev->priv;
+	if (!skb)
+		return;
+	if (lp->p_encap == ISDN_NET_ENCAP_ETHER) {
+		int pullsize = (ulong)skb->nh.raw - (ulong)skb->data - ETH_HLEN;
+		if (pullsize > 0) {
+			printk(KERN_DEBUG "isdn_net: Pull junk %d\n", pullsize);
+			skb_pull(skb, pullsize);
+		}
+	}
+}
+
+
+void isdn_net_tx_timeout(struct net_device * ndev)
+{
+	isdn_net_local *lp = (isdn_net_local *) ndev->priv;
+
+	printk(KERN_WARNING "isdn_tx_timeout dev %s dialstate %d\n", ndev->name, lp->dialstate);
+	if (!lp->dialstate){
+		lp->stats.tx_errors++;
+                /*
+		 * There is a certain probability that this currently
+		 * works at all because if we always wake up the interface,
+		 * then upper layer will try to send the next packet
+		 * immediately. And then, the old clean_up logic in the
+		 * driver will hopefully continue to work as it used to do.
+		 *
+		 * This is rather primitive right know, we better should
+		 * clean internal queues here, in particular for multilink and
+		 * ppp, and reset HL driver's channel, too.   --HE
+		 *
+		 * actually, this may not matter at all, because ISDN hardware
+		 * should not see transmitter hangs at all IMO
+		 * changed KERN_DEBUG to KERN_WARNING to find out if this is 
+		 * ever called   --KG
+		 */
+	}
+	ndev->trans_start = jiffies;
+	netif_wake_queue(ndev);
+}
+
+/*
+ * Try sending a packet.
+ * If this interface isn't connected to a ISDN-Channel, find a free channel,
+ * and start dialing.
+ */
+static int
+isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+	isdn_net_local *lp = (isdn_net_local *) ndev->priv;
+#ifdef CONFIG_ISDN_X25
+	struct concap_proto * cprot = lp -> netdev -> cprot;
+#endif
+#ifdef CONFIG_ISDN_X25
+/* At this point hard_start_xmit() passes control to the encapsulation
+   protocol (if present).
+   For X.25 auto-dialing is completly bypassed because:
+   - It does not conform with the semantics of a reliable datalink
+     service as needed by X.25 PLP.
+   - I don't want that the interface starts dialing when the network layer
+     sends a message which requests to disconnect the lapb link (or if it
+     sends any other message not resulting in data transmission).
+   Instead, dialing will be initiated by the encapsulation protocol entity
+   when a dl_establish request is received from the upper layer.
+*/
+	if( cprot ) {
+		int ret = cprot -> pops -> encap_and_xmit ( cprot , skb);
+		if(ret) netif_stop_queue(ndev);
+		return ret;
+	} else
+#endif
+	/* auto-dialing xmit function */
+	{
+#ifdef ISDN_DEBUG_NET_DUMP
+		u_char *buf;
+#endif
+		isdn_net_adjust_hdr(skb, ndev);
+#ifdef ISDN_DEBUG_NET_DUMP
+		buf = skb->data;
+		isdn_dumppkt("S:", buf, skb->len, 40);
+#endif
+
+		if (!(lp->flags & ISDN_NET_CONNECTED)) {
+			int chi;
+			/* only do autodial if allowed by config */
+			if (!(ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_AUTO)) {
+				isdn_net_unreachable(ndev, skb, "dial rejected: interface not in dialmode `auto'");
+				dev_kfree_skb(skb);
+				return 0;
+			}
+			if (lp->phone[1]) {
+				ulong flags;
+				save_flags(flags);
+				cli();
+
+				if(lp->dialwait_timer <= 0)
+					if(lp->dialstarted > 0 && lp->dialtimeout > 0 && time_before(jiffies, lp->dialstarted + lp->dialtimeout + lp->dialwait))
+						lp->dialwait_timer = lp->dialstarted + lp->dialtimeout + lp->dialwait;
+
+				if(lp->dialwait_timer > 0) {
+					if(time_before(jiffies, lp->dialwait_timer)) {
+						isdn_net_unreachable(ndev, skb, "dial rejected: retry-time not reached");
+						dev_kfree_skb(skb);
+						restore_flags(flags);
+						return 0;
+					} else
+						lp->dialwait_timer = 0;
+				}
+				/* Grab a free ISDN-Channel */
+				if (((chi =
+				     isdn_get_free_channel(
+					 		ISDN_USAGE_NET,
+							lp->l2_proto,
+							lp->l3_proto,
+							lp->pre_device,
+						 	lp->pre_channel,
+							lp->msn)
+							) < 0) &&
+					((chi =
+				     isdn_get_free_channel(
+					 		ISDN_USAGE_NET,
+							lp->l2_proto,
+							lp->l3_proto,
+							lp->pre_device,
+							lp->pre_channel^1,
+							lp->msn)
+							) < 0)) {
+					restore_flags(flags);
+					isdn_net_unreachable(ndev, skb,
+							   "No channel");
+					dev_kfree_skb(skb);
+					return 0;
+				}
+				/* Log packet, which triggered dialing */
+				if (dev->net_verbose)
+					isdn_net_log_skb(skb, lp);
+				lp->dialstate = 1;
+				/* Connect interface with channel */
+				isdn_net_bind_channel(lp, chi);
+#ifdef CONFIG_ISDN_PPP
+				if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) {
+					/* no 'first_skb' handling for syncPPP */
+					if (isdn_ppp_bind(lp) < 0) {
+						dev_kfree_skb(skb);
+						isdn_net_unbind_channel(lp);
+						restore_flags(flags);
+						return 0;	/* STN (skb to nirvana) ;) */
+					}
+					restore_flags(flags);
+					isdn_net_dial();	/* Initiate dialing */
+					netif_stop_queue(ndev);
+					return 1;	/* let upper layer requeue skb packet */
+				}
+#endif
+				/* Initiate dialing */
+				restore_flags(flags);
+				isdn_net_dial();
+				isdn_net_device_stop_queue(lp);
+				return 1;
+			} else {
+				isdn_net_unreachable(ndev, skb,
+						     "No phone number");
+				dev_kfree_skb(skb);
+				return 0;
+			}
+		} else {
+			/* Device is connected to an ISDN channel */ 
+			ndev->trans_start = jiffies;
+			if (!lp->dialstate) {
+				/* ISDN connection is established, try sending */
+				int ret;
+				ret = (isdn_net_xmit(ndev, skb));
+				if(ret) netif_stop_queue(ndev);
+				return ret;
+			} else
+				netif_stop_queue(ndev);
+		}
+	}
+	return 1;
+}
+
+/*
+ * Shutdown a net-interface.
+ */
+static int
+isdn_net_close(struct net_device *dev)
+{
+	struct net_device *p;
+#ifdef CONFIG_ISDN_X25
+	struct concap_proto * cprot =
+		( (isdn_net_local *) dev->priv ) -> netdev -> cprot;
+	/* printk(KERN_DEBUG "isdn_net_close %s\n" , dev-> name ); */
+#endif
+
+#ifdef CONFIG_ISDN_X25
+	if( cprot && cprot -> pops ) cprot -> pops -> close( cprot );
+#endif
+	netif_stop_queue(dev);
+	if ((p = (((isdn_net_local *) dev->priv)->slave))) {
+		/* If this interface has slaves, stop them also */
+		while (p) {
+#ifdef CONFIG_ISDN_X25
+			cprot = ( (isdn_net_local *) p->priv )
+				-> netdev -> cprot;
+			if( cprot && cprot -> pops )
+				cprot -> pops -> close( cprot );
+#endif
+			isdn_net_hangup(p);
+			p = (((isdn_net_local *) p->priv)->slave);
+		}
+	}
+	isdn_net_hangup(dev);
+	isdn_MOD_DEC_USE_COUNT();
+	return 0;
+}
+
+/*
+ * Get statistics
+ */
+static struct net_device_stats *
+isdn_net_get_stats(struct net_device *dev)
+{
+	isdn_net_local *lp = (isdn_net_local *) dev->priv;
+	return &lp->stats;
+}
+
+/*      This is simply a copy from std. eth.c EXCEPT we pull ETH_HLEN
+ *      instead of dev->hard_header_len off. This is done because the
+ *      lowlevel-driver has already pulled off its stuff when we get
+ *      here and this routine only gets called with p_encap == ETHER.
+ *      Determine the packet's protocol ID. The rule here is that we
+ *      assume 802.3 if the type field is short enough to be a length.
+ *      This is normal practice and works for any 'now in use' protocol.
+ */
+
+static unsigned short
+isdn_net_type_trans(struct sk_buff *skb, struct net_device *dev)
+{
+	struct ethhdr *eth;
+	unsigned char *rawp;
+
+	skb->mac.raw = skb->data;
+	skb_pull(skb, ETH_HLEN);
+	eth = skb->mac.ethernet;
+
+	if (*eth->h_dest & 1) {
+		if (memcmp(eth->h_dest, dev->broadcast, ETH_ALEN) == 0)
+			skb->pkt_type = PACKET_BROADCAST;
+		else
+			skb->pkt_type = PACKET_MULTICAST;
+	}
+	/*
+	 *      This ALLMULTI check should be redundant by 1.4
+	 *      so don't forget to remove it.
+	 */
+
+	else if (dev->flags & (IFF_PROMISC /*| IFF_ALLMULTI*/)) {
+		if (memcmp(eth->h_dest, dev->dev_addr, ETH_ALEN))
+			skb->pkt_type = PACKET_OTHERHOST;
+	}
+	if (ntohs(eth->h_proto) >= 1536)
+		return eth->h_proto;
+
+	rawp = skb->data;
+
+	/*
+	 *      This is a magic hack to spot IPX packets. Older Novell breaks
+	 *      the protocol design and runs IPX over 802.3 without an 802.2 LLC
+	 *      layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This
+	 *      won't work for fault tolerant netware but does for the rest.
+	 */
+	if (*(unsigned short *) rawp == 0xFFFF)
+		return htons(ETH_P_802_3);
+	/*
+	 *      Real 802.2 LLC
+	 */
+	return htons(ETH_P_802_2);
+}
+
+
+/* 
+ * CISCO HDLC keepalive specific stuff
+ */
+static struct sk_buff*
+isdn_net_ciscohdlck_alloc_skb(isdn_net_local *lp, int len)
+{
+	unsigned short hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen;
+	struct sk_buff *skb;
+
+	skb = alloc_skb(hl + len, GFP_ATOMIC);
+	if (!skb) {
+		printk("isdn out of mem at %s:%d!\n", __FILE__, __LINE__);
+		return 0;
+	}
+	skb_reserve(skb, hl);
+	return skb;
+}
+
+/* cisco hdlck device private ioctls */
+int
+isdn_ciscohdlck_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	isdn_net_local *lp = (isdn_net_local *) dev->priv;
+	unsigned long len = 0;
+	unsigned long expires = 0;
+	int tmp = 0;
+	int period = lp->cisco_keepalive_period;
+	char debserint = lp->cisco_debserint;
+	int rc = 0;
+
+	if (lp->p_encap != ISDN_NET_ENCAP_CISCOHDLCK)
+		return -EINVAL;
+
+	switch (cmd) {
+		/* get/set keepalive period */
+		case SIOCGKEEPPERIOD:
+			len = (unsigned long)sizeof(lp->cisco_keepalive_period);
+			if (copy_to_user((char *)ifr->ifr_ifru.ifru_data,
+				(int *)&lp->cisco_keepalive_period, len))
+				rc = -EFAULT;
+			break;
+		case SIOCSKEEPPERIOD:
+			tmp = lp->cisco_keepalive_period;
+			len = (unsigned long)sizeof(lp->cisco_keepalive_period);
+			if (copy_from_user((int *)&period,
+				(char *)ifr->ifr_ifru.ifru_data, len))
+				rc = -EFAULT;
+			if ((period > 0) && (period <= 32767))
+				lp->cisco_keepalive_period = period;
+			else
+				rc = -EINVAL;
+			if (!rc && (tmp != lp->cisco_keepalive_period)) {
+				expires = (unsigned long)(jiffies +
+					lp->cisco_keepalive_period * HZ);
+				mod_timer(&lp->cisco_timer, expires);
+				printk(KERN_INFO "%s: Keepalive period set "
+					"to %d seconds.\n",
+					lp->name, lp->cisco_keepalive_period);
+			}
+			break;
+
+		/* get/set debugging */
+		case SIOCGDEBSERINT:
+			len = (unsigned long)sizeof(lp->cisco_debserint);
+			if (copy_to_user((char *)ifr->ifr_ifru.ifru_data,
+				(char *)&lp->cisco_debserint, len))
+				rc = -EFAULT;
+			break;
+		case SIOCSDEBSERINT:
+			len = (unsigned long)sizeof(lp->cisco_debserint);
+			if (copy_from_user((char *)&debserint,
+				(char *)ifr->ifr_ifru.ifru_data, len))
+				rc = -EFAULT;
+			if ((debserint >= 0) && (debserint <= 64))
+				lp->cisco_debserint = debserint;
+			else
+				rc = -EINVAL;
+			break;
+
+		default:
+			rc = -EINVAL;
+			break;
+	}
+	return (rc);
+}
+
+/* called via cisco_timer.function */
+static void
+isdn_net_ciscohdlck_slarp_send_keepalive(unsigned long data)
+{
+	isdn_net_local *lp = (isdn_net_local *) data;
+	struct sk_buff *skb;
+	unsigned char *p;
+	unsigned long last_cisco_myseq = lp->cisco_myseq;
+	int myseq_diff = 0;
+
+	if (!(lp->flags & ISDN_NET_CONNECTED) || lp->dialstate) {
+		printk("isdn BUG at %s:%d!\n", __FILE__, __LINE__);
+		return;
+	}
+	lp->cisco_myseq++;
+
+	myseq_diff = (lp->cisco_myseq - lp->cisco_mineseen);
+	if ((lp->cisco_line_state) && ((myseq_diff >= 3)||(myseq_diff <= -3))) {
+		/* line up -> down */
+		lp->cisco_line_state = 0;
+		printk (KERN_WARNING
+				"UPDOWN: Line protocol on Interface %s,"
+				" changed state to down\n", lp->name);
+		/* should stop routing higher-level data accross */
+	} else if ((!lp->cisco_line_state) &&
+		(myseq_diff >= 0) && (myseq_diff <= 2)) {
+		/* line down -> up */
+		lp->cisco_line_state = 1;
+		printk (KERN_WARNING
+				"UPDOWN: Line protocol on Interface %s,"
+				" changed state to up\n", lp->name);
+		/* restart routing higher-level data accross */
+	}
+
+	if (lp->cisco_debserint)
+		printk (KERN_DEBUG "%s: HDLC "
+			"myseq %lu, mineseen %lu%c, yourseen %lu, %s\n",
+			lp->name, last_cisco_myseq, lp->cisco_mineseen,
+			((last_cisco_myseq == lp->cisco_mineseen) ? '*' : 040),
+			lp->cisco_yourseq,
+			((lp->cisco_line_state) ? "line up" : "line down"));
+
+	skb = isdn_net_ciscohdlck_alloc_skb(lp, 4 + 14);
+	if (!skb)
+		return;
+
+	p = skb_put(skb, 4 + 14);
+
+	/* cisco header */
+	p += put_u8 (p, CISCO_ADDR_UNICAST);
+	p += put_u8 (p, CISCO_CTRL);
+	p += put_u16(p, CISCO_TYPE_SLARP);
+
+	/* slarp keepalive */
+	p += put_u32(p, CISCO_SLARP_KEEPALIVE);
+	p += put_u32(p, lp->cisco_myseq);
+	p += put_u32(p, lp->cisco_yourseq);
+	p += put_u16(p, 0xffff); // reliablity, always 0xffff
+
+	isdn_net_write_super(lp, skb);
+
+	lp->cisco_timer.expires = jiffies + lp->cisco_keepalive_period * HZ;
+	
+	add_timer(&lp->cisco_timer);
+}
+
+static void
+isdn_net_ciscohdlck_slarp_send_request(isdn_net_local *lp)
+{
+	struct sk_buff *skb;
+	unsigned char *p;
+
+	skb = isdn_net_ciscohdlck_alloc_skb(lp, 4 + 14);
+	if (!skb)
+		return;
+
+	p = skb_put(skb, 4 + 14);
+
+	/* cisco header */
+	p += put_u8 (p, CISCO_ADDR_UNICAST);
+	p += put_u8 (p, CISCO_CTRL);
+	p += put_u16(p, CISCO_TYPE_SLARP);
+
+	/* slarp request */
+	p += put_u32(p, CISCO_SLARP_REQUEST);
+	p += put_u32(p, 0); // address
+	p += put_u32(p, 0); // netmask
+	p += put_u16(p, 0); // unused
+
+	isdn_net_write_super(lp, skb);
+}
+
+static void 
+isdn_net_ciscohdlck_connected(isdn_net_local *lp)
+{
+	lp->cisco_myseq = 0;
+	lp->cisco_mineseen = 0;
+	lp->cisco_yourseq = 0;
+	lp->cisco_keepalive_period = ISDN_TIMER_KEEPINT;
+	lp->cisco_last_slarp_in = 0;
+	lp->cisco_line_state = 0;
+	lp->cisco_debserint = 0;
+
+	/* send slarp request because interface/seq.no.s reset */
+	isdn_net_ciscohdlck_slarp_send_request(lp);
+
+	init_timer(&lp->cisco_timer);
+	lp->cisco_timer.data = (unsigned long) lp;
+	lp->cisco_timer.function = isdn_net_ciscohdlck_slarp_send_keepalive;
+	lp->cisco_timer.expires = jiffies + lp->cisco_keepalive_period * HZ;
+	add_timer(&lp->cisco_timer);
+}
+
+static void 
+isdn_net_ciscohdlck_disconnected(isdn_net_local *lp)
+{
+	del_timer(&lp->cisco_timer);
+}
+
+static void
+isdn_net_ciscohdlck_slarp_send_reply(isdn_net_local *lp)
+{
+	struct sk_buff *skb;
+	unsigned char *p;
+	struct in_device *in_dev = NULL;
+	u32 addr = 0;		/* local ipv4 address */
+	u32 mask = 0;		/* local netmask */
+
+	if ((in_dev = lp->netdev->dev.ip_ptr) != NULL) {
+		/* take primary(first) address of interface */
+		struct in_ifaddr *ifa = in_dev->ifa_list;
+		if (ifa != NULL) {
+			addr = ifa->ifa_local;
+			mask = ifa->ifa_mask;
+		}
+	}
+
+	skb = isdn_net_ciscohdlck_alloc_skb(lp, 4 + 14);
+	if (!skb)
+		return;
+
+	p = skb_put(skb, 4 + 14);
+
+	/* cisco header */
+	p += put_u8 (p, CISCO_ADDR_UNICAST);
+	p += put_u8 (p, CISCO_CTRL);
+	p += put_u16(p, CISCO_TYPE_SLARP);
+
+	/* slarp reply, send own ip/netmask; if values are nonsense remote
+	 * should think we are unable to provide it with an address via SLARP */
+	p += put_u32(p, CISCO_SLARP_REPLY);
+	p += put_u32(p, addr);	// address
+	p += put_u32(p, mask);	// netmask
+	p += put_u16(p, 0);	// unused
+
+	isdn_net_write_super(lp, skb);
+}
+
+static void
+isdn_net_ciscohdlck_slarp_in(isdn_net_local *lp, struct sk_buff *skb)
+{
+	unsigned char *p;
+	int period;
+	u32 code;
+	u32 my_seq, addr;
+	u32 your_seq, mask;
+	u32 local;
+	u16 unused;
+
+	if (skb->len < 14)
+		return;
+
+	p = skb->data;
+	p += get_u32(p, &code);
+	
+	switch (code) {
+	case CISCO_SLARP_REQUEST:
+		lp->cisco_yourseq = 0;
+		isdn_net_ciscohdlck_slarp_send_reply(lp);
+		break;
+	case CISCO_SLARP_REPLY:
+		addr = ntohl(*(u32 *)p);
+		mask = ntohl(*(u32 *)(p+4));
+		if (mask != 0xfffffffc)
+			goto slarp_reply_out;
+		if ((addr & 3) == 0 || (addr & 3) == 3)
+			goto slarp_reply_out;
+		local = addr ^ 3;
+		printk(KERN_INFO "%s: got slarp reply: "
+			"remote ip: %d.%d.%d.%d, "
+			"local ip: %d.%d.%d.%d "
+			"mask: %d.%d.%d.%d\n",
+		       lp->name,
+		       HIPQUAD(addr),
+		       HIPQUAD(local),
+		       HIPQUAD(mask));
+		break;
+  slarp_reply_out:
+		 printk(KERN_INFO "%s: got invalid slarp "
+				 "reply (%d.%d.%d.%d/%d.%d.%d.%d) "
+				 "- ignored\n", lp->name,
+				 HIPQUAD(addr), HIPQUAD(mask));
+		break;
+	case CISCO_SLARP_KEEPALIVE:
+		period = (int)((jiffies - lp->cisco_last_slarp_in
+				+ HZ/2 - 1) / HZ);
+		if (lp->cisco_debserint &&
+				(period != lp->cisco_keepalive_period) &&
+				lp->cisco_last_slarp_in) {
+			printk(KERN_DEBUG "%s: Keepalive period mismatch - "
+				"is %d but should be %d.\n",
+				lp->name, period, lp->cisco_keepalive_period);
+		}
+		lp->cisco_last_slarp_in = jiffies;
+		p += get_u32(p, &my_seq);
+		p += get_u32(p, &your_seq);
+		p += get_u16(p, &unused);
+		lp->cisco_yourseq = my_seq;
+		lp->cisco_mineseen = your_seq;
+		break;
+	}
+}
+
+static void
+isdn_net_ciscohdlck_receive(isdn_net_local *lp, struct sk_buff *skb)
+{
+	unsigned char *p;
+ 	u8 addr;
+ 	u8 ctrl;
+ 	u16 type;
+	
+	if (skb->len < 4)
+		goto out_free;
+
+	p = skb->data;
+	p += get_u8 (p, &addr);
+	p += get_u8 (p, &ctrl);
+	p += get_u16(p, &type);
+	skb_pull(skb, 4);
+	
+	if (addr != CISCO_ADDR_UNICAST && addr != CISCO_ADDR_BROADCAST) {
+		printk(KERN_WARNING "%s: Unknown Cisco addr 0x%02x\n",
+		       lp->name, addr);
+		goto out_free;
+	}
+	if (ctrl != CISCO_CTRL) {
+		printk(KERN_WARNING "%s: Unknown Cisco ctrl 0x%02x\n",
+		       lp->name, ctrl);
+		goto out_free;
+	}
+
+	switch (type) {
+	case CISCO_TYPE_INET:
+		skb->protocol = htons(ETH_P_IP);
+		netif_rx(skb);
+		break;
+	case CISCO_TYPE_SLARP:
+		isdn_net_ciscohdlck_slarp_in(lp, skb);
+		goto out_free;
+	case CISCO_TYPE_CDP:
+		if (lp->cisco_debserint)
+			printk(KERN_DEBUG "%s: Received CDP packet. use "
+				"\"no cdp enable\" on cisco.\n", lp->name);
+		goto out_free;
+	default:
+		printk(KERN_WARNING "%s: Unknown Cisco type 0x%04x\n",
+		       lp->name, type);
+		goto out_free;
+	}
+	return;
+
+ out_free:
+	kfree_skb(skb);
+}
+
+/*
+ * Got a packet from ISDN-Channel.
+ */
+static void
+isdn_net_receive(struct net_device *ndev, struct sk_buff *skb)
+{
+	isdn_net_local *lp = (isdn_net_local *) ndev->priv;
+	isdn_net_local *olp = lp;	/* original 'lp' */
+#ifdef CONFIG_ISDN_PPP
+	int proto = PPP_PROTOCOL(skb->data);
+#endif
+#ifdef CONFIG_ISDN_X25
+	struct concap_proto *cprot = lp -> netdev -> cprot;
+#endif
+	lp->transcount += skb->len;
+
+	lp->stats.rx_packets++;
+	lp->stats.rx_bytes += skb->len;
+	if (lp->master) {
+		/* Bundling: If device is a slave-device, deliver to master, also
+		 * handle master's statistics and hangup-timeout
+		 */
+		ndev = lp->master;
+		lp = (isdn_net_local *) ndev->priv;
+		lp->stats.rx_packets++;
+		lp->stats.rx_bytes += skb->len;
+	}
+	skb->dev = ndev;
+	skb->pkt_type = PACKET_HOST;
+	skb->mac.raw = skb->data;
+#ifdef ISDN_DEBUG_NET_DUMP
+	isdn_dumppkt("R:", skb->data, skb->len, 40);
+#endif
+	switch (lp->p_encap) {
+		case ISDN_NET_ENCAP_ETHER:
+			/* Ethernet over ISDN */
+			olp->huptimer = 0;
+			lp->huptimer = 0;
+			skb->protocol = isdn_net_type_trans(skb, ndev);
+			break;
+		case ISDN_NET_ENCAP_UIHDLC:
+			/* HDLC with UI-frame (for ispa with -h1 option) */
+			olp->huptimer = 0;
+			lp->huptimer = 0;
+			skb_pull(skb, 2);
+			/* Fall through */
+		case ISDN_NET_ENCAP_RAWIP:
+			/* RAW-IP without MAC-Header */
+			olp->huptimer = 0;
+			lp->huptimer = 0;
+			skb->protocol = htons(ETH_P_IP);
+			break;
+		case ISDN_NET_ENCAP_CISCOHDLCK:
+			isdn_net_ciscohdlck_receive(lp, skb);
+			return;
+		case ISDN_NET_ENCAP_CISCOHDLC:
+			/* CISCO-HDLC IP with type field and  fake I-frame-header */
+			skb_pull(skb, 2);
+			/* Fall through */
+		case ISDN_NET_ENCAP_IPTYP:
+			/* IP with type field */
+			olp->huptimer = 0;
+			lp->huptimer = 0;
+			skb->protocol = *(unsigned short *) &(skb->data[0]);
+			skb_pull(skb, 2);
+			if (*(unsigned short *) skb->data == 0xFFFF)
+				skb->protocol = htons(ETH_P_802_3);
+			break;
+#ifdef CONFIG_ISDN_PPP
+		case ISDN_NET_ENCAP_SYNCPPP:
+			/*
+			 * If encapsulation is syncppp, don't reset
+			 * huptimer on LCP packets.
+			 */
+			if (proto != PPP_LCP) {
+				olp->huptimer = 0;
+				lp->huptimer = 0;
+			}
+			isdn_ppp_receive(lp->netdev, olp, skb);
+			return;
+#endif
+
+		default:
+#ifdef CONFIG_ISDN_X25
+		  /* try if there are generic sync_device receiver routines */
+			if(cprot) if(cprot -> pops)
+				if( cprot -> pops -> data_ind){
+					cprot -> pops -> data_ind(cprot,skb);
+					return;
+				};
+#endif /* CONFIG_ISDN_X25 */
+			printk(KERN_WARNING "%s: unknown encapsulation, dropping\n",
+			       lp->name);
+			kfree_skb(skb);
+			return;
+	}
+
+	netif_rx(skb);
+	return;
+}
+
+/*
+ * A packet arrived via ISDN. Search interface-chain for a corresponding
+ * interface. If found, deliver packet to receiver-function and return 1,
+ * else return 0.
+ */
+int
+isdn_net_rcv_skb(int idx, struct sk_buff *skb)
+{
+	isdn_net_dev *p = dev->rx_netdev[idx];
+
+	if (p) {
+		isdn_net_local *lp = p->local;
+		if ((lp->flags & ISDN_NET_CONNECTED) &&
+		    (!lp->dialstate)) {
+			isdn_net_receive(&p->dev, skb);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static int
+my_eth_header(struct sk_buff *skb, struct net_device *dev, unsigned short type,
+	      void *daddr, void *saddr, unsigned len)
+{
+	struct ethhdr *eth = (struct ethhdr *) skb_push(skb, ETH_HLEN);
+
+	/*
+	 * Set the protocol type. For a packet of type ETH_P_802_3 we
+	 * put the length here instead. It is up to the 802.2 layer to
+	 * carry protocol information.
+	 */
+
+	if (type != ETH_P_802_3)
+		eth->h_proto = htons(type);
+	else
+		eth->h_proto = htons(len);
+
+	/*
+	 * Set the source hardware address.
+	 */
+	if (saddr)
+		memcpy(eth->h_source, saddr, dev->addr_len);
+	else
+		memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
+
+	/*
+	 * Anyway, the loopback-device should never use this function...
+	 */
+
+	if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) {
+		memset(eth->h_dest, 0, dev->addr_len);
+		return ETH_HLEN /*(dev->hard_header_len)*/;
+	}
+	if (daddr) {
+		memcpy(eth->h_dest, daddr, dev->addr_len);
+		return ETH_HLEN /*dev->hard_header_len*/;
+	}
+	return -ETH_HLEN /*dev->hard_header_len*/;
+}
+
+/*
+ *  build an header
+ *  depends on encaps that is being used.
+ */
+
+static int
+isdn_net_header(struct sk_buff *skb, struct net_device *dev, unsigned short type,
+		void *daddr, void *saddr, unsigned plen)
+{
+	isdn_net_local *lp = dev->priv;
+	unsigned char *p;
+	ushort len = 0;
+
+	switch (lp->p_encap) {
+		case ISDN_NET_ENCAP_ETHER:
+			len = my_eth_header(skb, dev, type, daddr, saddr, plen);
+			break;
+#ifdef CONFIG_ISDN_PPP
+		case ISDN_NET_ENCAP_SYNCPPP:
+			/* stick on a fake header to keep fragmentation code happy. */
+			len = IPPP_MAX_HEADER;
+			skb_push(skb,len);
+			break;
+#endif
+		case ISDN_NET_ENCAP_RAWIP:
+			printk(KERN_WARNING "isdn_net_header called with RAW_IP!\n");
+			len = 0;
+			break;
+		case ISDN_NET_ENCAP_IPTYP:
+			/* ethernet type field */
+			*((ushort *) skb_push(skb, 2)) = htons(type);
+			len = 2;
+			break;
+		case ISDN_NET_ENCAP_UIHDLC:
+			/* HDLC with UI-Frames (for ispa with -h1 option) */
+			*((ushort *) skb_push(skb, 2)) = htons(0x0103);
+			len = 2;
+			break;
+		case ISDN_NET_ENCAP_CISCOHDLC:
+		case ISDN_NET_ENCAP_CISCOHDLCK:
+			p = skb_push(skb, 4);
+			p += put_u8 (p, CISCO_ADDR_UNICAST);
+			p += put_u8 (p, CISCO_CTRL);
+			p += put_u16(p, type);
+			len = 4;
+			break;
+#ifdef CONFIG_ISDN_X25
+		default:
+		  /* try if there are generic concap protocol routines */
+			if( lp-> netdev -> cprot ){
+				printk(KERN_WARNING "isdn_net_header called with concap_proto!\n");
+				len = 0;
+				break;
+			}
+			break;
+#endif /* CONFIG_ISDN_X25 */
+	}
+	return len;
+}
+
+/* We don't need to send arp, because we have point-to-point connections. */
+static int
+isdn_net_rebuild_header(struct sk_buff *skb)
+{
+	struct net_device *dev = skb->dev;
+	isdn_net_local *lp = dev->priv;
+	int ret = 0;
+
+	if (lp->p_encap == ISDN_NET_ENCAP_ETHER) {
+		struct ethhdr *eth = (struct ethhdr *) skb->data;
+
+		/*
+		 *      Only ARP/IP is currently supported
+		 */
+
+		if (eth->h_proto != htons(ETH_P_IP)) {
+			printk(KERN_WARNING
+			       "isdn_net: %s don't know how to resolve type %d addresses?\n",
+			       dev->name, (int) eth->h_proto);
+			memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
+			return 0;
+		}
+		/*
+		 *      Try to get ARP to resolve the header.
+		 */
+#ifdef CONFIG_INET
+		ret = arp_find(eth->h_dest, skb);
+#endif
+	}
+	return ret;
+}
+
+/*
+ * Interface-setup. (just after registering a new interface)
+ */
+static int
+isdn_net_init(struct net_device *ndev)
+{
+	ushort max_hlhdr_len = 0;
+	isdn_net_local *lp = (isdn_net_local *) ndev->priv;
+	int drvidx, i;
+
+	ether_setup(ndev);
+	lp->org_hhc = ndev->hard_header_cache;
+	lp->org_hcu = ndev->header_cache_update;
+
+	/* Setup the generic properties */
+
+	ndev->hard_header = NULL;
+	ndev->hard_header_cache = NULL;
+	ndev->header_cache_update = NULL;
+	ndev->mtu = 1500;
+	ndev->flags = IFF_NOARP|IFF_POINTOPOINT;
+	ndev->type = ARPHRD_ETHER;
+	ndev->addr_len = ETH_ALEN;
+
+	/* for clients with MPPP maybe higher values better */
+	ndev->tx_queue_len = 30;
+
+	for (i = 0; i < ETH_ALEN; i++)
+		ndev->broadcast[i] = 0xff;
+
+	/* The ISDN-specific entries in the device structure. */
+	ndev->open = &isdn_net_open;
+	ndev->hard_start_xmit = &isdn_net_start_xmit;
+
+	/*
+	 *  up till binding we ask the protocol layer to reserve as much
+	 *  as we might need for HL layer
+	 */
+
+	for (drvidx = 0; drvidx < ISDN_MAX_DRIVERS; drvidx++)
+		if (dev->drv[drvidx])
+			if (max_hlhdr_len < dev->drv[drvidx]->interface->hl_hdrlen)
+				max_hlhdr_len = dev->drv[drvidx]->interface->hl_hdrlen;
+
+	ndev->hard_header_len = ETH_HLEN + max_hlhdr_len;
+	ndev->stop = &isdn_net_close;
+	ndev->get_stats = &isdn_net_get_stats;
+	ndev->rebuild_header = &isdn_net_rebuild_header;
+	ndev->do_ioctl = NULL;
+	return 0;
+}
+
+static void
+isdn_net_swapbind(int drvidx)
+{
+	isdn_net_dev *p;
+
+#ifdef ISDN_DEBUG_NET_ICALL
+	printk(KERN_DEBUG "n_fi: swapping ch of %d\n", drvidx);
+#endif
+	p = dev->netdev;
+	while (p) {
+		if (p->local->pre_device == drvidx)
+			switch (p->local->pre_channel) {
+				case 0:
+					p->local->pre_channel = 1;
+					break;
+				case 1:
+					p->local->pre_channel = 0;
+					break;
+			}
+		p = (isdn_net_dev *) p->next;
+	}
+}
+
+static void
+isdn_net_swap_usage(int i1, int i2)
+{
+	int u1 = dev->usage[i1] & ISDN_USAGE_EXCLUSIVE;
+	int u2 = dev->usage[i2] & ISDN_USAGE_EXCLUSIVE;
+
+#ifdef ISDN_DEBUG_NET_ICALL
+	printk(KERN_DEBUG "n_fi: usage of %d and %d\n", i1, i2);
+#endif
+	dev->usage[i1] &= ~ISDN_USAGE_EXCLUSIVE;
+	dev->usage[i1] |= u2;
+	dev->usage[i2] &= ~ISDN_USAGE_EXCLUSIVE;
+	dev->usage[i2] |= u1;
+	isdn_info_update();
+}
+
+/*
+ * An incoming call-request has arrived.
+ * Search the interface-chain for an appropriate interface.
+ * If found, connect the interface to the ISDN-channel and initiate
+ * D- and B-Channel-setup. If secure-flag is set, accept only
+ * configured phone-numbers. If callback-flag is set, initiate
+ * callback-dialing.
+ *
+ * Return-Value: 0 = No appropriate interface for this call.
+ *               1 = Call accepted
+ *               2 = Reject call, wait cbdelay, then call back
+ *               3 = Reject call
+ *               4 = Wait cbdelay, then call back
+ *               5 = No appropriate interface for this call,
+ *                   would eventually match if CID was longer.
+ */
+int
+isdn_net_find_icall(int di, int ch, int idx, setup_parm *setup)
+{
+	char *eaz;
+	int si1;
+	int si2;
+	int ematch;
+	int wret;
+	int swapped;
+	int sidx = 0;
+	isdn_net_dev *p;
+	isdn_net_phone *n;
+	ulong flags;
+	char nr[32];
+	/* Search name in netdev-chain */
+	save_flags(flags);
+	cli();
+	if (!setup->phone[0]) {
+		nr[0] = '0';
+		nr[1] = '\0';
+		printk(KERN_INFO "isdn_net: Incoming call without OAD, assuming '0'\n");
+	} else
+		strcpy(nr, setup->phone);
+	si1 = (int) setup->si1;
+	si2 = (int) setup->si2;
+	if (!setup->eazmsn[0]) {
+		printk(KERN_WARNING "isdn_net: Incoming call without CPN, assuming '0'\n");
+		eaz = "0";
+	} else
+		eaz = setup->eazmsn;
+	if (dev->net_verbose > 1)
+		printk(KERN_INFO "isdn_net: call from %s,%d,%d -> %s\n", nr, si1, si2, eaz);
+	/* Accept only calls with Si1 = 7 (Data-Transmission) */
+	if (si1 != 7) {
+		restore_flags(flags);
+		if (dev->net_verbose > 1)
+			printk(KERN_INFO "isdn_net: Service-Indicator not 7, ignored\n");
+		return 0;
+	}
+	n = (isdn_net_phone *) 0;
+	p = dev->netdev;
+	ematch = wret = swapped = 0;
+#ifdef ISDN_DEBUG_NET_ICALL
+	printk(KERN_DEBUG "n_fi: di=%d ch=%d idx=%d usg=%d\n", di, ch, idx,
+	       dev->usage[idx]);
+#endif
+	while (p) {
+		int matchret;
+		isdn_net_local *lp = p->local;
+
+		/* If last check has triggered as binding-swap, revert it */
+		switch (swapped) {
+			case 2:
+				isdn_net_swap_usage(idx, sidx);
+				/* fall through */
+			case 1:
+				isdn_net_swapbind(di);
+				break;
+		}
+		swapped = 0;
+		if (!(matchret = isdn_msncmp(eaz, isdn_map_eaz2msn(lp->msn, di))))
+			ematch = 1;
+		/* Remember if more numbers eventually can match */
+		if (matchret > wret)
+			wret = matchret;
+#ifdef ISDN_DEBUG_NET_ICALL
+		printk(KERN_DEBUG "n_fi: if='%s', l.msn=%s, l.flags=%d, l.dstate=%d\n",
+		       lp->name, lp->msn, lp->flags, lp->dialstate);
+#endif
+		if ((!matchret) &&                                        /* EAZ is matching   */
+		    (((!(lp->flags & ISDN_NET_CONNECTED)) &&              /* but not connected */
+		      (USG_NONE(dev->usage[idx]))) ||                     /* and ch. unused or */
+		     ((((lp->dialstate == 4) || (lp->dialstate == 12)) && /* if dialing        */
+		       (!(lp->flags & ISDN_NET_CALLBACK)))                /* but no callback   */
+		     )))
+			 {
+#ifdef ISDN_DEBUG_NET_ICALL
+			printk(KERN_DEBUG "n_fi: match1, pdev=%d pch=%d\n",
+			       lp->pre_device, lp->pre_channel);
+#endif
+			if (dev->usage[idx] & ISDN_USAGE_EXCLUSIVE) {
+				if ((lp->pre_channel != ch) ||
+				    (lp->pre_device != di)) {
+					/* Here we got a problem:
+					 * If using an ICN-Card, an incoming call is always signaled on
+					 * on the first channel of the card, if both channels are
+					 * down. However this channel may be bound exclusive. If the
+					 * second channel is free, this call should be accepted.
+					 * The solution is horribly but it runs, so what:
+					 * We exchange the exclusive bindings of the two channels, the
+					 * corresponding variables in the interface-structs.
+					 */
+					if (ch == 0) {
+						sidx = isdn_dc2minor(di, 1);
+#ifdef ISDN_DEBUG_NET_ICALL
+						printk(KERN_DEBUG "n_fi: ch is 0\n");
+#endif
+						if (USG_NONE(dev->usage[sidx])) {
+							/* Second Channel is free, now see if it is bound
+							 * exclusive too. */
+							if (dev->usage[sidx] & ISDN_USAGE_EXCLUSIVE) {
+#ifdef ISDN_DEBUG_NET_ICALL
+								printk(KERN_DEBUG "n_fi: 2nd channel is down and bound\n");
+#endif
+								/* Yes, swap bindings only, if the original
+								 * binding is bound to channel 1 of this driver */
+								if ((lp->pre_device == di) &&
+								    (lp->pre_channel == 1)) {
+									isdn_net_swapbind(di);
+									swapped = 1;
+								} else {
+									/* ... else iterate next device */
+									p = (isdn_net_dev *) p->next;
+									continue;
+								}
+							} else {
+#ifdef ISDN_DEBUG_NET_ICALL
+								printk(KERN_DEBUG "n_fi: 2nd channel is down and unbound\n");
+#endif
+								/* No, swap always and swap excl-usage also */
+								isdn_net_swap_usage(idx, sidx);
+								isdn_net_swapbind(di);
+								swapped = 2;
+							}
+							/* Now check for exclusive binding again */
+#ifdef ISDN_DEBUG_NET_ICALL
+							printk(KERN_DEBUG "n_fi: final check\n");
+#endif
+							if ((dev->usage[idx] & ISDN_USAGE_EXCLUSIVE) &&
+							    ((lp->pre_channel != ch) ||
+							     (lp->pre_device != di))) {
+#ifdef ISDN_DEBUG_NET_ICALL
+								printk(KERN_DEBUG "n_fi: final check failed\n");
+#endif
+								p = (isdn_net_dev *) p->next;
+								continue;
+							}
+						}
+					} else {
+						/* We are already on the second channel, so nothing to do */
+#ifdef ISDN_DEBUG_NET_ICALL
+						printk(KERN_DEBUG "n_fi: already on 2nd channel\n");
+#endif
+					}
+				}
+			}
+#ifdef ISDN_DEBUG_NET_ICALL
+			printk(KERN_DEBUG "n_fi: match2\n");
+#endif
+			n = lp->phone[0];
+			if (lp->flags & ISDN_NET_SECURE) {
+				while (n) {
+					if (!isdn_msncmp(nr, n->num))
+						break;
+					n = (isdn_net_phone *) n->next;
+				}
+			}
+			if (n || (!(lp->flags & ISDN_NET_SECURE))) {
+#ifdef ISDN_DEBUG_NET_ICALL
+				printk(KERN_DEBUG "n_fi: match3\n");
+#endif
+				/* matching interface found */
+
+				/*
+				 * Is the state STOPPED?
+				 * If so, no dialin is allowed,
+				 * so reject actively.
+				 * */
+				if (ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_OFF) {
+					restore_flags(flags);
+					printk(KERN_INFO "incoming call, interface %s `stopped' -> rejected\n",
+					       lp->name);
+					return 3;
+				}
+				/*
+				 * Is the interface up?
+				 * If not, reject the call actively.
+				 */
+				if (!isdn_net_device_started(p)) {
+					restore_flags(flags);
+					printk(KERN_INFO "%s: incoming call, interface down -> rejected\n",
+					       lp->name);
+					return 3;
+				}
+				/* Interface is up, now see if it's a slave. If so, see if
+				 * it's master and parent slave is online. If not, reject the call.
+				 */
+				if (lp->master) {
+					isdn_net_local *mlp = (isdn_net_local *) lp->master->priv;
+					printk(KERN_DEBUG "ICALLslv: %s\n", lp->name);
+					printk(KERN_DEBUG "master=%s\n", mlp->name);
+					if (mlp->flags & ISDN_NET_CONNECTED) {
+						printk(KERN_DEBUG "master online\n");
+						/* Master is online, find parent-slave (master if first slave) */
+						while (mlp->slave) {
+							if ((isdn_net_local *) mlp->slave->priv == lp)
+								break;
+							mlp = (isdn_net_local *) mlp->slave->priv;
+						}
+					} else
+						printk(KERN_DEBUG "master offline\n");
+					/* Found parent, if it's offline iterate next device */
+					printk(KERN_DEBUG "mlpf: %d\n", mlp->flags & ISDN_NET_CONNECTED);
+					if (!(mlp->flags & ISDN_NET_CONNECTED)) {
+						p = (isdn_net_dev *) p->next;
+						continue;
+					}
+				} 
+				if (lp->flags & ISDN_NET_CALLBACK) {
+					int chi;
+					/*
+					 * Is the state MANUAL?
+					 * If so, no callback can be made,
+					 * so reject actively.
+					 * */
+					if (ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_OFF) {
+						restore_flags(flags);
+						printk(KERN_INFO "incoming call for callback, interface %s `off' -> rejected\n",
+						       lp->name);
+						return 3;
+					}
+					printk(KERN_DEBUG "%s: call from %s -> %s, start callback\n",
+					       lp->name, nr, eaz);
+					if (lp->phone[1]) {
+						/* Grab a free ISDN-Channel */
+						if ((chi = 
+							isdn_get_free_channel(
+								ISDN_USAGE_NET,
+								lp->l2_proto,
+								lp->l3_proto,
+							  	lp->pre_device,
+						 		lp->pre_channel,
+						 		lp->msn)
+								) < 0) {
+
+							printk(KERN_WARNING "isdn_net_find_icall: No channel for %s\n", lp->name);
+							restore_flags(flags);
+							return 0;
+						}
+						/* Setup dialstate. */
+						lp->dtimer = 0;
+						lp->dialstate = 11;
+						/* Connect interface with channel */
+						isdn_net_bind_channel(lp, chi);
+#ifdef CONFIG_ISDN_PPP
+						if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+							if (isdn_ppp_bind(lp) < 0) {
+								isdn_net_unbind_channel(lp);
+								restore_flags(flags);
+								return 0;
+							}
+#endif
+						/* Initiate dialing by returning 2 or 4 */
+						restore_flags(flags);
+						return (lp->flags & ISDN_NET_CBHUP) ? 2 : 4;
+					} else
+						printk(KERN_WARNING "isdn_net: %s: No phone number\n", lp->name);
+					restore_flags(flags);
+					return 0;
+				} else {
+					printk(KERN_DEBUG "%s: call from %s -> %s accepted\n", lp->name, nr,
+					       eaz);
+					/* if this interface is dialing, it does it probably on a different
+					   device, so free this device */
+					if ((lp->dialstate == 4) || (lp->dialstate == 12)) {
+#ifdef CONFIG_ISDN_PPP
+						if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+							isdn_ppp_free(lp);
+#endif
+						isdn_net_lp_disconnected(lp);
+						isdn_free_channel(lp->isdn_device, lp->isdn_channel,
+							 ISDN_USAGE_NET);
+					}
+					dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE;
+					dev->usage[idx] |= ISDN_USAGE_NET;
+					strcpy(dev->num[idx], nr);
+					isdn_info_update();
+					dev->st_netdev[idx] = lp->netdev;
+					lp->isdn_device = di;
+					lp->isdn_channel = ch;
+					lp->ppp_slot = -1;
+					lp->flags |= ISDN_NET_CONNECTED;
+					lp->dialstate = 7;
+					lp->dtimer = 0;
+					lp->outgoing = 0;
+					lp->huptimer = 0;
+					lp->hupflags |= ISDN_WAITCHARGE;
+					lp->hupflags &= ~ISDN_HAVECHARGE;
+#ifdef CONFIG_ISDN_PPP
+					if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+						if (isdn_ppp_bind(lp) < 0) {
+							isdn_net_unbind_channel(lp);
+							restore_flags(flags);
+							return 0;
+						}
+#endif
+					restore_flags(flags);
+					return 1;
+				}
+			}
+		}
+		p = (isdn_net_dev *) p->next;
+	}
+	/* If none of configured EAZ/MSN matched and not verbose, be silent */
+	if (!ematch || dev->net_verbose)
+		printk(KERN_INFO "isdn_net: call from %s -> %d %s ignored\n", nr, di, eaz);
+	restore_flags(flags);
+	return (wret == 2)?5:0;
+}
+
+/*
+ * Search list of net-interfaces for an interface with given name.
+ */
+isdn_net_dev *
+isdn_net_findif(char *name)
+{
+	isdn_net_dev *p = dev->netdev;
+
+	while (p) {
+		if (!strcmp(p->local->name, name))
+			return p;
+		p = (isdn_net_dev *) p->next;
+	}
+	return (isdn_net_dev *) NULL;
+}
+
+/*
+ * Force a net-interface to dial out.
+ * This is called from the userlevel-routine below or
+ * from isdn_net_start_xmit().
+ */
+int
+isdn_net_force_dial_lp(isdn_net_local * lp)
+{
+	if ((!(lp->flags & ISDN_NET_CONNECTED)) && !lp->dialstate) {
+		int chi;
+		if (lp->phone[1]) {
+			ulong flags;
+			save_flags(flags);
+			cli();
+
+			/* Grab a free ISDN-Channel */
+			if ((chi = 
+						isdn_get_free_channel(
+							ISDN_USAGE_NET,
+							lp->l2_proto,
+							lp->l3_proto,
+							lp->pre_device,
+						 	lp->pre_channel,
+							lp->msn)
+							) < 0) {
+				printk(KERN_WARNING "isdn_net_force_dial: No channel for %s\n", lp->name);
+				restore_flags(flags);
+				return -EAGAIN;
+			}
+			lp->dialstate = 1;
+			/* Connect interface with channel */
+			isdn_net_bind_channel(lp, chi);
+#ifdef CONFIG_ISDN_PPP
+			if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+				if (isdn_ppp_bind(lp) < 0) {
+					isdn_net_unbind_channel(lp);
+					restore_flags(flags);
+					return -EAGAIN;
+				}
+#endif
+			/* Initiate dialing */
+			restore_flags(flags);
+			isdn_net_dial();
+			return 0;
+		} else
+			return -EINVAL;
+	} else
+		return -EBUSY;
+}
+
+/*
+ * This is called from certain upper protocol layers (multilink ppp
+ * and x25iface encapsulation module) that want to initiate dialing
+ * themselves.
+ */
+int
+isdn_net_dial_req(isdn_net_local * lp)
+{
+	/* is there a better error code? */
+	if (!(ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_AUTO)) return -EBUSY;
+
+	return isdn_net_force_dial_lp(lp);
+}
+
+/*
+ * Force a net-interface to dial out.
+ * This is always called from within userspace (ISDN_IOCTL_NET_DIAL).
+ */
+int
+isdn_net_force_dial(char *name)
+{
+	isdn_net_dev *p = isdn_net_findif(name);
+
+	if (!p)
+		return -ENODEV;
+	return (isdn_net_force_dial_lp(p->local));
+}
+
+/*
+ * Allocate a new network-interface and initialize its data structures.
+ */
+char *
+isdn_net_new(char *name, struct net_device *master)
+{
+	isdn_net_dev *netdev;
+
+	/* Avoid creating an existing interface */
+	if (isdn_net_findif(name)) {
+		printk(KERN_WARNING "isdn_net: interface %s already exists\n", name);
+		return NULL;
+	}
+	if (!(netdev = (isdn_net_dev *) kmalloc(sizeof(isdn_net_dev), GFP_KERNEL))) {
+		printk(KERN_WARNING "isdn_net: Could not allocate net-device\n");
+		return NULL;
+	}
+	memset(netdev, 0, sizeof(isdn_net_dev));
+	if (!(netdev->local = (isdn_net_local *) kmalloc(sizeof(isdn_net_local), GFP_KERNEL))) {
+		printk(KERN_WARNING "isdn_net: Could not allocate device locals\n");
+		kfree(netdev);
+		return NULL;
+	}
+	memset(netdev->local, 0, sizeof(isdn_net_local));
+	if (name == NULL)
+		strcpy(netdev->local->name, "         ");
+	else
+		strcpy(netdev->local->name, name);
+	strcpy(netdev->dev.name, netdev->local->name);
+	netdev->dev.priv = netdev->local;
+	netdev->dev.init = isdn_net_init;
+	netdev->local->p_encap = ISDN_NET_ENCAP_RAWIP;
+	if (master) {
+		/* Device shall be a slave */
+		struct net_device *p = (((isdn_net_local *) master->priv)->slave);
+		struct net_device *q = master;
+
+		netdev->local->master = master;
+		/* Put device at end of slave-chain */
+		while (p) {
+			q = p;
+			p = (((isdn_net_local *) p->priv)->slave);
+		}
+		((isdn_net_local *) q->priv)->slave = &(netdev->dev);
+	} else {
+		/* Device shall be a master */
+		/*
+		 * Watchdog timer (currently) for master only.
+		 */
+		netdev->dev.tx_timeout = isdn_net_tx_timeout;
+		netdev->dev.watchdog_timeo = ISDN_NET_TX_TIMEOUT;
+		if (register_netdev(&netdev->dev) != 0) {
+			printk(KERN_WARNING "isdn_net: Could not register net-device\n");
+			kfree(netdev->local);
+			kfree(netdev);
+			return NULL;
+		}
+	}
+	netdev->local->magic = ISDN_NET_MAGIC;
+
+	netdev->queue = netdev->local;
+	spin_lock_init(&netdev->queue_lock);
+
+	netdev->local->last = netdev->local;
+	netdev->local->netdev = netdev;
+	netdev->local->next = netdev->local;
+
+	netdev->local->tqueue.sync = 0;
+	netdev->local->tqueue.routine = isdn_net_softint;
+	netdev->local->tqueue.data = netdev->local;
+	spin_lock_init(&netdev->local->xmit_lock);
+
+	netdev->local->isdn_device = -1;
+	netdev->local->isdn_channel = -1;
+	netdev->local->pre_device = -1;
+	netdev->local->pre_channel = -1;
+	netdev->local->exclusive = -1;
+	netdev->local->ppp_slot = -1;
+	netdev->local->pppbind = -1;
+	skb_queue_head_init(&netdev->local->super_tx_queue);
+	netdev->local->l2_proto = ISDN_PROTO_L2_X75I;
+	netdev->local->l3_proto = ISDN_PROTO_L3_TRANS;
+	netdev->local->triggercps = 6000;
+	netdev->local->slavedelay = 10 * HZ;
+	netdev->local->hupflags = ISDN_INHUP;	/* Do hangup even on incoming calls */
+	netdev->local->onhtime = 10;	/* Default hangup-time for saving costs
+	   of those who forget configuring this */
+	netdev->local->dialmax = 1;
+	netdev->local->flags = ISDN_NET_CBHUP | ISDN_NET_DM_MANUAL;	/* Hangup before Callback, manual dial */
+	netdev->local->cbdelay = 25;	/* Wait 5 secs before Callback */
+	netdev->local->dialtimeout = -1;  /* Infinite Dial-Timeout */
+	netdev->local->dialwait = 5 * HZ; /* Wait 5 sec. after failed dial */
+	netdev->local->dialstarted = 0;   /* Jiffies of last dial-start */
+	netdev->local->dialwait_timer = 0;  /* Jiffies of earliest next dial-start */
+
+	/* Put into to netdev-chain */
+	netdev->next = (void *) dev->netdev;
+	dev->netdev = netdev;
+	return netdev->dev.name;
+}
+
+char *
+isdn_net_newslave(char *parm)
+{
+	char *p = strchr(parm, ',');
+	isdn_net_dev *n;
+	char newname[10];
+
+	if (p) {
+		/* Slave-Name MUST not be empty */
+		if (!strlen(p + 1))
+			return NULL;
+		strcpy(newname, p + 1);
+		*p = 0;
+		/* Master must already exist */
+		if (!(n = isdn_net_findif(parm)))
+			return NULL;
+		/* Master must be a real interface, not a slave */
+		if (n->local->master)
+			return NULL;
+		/* Master must not be started yet */
+		if (isdn_net_device_started(n)) 
+			return NULL;
+		return (isdn_net_new(newname, &(n->dev)));
+	}
+	return NULL;
+}
+
+/*
+ * Set interface-parameters.
+ * Always set all parameters, so the user-level application is responsible
+ * for not overwriting existing setups. It has to get the current
+ * setup first, if only selected parameters are to be changed.
+ */
+int
+isdn_net_setcfg(isdn_net_ioctl_cfg * cfg)
+{
+	isdn_net_dev *p = isdn_net_findif(cfg->name);
+	ulong features;
+	int i;
+	int drvidx;
+	int chidx;
+	char drvid[25];
+#ifdef CONFIG_ISDN_X25
+	ulong flags;
+#endif
+	if (p) {
+		isdn_net_local *lp = p->local;
+
+		/* See if any registered driver supports the features we want */
+		features = ((1 << cfg->l2_proto) << ISDN_FEATURE_L2_SHIFT) |
+			((1 << cfg->l3_proto) << ISDN_FEATURE_L3_SHIFT);
+		for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+			if (dev->drv[i])
+				if ((dev->drv[i]->interface->features & features) == features)
+					break;
+		if (i == ISDN_MAX_DRIVERS) {
+			printk(KERN_WARNING "isdn_net: No driver with selected features\n");
+			return -ENODEV;
+		}
+		if (lp->p_encap != cfg->p_encap){
+#ifdef CONFIG_ISDN_X25
+			struct concap_proto * cprot = p -> cprot;
+#endif
+			if (isdn_net_device_started(p)) {
+				printk(KERN_WARNING "%s: cannot change encap when if is up\n",
+				       lp->name);
+				return -EBUSY;
+			}
+#ifdef CONFIG_ISDN_X25
+			/* delete old encapsulation protocol if present ... */
+			save_flags(flags);
+			cli(); /* avoid races with incoming events trying to
+				  call cprot->pops methods */
+			if( cprot && cprot -> pops )
+				cprot -> pops -> proto_del ( cprot );
+			p -> cprot = NULL;
+			lp -> dops = NULL;
+			restore_flags(flags);
+			/* ... ,  prepare for configuration of new one ... */
+			switch ( cfg -> p_encap ){
+			case ISDN_NET_ENCAP_X25IFACE:
+				lp -> dops = &isdn_concap_reliable_dl_dops;
+			}
+			/* ... and allocate new one ... */
+			p -> cprot = isdn_concap_new( cfg -> p_encap );
+			/* p -> cprot == NULL now if p_encap is not supported
+			   by means of the concap_proto mechanism */
+			/* the protocol is not configured yet; this will
+			   happen later when isdn_net_reset() is called */
+#endif
+		}
+		switch ( cfg->p_encap ) {
+		case ISDN_NET_ENCAP_SYNCPPP:
+#ifndef CONFIG_ISDN_PPP
+			printk(KERN_WARNING "%s: SyncPPP support not configured\n",
+			       lp->name);
+			return -EINVAL;
+#else
+			p->dev.type = ARPHRD_PPP;	/* change ARP type */
+			p->dev.addr_len = 0;
+			p->dev.do_ioctl = isdn_ppp_dev_ioctl;
+#endif
+			break;
+		case ISDN_NET_ENCAP_X25IFACE:
+#ifndef CONFIG_ISDN_X25
+			printk(KERN_WARNING "%s: isdn-x25 support not configured\n",
+			       p->local->name);
+			return -EINVAL;
+#else
+			p->dev.type = ARPHRD_X25;	/* change ARP type */
+			p->dev.addr_len = 0;
+#endif
+			break;
+		case ISDN_NET_ENCAP_CISCOHDLCK:
+			p->dev.do_ioctl = isdn_ciscohdlck_dev_ioctl;
+			break;
+		default:
+			if( cfg->p_encap >= 0 &&
+			    cfg->p_encap <= ISDN_NET_ENCAP_MAX_ENCAP )
+				break;
+			printk(KERN_WARNING
+			       "%s: encapsulation protocol %d not supported\n",
+			       p->local->name, cfg->p_encap);
+			return -EINVAL;
+		}
+		if (strlen(cfg->drvid)) {
+			/* A bind has been requested ... */
+			char *c,
+			*e;
+
+			drvidx = -1;
+			chidx = -1;
+			strcpy(drvid, cfg->drvid);
+			if ((c = strchr(drvid, ','))) {
+				/* The channel-number is appended to the driver-Id with a comma */
+				chidx = (int) simple_strtoul(c + 1, &e, 10);
+				if (e == c)
+					chidx = -1;
+				*c = '\0';
+			}
+			for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+				/* Lookup driver-Id in array */
+				if (!(strcmp(dev->drvid[i], drvid))) {
+					drvidx = i;
+					break;
+				}
+			if ((drvidx == -1) || (chidx == -1))
+				/* Either driver-Id or channel-number invalid */
+				return -ENODEV;
+		} else {
+			/* Parameters are valid, so get them */
+			drvidx = lp->pre_device;
+			chidx = lp->pre_channel;
+		}
+		if (cfg->exclusive > 0) {
+			unsigned long flags;
+
+			/* If binding is exclusive, try to grab the channel */
+			save_flags(flags);
+			if ((i = isdn_get_free_channel(ISDN_USAGE_NET,
+				lp->l2_proto, lp->l3_proto, drvidx,
+				chidx, lp->msn)) < 0) {
+				/* Grab failed, because desired channel is in use */
+				lp->exclusive = -1;
+				restore_flags(flags);
+				return -EBUSY;
+			}
+			/* All went ok, so update isdninfo */
+			dev->usage[i] = ISDN_USAGE_EXCLUSIVE;
+			isdn_info_update();
+			restore_flags(flags);
+			lp->exclusive = i;
+		} else {
+			/* Non-exclusive binding or unbind. */
+			lp->exclusive = -1;
+			if ((lp->pre_device != -1) && (cfg->exclusive == -1)) {
+				isdn_unexclusive_channel(lp->pre_device, lp->pre_channel);
+				isdn_free_channel(lp->pre_device, lp->pre_channel, ISDN_USAGE_NET);
+				drvidx = -1;
+				chidx = -1;
+			}
+		}
+		strcpy(lp->msn, cfg->eaz);
+		lp->pre_device = drvidx;
+		lp->pre_channel = chidx;
+		lp->onhtime = cfg->onhtime;
+		lp->charge = cfg->charge;
+		lp->l2_proto = cfg->l2_proto;
+		lp->l3_proto = cfg->l3_proto;
+		lp->cbdelay = cfg->cbdelay;
+		lp->dialmax = cfg->dialmax;
+		lp->triggercps = cfg->triggercps;
+		lp->slavedelay = cfg->slavedelay * HZ;
+		lp->pppbind = cfg->pppbind;
+		lp->dialtimeout = cfg->dialtimeout >= 0 ? cfg->dialtimeout * HZ : -1;
+		lp->dialwait = cfg->dialwait * HZ;
+		if (cfg->secure)
+			lp->flags |= ISDN_NET_SECURE;
+		else
+			lp->flags &= ~ISDN_NET_SECURE;
+		if (cfg->cbhup)
+			lp->flags |= ISDN_NET_CBHUP;
+		else
+			lp->flags &= ~ISDN_NET_CBHUP;
+		switch (cfg->callback) {
+			case 0:
+				lp->flags &= ~(ISDN_NET_CALLBACK | ISDN_NET_CBOUT);
+				break;
+			case 1:
+				lp->flags |= ISDN_NET_CALLBACK;
+				lp->flags &= ~ISDN_NET_CBOUT;
+				break;
+			case 2:
+				lp->flags |= ISDN_NET_CBOUT;
+				lp->flags &= ~ISDN_NET_CALLBACK;
+				break;
+		}
+		lp->flags &= ~ISDN_NET_DIALMODE_MASK;	/* first all bits off */
+		if (cfg->dialmode && !(cfg->dialmode & ISDN_NET_DIALMODE_MASK)) {
+			/* old isdnctrl version, where only 0 or 1 is given */
+			printk(KERN_WARNING
+			     "Old isdnctrl version detected! Please update.\n");
+			lp->flags |= ISDN_NET_DM_OFF; /* turn on `off' bit */
+		}
+		else {
+			lp->flags |= cfg->dialmode;  /* turn on selected bits */
+		}
+		if (cfg->chargehup)
+			lp->hupflags |= ISDN_CHARGEHUP;
+		else
+			lp->hupflags &= ~ISDN_CHARGEHUP;
+		if (cfg->ihup)
+			lp->hupflags |= ISDN_INHUP;
+		else
+			lp->hupflags &= ~ISDN_INHUP;
+		if (cfg->chargeint > 10) {
+			lp->hupflags |= ISDN_HAVECHARGE | ISDN_MANCHARGE;
+			lp->chargeint = cfg->chargeint * HZ;
+		}
+		if (cfg->p_encap != lp->p_encap) {
+			if (cfg->p_encap == ISDN_NET_ENCAP_RAWIP) {
+				p->dev.hard_header = NULL;
+				p->dev.hard_header_cache = NULL;
+				p->dev.header_cache_update = NULL;
+				p->dev.flags = IFF_NOARP|IFF_POINTOPOINT;
+			} else {
+				p->dev.hard_header = isdn_net_header;
+				if (cfg->p_encap == ISDN_NET_ENCAP_ETHER) {
+					p->dev.hard_header_cache = lp->org_hhc;
+					p->dev.header_cache_update = lp->org_hcu;
+					p->dev.flags = IFF_BROADCAST | IFF_MULTICAST;
+				} else {
+					p->dev.hard_header_cache = NULL;
+					p->dev.header_cache_update = NULL;
+					p->dev.flags = IFF_NOARP|IFF_POINTOPOINT;
+				}
+			}
+		}
+		lp->p_encap = cfg->p_encap;
+		return 0;
+	}
+	return -ENODEV;
+}
+
+/*
+ * Perform get-interface-parameters.ioctl
+ */
+int
+isdn_net_getcfg(isdn_net_ioctl_cfg * cfg)
+{
+	isdn_net_dev *p = isdn_net_findif(cfg->name);
+
+	if (p) {
+		isdn_net_local *lp = p->local;
+
+		strcpy(cfg->eaz, lp->msn);
+		cfg->exclusive = lp->exclusive;
+		if (lp->pre_device >= 0) {
+			sprintf(cfg->drvid, "%s,%d", dev->drvid[lp->pre_device],
+				lp->pre_channel);
+		} else
+			cfg->drvid[0] = '\0';
+		cfg->onhtime = lp->onhtime;
+		cfg->charge = lp->charge;
+		cfg->l2_proto = lp->l2_proto;
+		cfg->l3_proto = lp->l3_proto;
+		cfg->p_encap = lp->p_encap;
+		cfg->secure = (lp->flags & ISDN_NET_SECURE) ? 1 : 0;
+		cfg->callback = 0;
+		if (lp->flags & ISDN_NET_CALLBACK)
+			cfg->callback = 1;
+		if (lp->flags & ISDN_NET_CBOUT)
+			cfg->callback = 2;
+		cfg->cbhup = (lp->flags & ISDN_NET_CBHUP) ? 1 : 0;
+		cfg->dialmode = lp->flags & ISDN_NET_DIALMODE_MASK;
+		cfg->chargehup = (lp->hupflags & 4) ? 1 : 0;
+		cfg->ihup = (lp->hupflags & 8) ? 1 : 0;
+		cfg->cbdelay = lp->cbdelay;
+		cfg->dialmax = lp->dialmax;
+		cfg->triggercps = lp->triggercps;
+		cfg->slavedelay = lp->slavedelay / HZ;
+		cfg->chargeint = (lp->hupflags & ISDN_CHARGEHUP) ?
+		    (lp->chargeint / HZ) : 0;
+		cfg->pppbind = lp->pppbind;
+		cfg->dialtimeout = lp->dialtimeout >= 0 ? lp->dialtimeout / HZ : -1;
+		cfg->dialwait = lp->dialwait / HZ;
+		if (lp->slave)
+			strcpy(cfg->slave, ((isdn_net_local *) lp->slave->priv)->name);
+		else
+			cfg->slave[0] = '\0';
+		if (lp->master)
+			strcpy(cfg->master, ((isdn_net_local *) lp->master->priv)->name);
+		else
+			cfg->master[0] = '\0';
+		return 0;
+	}
+	return -ENODEV;
+}
+
+/*
+ * Add a phone-number to an interface.
+ */
+int
+isdn_net_addphone(isdn_net_ioctl_phone * phone)
+{
+	isdn_net_dev *p = isdn_net_findif(phone->name);
+	isdn_net_phone *n;
+
+	if (p) {
+		if (!(n = (isdn_net_phone *) kmalloc(sizeof(isdn_net_phone), GFP_KERNEL)))
+			return -ENOMEM;
+		strcpy(n->num, phone->phone);
+		n->next = p->local->phone[phone->outgoing & 1];
+		p->local->phone[phone->outgoing & 1] = n;
+		return 0;
+	}
+	return -ENODEV;
+}
+
+/*
+ * Copy a string of all phone-numbers of an interface to user space.
+ * This might sleep and must be called with the isdn semaphore down.
+ */
+int
+isdn_net_getphones(isdn_net_ioctl_phone * phone, char *phones)
+{
+	isdn_net_dev *p = isdn_net_findif(phone->name);
+	int inout = phone->outgoing & 1;
+	int more = 0;
+	int count = 0;
+	isdn_net_phone *n;
+
+	if (!p)
+		return -ENODEV;
+	inout &= 1;
+	for (n = p->local->phone[inout]; n; n = n->next) {
+		if (more) {
+			put_user(' ', phones++);
+			count++;
+		}
+		if (copy_to_user(phones, n->num, strlen(n->num) + 1)) {
+			return -EFAULT;
+		}
+		phones += strlen(n->num);
+		count += strlen(n->num);
+		more = 1;
+	}
+	put_user(0, phones);
+	count++;
+	return count;
+}
+
+/*
+ * Copy a string containing the peer's phone number of a connected interface
+ * to user space.
+ */
+int
+isdn_net_getpeer(isdn_net_ioctl_phone *phone, isdn_net_ioctl_phone *peer)
+{
+	isdn_net_dev *p = isdn_net_findif(phone->name);
+	int ch, dv, idx;
+
+	if (!p) return -ENODEV;
+	/*
+	 * Theoretical race: while this executes, the remote number might
+	 * become invalid (hang up) or change (new connection), resulting
+         * in (partially) wrong number copied to user. This race
+	 * currently ignored.
+	 */
+	ch = p->local->isdn_channel;
+	dv = p->local->isdn_device;
+	if(ch<0 && dv<0) return -ENOTCONN;
+	idx = isdn_dc2minor(dv, ch);
+	if (idx<0) return -ENODEV;
+	/* for pre-bound channels, we need this extra check */
+	if ( strncmp(dev->num[idx],"???",3) == 0 ) return -ENOTCONN;
+	strncpy(phone->phone,dev->num[idx],ISDN_MSNLEN);
+	phone->outgoing=USG_OUTGOING(dev->usage[idx]);
+	if ( copy_to_user(peer,phone,sizeof(*peer)) ) return -EFAULT;
+	return 0;
+}
+/*
+ * Delete a phone-number from an interface.
+ */
+int
+isdn_net_delphone(isdn_net_ioctl_phone * phone)
+{
+	isdn_net_dev *p = isdn_net_findif(phone->name);
+	int inout = phone->outgoing & 1;
+	isdn_net_phone *n;
+	isdn_net_phone *m;
+	unsigned long flags;
+
+	if (p) {
+		save_flags(flags);
+		cli();
+		n = p->local->phone[inout];
+		m = NULL;
+		while (n) {
+			if (!strcmp(n->num, phone->phone)) {
+				if (p->local->dial == n)
+					p->local->dial = n->next;
+				if (m)
+					m->next = n->next;
+				else
+					p->local->phone[inout] = n->next;
+				kfree(n);
+				restore_flags(flags);
+				return 0;
+			}
+			m = n;
+			n = (isdn_net_phone *) n->next;
+		}
+		restore_flags(flags);
+		return -EINVAL;
+	}
+	return -ENODEV;
+}
+
+/*
+ * Delete all phone-numbers of an interface.
+ */
+static int
+isdn_net_rmallphone(isdn_net_dev * p)
+{
+	isdn_net_phone *n;
+	isdn_net_phone *m;
+	unsigned long flags;
+	int i;
+
+	save_flags(flags);
+	cli();
+	for (i = 0; i < 2; i++) {
+		n = p->local->phone[i];
+		while (n) {
+			m = n->next;
+			kfree(n);
+			n = m;
+		}
+		p->local->phone[i] = NULL;
+	}
+	p->local->dial = NULL;
+	restore_flags(flags);
+	return 0;
+}
+
+/*
+ * Force a hangup of a network-interface.
+ */
+int
+isdn_net_force_hangup(char *name)
+{
+	isdn_net_dev *p = isdn_net_findif(name);
+	struct net_device *q;
+
+	if (p) {
+		if (p->local->isdn_device < 0)
+			return 1;
+		q = p->local->slave;
+		/* If this interface has slaves, do a hangup for them also. */
+		while (q) {
+			isdn_net_hangup(q);
+			q = (((isdn_net_local *) q->priv)->slave);
+		}
+		isdn_net_hangup(&p->dev);
+		return 0;
+	}
+	return -ENODEV;
+}
+
+/*
+ * Helper-function for isdn_net_rm: Do the real work.
+ */
+static int
+isdn_net_realrm(isdn_net_dev * p, isdn_net_dev * q)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	if (isdn_net_device_started(p)) {
+		restore_flags(flags);
+		return -EBUSY;
+	}
+#ifdef CONFIG_ISDN_X25
+	if( p -> cprot && p -> cprot -> pops )
+		p -> cprot -> pops -> proto_del ( p -> cprot );
+#endif
+	/* Free all phone-entries */
+	isdn_net_rmallphone(p);
+	/* If interface is bound exclusive, free channel-usage */
+	if (p->local->exclusive != -1)
+		isdn_unexclusive_channel(p->local->pre_device, p->local->pre_channel);
+	if (p->local->master) {
+		/* It's a slave-device, so update master's slave-pointer if necessary */
+		if (((isdn_net_local *) (p->local->master->priv))->slave == &p->dev)
+			((isdn_net_local *) (p->local->master->priv))->slave = p->local->slave;
+	} else {
+		/* Unregister only if it's a master-device */
+		p->dev.hard_header_cache = p->local->org_hhc;
+		p->dev.header_cache_update = p->local->org_hcu;
+		unregister_netdev(&p->dev);
+	}
+	/* Unlink device from chain */
+	if (q)
+		q->next = p->next;
+	else
+		dev->netdev = p->next;
+	if (p->local->slave) {
+		/* If this interface has a slave, remove it also */
+		char *slavename = ((isdn_net_local *) (p->local->slave->priv))->name;
+		isdn_net_dev *n = dev->netdev;
+		q = NULL;
+		while (n) {
+			if (!strcmp(n->local->name, slavename)) {
+				isdn_net_realrm(n, q);
+				break;
+			}
+			q = n;
+			n = (isdn_net_dev *) n->next;
+		}
+	}
+	/* If no more net-devices remain, disable auto-hangup timer */
+	if (dev->netdev == NULL)
+		isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 0);
+	restore_flags(flags);
+	kfree(p->local);
+	kfree(p);
+
+	return 0;
+}
+
+/*
+ * Remove a single network-interface.
+ */
+int
+isdn_net_rm(char *name)
+{
+	isdn_net_dev *p;
+	isdn_net_dev *q;
+
+	/* Search name in netdev-chain */
+	p = dev->netdev;
+	q = NULL;
+	while (p) {
+		if (!strcmp(p->local->name, name))
+			return (isdn_net_realrm(p, q));
+		q = p;
+		p = (isdn_net_dev *) p->next;
+	}
+	/* If no more net-devices remain, disable auto-hangup timer */
+	if (dev->netdev == NULL)
+		isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 0);
+	return -ENODEV;
+}
+
+/*
+ * Remove all network-interfaces
+ */
+int
+isdn_net_rmall(void)
+{
+	unsigned long flags;
+	int ret;
+
+	/* Walk through netdev-chain */
+	save_flags(flags);
+	cli();
+	while (dev->netdev) {
+		if (!dev->netdev->local->master) {
+			/* Remove master-devices only, slaves get removed with their master */
+			if ((ret = isdn_net_realrm(dev->netdev, NULL))) {
+				restore_flags(flags);
+				return ret;
+			}
+		}
+	}
+	dev->netdev = NULL;
+	restore_flags(flags);
+	return 0;
+}
diff -Nru a/drivers/isdn/i4l/isdn_net.h b/drivers/isdn/i4l/isdn_net.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/i4l/isdn_net.h	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,180 @@
+/* $Id: isdn_net.h,v 1.19.6.4 2001/09/28 08:05:29 kai Exp $
+ *
+ * header for Linux ISDN subsystem, network related functions (linklevel).
+ *
+ * Copyright 1994-1999  by Fritz Elfert (fritz@isdn4linux.de)
+ * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
+ * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+			      /* Definitions for hupflags:                */
+#define ISDN_WAITCHARGE  1      /* did not get a charge info yet            */
+#define ISDN_HAVECHARGE  2      /* We know a charge info                    */
+#define ISDN_CHARGEHUP   4      /* We want to use the charge mechanism      */
+#define ISDN_INHUP       8      /* Even if incoming, close after huptimeout */
+#define ISDN_MANCHARGE  16      /* Charge Interval manually set             */
+
+/*
+ * Definitions for Cisco-HDLC header.
+ */
+
+#define CISCO_ADDR_UNICAST    0x0f
+#define CISCO_ADDR_BROADCAST  0x8f
+#define CISCO_CTRL            0x00
+#define CISCO_TYPE_CDP        0x2000
+#define CISCO_TYPE_INET       0x0800
+#define CISCO_TYPE_SLARP      0x8035
+#define CISCO_SLARP_REQUEST   0
+#define CISCO_SLARP_REPLY     1
+#define CISCO_SLARP_KEEPALIVE 2
+
+extern char *isdn_net_new(char *, struct net_device *);
+extern char *isdn_net_newslave(char *);
+extern int isdn_net_rm(char *);
+extern int isdn_net_rmall(void);
+extern int isdn_net_stat_callback(int, isdn_ctrl *);
+extern int isdn_net_setcfg(isdn_net_ioctl_cfg *);
+extern int isdn_net_getcfg(isdn_net_ioctl_cfg *);
+extern int isdn_net_addphone(isdn_net_ioctl_phone *);
+extern int isdn_net_getphones(isdn_net_ioctl_phone *, char *);
+extern int isdn_net_getpeer(isdn_net_ioctl_phone *, isdn_net_ioctl_phone *);
+extern int isdn_net_delphone(isdn_net_ioctl_phone *);
+extern int isdn_net_find_icall(int, int, int, setup_parm *);
+extern void isdn_net_hangup(struct net_device *);
+extern void isdn_net_dial(void);
+extern void isdn_net_autohup(void);
+extern int isdn_net_force_hangup(char *);
+extern int isdn_net_force_dial(char *);
+extern isdn_net_dev *isdn_net_findif(char *);
+extern int isdn_net_rcv_skb(int, struct sk_buff *);
+extern int isdn_net_dial_req(isdn_net_local *);
+extern void isdn_net_writebuf_skb(isdn_net_local *lp, struct sk_buff *skb);
+extern void isdn_net_write_super(isdn_net_local *lp, struct sk_buff *skb);
+
+#define ISDN_NET_MAX_QUEUE_LENGTH 2
+
+/*
+ * is this particular channel busy?
+ */
+static __inline__ int isdn_net_lp_busy(isdn_net_local *lp)
+{
+	if (atomic_read(&lp->frame_cnt) < ISDN_NET_MAX_QUEUE_LENGTH)
+		return 0;
+	else 
+		return 1;
+}
+
+/*
+ * For the given net device, this will get a non-busy channel out of the
+ * corresponding bundle. The returned channel is locked.
+ */
+static __inline__ isdn_net_local * isdn_net_get_locked_lp(isdn_net_dev *nd)
+{
+	unsigned long flags;
+	isdn_net_local *lp;
+
+	spin_lock_irqsave(&nd->queue_lock, flags);
+	lp = nd->queue;         /* get lp on top of queue */
+	spin_lock_bh(&nd->queue->xmit_lock);
+	while (isdn_net_lp_busy(nd->queue)) {
+		spin_unlock_bh(&nd->queue->xmit_lock);
+		nd->queue = nd->queue->next;
+		if (nd->queue == lp) { /* not found -- should never happen */
+			lp = NULL;
+			goto errout;
+		}
+		spin_lock_bh(&nd->queue->xmit_lock);
+	}
+	lp = nd->queue;
+	nd->queue = nd->queue->next;
+errout:
+	spin_unlock_irqrestore(&nd->queue_lock, flags);
+	return lp;
+}
+
+/*
+ * add a channel to a bundle
+ */
+static __inline__ void isdn_net_add_to_bundle(isdn_net_dev *nd, isdn_net_local *nlp)
+{
+	isdn_net_local *lp;
+	unsigned long flags;
+
+	spin_lock_irqsave(&nd->queue_lock, flags);
+
+	lp = nd->queue;
+	nlp->last = lp->last;
+	lp->last->next = nlp;
+	lp->last = nlp;
+	nlp->next = lp;
+	nd->queue = nlp;
+
+	spin_unlock_irqrestore(&nd->queue_lock, flags);
+}
+/*
+ * remove a channel from the bundle it belongs to
+ */
+static __inline__ void isdn_net_rm_from_bundle(isdn_net_local *lp)
+{
+	isdn_net_local *master_lp = lp;
+	unsigned long flags;
+
+	if (lp->master)
+		master_lp = (isdn_net_local *) lp->master->priv;
+
+	spin_lock_irqsave(&master_lp->netdev->queue_lock, flags);
+	lp->last->next = lp->next;
+	lp->next->last = lp->last;
+	if (master_lp->netdev->queue == lp)
+		master_lp->netdev->queue = lp->next;
+	lp->next = lp->last = lp;	/* (re)set own pointers */
+	spin_unlock_irqrestore(&master_lp->netdev->queue_lock, flags);
+}
+
+static inline int
+put_u8(unsigned char *p, u8 x)
+{
+	*p = x;
+	return 1;
+}
+
+static inline int
+put_u16(unsigned char *p, u16 x)
+{
+	*((u16 *)p) = htons(x);
+	return 2;
+}
+
+static inline int
+put_u32(unsigned char *p, u32 x)
+{
+	*((u32 *)p) = htonl(x);
+	return 4;
+}
+
+static inline int
+get_u8(unsigned char *p, u8 *x)
+{
+	*x = *p;
+	return 1;
+}
+
+static inline int
+get_u16(unsigned char *p, u16 *x)
+{
+	*x = ntohs(*((u16 *)p));
+	return 2;
+}
+
+static inline int
+get_u32(unsigned char *p, u32 *x)
+{
+	*x = ntohl(*((u32 *)p));
+	return 4;
+}
+
+
diff -Nru a/drivers/isdn/i4l/isdn_ppp.c b/drivers/isdn/i4l/isdn_ppp.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/i4l/isdn_ppp.c	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,2829 @@
+/* $Id: isdn_ppp.c,v 1.85.6.9 2001/11/06 20:58:28 kai Exp $
+ *
+ * Linux ISDN subsystem, functions for synchronous PPP (linklevel).
+ *
+ * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/isdn.h>
+#include <linux/smp_lock.h>
+#include <linux/poll.h>
+#include <linux/ppp-comp.h>
+
+#include "isdn_common.h"
+#include "isdn_ppp.h"
+#include "isdn_net.h"
+
+#ifndef PPP_IPX
+#define PPP_IPX 0x002b
+#endif
+
+/* Prototypes */
+static int isdn_ppp_fill_rq(unsigned char *buf, int len, int proto, int slot);
+static int isdn_ppp_closewait(int slot);
+static void isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp,
+				 struct sk_buff *skb, int proto);
+static int isdn_ppp_if_get_unit(char *namebuf);
+static int isdn_ppp_set_compressor(struct ippp_struct *is,struct isdn_ppp_comp_data *);
+static struct sk_buff *isdn_ppp_decompress(struct sk_buff *,
+				struct ippp_struct *,struct ippp_struct *,int *proto);
+static void isdn_ppp_receive_ccp(isdn_net_dev * net_dev, isdn_net_local * lp,
+				struct sk_buff *skb,int proto);
+static struct sk_buff *isdn_ppp_compress(struct sk_buff *skb_in,int *proto,
+	struct ippp_struct *is,struct ippp_struct *master,int type);
+static void isdn_ppp_send_ccp(isdn_net_dev *net_dev, isdn_net_local *lp,
+	 struct sk_buff *skb);
+
+/* New CCP stuff */
+static void isdn_ppp_ccp_kickup(struct ippp_struct *is);
+static void isdn_ppp_ccp_xmit_reset(struct ippp_struct *is, int proto,
+				    unsigned char code, unsigned char id,
+				    unsigned char *data, int len);
+static struct ippp_ccp_reset *isdn_ppp_ccp_reset_alloc(struct ippp_struct *is);
+static void isdn_ppp_ccp_reset_free(struct ippp_struct *is);
+static void isdn_ppp_ccp_reset_free_state(struct ippp_struct *is,
+					  unsigned char id);
+static void isdn_ppp_ccp_timer_callback(unsigned long closure);
+static struct ippp_ccp_reset_state *isdn_ppp_ccp_reset_alloc_state(struct ippp_struct *is,
+						      unsigned char id);
+static void isdn_ppp_ccp_reset_trans(struct ippp_struct *is,
+				     struct isdn_ppp_resetparams *rp);
+static void isdn_ppp_ccp_reset_ack_rcvd(struct ippp_struct *is,
+					unsigned char id);
+
+
+
+#ifdef CONFIG_ISDN_MPP
+static ippp_bundle * isdn_ppp_bundle_arr = NULL;
+ 
+static int isdn_ppp_mp_bundle_array_init(void);
+static int isdn_ppp_mp_init( isdn_net_local * lp, ippp_bundle * add_to );
+static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, 
+							struct sk_buff *skb);
+static void isdn_ppp_mp_cleanup( isdn_net_local * lp );
+
+static int isdn_ppp_bundle(struct ippp_struct *, int unit);
+#endif	/* CONFIG_ISDN_MPP */
+  
+char *isdn_ppp_revision = "$Revision: 1.85.6.9 $";
+
+static struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS];
+
+static struct isdn_ppp_compressor *ipc_head = NULL;
+
+/*
+ * frame log (debug)
+ */
+static void
+isdn_ppp_frame_log(char *info, char *data, int len, int maxlen,int unit,int slot)
+{
+	int cnt,
+	 j,
+	 i;
+	char buf[80];
+
+	if (len < maxlen)
+		maxlen = len;
+
+	for (i = 0, cnt = 0; cnt < maxlen; i++) {
+		for (j = 0; j < 16 && cnt < maxlen; j++, cnt++)
+			sprintf(buf + j * 3, "%02x ", (unsigned char) data[cnt]);
+		printk(KERN_DEBUG "[%d/%d].%s[%d]: %s\n",unit,slot, info, i, buf);
+	}
+}
+
+/*
+ * unbind isdn_net_local <=> ippp-device
+ * note: it can happen, that we hangup/free the master before the slaves
+ *       in this case we bind another lp to the master device
+ */
+int
+isdn_ppp_free(isdn_net_local * lp)
+{
+	unsigned long flags;
+	struct ippp_struct *is;
+
+	if (lp->ppp_slot < 0 || lp->ppp_slot > ISDN_MAX_CHANNELS)
+		return 0;
+
+	save_flags(flags);
+	cli();
+
+#ifdef CONFIG_ISDN_MPP
+	spin_lock(&lp->netdev->pb->lock);
+#endif
+	isdn_net_rm_from_bundle(lp);
+#ifdef CONFIG_ISDN_MPP
+	if (lp->netdev->pb->ref_ct == 1)	/* last link in queue? */
+		isdn_ppp_mp_cleanup(lp);
+
+	lp->netdev->pb->ref_ct--;
+	spin_unlock(&lp->netdev->pb->lock);
+#endif /* CONFIG_ISDN_MPP */
+
+	is = ippp_table[lp->ppp_slot];
+	if ((is->state & IPPP_CONNECT))
+		isdn_ppp_closewait(lp->ppp_slot);	/* force wakeup on ippp device */
+	else if (is->state & IPPP_ASSIGNED)
+		is->state = IPPP_OPEN;	/* fallback to 'OPEN but not ASSIGNED' state */
+
+	if (is->debug & 0x1)
+		printk(KERN_DEBUG "isdn_ppp_free %d %lx %lx\n", lp->ppp_slot, (long) lp, (long) is->lp);
+
+	is->lp = NULL;          /* link is down .. set lp to NULL */
+	lp->ppp_slot = -1;      /* is this OK ?? */
+
+	restore_flags(flags);
+	return 0;
+}
+
+/*
+ * bind isdn_net_local <=> ippp-device
+ */
+int
+isdn_ppp_bind(isdn_net_local * lp)
+{
+	int i;
+	int unit = 0;
+	long flags;
+	struct ippp_struct *is;
+	int retval;
+
+	save_flags(flags);
+	cli();
+	if (lp->pppbind < 0) {  /* device bounded to ippp device ? */
+		isdn_net_dev *net_dev = dev->netdev;
+		char exclusive[ISDN_MAX_CHANNELS];	/* exclusive flags */
+		memset(exclusive, 0, ISDN_MAX_CHANNELS);
+		while (net_dev) {	/* step through net devices to find exclusive minors */
+			isdn_net_local *lp = net_dev->local;
+			if (lp->pppbind >= 0)
+				exclusive[lp->pppbind] = 1;
+			net_dev = net_dev->next;
+		}
+		/*
+		 * search a free device / slot
+		 */
+		for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+			if (ippp_table[i]->state == IPPP_OPEN && !exclusive[ippp_table[i]->minor]) {	/* OPEN, but not connected! */
+				break;
+			}
+		}
+	} else {
+		for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+			if (ippp_table[i]->minor == lp->pppbind &&
+			    (ippp_table[i]->state & IPPP_OPEN) == IPPP_OPEN)
+				break;
+		}
+	}
+
+	if (i >= ISDN_MAX_CHANNELS) {
+		restore_flags(flags);
+		printk(KERN_WARNING "isdn_ppp_bind: Can't find a (free) connection to the ipppd daemon.\n");
+		retval = -1;
+		goto out;
+	}
+	unit = isdn_ppp_if_get_unit(lp->name);	/* get unit number from interface name .. ugly! */
+	if (unit < 0) {
+		printk(KERN_ERR "isdn_ppp_bind: illegal interface name %s.\n", lp->name);
+		retval = -1;
+		goto out;
+	}
+	
+	lp->ppp_slot = i;
+	is = ippp_table[i];
+	is->lp = lp;
+	is->unit = unit;
+	is->state = IPPP_OPEN | IPPP_ASSIGNED;	/* assigned to a netdevice but not connected */
+#ifdef CONFIG_ISDN_MPP
+	retval = isdn_ppp_mp_init(lp, NULL);
+	if (retval < 0)
+		goto out;
+#endif /* CONFIG_ISDN_MPP */
+
+	retval = lp->ppp_slot;
+
+ out:
+	restore_flags(flags);
+	return retval;
+}
+
+/*
+ * kick the ipppd on the device
+ * (wakes up daemon after B-channel connect)
+ */
+
+void
+isdn_ppp_wakeup_daemon(isdn_net_local * lp)
+{
+	if (lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS)
+		return;
+
+	ippp_table[lp->ppp_slot]->state = IPPP_OPEN | IPPP_CONNECT | IPPP_NOBLOCK;
+
+		wake_up_interruptible(&ippp_table[lp->ppp_slot]->wq);
+}
+
+/*
+ * there was a hangup on the netdevice
+ * force wakeup of the ippp device
+ * go into 'device waits for release' state
+ */
+static int
+isdn_ppp_closewait(int slot)
+{
+	struct ippp_struct *is;
+
+	if (slot < 0 || slot >= ISDN_MAX_CHANNELS)
+		return 0;
+	is = ippp_table[slot];
+
+	if (is->state)
+		wake_up_interruptible(&is->wq);
+
+	is->state = IPPP_CLOSEWAIT;
+	return 1;
+}
+
+/*
+ * isdn_ppp_find_slot / isdn_ppp_free_slot
+ */
+
+static int
+isdn_ppp_get_slot(void)
+{
+	int i;
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		if (!ippp_table[i]->state)
+			return i;
+	}
+	return -1;
+}
+
+/*
+ * isdn_ppp_open
+ */
+
+static int
+isdn_ppp_open(struct inode *ino, struct file *file)
+{
+	uint minor = minor(ino->i_rdev) - ISDN_MINOR_PPP;
+	int slot;
+	struct ippp_struct *is;
+
+	slot = isdn_ppp_get_slot();
+	if (slot < 0) {
+		return -EBUSY;
+	}
+	is = file->private_data = ippp_table[slot];
+
+		printk(KERN_DEBUG "ippp, open, slot: %d, minor: %d, state: %04x\n", slot, minor, is->state);
+
+	/* compression stuff */
+	is->link_compressor   = is->compressor = NULL;
+	is->link_decompressor = is->decompressor = NULL;
+	is->link_comp_stat    = is->comp_stat = NULL;
+	is->link_decomp_stat  = is->decomp_stat = NULL;
+	is->compflags = 0;
+
+	is->reset = isdn_ppp_ccp_reset_alloc(is);
+
+	is->lp = NULL;
+	is->mp_seqno = 0;       /* MP sequence number */
+	is->pppcfg = 0;         /* ppp configuration */
+	is->mpppcfg = 0;        /* mppp configuration */
+	is->last_link_seqno = -1;	/* MP: maybe set to Bundle-MIN, when joining a bundle ?? */
+	is->unit = -1;          /* set, when we have our interface */
+	is->mru = 1524;         /* MRU, default 1524 */
+	is->maxcid = 16;        /* VJ: maxcid */
+	is->tk = current;
+	init_waitqueue_head(&is->wq);
+	is->first = is->rq + NUM_RCV_BUFFS - 1;	/* receive queue */
+	is->last = is->rq;
+	is->minor = minor;
+#ifdef CONFIG_ISDN_PPP_VJ
+	/*
+	 * VJ header compression init
+	 */
+	is->slcomp = slhc_init(16, 16);	/* not necessary for 2. link in bundle */
+#endif
+
+	is->state = IPPP_OPEN;
+	isdn_lock_drivers();
+
+	return 0;
+}
+
+/*
+ * release ippp device
+ */
+static int
+isdn_ppp_release(struct inode *ino, struct file *file)
+{
+	uint minor = minor(ino->i_rdev) - ISDN_MINOR_PPP;
+	int i;
+	struct ippp_struct *is;
+
+	lock_kernel();
+
+	is = file->private_data;
+
+	if (is->debug & 0x1)
+		printk(KERN_DEBUG "ippp: release, minor: %d %lx\n", minor, (long) is->lp);
+
+	if (is->lp) {           /* a lp address says: this link is still up */
+		isdn_net_dev *p = is->lp->netdev;
+
+		is->state &= ~IPPP_CONNECT;	/* -> effect: no call of wakeup */
+		/*
+		 * isdn_net_hangup() calls isdn_ppp_free()
+		 * isdn_ppp_free() sets is->lp to NULL and lp->ppp_slot to -1
+		 * removing the IPPP_CONNECT flag omits calling of isdn_ppp_wakeup_daemon()
+		 */
+		isdn_net_hangup(&p->dev);
+	}
+	for (i = 0; i < NUM_RCV_BUFFS; i++) {
+		if (is->rq[i].buf) {
+			kfree(is->rq[i].buf);
+			is->rq[i].buf = NULL;
+		}
+	}
+	is->first = is->rq + NUM_RCV_BUFFS - 1;	/* receive queue */
+	is->last = is->rq;
+
+#ifdef CONFIG_ISDN_PPP_VJ
+/* TODO: if this was the previous master: link the slcomp to the new master */
+	slhc_free(is->slcomp);
+	is->slcomp = NULL;
+#endif
+
+/* TODO: if this was the previous master: link the the stuff to the new master */
+	if(is->comp_stat)
+		is->compressor->free(is->comp_stat);
+	if(is->link_comp_stat)
+		is->link_compressor->free(is->link_comp_stat);
+	if(is->link_decomp_stat)
+		is->link_decompressor->free(is->link_decomp_stat);
+	if(is->decomp_stat)
+		is->decompressor->free(is->decomp_stat);
+        is->compressor   = is->link_compressor   = NULL;
+        is->decompressor = is->link_decompressor = NULL;
+	is->comp_stat    = is->link_comp_stat    = NULL;
+        is->decomp_stat  = is->link_decomp_stat  = NULL;
+
+	/* Clean up if necessary */
+	if(is->reset)
+		isdn_ppp_ccp_reset_free(is);
+
+	/* this slot is ready for new connections */
+	is->state = 0;
+
+	isdn_unlock_drivers();
+	
+	unlock_kernel();
+	return 0;
+}
+
+/*
+ * get_arg .. ioctl helper
+ */
+static int
+get_arg(void *b, void *val, int len)
+{
+	if (len <= 0)
+		len = sizeof(void *);
+	if (copy_from_user((void *) val, b, len))
+		return -EFAULT;
+	return 0;
+}
+
+/*
+ * set arg .. ioctl helper
+ */
+static int
+set_arg(void *b, void *val,int len)
+{
+	if(len <= 0)
+		len = sizeof(void *);
+	if (copy_to_user(b, (void *) val, len))
+		return -EFAULT;
+	return 0;
+}
+
+/*
+ * ippp device ioctl
+ */
+static int
+isdn_ppp_ioctl(struct inode *ino, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	unsigned long val;
+	int r,i,j;
+	struct ippp_struct *is;
+	isdn_net_local *lp;
+	struct isdn_ppp_comp_data data;
+
+	is = (struct ippp_struct *) file->private_data;
+	lp = is->lp;
+
+	if (is->debug & 0x1)
+		printk(KERN_DEBUG "isdn_ppp_ioctl: minor: %d cmd: %x state: %x\n", is->minor, cmd, is->state);
+
+	if (!(is->state & IPPP_OPEN))
+		return -EINVAL;
+
+	switch (cmd) {
+		case PPPIOCBUNDLE:
+#ifdef CONFIG_ISDN_MPP
+			if (!(is->state & IPPP_CONNECT))
+				return -EINVAL;
+			if ((r = get_arg((void *) arg, &val, sizeof(val) )))
+				return r;
+			printk(KERN_DEBUG "iPPP-bundle: minor: %d, slave unit: %d, master unit: %d\n",
+			       (int) is->minor, (int) is->unit, (int) val);
+			return isdn_ppp_bundle(is, val);
+#else
+			return -1;
+#endif
+			break;
+		case PPPIOCGUNIT:	/* get ppp/isdn unit number */
+			if ((r = set_arg((void *) arg, &is->unit, sizeof(is->unit) )))
+				return r;
+			break;
+		case PPPIOCGIFNAME:
+			if(!lp)
+				return -EINVAL;
+			if ((r = set_arg((void *) arg, lp->name, strlen(lp->name))))
+				return r;
+			break;
+		case PPPIOCGMPFLAGS:	/* get configuration flags */
+			if ((r = set_arg((void *) arg, &is->mpppcfg, sizeof(is->mpppcfg) )))
+				return r;
+			break;
+		case PPPIOCSMPFLAGS:	/* set configuration flags */
+			if ((r = get_arg((void *) arg, &val, sizeof(val) )))
+				return r;
+			is->mpppcfg = val;
+			break;
+		case PPPIOCGFLAGS:	/* get configuration flags */
+			if ((r = set_arg((void *) arg, &is->pppcfg,sizeof(is->pppcfg) )))
+				return r;
+			break;
+		case PPPIOCSFLAGS:	/* set configuration flags */
+			if ((r = get_arg((void *) arg, &val, sizeof(val) ))) {
+				return r;
+			}
+			if (val & SC_ENABLE_IP && !(is->pppcfg & SC_ENABLE_IP) && (is->state & IPPP_CONNECT)) {
+				if (lp) {
+					/* OK .. we are ready to send buffers */
+					netif_wake_queue(&lp->netdev->dev);
+				}
+			}
+			is->pppcfg = val;
+			break;
+		case PPPIOCGIDLE:	/* get idle time information */
+			if (lp) {
+				struct ppp_idle pidle;
+				pidle.xmit_idle = pidle.recv_idle = lp->huptimer;
+				if ((r = set_arg((void *) arg, &pidle,sizeof(struct ppp_idle))))
+					 return r;
+			}
+			break;
+		case PPPIOCSMRU:	/* set receive unit size for PPP */
+			if ((r = get_arg((void *) arg, &val, sizeof(val) )))
+				return r;
+			is->mru = val;
+			break;
+		case PPPIOCSMPMRU:
+			break;
+		case PPPIOCSMPMTU:
+			break;
+		case PPPIOCSMAXCID:	/* set the maximum compression slot id */
+			if ((r = get_arg((void *) arg, &val, sizeof(val) )))
+				return r;
+			val++;
+			if (is->maxcid != val) {
+#ifdef CONFIG_ISDN_PPP_VJ
+				struct slcompress *sltmp;
+#endif
+				if (is->debug & 0x1)
+					printk(KERN_DEBUG "ippp, ioctl: changed MAXCID to %ld\n", val);
+				is->maxcid = val;
+#ifdef CONFIG_ISDN_PPP_VJ
+				sltmp = slhc_init(16, val);
+				if (!sltmp) {
+					printk(KERN_ERR "ippp, can't realloc slhc struct\n");
+					return -ENOMEM;
+				}
+				if (is->slcomp)
+					slhc_free(is->slcomp);
+				is->slcomp = sltmp;
+#endif
+			}
+			break;
+		case PPPIOCGDEBUG:
+			if ((r = set_arg((void *) arg, &is->debug, sizeof(is->debug) )))
+				return r;
+			break;
+		case PPPIOCSDEBUG:
+			if ((r = get_arg((void *) arg, &val, sizeof(val) )))
+				return r;
+			is->debug = val;
+			break;
+		case PPPIOCGCOMPRESSORS:
+			{
+				unsigned long protos[8] = {0,};
+				struct isdn_ppp_compressor *ipc = ipc_head;
+				while(ipc) {
+					j = ipc->num / (sizeof(long)*8);
+					i = ipc->num % (sizeof(long)*8);
+					if(j < 8)
+						protos[j] |= (0x1<<i);
+					ipc = ipc->next;
+				}
+				if ((r = set_arg((void *) arg,protos,8*sizeof(long) )))
+					return r;
+			}
+			break;
+		case PPPIOCSCOMPRESSOR:
+			if ((r = get_arg((void *) arg, &data, sizeof(struct isdn_ppp_comp_data))))
+				return r;
+			return isdn_ppp_set_compressor(is, &data);
+		case PPPIOCGCALLINFO:
+			{
+				struct pppcallinfo pci;
+				memset((char *) &pci,0,sizeof(struct pppcallinfo));
+				if(lp)
+				{
+					strncpy(pci.local_num,lp->msn,63);
+					if(lp->dial) {
+						strncpy(pci.remote_num,lp->dial->num,63);
+					}
+					pci.charge_units = lp->charge;
+					if(lp->outgoing)
+						pci.calltype = CALLTYPE_OUTGOING;
+					else
+						pci.calltype = CALLTYPE_INCOMING;
+					if(lp->flags & ISDN_NET_CALLBACK)
+						pci.calltype |= CALLTYPE_CALLBACK;
+				}
+				return set_arg((void *)arg,&pci,sizeof(struct pppcallinfo));
+			}
+		default:
+			break;
+	}
+	return 0;
+}
+
+static unsigned int
+isdn_ppp_poll(struct file *file, poll_table * wait)
+{
+	unsigned int mask;
+	struct ippp_buf_queue *bf;
+	struct ippp_buf_queue *bl;
+	unsigned long flags;
+	struct ippp_struct *is;
+
+	lock_kernel();
+	is = file->private_data;
+
+	if (is->debug & 0x2)
+		printk(KERN_DEBUG "isdn_ppp_poll: minor: %d\n",
+				minor(file->f_dentry->d_inode->i_rdev));
+
+	/* just registers wait_queue hook. This doesn't really wait. */
+	poll_wait(file, &is->wq, wait);
+
+	if (!(is->state & IPPP_OPEN)) {
+		if(is->state == IPPP_CLOSEWAIT) {
+			mask = POLLHUP;
+			goto out;
+		}
+		printk(KERN_DEBUG "isdn_ppp: device not open\n");
+		mask = POLLERR;
+		goto out;
+	}
+	/* we're always ready to send .. */
+	mask = POLLOUT | POLLWRNORM;
+
+	save_flags(flags);
+	cli();
+	bl = is->last;
+	bf = is->first;
+	/*
+	 * if IPPP_NOBLOCK is set we return even if we have nothing to read
+	 */
+	if (bf->next != bl || (is->state & IPPP_NOBLOCK)) {
+		is->state &= ~IPPP_NOBLOCK;
+		mask |= POLLIN | POLLRDNORM;
+	}
+	restore_flags(flags);
+
+ out:
+	unlock_kernel();
+	return mask;
+}
+
+/*
+ *  fill up isdn_ppp_read() queue ..
+ */
+
+static int
+isdn_ppp_fill_rq(unsigned char *buf, int len, int proto, int slot)
+{
+	struct ippp_buf_queue *bf,
+	*bl;
+	unsigned long flags;
+	unsigned char *nbuf;
+	struct ippp_struct *is;
+
+	if (slot < 0 || slot >= ISDN_MAX_CHANNELS) {
+		printk(KERN_WARNING "ippp: illegal slot.\n");
+		return 0;
+	}
+	is = ippp_table[slot];
+
+	if (!(is->state & IPPP_CONNECT)) {
+		printk(KERN_DEBUG "ippp: device not activated.\n");
+		return 0;
+	}
+	nbuf = (unsigned char *) kmalloc(len + 4, GFP_ATOMIC);
+	if (!nbuf) {
+		printk(KERN_WARNING "ippp: Can't alloc buf\n");
+		return 0;
+	}
+	nbuf[0] = PPP_ALLSTATIONS;
+	nbuf[1] = PPP_UI;
+	nbuf[2] = proto >> 8;
+	nbuf[3] = proto & 0xff;
+	memcpy(nbuf + 4, buf, len);
+
+	save_flags(flags);
+	cli();
+
+	bf = is->first;
+	bl = is->last;
+
+	if (bf == bl) {
+		printk(KERN_WARNING "ippp: Queue is full; discarding first buffer\n");
+		bf = bf->next;
+		kfree(bf->buf);
+		is->first = bf;
+	}
+	bl->buf = (char *) nbuf;
+	bl->len = len + 4;
+
+	is->last = bl->next;
+	restore_flags(flags);
+
+		wake_up_interruptible(&is->wq);
+
+	return len;
+}
+
+/*
+ * read() .. non-blocking: ipppd calls it only after select()
+ *           reports, that there is data
+ */
+
+static int
+isdn_ppp_read(struct file *file, char *buf, size_t count, loff_t *off)
+{
+	struct ippp_struct *is;
+	struct ippp_buf_queue *b;
+	unsigned long flags;
+	unsigned char *save_buf;
+	int retval;
+
+	if (off != &file->f_pos)
+		return -ESPIPE;
+	
+	lock_kernel();
+
+	is = file->private_data;
+
+	if (!(is->state & IPPP_OPEN)) {
+		retval = 0;
+		goto out;
+	}
+	retval = verify_area(VERIFY_WRITE, (void *) buf, count);
+	if (retval)
+		goto out;
+
+	save_flags(flags);
+	cli();
+
+	b = is->first->next;
+	save_buf = b->buf;
+	if (!save_buf) {
+		restore_flags(flags);
+		retval = -EAGAIN;
+		goto out;
+	}
+	if (b->len < count)
+		count = b->len;
+	b->buf = NULL;
+	is->first = b;
+
+	restore_flags(flags);
+
+	copy_to_user(buf, save_buf, count);
+	kfree(save_buf);
+
+	retval = count;
+
+ out:
+	unlock_kernel();
+	return retval;
+}
+
+/*
+ * ipppd wanna write a packet to the card .. non-blocking
+ */
+
+static int
+isdn_ppp_write(struct file *file, const char *buf, size_t count, loff_t *off)
+{
+	isdn_net_local *lp;
+	struct ippp_struct *is;
+	int proto;
+	unsigned char protobuf[4];
+	int retval;
+
+	if (off != &file->f_pos)
+		return -ESPIPE;
+
+	lock_kernel();
+
+	is = file->private_data;
+
+	if (!(is->state & IPPP_CONNECT)) {
+		retval = 0;
+		goto out;
+	}
+
+	lp = is->lp;
+
+	/* -> push it directly to the lowlevel interface */
+
+	if (!lp)
+		printk(KERN_DEBUG "isdn_ppp_write: lp == NULL\n");
+	else {
+		/*
+		 * Don't reset huptimer for
+		 * LCP packets. (Echo requests).
+		 */
+		if (copy_from_user(protobuf, buf, 4)) {
+			retval = -EFAULT;
+			goto out;
+		}
+		proto = PPP_PROTOCOL(protobuf);
+		if (proto != PPP_LCP)
+			lp->huptimer = 0;
+
+		if (lp->isdn_device < 0 || lp->isdn_channel < 0) {
+			retval = 0;
+			goto out;
+		}
+		if ((dev->drv[lp->isdn_device]->flags & DRV_FLAG_RUNNING) &&
+			lp->dialstate == 0 &&
+		    (lp->flags & ISDN_NET_CONNECTED)) {
+			unsigned short hl;
+			struct sk_buff *skb;
+			/*
+			 * we need to reserve enought space in front of
+			 * sk_buff. old call to dev_alloc_skb only reserved
+			 * 16 bytes, now we are looking what the driver want
+			 */
+			hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen;
+			skb = alloc_skb(hl+count, GFP_ATOMIC);
+			if (!skb) {
+				printk(KERN_WARNING "isdn_ppp_write: out of memory!\n");
+				retval = count;
+				goto out;
+			}
+			skb_reserve(skb, hl);
+			if (copy_from_user(skb_put(skb, count), buf, count))
+			{
+				kfree_skb(skb);
+				retval = -EFAULT;
+				goto out;
+			}
+			if (is->debug & 0x40) {
+				printk(KERN_DEBUG "ppp xmit: len %d\n", (int) skb->len);
+				isdn_ppp_frame_log("xmit", skb->data, skb->len, 32,is->unit,lp->ppp_slot);
+			}
+
+			isdn_ppp_send_ccp(lp->netdev,lp,skb); /* keeps CCP/compression states in sync */
+
+			isdn_net_write_super(lp, skb);
+		}
+	}
+	retval = count;
+	
+ out:
+	unlock_kernel();
+	return retval;
+}
+
+struct file_operations isdn_ppp_fops =
+{
+	owner:		THIS_MODULE,
+	llseek:		no_llseek,
+	read:		isdn_ppp_read,
+	write:		isdn_ppp_write,
+	poll:		isdn_ppp_poll,
+	ioctl:		isdn_ppp_ioctl,
+	open:		isdn_ppp_open,
+	release:	isdn_ppp_release,
+};
+
+/*
+ * init memory, structures etc.
+ */
+
+int
+isdn_ppp_init(void)
+{
+	int i,
+	 j;
+	 
+#ifdef CONFIG_ISDN_MPP
+	if( isdn_ppp_mp_bundle_array_init() < 0 )
+		return -ENOMEM;
+#endif /* CONFIG_ISDN_MPP */
+
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		if (!(ippp_table[i] = (struct ippp_struct *)
+		      kmalloc(sizeof(struct ippp_struct), GFP_KERNEL))) {
+			printk(KERN_WARNING "isdn_ppp_init: Could not alloc ippp_table\n");
+			for (j = 0; j < i; j++)
+				kfree(ippp_table[j]);
+			return -1;
+		}
+		memset((char *) ippp_table[i], 0, sizeof(struct ippp_struct));
+		ippp_table[i]->state = 0;
+		ippp_table[i]->first = ippp_table[i]->rq + NUM_RCV_BUFFS - 1;
+		ippp_table[i]->last = ippp_table[i]->rq;
+
+		for (j = 0; j < NUM_RCV_BUFFS; j++) {
+			ippp_table[i]->rq[j].buf = NULL;
+			ippp_table[i]->rq[j].last = ippp_table[i]->rq +
+			    (NUM_RCV_BUFFS + j - 1) % NUM_RCV_BUFFS;
+			ippp_table[i]->rq[j].next = ippp_table[i]->rq + (j + 1) % NUM_RCV_BUFFS;
+		}
+	}
+	return 0;
+}
+
+void
+isdn_ppp_cleanup(void)
+{
+	int i;
+
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+		kfree(ippp_table[i]);
+
+#ifdef CONFIG_ISDN_MPP
+	if (isdn_ppp_bundle_arr)
+		kfree(isdn_ppp_bundle_arr);
+#endif /* CONFIG_ISDN_MPP */
+
+}
+
+/*
+ * check for address/control field and skip if allowed
+ * retval != 0 -> discard packet silently
+ */
+static int isdn_ppp_skip_ac(struct ippp_struct *is, struct sk_buff *skb) 
+{
+	if (skb->len < 1)
+		return -1;
+
+	if (skb->data[0] == 0xff) {
+		if (skb->len < 2)
+			return -1;
+
+		if (skb->data[1] != 0x03)
+			return -1;
+
+		// skip address/control (AC) field
+		skb_pull(skb, 2);
+	} else { 
+		if (is->pppcfg & SC_REJ_COMP_AC)
+			// if AC compression was not negotiated, but used, discard packet
+			return -1;
+	}
+	return 0;
+}
+
+/*
+ * get the PPP protocol header and pull skb
+ * retval < 0 -> discard packet silently
+ */
+static int isdn_ppp_strip_proto(struct sk_buff *skb) 
+{
+	int proto;
+	
+	if (skb->len < 1)
+		return -1;
+
+	if (skb->data[0] & 0x1) {
+		// protocol field is compressed
+		proto = skb->data[0];
+		skb_pull(skb, 1);
+	} else {
+		if (skb->len < 2)
+			return -1;
+		proto = ((int) skb->data[0] << 8) + skb->data[1];
+		skb_pull(skb, 2);
+	}
+	return proto;
+}
+
+
+/*
+ * handler for incoming packets on a syncPPP interface
+ */
+void isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb)
+{
+	struct ippp_struct *is;
+	int slot;
+	int proto;
+
+	if (net_dev->local->master)
+		BUG(); // we're called with the master device always
+
+	slot = lp->ppp_slot;
+	if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
+		printk(KERN_ERR "isdn_ppp_receive: lp->ppp_slot %d\n", lp->ppp_slot);
+		kfree_skb(skb);
+		return;
+	}
+	is = ippp_table[slot];
+
+	if (is->debug & 0x4) {
+		printk(KERN_DEBUG "ippp_receive: is:%08lx lp:%08lx slot:%d unit:%d len:%d\n",
+		       (long)is,(long)lp,lp->ppp_slot,is->unit,(int) skb->len);
+		isdn_ppp_frame_log("receive", skb->data, skb->len, 32,is->unit,lp->ppp_slot);
+	}
+
+ 	if (isdn_ppp_skip_ac(is, skb) < 0) {
+ 		kfree_skb(skb);
+ 		return;
+ 	}
+  	proto = isdn_ppp_strip_proto(skb);
+ 	if (proto < 0) {
+ 		kfree_skb(skb);
+ 		return;
+ 	}
+  
+#ifdef CONFIG_ISDN_MPP
+ 	if (is->compflags & SC_LINK_DECOMP_ON) {
+ 		skb = isdn_ppp_decompress(skb, is, NULL, &proto);
+ 		if (!skb) // decompression error
+ 			return;
+ 	}
+	
+ 	if (!(is->mpppcfg & SC_REJ_MP_PROT)) { // we agreed to receive MPPP
+  		if (proto == PPP_MP) {
+  			isdn_ppp_mp_receive(net_dev, lp, skb);
+ 			return;
+ 		}
+ 	} 
+#endif
+ 	isdn_ppp_push_higher(net_dev, lp, skb, proto);
+}
+
+/*
+ * we receive a reassembled frame, MPPP has been taken care of before.
+ * address/control and protocol have been stripped from the skb
+ * note: net_dev has to be master net_dev
+ */
+static void
+isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb, int proto)
+{
+	struct net_device *dev = &net_dev->dev;
+ 	struct ippp_struct *is, *mis;
+	int slot;
+
+	slot = lp->ppp_slot;
+	if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
+		printk(KERN_ERR "isdn_ppp_push_higher: lp->ppp_slot %d\n", lp->ppp_slot);
+		goto drop_packet;
+	}
+	is = ippp_table[slot];
+ 	
+ 	if (lp->master) { // FIXME?
+ 		slot = ((isdn_net_local *) (lp->master->priv))->ppp_slot;
+ 		if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
+ 			printk(KERN_ERR "isdn_ppp_push_higher: master->ppp_slot %d\n", lp->ppp_slot);
+			goto drop_packet;
+ 		}
+ 	}
+ 	mis = ippp_table[slot];
+
+	if (is->debug & 0x10) {
+		printk(KERN_DEBUG "push, skb %d %04x\n", (int) skb->len, proto);
+		isdn_ppp_frame_log("rpush", skb->data, skb->len, 32,is->unit,lp->ppp_slot);
+	}
+	if (mis->compflags & SC_DECOMP_ON) {
+		skb = isdn_ppp_decompress(skb, is, mis, &proto);
+		if (!skb) // decompression error
+  			return;
+  	}
+	switch (proto) {
+		case PPP_IPX:  /* untested */
+			if (is->debug & 0x20)
+				printk(KERN_DEBUG "isdn_ppp: IPX\n");
+			skb->protocol = htons(ETH_P_IPX);
+			break;
+		case PPP_IP:
+			if (is->debug & 0x20)
+				printk(KERN_DEBUG "isdn_ppp: IP\n");
+			skb->protocol = htons(ETH_P_IP);
+			break;
+		case PPP_COMP:
+		case PPP_COMPFRAG:
+			printk(KERN_INFO "isdn_ppp: unexpected compressed frame dropped\n");
+			goto drop_packet;
+#ifdef CONFIG_ISDN_PPP_VJ
+		case PPP_VJC_UNCOMP:
+			if (is->debug & 0x20)
+				printk(KERN_DEBUG "isdn_ppp: VJC_UNCOMP\n");
+			if (slhc_remember(ippp_table[net_dev->local->ppp_slot]->slcomp, skb->data, skb->len) <= 0) {
+				printk(KERN_WARNING "isdn_ppp: received illegal VJC_UNCOMP frame!\n");
+				goto drop_packet;
+			}
+			skb->protocol = htons(ETH_P_IP);
+			break;
+		case PPP_VJC_COMP:
+			if (is->debug & 0x20)
+				printk(KERN_DEBUG "isdn_ppp: VJC_COMP\n");
+			{
+				struct sk_buff *skb_old = skb;
+				int pkt_len;
+				skb = dev_alloc_skb(skb_old->len + 128);
+
+				if (!skb) {
+					printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name);
+					skb = skb_old;
+					goto drop_packet;
+				}
+				skb_put(skb, skb_old->len + 128);
+				memcpy(skb->data, skb_old->data, skb_old->len);
+				pkt_len = slhc_uncompress(ippp_table[net_dev->local->ppp_slot]->slcomp,
+						skb->data, skb_old->len);
+				kfree_skb(skb_old);
+				if (pkt_len < 0)
+					goto drop_packet;
+
+				skb_trim(skb, pkt_len);
+				skb->protocol = htons(ETH_P_IP);
+			}
+			break;
+#endif
+		case PPP_CCP:
+		case PPP_CCPFRAG:
+			isdn_ppp_receive_ccp(net_dev,lp,skb,proto);
+			/* Dont pop up ResetReq/Ack stuff to the daemon any
+			   longer - the job is done already */
+			if(skb->data[0] == CCP_RESETREQ ||
+			   skb->data[0] == CCP_RESETACK)
+				break;
+			/* fall through */
+		default:
+			isdn_ppp_fill_rq(skb->data, skb->len, proto, lp->ppp_slot);	/* push data to pppd device */
+			kfree_skb(skb);
+			return;
+	}
+
+ 	/* Reset hangup-timer */
+ 	lp->huptimer = 0;
+
+	skb->dev = dev;
+	skb->mac.raw = skb->data;
+	netif_rx(skb);
+	/* net_dev->local->stats.rx_packets++; done in isdn_net.c */
+	return;
+
+ drop_packet:
+	net_dev->local->stats.rx_dropped++;
+	kfree_skb(skb);
+}
+
+/*
+ * isdn_ppp_skb_push ..
+ * checks whether we have enough space at the beginning of the skb
+ * and allocs a new SKB if necessary
+ */
+static unsigned char *isdn_ppp_skb_push(struct sk_buff **skb_p,int len)
+{
+	struct sk_buff *skb = *skb_p;
+
+	if(skb_headroom(skb) < len) {
+		struct sk_buff *nskb = skb_realloc_headroom(skb, len);
+
+		if (!nskb) {
+			printk(KERN_ERR "isdn_ppp_skb_push: can't realloc headroom!\n");
+			dev_kfree_skb(skb);
+			return NULL;
+		}
+		printk(KERN_DEBUG "isdn_ppp_skb_push:under %d %d\n",skb_headroom(skb),len);
+		dev_kfree_skb(skb);
+		*skb_p = nskb;
+		return skb_push(nskb, len);
+	}
+	return skb_push(skb,len);
+}
+
+
+/*
+ * send ppp frame .. we expect a PIDCOMPressable proto --
+ *  (here: currently always PPP_IP,PPP_VJC_COMP,PPP_VJC_UNCOMP)
+ *
+ * VJ compression may change skb pointer!!! .. requeue with old
+ * skb isn't allowed!!
+ */
+
+int
+isdn_ppp_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+	isdn_net_local *lp,*mlp;
+	isdn_net_dev *nd;
+	unsigned int proto = PPP_IP;     /* 0x21 */
+	struct ippp_struct *ipt,*ipts;
+	int slot;
+
+	mlp = (isdn_net_local *) (netdev->priv);
+	nd = mlp->netdev;       /* get master lp */
+
+	slot = mlp->ppp_slot;
+	if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
+		printk(KERN_ERR "isdn_ppp_xmit: lp->ppp_slot %d\n", mlp->ppp_slot);
+		kfree_skb(skb);
+		return 0;
+	}
+	ipts = ippp_table[slot];
+
+	if (!(ipts->pppcfg & SC_ENABLE_IP)) {	/* PPP connected ? */
+		if (ipts->debug & 0x1)
+			printk(KERN_INFO "%s: IP frame delayed.\n", netdev->name);
+		return 1;
+	}
+
+	switch (ntohs(skb->protocol)) {
+		case ETH_P_IP:
+			proto = PPP_IP;
+			break;
+		case ETH_P_IPX:
+			proto = PPP_IPX;	/* untested */
+			break;
+		default:
+			printk(KERN_ERR "isdn_ppp: skipped unsupported protocol: %#x.\n", 
+			       skb->protocol);
+			dev_kfree_skb(skb);
+			return 0;
+	}
+
+	lp = isdn_net_get_locked_lp(nd);
+	if (!lp) {
+		printk(KERN_WARNING "%s: all channels busy - requeuing!\n", netdev->name);
+		return 1;
+	}
+	/* we have our lp locked from now on */
+
+	slot = lp->ppp_slot;
+	if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
+		printk(KERN_ERR "isdn_ppp_xmit: lp->ppp_slot %d\n", lp->ppp_slot);
+		kfree_skb(skb);
+		return 0;
+	}
+	ipt = ippp_table[slot];
+	lp->huptimer = 0;
+
+	/*
+	 * after this line .. requeueing in the device queue is no longer allowed!!!
+	 */
+
+	/* Pull off the fake header we stuck on earlier to keep
+	 * the fragmentation code happy.
+	 */
+	skb_pull(skb,IPPP_MAX_HEADER);
+
+	if (ipt->debug & 0x4)
+		printk(KERN_DEBUG "xmit skb, len %d\n", (int) skb->len);
+        if (ipts->debug & 0x40)
+                isdn_ppp_frame_log("xmit0", skb->data, skb->len, 32,ipts->unit,lp->ppp_slot);
+
+#ifdef CONFIG_ISDN_PPP_VJ
+	if (proto == PPP_IP && ipts->pppcfg & SC_COMP_TCP) {	/* ipts here? probably yes, but check this again */
+		struct sk_buff *new_skb;
+	        unsigned short hl;
+		/*
+		 * we need to reserve enought space in front of
+		 * sk_buff. old call to dev_alloc_skb only reserved
+		 * 16 bytes, now we are looking what the driver want.
+		 */
+		hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen + IPPP_MAX_HEADER;
+		/* 
+		 * Note: hl might still be insufficient because the method
+		 * above does not account for a possibible MPPP slave channel
+		 * which had larger HL header space requirements than the
+		 * master.
+		 */
+		new_skb = alloc_skb(hl+skb->len, GFP_ATOMIC);
+		if (new_skb) {
+			u_char *buf;
+			int pktlen;
+
+			skb_reserve(new_skb, hl);
+			new_skb->dev = skb->dev;
+			skb_put(new_skb, skb->len);
+			buf = skb->data;
+
+			pktlen = slhc_compress(ipts->slcomp, skb->data, skb->len, new_skb->data,
+				 &buf, !(ipts->pppcfg & SC_NO_TCP_CCID));
+
+			if (buf != skb->data) {	
+				if (new_skb->data != buf)
+					printk(KERN_ERR "isdn_ppp: FATAL error after slhc_compress!!\n");
+				dev_kfree_skb(skb);
+				skb = new_skb;
+			} else {
+				dev_kfree_skb(new_skb);
+			}
+
+			skb_trim(skb, pktlen);
+			if (skb->data[0] & SL_TYPE_COMPRESSED_TCP) {	/* cslip? style -> PPP */
+				proto = PPP_VJC_COMP;
+				skb->data[0] ^= SL_TYPE_COMPRESSED_TCP;
+			} else {
+				if (skb->data[0] >= SL_TYPE_UNCOMPRESSED_TCP)
+					proto = PPP_VJC_UNCOMP;
+				skb->data[0] = (skb->data[0] & 0x0f) | 0x40;
+			}
+		}
+	}
+#endif
+
+	/*
+	 * normal (single link) or bundle compression
+	 */
+	if(ipts->compflags & SC_COMP_ON) {
+		/* We send compressed only if both down- und upstream
+		   compression is negotiated, that means, CCP is up */
+		if(ipts->compflags & SC_DECOMP_ON) {
+			skb = isdn_ppp_compress(skb,&proto,ipt,ipts,0);
+		} else {
+			printk(KERN_DEBUG "isdn_ppp: CCP not yet up - sending as-is\n");
+		}
+	}
+
+	if (ipt->debug & 0x24)
+		printk(KERN_DEBUG "xmit2 skb, len %d, proto %04x\n", (int) skb->len, proto);
+
+#ifdef CONFIG_ISDN_MPP
+	if (ipt->mpppcfg & SC_MP_PROT) {
+		/* we get mp_seqno from static isdn_net_local */
+		long mp_seqno = ipts->mp_seqno;
+		ipts->mp_seqno++;
+		if (ipt->mpppcfg & SC_OUT_SHORT_SEQ) {
+			unsigned char *data = isdn_ppp_skb_push(&skb, 3);
+			if(!data)
+				goto unlock;
+			mp_seqno &= 0xfff;
+			data[0] = MP_BEGIN_FRAG | MP_END_FRAG | ((mp_seqno >> 8) & 0xf);	/* (B)egin & (E)ndbit .. */
+			data[1] = mp_seqno & 0xff;
+			data[2] = proto;	/* PID compression */
+		} else {
+			unsigned char *data = isdn_ppp_skb_push(&skb, 5);
+			if(!data)
+				goto unlock;
+			data[0] = MP_BEGIN_FRAG | MP_END_FRAG;	/* (B)egin & (E)ndbit .. */
+			data[1] = (mp_seqno >> 16) & 0xff;	/* sequence number: 24bit */
+			data[2] = (mp_seqno >> 8) & 0xff;
+			data[3] = (mp_seqno >> 0) & 0xff;
+			data[4] = proto;	/* PID compression */
+		}
+		proto = PPP_MP; /* MP Protocol, 0x003d */
+	}
+#endif
+
+	/*
+	 * 'link in bundle' compression  ...
+	 */
+	if(ipt->compflags & SC_LINK_COMP_ON)
+		skb = isdn_ppp_compress(skb,&proto,ipt,ipts,1);
+
+	if( (ipt->pppcfg & SC_COMP_PROT) && (proto <= 0xff) ) {
+		unsigned char *data = isdn_ppp_skb_push(&skb,1);
+		if(!data)
+			goto unlock;
+		data[0] = proto & 0xff;
+	}
+	else {
+		unsigned char *data = isdn_ppp_skb_push(&skb,2);
+		if(!data)
+			goto unlock;
+		data[0] = (proto >> 8) & 0xff;
+		data[1] = proto & 0xff;
+	}
+	if(!(ipt->pppcfg & SC_COMP_AC)) {
+		unsigned char *data = isdn_ppp_skb_push(&skb,2);
+		if(!data)
+			goto unlock;
+		data[0] = 0xff;    /* All Stations */
+		data[1] = 0x03;    /* Unnumbered information */
+	}
+
+	/* tx-stats are now updated via BSENT-callback */
+
+	if (ipts->debug & 0x40) {
+		printk(KERN_DEBUG "skb xmit: len: %d\n", (int) skb->len);
+		isdn_ppp_frame_log("xmit", skb->data, skb->len, 32,ipt->unit,lp->ppp_slot);
+	}
+	
+	isdn_net_writebuf_skb(lp, skb);
+
+ unlock:
+	spin_unlock_bh(&lp->xmit_lock);
+	return 0;
+}
+
+#ifdef CONFIG_ISDN_MPP
+
+/* this is _not_ rfc1990 header, but something we convert both short and long
+ * headers to for convinience's sake:
+ * 	byte 0 is flags as in rfc1990
+ *	bytes 1...4 is 24-bit seqence number converted to host byte order 
+ */
+#define MP_HEADER_LEN	5
+
+#define MP_LONGSEQ_MASK		0x00ffffff
+#define MP_SHORTSEQ_MASK	0x00000fff
+#define MP_LONGSEQ_MAX		MP_LONGSEQ_MASK
+#define MP_SHORTSEQ_MAX		MP_SHORTSEQ_MASK
+#define MP_LONGSEQ_MAXBIT	((MP_LONGSEQ_MASK+1)>>1)
+#define MP_SHORTSEQ_MAXBIT	((MP_SHORTSEQ_MASK+1)>>1)
+
+/* sequence-wrap safe comparisions (for long sequence)*/ 
+#define MP_LT(a,b)	((a-b)&MP_LONGSEQ_MAXBIT)
+#define MP_LE(a,b) 	!((b-a)&MP_LONGSEQ_MAXBIT)
+#define MP_GT(a,b) 	((b-a)&MP_LONGSEQ_MAXBIT)
+#define MP_GE(a,b)	!((a-b)&MP_LONGSEQ_MAXBIT)
+
+#define MP_SEQ(f)	((*(u32*)(f->data+1)))
+#define MP_FLAGS(f)	(f->data[0])
+
+static int isdn_ppp_mp_bundle_array_init(void)
+{
+	int i;
+	int sz = ISDN_MAX_CHANNELS*sizeof(ippp_bundle);
+	if( (isdn_ppp_bundle_arr = (ippp_bundle*)kmalloc(sz, 
+							GFP_KERNEL)) == NULL )
+		return -ENOMEM;
+	memset(isdn_ppp_bundle_arr, 0, sz);
+	for( i = 0; i < ISDN_MAX_CHANNELS; i++ )
+		spin_lock_init(&isdn_ppp_bundle_arr[i].lock);
+	return 0;
+}
+
+static ippp_bundle * isdn_ppp_mp_bundle_alloc(void)
+{
+	int i;
+	for( i = 0; i < ISDN_MAX_CHANNELS; i++ )
+		if (isdn_ppp_bundle_arr[i].ref_ct <= 0)
+			return (isdn_ppp_bundle_arr + i);
+	return NULL;
+}
+
+static int isdn_ppp_mp_init( isdn_net_local * lp, ippp_bundle * add_to )
+{
+	struct ippp_struct * is = ippp_table[lp->ppp_slot];
+   
+	if (add_to) {
+		if( lp->netdev->pb )
+			lp->netdev->pb->ref_ct--;
+		lp->netdev->pb = add_to;
+	} else {		/* first link in a bundle */
+		is->mp_seqno = 0;
+		if ((lp->netdev->pb = isdn_ppp_mp_bundle_alloc()) == NULL)
+			return -ENOMEM;
+		lp->next = lp->last = lp;	/* nobody else in a queue */
+		lp->netdev->pb->frags = NULL;
+		lp->netdev->pb->frames = 0;
+		lp->netdev->pb->seq = LONG_MAX;
+	}
+	lp->netdev->pb->ref_ct++;
+	
+	is->last_link_seqno = 0;
+	return 0;
+}
+
+static u32 isdn_ppp_mp_get_seq( int short_seq, 
+					struct sk_buff * skb, u32 last_seq );
+static struct sk_buff * isdn_ppp_mp_discard( ippp_bundle * mp,
+			struct sk_buff * from, struct sk_buff * to );
+static void isdn_ppp_mp_reassembly( isdn_net_dev * net_dev, isdn_net_local * lp,
+				struct sk_buff * from, struct sk_buff * to );
+static void isdn_ppp_mp_free_skb( ippp_bundle * mp, struct sk_buff * skb );
+static void isdn_ppp_mp_print_recv_pkt( int slot, struct sk_buff * skb );
+
+static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, 
+							struct sk_buff *skb)
+{
+	struct ippp_struct *is;
+	isdn_net_local * lpq;
+	ippp_bundle * mp;
+	isdn_mppp_stats * stats;
+	struct sk_buff * newfrag, * frag, * start, *nextf;
+	u32 newseq, minseq, thisseq;
+	unsigned long flags;
+	int slot;
+
+	spin_lock_irqsave(&net_dev->pb->lock, flags);
+    	mp = net_dev->pb;
+        stats = &mp->stats;
+	slot = lp->ppp_slot;
+	if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
+		printk(KERN_ERR "isdn_ppp_mp_receive: lp->ppp_slot %d\n", lp->ppp_slot);
+		stats->frame_drops++;
+		dev_kfree_skb(skb);
+		spin_unlock_irqrestore(&mp->lock, flags);
+		return;
+	}
+	is = ippp_table[slot];
+    	if( ++mp->frames > stats->max_queue_len )
+		stats->max_queue_len = mp->frames;
+	
+	if (is->debug & 0x8)
+		isdn_ppp_mp_print_recv_pkt(lp->ppp_slot, skb);
+
+	newseq = isdn_ppp_mp_get_seq(is->mpppcfg & SC_IN_SHORT_SEQ, 
+						skb, is->last_link_seqno);
+
+
+	/* if this packet seq # is less than last already processed one,
+	 * toss it right away, but check for sequence start case first 
+	 */
+	if( mp->seq > MP_LONGSEQ_MAX && (newseq & MP_LONGSEQ_MAXBIT) ) {
+		mp->seq = newseq;	/* the first packet: required for
+					 * rfc1990 non-compliant clients --
+					 * prevents constant packet toss */
+	} else if( MP_LT(newseq, mp->seq) ) {
+		stats->frame_drops++;
+		isdn_ppp_mp_free_skb(mp, skb);
+		spin_unlock_irqrestore(&mp->lock, flags);
+		return;
+	}
+	
+	/* find the minimum received sequence number over all links */
+	is->last_link_seqno = minseq = newseq;
+	for (lpq = net_dev->queue;;) {
+		slot = lpq->ppp_slot;
+		if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
+			printk(KERN_ERR "isdn_ppp_mp_receive: lpq->ppp_slot %d\n", lpq->ppp_slot);
+		} else {
+			u32 lls = ippp_table[slot]->last_link_seqno;
+			if (MP_LT(lls, minseq))
+				minseq = lls;
+		}
+		if ((lpq = lpq->next) == net_dev->queue)
+			break;
+	}
+	if (MP_LT(minseq, mp->seq))
+		minseq = mp->seq;	/* can't go beyond already processed
+					 * packets */
+	newfrag = skb;
+
+  	/* if this new fragment is before the first one, then enqueue it now. */
+  	if ((frag = mp->frags) == NULL || MP_LT(newseq, MP_SEQ(frag))) {
+		newfrag->next = frag;
+    		mp->frags = frag = newfrag;
+    		newfrag = NULL;
+  	}
+
+  	start = MP_FLAGS(frag) & MP_BEGIN_FRAG &&
+				MP_SEQ(frag) == mp->seq ? frag : NULL;
+
+	/* 
+	 * main fragment traversing loop
+	 *
+	 * try to accomplish several tasks:
+	 * - insert new fragment into the proper sequence slot (once that's done
+	 *   newfrag will be set to NULL)
+	 * - reassemble any complete fragment sequence (non-null 'start'
+	 *   indicates there is a continguous sequence present)
+	 * - discard any incomplete sequences that are below minseq -- due
+	 *   to the fact that sender always increment sequence number, if there
+	 *   is an incomplete sequence below minseq, no new fragments would
+	 *   come to complete such sequence and it should be discarded
+	 *
+	 * loop completes when we accomplished the following tasks:
+	 * - new fragment is inserted in the proper sequence ('newfrag' is 
+	 *   set to NULL)
+	 * - we hit a gap in the sequence, so no reassembly/processing is 
+	 *   possible ('start' would be set to NULL)
+	 *
+	 * algorightm for this code is derived from code in the book
+	 * 'PPP Design And Debugging' by James Carlson (Addison-Wesley)
+	 */
+  	while (start != NULL || newfrag != NULL) {
+
+    		thisseq = MP_SEQ(frag);
+    		nextf = frag->next;
+
+    		/* drop any duplicate fragments */
+    		if (newfrag != NULL && thisseq == newseq) {
+      			isdn_ppp_mp_free_skb(mp, newfrag);
+      			newfrag = NULL;
+    		}
+
+    		/* insert new fragment before next element if possible. */
+    		if (newfrag != NULL && (nextf == NULL || 
+						MP_LT(newseq, MP_SEQ(nextf)))) {
+      			newfrag->next = nextf;
+      			frag->next = nextf = newfrag;
+      			newfrag = NULL;
+    		}
+
+    		if (start != NULL) {
+	    		/* check for misplaced start */
+      			if (start != frag && (MP_FLAGS(frag) & MP_BEGIN_FRAG)) {
+				printk(KERN_WARNING"isdn_mppp(seq %d): new "
+				      "BEGIN flag with no prior END", thisseq);
+				stats->seqerrs++;
+				stats->frame_drops++;
+				start = isdn_ppp_mp_discard(mp, start,frag);
+				nextf = frag->next;
+      			}
+    		} else if (MP_LE(thisseq, minseq)) {		
+      			if (MP_FLAGS(frag) & MP_BEGIN_FRAG)
+				start = frag;
+      			else {
+				if (MP_FLAGS(frag) & MP_END_FRAG)
+	  				stats->frame_drops++;
+				if( mp->frags == frag )
+					mp->frags = nextf;	
+				isdn_ppp_mp_free_skb(mp, frag);
+				frag = nextf;
+				continue;
+      			}
+		}
+		
+		/* if start is non-null and we have end fragment, then
+		 * we have full reassembly sequence -- reassemble 
+		 * and process packet now
+		 */
+    		if (start != NULL && (MP_FLAGS(frag) & MP_END_FRAG)) {
+      			minseq = mp->seq = (thisseq+1) & MP_LONGSEQ_MASK;
+      			/* Reassemble the packet then dispatch it */
+			isdn_ppp_mp_reassembly(net_dev, lp, start, nextf);
+      
+      			start = NULL;
+      			frag = NULL;
+
+      			mp->frags = nextf;
+    		}
+
+		/* check if need to update start pointer: if we just
+		 * reassembled the packet and sequence is contiguous
+		 * then next fragment should be the start of new reassembly
+		 * if sequence is contiguous, but we haven't reassembled yet,
+		 * keep going.
+		 * if sequence is not contiguous, either clear everyting
+		 * below low watermark and set start to the next frag or
+		 * clear start ptr.
+		 */ 
+    		if (nextf != NULL && 
+		    ((thisseq+1) & MP_LONGSEQ_MASK) == MP_SEQ(nextf)) {
+      			/* if we just reassembled and the next one is here, 
+			 * then start another reassembly. */
+
+      			if (frag == NULL) {
+				if (MP_FLAGS(nextf) & MP_BEGIN_FRAG)
+	  				start = nextf;
+				else
+				{
+	  				printk(KERN_WARNING"isdn_mppp(seq %d):"
+						" END flag with no following "
+						"BEGIN", thisseq);
+					stats->seqerrs++;
+				}
+			}
+
+    		} else {
+			if ( nextf != NULL && frag != NULL &&
+						MP_LT(thisseq, minseq)) {
+				/* we've got a break in the sequence
+				 * and we not at the end yet
+				 * and we did not just reassembled
+				 *(if we did, there wouldn't be anything before)
+				 * and we below the low watermark 
+			 	 * discard all the frames below low watermark 
+				 * and start over */
+				stats->frame_drops++;
+				mp->frags = isdn_ppp_mp_discard(mp,start,nextf);
+			}
+			/* break in the sequence, no reassembly */
+      			start = NULL;
+    		}
+	  			
+    		frag = nextf;
+  	}	/* while -- main loop */
+	
+  	if (mp->frags == NULL)
+    		mp->frags = frag;
+		
+	/* rather straighforward way to deal with (not very) possible 
+	 * queue overflow */
+	if (mp->frames > MP_MAX_QUEUE_LEN) {
+		stats->overflows++;
+		while (mp->frames > MP_MAX_QUEUE_LEN) {
+			frag = mp->frags->next;
+			isdn_ppp_mp_free_skb(mp, mp->frags);
+			mp->frags = frag;
+		}
+	}
+	spin_unlock_irqrestore(&mp->lock, flags);
+}
+
+static void isdn_ppp_mp_cleanup( isdn_net_local * lp )
+{
+	struct sk_buff * frag = lp->netdev->pb->frags;
+	struct sk_buff * nextfrag;
+    	while( frag ) {
+		nextfrag = frag->next;
+		isdn_ppp_mp_free_skb(lp->netdev->pb, frag);
+		frag = nextfrag;
+	}
+	lp->netdev->pb->frags = NULL;
+}
+
+static u32 isdn_ppp_mp_get_seq( int short_seq, 
+					struct sk_buff * skb, u32 last_seq )
+{
+	u32 seq;
+	int flags = skb->data[0] & (MP_BEGIN_FRAG | MP_END_FRAG);
+   
+   	if( !short_seq )
+	{
+		seq = ntohl(*(u32*)skb->data) & MP_LONGSEQ_MASK;
+		skb_push(skb,1);
+	}
+	else
+	{
+		/* convert 12-bit short seq number to 24-bit long one 
+	 	*/
+		seq = ntohs(*(u16*)skb->data) & MP_SHORTSEQ_MASK;
+	
+		/* check for seqence wrap */
+		if( !(seq &  MP_SHORTSEQ_MAXBIT) && 
+		     (last_seq &  MP_SHORTSEQ_MAXBIT) && 
+		     (unsigned long)last_seq <= MP_LONGSEQ_MAX )
+			seq |= (last_seq + MP_SHORTSEQ_MAX+1) & 
+					(~MP_SHORTSEQ_MASK & MP_LONGSEQ_MASK);
+		else
+			seq |= last_seq & (~MP_SHORTSEQ_MASK & MP_LONGSEQ_MASK);
+		
+		skb_push(skb, 3);	/* put converted seqence back in skb */
+	}
+	*(u32*)(skb->data+1) = seq; 	/* put seqence back in _host_ byte
+					 * order */
+	skb->data[0] = flags;	        /* restore flags */
+	return seq;
+}
+
+struct sk_buff * isdn_ppp_mp_discard( ippp_bundle * mp,
+			struct sk_buff * from, struct sk_buff * to )
+{
+	if( from )
+		while (from != to) {
+	  		struct sk_buff * next = from->next;
+			isdn_ppp_mp_free_skb(mp, from);
+	  		from = next;
+		}
+	return from;
+}
+
+void isdn_ppp_mp_reassembly( isdn_net_dev * net_dev, isdn_net_local * lp,
+				struct sk_buff * from, struct sk_buff * to )
+{
+	ippp_bundle * mp = net_dev->pb;
+	int proto;
+	struct sk_buff * skb;
+	unsigned int tot_len;
+
+	if( MP_FLAGS(from) == (MP_BEGIN_FRAG | MP_END_FRAG) ) {
+		if( ippp_table[lp->ppp_slot]->debug & 0x40 )
+			printk(KERN_DEBUG"isdn_mppp: reassembly: frame %d, "
+					"len %d\n", MP_SEQ(from), from->len );
+		skb = from;
+		skb_pull(skb, MP_HEADER_LEN);
+		mp->frames--;	
+	} else {
+		struct sk_buff * frag;
+		int n;
+
+		for(tot_len=n=0, frag=from; frag != to; frag=frag->next, n++)
+			tot_len += frag->len - MP_HEADER_LEN;
+
+		if( ippp_table[lp->ppp_slot]->debug & 0x40 )
+			printk(KERN_DEBUG"isdn_mppp: reassembling frames %d "
+				"to %d, len %d\n", MP_SEQ(from), 
+				(MP_SEQ(from)+n-1) & MP_LONGSEQ_MASK, tot_len );
+		if( (skb = dev_alloc_skb(tot_len)) == NULL ) {
+			printk(KERN_ERR "isdn_mppp: cannot allocate sk buff "
+					"of size %d\n", tot_len);
+			isdn_ppp_mp_discard(mp, from, to);
+			return;
+		}
+
+		while( from != to ) {
+			unsigned int len = from->len - MP_HEADER_LEN;
+
+			memcpy(skb_put(skb,len), from->data+MP_HEADER_LEN, len);
+			frag = from->next;
+			isdn_ppp_mp_free_skb(mp, from);
+			from = frag; 
+		}
+	}
+   	proto = isdn_ppp_strip_proto(skb);
+	isdn_ppp_push_higher(net_dev, lp, skb, proto);
+}
+
+static void isdn_ppp_mp_free_skb(ippp_bundle * mp, struct sk_buff * skb)
+{
+	dev_kfree_skb(skb);
+	mp->frames--;
+}
+
+static void isdn_ppp_mp_print_recv_pkt( int slot, struct sk_buff * skb )
+{
+	printk(KERN_DEBUG "mp_recv: %d/%d -> %02x %02x %02x %02x %02x %02x\n", 
+		slot, (int) skb->len, 
+		(int) skb->data[0], (int) skb->data[1], (int) skb->data[2],
+		(int) skb->data[3], (int) skb->data[4], (int) skb->data[5]);
+}
+
+static int
+isdn_ppp_bundle(struct ippp_struct *is, int unit)
+{
+	char ifn[IFNAMSIZ + 1];
+	isdn_net_dev *p;
+	isdn_net_local *lp, *nlp;
+	int rc;
+	unsigned long flags;
+
+	sprintf(ifn, "ippp%d", unit);
+	p = isdn_net_findif(ifn);
+	if (!p) {
+		printk(KERN_ERR "ippp_bundle: cannot find %s\n", ifn);
+		return -EINVAL;
+	}
+
+    	spin_lock_irqsave(&p->pb->lock, flags);
+
+	nlp = is->lp;
+	lp = p->queue;
+	if( nlp->ppp_slot < 0 || nlp->ppp_slot >= ISDN_MAX_CHANNELS ||
+		lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS ) {
+		printk(KERN_ERR "ippp_bundle: binding to invalid slot %d\n",
+			nlp->ppp_slot < 0 || nlp->ppp_slot >= ISDN_MAX_CHANNELS ? 
+			nlp->ppp_slot : lp->ppp_slot );
+		rc = -EINVAL;
+		goto out;
+ 	}
+
+	isdn_net_add_to_bundle(p, nlp);
+
+	ippp_table[nlp->ppp_slot]->unit = ippp_table[lp->ppp_slot]->unit;
+
+	/* maybe also SC_CCP stuff */
+	ippp_table[nlp->ppp_slot]->pppcfg |= ippp_table[lp->ppp_slot]->pppcfg &
+		(SC_ENABLE_IP | SC_NO_TCP_CCID | SC_REJ_COMP_TCP);
+	ippp_table[nlp->ppp_slot]->mpppcfg |= ippp_table[lp->ppp_slot]->mpppcfg &
+		(SC_MP_PROT | SC_REJ_MP_PROT | SC_OUT_SHORT_SEQ | SC_IN_SHORT_SEQ);
+	rc = isdn_ppp_mp_init(nlp, p->pb);
+out:
+	spin_unlock_irqrestore(&p->pb->lock, flags);
+	return rc;
+}
+  
+#endif /* CONFIG_ISDN_MPP */
+  
+/*
+ * network device ioctl handlers
+ */
+
+static int
+isdn_ppp_dev_ioctl_stats(int slot, struct ifreq *ifr, struct net_device *dev)
+{
+	struct ppp_stats *res,
+	 t;
+	isdn_net_local *lp = (isdn_net_local *) dev->priv;
+	int err;
+
+	res = (struct ppp_stats *) ifr->ifr_ifru.ifru_data;
+	err = verify_area(VERIFY_WRITE, res, sizeof(struct ppp_stats));
+
+	if (err)
+		return err;
+
+	/* build a temporary stat struct and copy it to user space */
+
+	memset(&t, 0, sizeof(struct ppp_stats));
+	if (dev->flags & IFF_UP) {
+		t.p.ppp_ipackets = lp->stats.rx_packets;
+		t.p.ppp_ierrors = lp->stats.rx_errors;
+		t.p.ppp_opackets = lp->stats.tx_packets;
+		t.p.ppp_oerrors = lp->stats.tx_errors;
+#ifdef CONFIG_ISDN_PPP_VJ
+		if (slot >= 0 && ippp_table[slot]->slcomp) {
+			struct slcompress *slcomp = ippp_table[slot]->slcomp;
+			t.vj.vjs_packets = slcomp->sls_o_compressed + slcomp->sls_o_uncompressed;
+			t.vj.vjs_compressed = slcomp->sls_o_compressed;
+			t.vj.vjs_searches = slcomp->sls_o_searches;
+			t.vj.vjs_misses = slcomp->sls_o_misses;
+			t.vj.vjs_errorin = slcomp->sls_i_error;
+			t.vj.vjs_tossed = slcomp->sls_i_tossed;
+			t.vj.vjs_uncompressedin = slcomp->sls_i_uncompressed;
+			t.vj.vjs_compressedin = slcomp->sls_i_compressed;
+		}
+#endif
+	}
+	if( copy_to_user(res, &t, sizeof(struct ppp_stats))) return -EFAULT;
+	return 0;
+}
+
+int
+isdn_ppp_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	int error=0;
+	char *r;
+	int len;
+	isdn_net_local *lp = (isdn_net_local *) dev->priv;
+
+
+	if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP)
+		return -EINVAL;
+
+	switch (cmd) {
+		case SIOCGPPPVER:
+			r = (char *) ifr->ifr_ifru.ifru_data;
+			len = strlen(PPP_VERSION) + 1;
+			if(copy_to_user(r, PPP_VERSION, len)) error = -EFAULT;
+			break;
+		case SIOCGPPPSTATS:
+			error = isdn_ppp_dev_ioctl_stats(lp->ppp_slot, ifr, dev);
+			break;
+		default:
+			error = -EINVAL;
+			break;
+	}
+	return error;
+}
+
+static int
+isdn_ppp_if_get_unit(char *name)
+{
+	int len,
+	 i,
+	 unit = 0,
+	 deci;
+
+	len = strlen(name);
+
+	if (strncmp("ippp", name, 4) || len > 8)
+		return -1;
+
+	for (i = 0, deci = 1; i < len; i++, deci *= 10) {
+		char a = name[len - i - 1];
+		if (a >= '0' && a <= '9')
+			unit += (a - '0') * deci;
+		else
+			break;
+	}
+	if (!i || len - i != 4)
+		unit = -1;
+
+	return unit;
+}
+
+
+int
+isdn_ppp_dial_slave(char *name)
+{
+#ifdef CONFIG_ISDN_MPP
+	isdn_net_dev *ndev;
+	isdn_net_local *lp;
+	struct net_device *sdev;
+
+	if (!(ndev = isdn_net_findif(name)))
+		return 1;
+	lp = ndev->local;
+	if (!(lp->flags & ISDN_NET_CONNECTED))
+		return 5;
+
+	sdev = lp->slave;
+	while (sdev) {
+		isdn_net_local *mlp = (isdn_net_local *) sdev->priv;
+		if (!(mlp->flags & ISDN_NET_CONNECTED))
+			break;
+		sdev = mlp->slave;
+	}
+	if (!sdev)
+		return 2;
+
+	isdn_net_dial_req((isdn_net_local *) sdev->priv);
+	return 0;
+#else
+	return -1;
+#endif
+}
+
+int
+isdn_ppp_hangup_slave(char *name)
+{
+#ifdef CONFIG_ISDN_MPP
+	isdn_net_dev *ndev;
+	isdn_net_local *lp;
+	struct net_device *sdev;
+
+	if (!(ndev = isdn_net_findif(name)))
+		return 1;
+	lp = ndev->local;
+	if (!(lp->flags & ISDN_NET_CONNECTED))
+		return 5;
+
+	sdev = lp->slave;
+	while (sdev) {
+		isdn_net_local *mlp = (isdn_net_local *) sdev->priv;
+
+		if (mlp->slave) { /* find last connected link in chain */
+			isdn_net_local *nlp = (isdn_net_local *) mlp->slave->priv;
+
+			if (!(nlp->flags & ISDN_NET_CONNECTED))
+				break;
+		} else if (mlp->flags & ISDN_NET_CONNECTED)
+			break;
+		
+		sdev = mlp->slave;
+	}
+	if (!sdev)
+		return 2;
+
+	isdn_net_hangup(sdev);
+	return 0;
+#else
+	return -1;
+#endif
+}
+
+/*
+ * PPP compression stuff
+ */
+
+
+/* Push an empty CCP Data Frame up to the daemon to wake it up and let it
+   generate a CCP Reset-Request or tear down CCP altogether */
+
+static void isdn_ppp_ccp_kickup(struct ippp_struct *is)
+{
+	isdn_ppp_fill_rq(NULL, 0, PPP_COMP, is->lp->ppp_slot);
+}
+
+/* In-kernel handling of CCP Reset-Request and Reset-Ack is necessary,
+   but absolutely nontrivial. The most abstruse problem we are facing is
+   that the generation, reception and all the handling of timeouts and
+   resends including proper request id management should be entirely left
+   to the (de)compressor, but indeed is not covered by the current API to
+   the (de)compressor. The API is a prototype version from PPP where only
+   some (de)compressors have yet been implemented and all of them are
+   rather simple in their reset handling. Especially, their is only one
+   outstanding ResetAck at a time with all of them and ResetReq/-Acks do
+   not have parameters. For this very special case it was sufficient to
+   just return an error code from the decompressor and have a single
+   reset() entry to communicate all the necessary information between
+   the framework and the (de)compressor. Bad enough, LZS is different
+   (and any other compressor may be different, too). It has multiple
+   histories (eventually) and needs to Reset each of them independently
+   and thus uses multiple outstanding Acks and history numbers as an
+   additional parameter to Reqs/Acks.
+   All that makes it harder to port the reset state engine into the
+   kernel because it is not just the same simple one as in (i)pppd but
+   it must be able to pass additional parameters and have multiple out-
+   standing Acks. We are trying to achieve the impossible by handling
+   reset transactions independent by their id. The id MUST change when
+   the data portion changes, thus any (de)compressor who uses more than
+   one resettable state must provide and recognize individual ids for
+   each individual reset transaction. The framework itself does _only_
+   differentiate them by id, because it has no other semantics like the
+   (de)compressor might.
+   This looks like a major redesign of the interface would be nice,
+   but I don't have an idea how to do it better. */
+
+/* Send a CCP Reset-Request or Reset-Ack directly from the kernel. This is
+   getting that lengthy because there is no simple "send-this-frame-out"
+   function above but every wrapper does a bit different. Hope I guess
+   correct in this hack... */
+
+static void isdn_ppp_ccp_xmit_reset(struct ippp_struct *is, int proto,
+				    unsigned char code, unsigned char id,
+				    unsigned char *data, int len)
+{
+	struct sk_buff *skb;
+	unsigned char *p;
+	int hl;
+	int cnt = 0;
+	isdn_net_local *lp = is->lp;
+
+	/* Alloc large enough skb */
+	hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen;
+	skb = alloc_skb(len + hl + 16,GFP_ATOMIC);
+	if(!skb) {
+		printk(KERN_WARNING
+		       "ippp: CCP cannot send reset - out of memory\n");
+		return;
+	}
+	skb_reserve(skb, hl);
+
+	/* We may need to stuff an address and control field first */
+	if(!(is->pppcfg & SC_COMP_AC)) {
+		p = skb_put(skb, 2);
+		*p++ = 0xff;
+		*p++ = 0x03;
+	}
+
+	/* Stuff proto, code, id and length */
+	p = skb_put(skb, 6);
+	*p++ = (proto >> 8);
+	*p++ = (proto & 0xff);
+	*p++ = code;
+	*p++ = id;
+	cnt = 4 + len;
+	*p++ = (cnt >> 8);
+	*p++ = (cnt & 0xff);
+
+	/* Now stuff remaining bytes */
+	if(len) {
+		p = skb_put(skb, len);
+		memcpy(p, data, len);
+	}
+
+	/* skb is now ready for xmit */
+	printk(KERN_DEBUG "Sending CCP Frame:\n");
+	isdn_ppp_frame_log("ccp-xmit", skb->data, skb->len, 32, is->unit,lp->ppp_slot);
+
+	isdn_net_write_super(lp, skb);
+}
+
+/* Allocate the reset state vector */
+static struct ippp_ccp_reset *isdn_ppp_ccp_reset_alloc(struct ippp_struct *is)
+{
+	struct ippp_ccp_reset *r;
+	r = kmalloc(sizeof(struct ippp_ccp_reset), GFP_KERNEL);
+	if(!r) {
+		printk(KERN_ERR "ippp_ccp: failed to allocate reset data"
+		       " structure - no mem\n");
+		return NULL;
+	}
+	memset(r, 0, sizeof(struct ippp_ccp_reset));
+	printk(KERN_DEBUG "ippp_ccp: allocated reset data structure %p\n", r);
+	is->reset = r;
+	return r;
+}
+
+/* Destroy the reset state vector. Kill all pending timers first. */
+static void isdn_ppp_ccp_reset_free(struct ippp_struct *is)
+{
+	unsigned int id;
+
+	printk(KERN_DEBUG "ippp_ccp: freeing reset data structure %p\n",
+	       is->reset);
+	for(id = 0; id < 256; id++) {
+		if(is->reset->rs[id]) {
+			isdn_ppp_ccp_reset_free_state(is, (unsigned char)id);
+		}
+	}
+	kfree(is->reset);
+	is->reset = NULL;
+}
+
+/* Free a given state and clear everything up for later reallocation */
+static void isdn_ppp_ccp_reset_free_state(struct ippp_struct *is,
+					  unsigned char id)
+{
+	struct ippp_ccp_reset_state *rs;
+
+	if(is->reset->rs[id]) {
+		printk(KERN_DEBUG "ippp_ccp: freeing state for id %d\n", id);
+		rs = is->reset->rs[id];
+		/* Make sure the kernel will not call back later */
+		if(rs->ta)
+			del_timer(&rs->timer);
+		is->reset->rs[id] = NULL;
+		kfree(rs);
+	} else {
+		printk(KERN_WARNING "ippp_ccp: id %d is not allocated\n", id);
+	}
+}
+
+/* The timer callback function which is called when a ResetReq has timed out,
+   aka has never been answered by a ResetAck */
+static void isdn_ppp_ccp_timer_callback(unsigned long closure)
+{
+	struct ippp_ccp_reset_state *rs =
+		(struct ippp_ccp_reset_state *)closure;
+
+	if(!rs) {
+		printk(KERN_ERR "ippp_ccp: timer cb with zero closure.\n");
+		return;
+	}
+	if(rs->ta && rs->state == CCPResetSentReq) {
+		/* We are correct here */
+		if(!rs->expra) {
+			/* Hmm, there is no Ack really expected. We can clean
+			   up the state now, it will be reallocated if the
+			   decompressor insists on another reset */
+			rs->ta = 0;
+			isdn_ppp_ccp_reset_free_state(rs->is, rs->id);
+			return;
+		}
+		printk(KERN_DEBUG "ippp_ccp: CCP Reset timed out for id %d\n",
+		       rs->id);
+		/* Push it again */
+		isdn_ppp_ccp_xmit_reset(rs->is, PPP_CCP, CCP_RESETREQ, rs->id,
+					rs->data, rs->dlen);
+		/* Restart timer */
+		rs->timer.expires = jiffies + HZ*5;
+		add_timer(&rs->timer);
+	} else {
+		printk(KERN_WARNING "ippp_ccp: timer cb in wrong state %d\n",
+		       rs->state);
+	}
+}
+
+/* Allocate a new reset transaction state */
+static struct ippp_ccp_reset_state *isdn_ppp_ccp_reset_alloc_state(struct ippp_struct *is,
+						      unsigned char id)
+{
+	struct ippp_ccp_reset_state *rs;
+	if(is->reset->rs[id]) {
+		printk(KERN_WARNING "ippp_ccp: old state exists for id %d\n",
+		       id);
+		return NULL;
+	} else {
+		rs = kmalloc(sizeof(struct ippp_ccp_reset_state), GFP_KERNEL);
+		if(!rs)
+			return NULL;
+		memset(rs, 0, sizeof(struct ippp_ccp_reset_state));
+		rs->state = CCPResetIdle;
+		rs->is = is;
+		rs->id = id;
+		rs->timer.data = (unsigned long)rs;
+		rs->timer.function = isdn_ppp_ccp_timer_callback;
+		is->reset->rs[id] = rs;
+	}
+	return rs;
+}
+
+
+/* A decompressor wants a reset with a set of parameters - do what is
+   necessary to fulfill it */
+static void isdn_ppp_ccp_reset_trans(struct ippp_struct *is,
+				     struct isdn_ppp_resetparams *rp)
+{
+	struct ippp_ccp_reset_state *rs;
+
+	if(rp->valid) {
+		/* The decompressor defines parameters by itself */
+		if(rp->rsend) {
+			/* And he wants us to send a request */
+			if(!(rp->idval)) {
+				printk(KERN_ERR "ippp_ccp: decompressor must"
+				       " specify reset id\n");
+				return;
+			}
+			if(is->reset->rs[rp->id]) {
+				/* There is already a transaction in existence
+				   for this id. May be still waiting for a
+				   Ack or may be wrong. */
+				rs = is->reset->rs[rp->id];
+				if(rs->state == CCPResetSentReq && rs->ta) {
+					printk(KERN_DEBUG "ippp_ccp: reset"
+					       " trans still in progress"
+					       " for id %d\n", rp->id);
+				} else {
+					printk(KERN_WARNING "ippp_ccp: reset"
+					       " trans in wrong state %d for"
+					       " id %d\n", rs->state, rp->id);
+				}
+			} else {
+				/* Ok, this is a new transaction */
+				printk(KERN_DEBUG "ippp_ccp: new trans for id"
+				       " %d to be started\n", rp->id);
+				rs = isdn_ppp_ccp_reset_alloc_state(is, rp->id);
+				if(!rs) {
+					printk(KERN_ERR "ippp_ccp: out of mem"
+					       " allocing ccp trans\n");
+					return;
+				}
+				rs->state = CCPResetSentReq;
+				rs->expra = rp->expra;
+				if(rp->dtval) {
+					rs->dlen = rp->dlen;
+					memcpy(rs->data, rp->data, rp->dlen);
+				}
+				/* HACK TODO - add link comp here */
+				isdn_ppp_ccp_xmit_reset(is, PPP_CCP,
+							CCP_RESETREQ, rs->id,
+							rs->data, rs->dlen);
+				/* Start the timer */
+				rs->timer.expires = jiffies + 5*HZ;
+				add_timer(&rs->timer);
+				rs->ta = 1;
+			}
+		} else {
+			printk(KERN_DEBUG "ippp_ccp: no reset sent\n");
+		}
+	} else {
+		/* The reset params are invalid. The decompressor does not
+		   care about them, so we just send the minimal requests
+		   and increase ids only when an Ack is received for a
+		   given id */
+		if(is->reset->rs[is->reset->lastid]) {
+			/* There is already a transaction in existence
+			   for this id. May be still waiting for a
+			   Ack or may be wrong. */
+			rs = is->reset->rs[is->reset->lastid];
+			if(rs->state == CCPResetSentReq && rs->ta) {
+				printk(KERN_DEBUG "ippp_ccp: reset"
+				       " trans still in progress"
+				       " for id %d\n", rp->id);
+			} else {
+				printk(KERN_WARNING "ippp_ccp: reset"
+				       " trans in wrong state %d for"
+				       " id %d\n", rs->state, rp->id);
+			}
+		} else {
+			printk(KERN_DEBUG "ippp_ccp: new trans for id"
+			       " %d to be started\n", is->reset->lastid);
+			rs = isdn_ppp_ccp_reset_alloc_state(is,
+							    is->reset->lastid);
+			if(!rs) {
+				printk(KERN_ERR "ippp_ccp: out of mem"
+				       " allocing ccp trans\n");
+				return;
+			}
+			rs->state = CCPResetSentReq;
+			/* We always expect an Ack if the decompressor doesnt
+			   know	better */
+			rs->expra = 1;
+			rs->dlen = 0;
+			/* HACK TODO - add link comp here */
+			isdn_ppp_ccp_xmit_reset(is, PPP_CCP, CCP_RESETREQ,
+						rs->id, NULL, 0);
+			/* Start the timer */
+			rs->timer.expires = jiffies + 5*HZ;
+			add_timer(&rs->timer);
+			rs->ta = 1;
+		}
+	}
+}
+
+/* An Ack was received for this id. This means we stop the timer and clean
+   up the state prior to calling the decompressors reset routine. */
+static void isdn_ppp_ccp_reset_ack_rcvd(struct ippp_struct *is,
+					unsigned char id)
+{
+	struct ippp_ccp_reset_state *rs = is->reset->rs[id];
+
+	if(rs) {
+		if(rs->ta && rs->state == CCPResetSentReq) {
+			/* Great, we are correct */
+			if(!rs->expra)
+				printk(KERN_DEBUG "ippp_ccp: ResetAck received"
+				       " for id %d but not expected\n", id);
+		} else {
+			printk(KERN_INFO "ippp_ccp: ResetAck received out of"
+			       "sync for id %d\n", id);
+		}
+		if(rs->ta) {
+			rs->ta = 0;
+			del_timer(&rs->timer);
+		}
+		isdn_ppp_ccp_reset_free_state(is, id);
+	} else {
+		printk(KERN_INFO "ippp_ccp: ResetAck received for unknown id"
+		       " %d\n", id);
+	}
+	/* Make sure the simple reset stuff uses a new id next time */
+	is->reset->lastid++;
+}
+
+/* 
+ * decompress packet
+ *
+ * if master = 0, we're trying to uncompress an per-link compressed packet,
+ * as opposed to an compressed reconstructed-from-MPPP packet.
+ * proto is updated to protocol field of uncompressed packet.
+ *
+ * retval: decompressed packet,
+ *         same packet if uncompressed,
+ *	   NULL if decompression error
+ */
+
+static struct sk_buff *isdn_ppp_decompress(struct sk_buff *skb,struct ippp_struct *is,struct ippp_struct *master,
+	int *proto)
+{
+	void *stat = NULL;
+	struct isdn_ppp_compressor *ipc = NULL;
+	struct sk_buff *skb_out;
+	int len;
+	struct ippp_struct *ri;
+	struct isdn_ppp_resetparams rsparm;
+	unsigned char rsdata[IPPP_RESET_MAXDATABYTES];
+
+	if(!master) {
+		// per-link decompression 
+		stat = is->link_decomp_stat;
+		ipc = is->link_decompressor;
+		ri = is;
+	} else {
+		stat = master->decomp_stat;
+		ipc = master->decompressor;
+		ri = master;
+	}
+
+	if (!ipc) {
+		// no decompressor -> we can't decompress.
+		printk(KERN_DEBUG "ippp: no decompressor defined!\n");
+		return skb;
+	}
+	if (!stat) // if we have a compressor, stat has been set as well
+		BUG();
+
+	if((master && *proto == PPP_COMP) || (!master && *proto == PPP_COMPFRAG) ) {
+		// compressed packets are compressed by their protocol type
+
+		// Set up reset params for the decompressor
+  		memset(&rsparm, 0, sizeof(rsparm));
+  		rsparm.data = rsdata;
+  		rsparm.maxdlen = IPPP_RESET_MAXDATABYTES;
+  
+  		skb_out = dev_alloc_skb(is->mru + PPP_HDRLEN);
+		len = ipc->decompress(stat, skb, skb_out, &rsparm);
+		kfree_skb(skb);
+		if (len <= 0) {
+			switch(len) {
+			case DECOMP_ERROR:
+				printk(KERN_INFO "ippp: decomp wants reset %s params\n",
+				       rsparm.valid ? "with" : "without");
+				
+				isdn_ppp_ccp_reset_trans(ri, &rsparm);
+				break;
+			case DECOMP_FATALERROR:
+				ri->pppcfg |= SC_DC_FERROR;
+				/* Kick ipppd to recognize the error */
+				isdn_ppp_ccp_kickup(ri);
+				break;
+			}
+			kfree_skb(skb_out);
+			return NULL;
+		}
+		*proto = isdn_ppp_strip_proto(skb_out);
+		if (*proto < 0) {
+			kfree_skb(skb_out);
+			return NULL;
+		}
+		return skb_out;
+	} else { 
+		// uncompressed packets are fed through the decompressor to
+		// update the decompressor state
+		ipc->incomp(stat, skb, *proto);
+		return skb;
+	}
+}
+
+/*
+ * compress a frame 
+ *   type=0: normal/bundle compression
+ *       =1: link compression
+ * returns original skb if we haven't compressed the frame
+ * and a new skb pointer if we've done it
+ */
+static struct sk_buff *isdn_ppp_compress(struct sk_buff *skb_in,int *proto,
+	struct ippp_struct *is,struct ippp_struct *master,int type)
+{
+    int ret;
+    int new_proto;
+    struct isdn_ppp_compressor *compressor;
+    void *stat;
+    struct sk_buff *skb_out;
+
+	/* we do not compress control protocols */
+    if(*proto < 0 || *proto > 0x3fff) {
+	    return skb_in;
+    }
+
+	if(type) { /* type=1 => Link compression */
+		return skb_in;
+	}
+	else {
+		if(!master) {
+			compressor = is->compressor;
+			stat = is->comp_stat;
+		}
+		else {
+			compressor = master->compressor;
+			stat = master->comp_stat;
+		}
+		new_proto = PPP_COMP;
+	}
+
+	if(!compressor) {
+		printk(KERN_ERR "isdn_ppp: No compressor set!\n");
+		return skb_in;
+	}
+	if(!stat) {
+		printk(KERN_ERR "isdn_ppp: Compressor not initialized?\n");
+		return skb_in;
+	}
+
+	/* Allow for at least 150 % expansion (for now) */
+	skb_out = alloc_skb(skb_in->len + skb_in->len/2 + 32 +
+		skb_headroom(skb_in), GFP_ATOMIC);
+	if(!skb_out)
+		return skb_in;
+	skb_reserve(skb_out, skb_headroom(skb_in));
+
+	ret = (compressor->compress)(stat,skb_in,skb_out,*proto);
+	if(!ret) {
+		dev_kfree_skb(skb_out);
+		return skb_in;
+	}
+	
+	dev_kfree_skb(skb_in);
+	*proto = new_proto;
+	return skb_out;
+}
+
+/*
+ * we received a CCP frame .. 
+ * not a clean solution, but we MUST handle a few cases in the kernel
+ */
+static void isdn_ppp_receive_ccp(isdn_net_dev *net_dev, isdn_net_local *lp,
+	 struct sk_buff *skb,int proto)
+{
+	struct ippp_struct *is = ippp_table[lp->ppp_slot];
+	struct ippp_struct *mis;
+	int len;
+	struct isdn_ppp_resetparams rsparm;
+	unsigned char rsdata[IPPP_RESET_MAXDATABYTES];	
+
+	printk(KERN_DEBUG "Received CCP frame from peer\n");
+	isdn_ppp_frame_log("ccp-rcv", skb->data, skb->len, 32, is->unit,lp->ppp_slot);
+
+	if(lp->master)
+		mis = ippp_table[((isdn_net_local *) (lp->master->priv))->ppp_slot];
+	else
+		mis = is;
+
+	switch(skb->data[0]) {
+	case CCP_CONFREQ:
+		if(is->debug & 0x10)
+			printk(KERN_DEBUG "Disable compression here!\n");
+		if(proto == PPP_CCP)
+			mis->compflags &= ~SC_COMP_ON;		
+		else
+			is->compflags &= ~SC_LINK_COMP_ON;		
+		break;
+	case CCP_TERMREQ:
+	case CCP_TERMACK:
+		if(is->debug & 0x10)
+			printk(KERN_DEBUG "Disable (de)compression here!\n");
+		if(proto == PPP_CCP)
+			mis->compflags &= ~(SC_DECOMP_ON|SC_COMP_ON);		
+		else
+			is->compflags &= ~(SC_LINK_DECOMP_ON|SC_LINK_COMP_ON);		
+		break;
+	case CCP_CONFACK:
+		/* if we RECEIVE an ackowledge we enable the decompressor */
+		if(is->debug & 0x10)
+			printk(KERN_DEBUG "Enable decompression here!\n");
+		if(proto == PPP_CCP) {
+			if (!mis->decompressor)
+				break;
+			mis->compflags |= SC_DECOMP_ON;
+		} else {
+			if (!is->decompressor)
+				break;
+			is->compflags |= SC_LINK_DECOMP_ON;
+		}
+		break;
+
+	case CCP_RESETACK:
+		printk(KERN_DEBUG "Received ResetAck from peer\n");
+		len = (skb->data[2] << 8) | skb->data[3];
+		len -= 4;
+
+		if(proto == PPP_CCP) {
+			/* If a reset Ack was outstanding for this id, then
+			   clean up the state engine */
+			isdn_ppp_ccp_reset_ack_rcvd(mis, skb->data[1]);
+			if(mis->decompressor && mis->decomp_stat)
+				mis->decompressor->
+					reset(mis->decomp_stat,
+					      skb->data[0],
+					      skb->data[1],
+					      len ? &skb->data[4] : NULL,
+					      len, NULL);
+			/* TODO: This is not easy to decide here */
+			mis->compflags &= ~SC_DECOMP_DISCARD;
+		}
+		else {
+			isdn_ppp_ccp_reset_ack_rcvd(is, skb->data[1]);
+			if(is->link_decompressor && is->link_decomp_stat)
+				is->link_decompressor->
+					reset(is->link_decomp_stat,
+					      skb->data[0],
+					      skb->data[1],
+					      len ? &skb->data[4] : NULL,
+					      len, NULL);
+			/* TODO: neither here */
+			is->compflags &= ~SC_LINK_DECOMP_DISCARD;
+		}
+		break;
+
+	case CCP_RESETREQ:
+		printk(KERN_DEBUG "Received ResetReq from peer\n");
+		/* Receiving a ResetReq means we must reset our compressor */
+		/* Set up reset params for the reset entry */
+		memset(&rsparm, 0, sizeof(rsparm));
+		rsparm.data = rsdata;
+		rsparm.maxdlen = IPPP_RESET_MAXDATABYTES; 
+		/* Isolate data length */
+		len = (skb->data[2] << 8) | skb->data[3];
+		len -= 4;
+		if(proto == PPP_CCP) {
+			if(mis->compressor && mis->comp_stat)
+				mis->compressor->
+					reset(mis->comp_stat,
+					      skb->data[0],
+					      skb->data[1],
+					      len ? &skb->data[4] : NULL,
+					      len, &rsparm);
+		}
+		else {
+			if(is->link_compressor && is->link_comp_stat)
+				is->link_compressor->
+					reset(is->link_comp_stat,
+					      skb->data[0],
+					      skb->data[1],
+					      len ? &skb->data[4] : NULL,
+					      len, &rsparm);
+		}
+		/* Ack the Req as specified by rsparm */
+		if(rsparm.valid) {
+			/* Compressor reset handler decided how to answer */
+			if(rsparm.rsend) {
+				/* We should send a Frame */
+				isdn_ppp_ccp_xmit_reset(is, proto, CCP_RESETACK,
+							rsparm.idval ? rsparm.id
+							: skb->data[1],
+							rsparm.dtval ?
+							rsparm.data : NULL,
+							rsparm.dtval ?
+							rsparm.dlen : 0);
+			} else {
+				printk(KERN_DEBUG "ResetAck suppressed\n");
+			}
+		} else {
+			/* We answer with a straight reflected Ack */
+			isdn_ppp_ccp_xmit_reset(is, proto, CCP_RESETACK,
+						skb->data[1],
+						len ? &skb->data[4] : NULL,
+						len);
+		}
+		break;
+	}
+}
+
+
+/*
+ * Daemon sends a CCP frame ...
+ */
+
+/* TODO: Clean this up with new Reset semantics */
+
+/* I believe the CCP handling as-is is done wrong. Compressed frames
+ * should only be sent/received after CCP reaches UP state, which means
+ * both sides have sent CONF_ACK. Currently, we handle both directions
+ * independently, which means we may accept compressed frames too early
+ * (supposedly not a problem), but may also mean we send compressed frames
+ * too early, which may turn out to be a problem.
+ * This part of state machine should actually be handled by (i)pppd, but
+ * that's too big of a change now. --kai
+ */
+
+/* Actually, we might turn this into an advantage: deal with the RFC in
+ * the old tradition of beeing generous on what we accept, but beeing
+ * strict on what we send. Thus we should just
+ * - accept compressed frames as soon as decompression is negotiated
+ * - send compressed frames only when decomp *and* comp are negotiated
+ * - drop rx compressed frames if we cannot decomp (instead of pushing them
+ *   up to ipppd)
+ * and I tried to modify this file according to that. --abp
+ */
+
+static void isdn_ppp_send_ccp(isdn_net_dev *net_dev, isdn_net_local *lp, struct sk_buff *skb)
+{
+	struct ippp_struct *mis,*is = ippp_table[lp->ppp_slot];
+	int proto;
+	unsigned char *data;
+
+	if(!skb || skb->len < 3)
+		return;
+
+	/* Daemon may send with or without address and control field comp */
+	data = skb->data;
+	if(!(is->pppcfg & SC_COMP_AC) && data[0] == 0xff && data[1] == 0x03) {
+		data += 2;
+		if(skb->len < 5)
+			return;
+	}
+
+	proto = ((int)data[0]<<8)+data[1];
+	if(proto != PPP_CCP && proto != PPP_CCPFRAG)
+		return;
+
+	printk(KERN_DEBUG "Received CCP frame from daemon:\n");
+	isdn_ppp_frame_log("ccp-xmit", skb->data, skb->len, 32, is->unit,lp->ppp_slot);
+
+        if(lp->master)
+                mis = ippp_table[((isdn_net_local *) (lp->master->priv))->ppp_slot];
+        else
+                mis = is;
+	
+	if(mis != is)
+		printk(KERN_DEBUG "isdn_ppp: Ouch! Master CCP sends on slave slot!\n");
+	
+        switch(data[2]) {
+	case CCP_CONFREQ:
+		if(is->debug & 0x10)
+			printk(KERN_DEBUG "Disable decompression here!\n");
+		if(proto == PPP_CCP)
+			is->compflags &= ~SC_DECOMP_ON;
+		else
+			is->compflags &= ~SC_LINK_DECOMP_ON;
+		break;
+	case CCP_TERMREQ:
+	case CCP_TERMACK:
+		if(is->debug & 0x10)
+			printk(KERN_DEBUG "Disable (de)compression here!\n");
+		if(proto == PPP_CCP)
+			is->compflags &= ~(SC_DECOMP_ON|SC_COMP_ON);
+		else
+			is->compflags &= ~(SC_LINK_DECOMP_ON|SC_LINK_COMP_ON);
+		break;
+	case CCP_CONFACK:
+		/* if we SEND an ackowledge we can/must enable the compressor */
+		if(is->debug & 0x10)
+			printk(KERN_DEBUG "Enable compression here!\n");
+		if(proto == PPP_CCP) {
+			if (!is->compressor)
+				break;
+			is->compflags |= SC_COMP_ON;
+		} else {
+			if (!is->compressor)
+				break;
+			is->compflags |= SC_LINK_COMP_ON;
+		}
+		break;
+	case CCP_RESETACK:
+		/* If we send a ACK we should reset our compressor */
+		if(is->debug & 0x10)
+			printk(KERN_DEBUG "Reset decompression state here!\n");
+		printk(KERN_DEBUG "ResetAck from daemon passed by\n");
+		if(proto == PPP_CCP) {
+			/* link to master? */
+			if(is->compressor && is->comp_stat)
+				is->compressor->reset(is->comp_stat, 0, 0,
+						      NULL, 0, NULL);
+			is->compflags &= ~SC_COMP_DISCARD;	
+		}
+		else {
+			if(is->link_compressor && is->link_comp_stat)
+				is->link_compressor->reset(is->link_comp_stat,
+							   0, 0, NULL, 0, NULL);
+			is->compflags &= ~SC_LINK_COMP_DISCARD;	
+		}
+		break;
+	case CCP_RESETREQ:
+		/* Just let it pass by */
+		printk(KERN_DEBUG "ResetReq from daemon passed by\n");
+		break;
+	}
+}
+
+int isdn_ppp_register_compressor(struct isdn_ppp_compressor *ipc)
+{
+	ipc->next = ipc_head;
+	ipc->prev = NULL;
+	if(ipc_head) {
+		ipc_head->prev = ipc;
+	}
+	ipc_head = ipc;
+	return 0;
+}
+
+int isdn_ppp_unregister_compressor(struct isdn_ppp_compressor *ipc)
+{
+	if(ipc->prev)
+		ipc->prev->next = ipc->next;
+	else
+		ipc_head = ipc->next;
+	if(ipc->next)
+		ipc->next->prev = ipc->prev;
+	ipc->prev = ipc->next = NULL;
+	return 0;
+}
+
+static int isdn_ppp_set_compressor(struct ippp_struct *is, struct isdn_ppp_comp_data *data)
+{
+	struct isdn_ppp_compressor *ipc = ipc_head;
+	int ret;
+	void *stat;
+	int num = data->num;
+
+	if(is->debug & 0x10)
+		printk(KERN_DEBUG "[%d] Set %s type %d\n",is->unit,
+			(data->flags&IPPP_COMP_FLAG_XMIT)?"compressor":"decompressor",num);
+
+	/* If is has no valid reset state vector, we cannot allocate a
+	   decompressor. The decompressor would cause reset transactions
+	   sooner or later, and they need that vector. */
+
+	if(!(data->flags & IPPP_COMP_FLAG_XMIT) && !is->reset) {
+		printk(KERN_ERR "ippp_ccp: no reset data structure - can't"
+		       " allow decompression.\n");
+		return -ENOMEM;
+	}
+
+	while(ipc) {
+		if(ipc->num == num) {
+			stat = ipc->alloc(data);
+			if(stat) {
+				ret = ipc->init(stat,data,is->unit,0);
+				if(!ret) {
+					printk(KERN_ERR "Can't init (de)compression!\n");
+					ipc->free(stat);
+					stat = NULL;
+					break;
+				}
+			}
+			else {
+				printk(KERN_ERR "Can't alloc (de)compression!\n");
+				break;
+			}
+
+                        if(data->flags & IPPP_COMP_FLAG_XMIT) {
+				if(data->flags & IPPP_COMP_FLAG_LINK) {
+					if(is->link_comp_stat)
+						is->link_compressor->free(is->link_comp_stat);
+					is->link_comp_stat = stat;
+                                	is->link_compressor = ipc;
+				}
+				else {
+					if(is->comp_stat)
+						is->compressor->free(is->comp_stat);
+					is->comp_stat = stat;
+                                	is->compressor = ipc;
+				}
+			}
+                        else {
+				if(data->flags & IPPP_COMP_FLAG_LINK) {
+					if(is->link_decomp_stat)
+						is->link_decompressor->free(is->link_decomp_stat);
+					is->link_decomp_stat = stat;
+        	                        is->link_decompressor = ipc;
+				}
+				else {
+					if(is->decomp_stat)
+						is->decompressor->free(is->decomp_stat);
+					is->decomp_stat = stat;
+        	                        is->decompressor = ipc;
+				}
+			}
+			return 0;
+		}
+		ipc = ipc->next;
+	}
+	return -EINVAL;
+}
diff -Nru a/drivers/isdn/i4l/isdn_ppp.h b/drivers/isdn/i4l/isdn_ppp.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/i4l/isdn_ppp.h	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,38 @@
+/* $Id: isdn_ppp.h,v 1.17.6.1 2001/09/23 22:24:32 kai Exp $
+ *
+ * header for Linux ISDN subsystem, functions for synchronous PPP (linklevel).
+ *
+ * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/ppp_defs.h>     /* for PPP_PROTOCOL */
+#include <linux/isdn_ppp.h>	/* for isdn_ppp info */
+
+extern struct file_operations isdn_ppp_fops;
+
+extern int isdn_ppp_init(void);
+extern void isdn_ppp_cleanup(void);
+extern int isdn_ppp_free(isdn_net_local *);
+extern int isdn_ppp_bind(isdn_net_local *);
+extern int isdn_ppp_xmit(struct sk_buff *, struct net_device *);
+extern void isdn_ppp_receive(isdn_net_dev *, isdn_net_local *, struct sk_buff *);
+extern int isdn_ppp_dev_ioctl(struct net_device *, struct ifreq *, int);
+extern int isdn_ppp_dial_slave(char *);
+extern void isdn_ppp_wakeup_daemon(isdn_net_local *);
+
+extern int isdn_ppp_register_compressor(struct isdn_ppp_compressor *ipc);
+extern int isdn_ppp_unregister_compressor(struct isdn_ppp_compressor *ipc);
+
+#define IPPP_OPEN	0x01
+#define IPPP_CONNECT	0x02
+#define IPPP_CLOSEWAIT	0x04
+#define IPPP_NOBLOCK	0x08
+#define IPPP_ASSIGNED	0x10
+
+#define IPPP_MAX_HEADER 10
+
+
diff -Nru a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/i4l/isdn_tty.c	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,4052 @@
+/* $Id: isdn_tty.c,v 1.94.6.9 2001/11/06 20:58:29 kai Exp $
+ *
+ * Linux ISDN subsystem, tty functions and AT-command emulator (linklevel).
+ *
+ * Copyright 1994-1999  by Fritz Elfert (fritz@isdn4linux.de)
+ * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+#undef ISDN_TTY_STAT_DEBUG
+
+#include <linux/config.h>
+#include <linux/isdn.h>
+#include "isdn_common.h"
+#include "isdn_tty.h"
+#ifdef CONFIG_ISDN_AUDIO
+#include "isdn_audio.h"
+#define VBUF 0x3e0
+#define VBUFX (VBUF/16)
+#endif
+
+#define FIX_FILE_TRANSFER
+#define	DUMMY_HAYES_AT
+
+/* Prototypes */
+
+static int isdn_tty_edit_at(const char *, int, modem_info *, int);
+static void isdn_tty_check_esc(const u_char *, u_char, int, int *, int *, int);
+static void isdn_tty_modem_reset_regs(modem_info *, int);
+static void isdn_tty_cmd_ATA(modem_info *);
+static void isdn_tty_flush_buffer(struct tty_struct *);
+static void isdn_tty_modem_result(int, modem_info *);
+#ifdef CONFIG_ISDN_AUDIO
+static int isdn_tty_countDLE(unsigned char *, int);
+#endif
+
+/* Leave this unchanged unless you know what you do! */
+#define MODEM_PARANOIA_CHECK
+#define MODEM_DO_RESTART
+
+#ifdef CONFIG_DEVFS_FS
+static char *isdn_ttyname_ttyI = "isdn/ttyI%d";
+static char *isdn_ttyname_cui = "isdn/cui%d";
+#else
+static char *isdn_ttyname_ttyI = "ttyI";
+static char *isdn_ttyname_cui = "cui";
+#endif
+
+static int bit2si[8] =
+{1, 5, 7, 7, 7, 7, 7, 7};
+static int si2bit[8] =
+{4, 1, 4, 4, 4, 4, 4, 4};
+
+char *isdn_tty_revision = "$Revision: 1.94.6.9 $";
+
+
+/* isdn_tty_try_read() is called from within isdn_tty_rcv_skb()
+ * to stuff incoming data directly into a tty's flip-buffer. This
+ * is done to speed up tty-receiving if the receive-queue is empty.
+ * This routine MUST be called with interrupts off.
+ * Return:
+ *  1 = Success
+ *  0 = Failure, data has to be buffered and later processed by
+ *      isdn_tty_readmodem().
+ */
+static int
+isdn_tty_try_read(modem_info * info, struct sk_buff *skb)
+{
+	int c;
+	int len;
+	struct tty_struct *tty;
+
+	if (info->online) {
+		if ((tty = info->tty)) {
+			if (info->mcr & UART_MCR_RTS) {
+				c = TTY_FLIPBUF_SIZE - tty->flip.count;
+				len = skb->len
+#ifdef CONFIG_ISDN_AUDIO
+					+ ISDN_AUDIO_SKB_DLECOUNT(skb)
+#endif
+					;
+				if (c >= len) {
+#ifdef CONFIG_ISDN_AUDIO
+					if (ISDN_AUDIO_SKB_DLECOUNT(skb))
+						while (skb->len--) {
+							if (*skb->data == DLE)
+								tty_insert_flip_char(tty, DLE, 0);
+							tty_insert_flip_char(tty, *skb->data++, 0);
+					} else {
+#endif
+						memcpy(tty->flip.char_buf_ptr,
+						       skb->data, len);
+						tty->flip.count += len;
+						tty->flip.char_buf_ptr += len;
+						memset(tty->flip.flag_buf_ptr, 0, len);
+						tty->flip.flag_buf_ptr += len;
+#ifdef CONFIG_ISDN_AUDIO
+					}
+#endif
+					if (info->emu.mdmreg[REG_CPPP] & BIT_CPPP)
+						tty->flip.flag_buf_ptr[len - 1] = 0xff;
+					queue_task(&tty->flip.tqueue, &tq_timer);
+					kfree_skb(skb);
+					return 1;
+				}
+			}
+		}
+	}
+	return 0;
+}
+
+/* isdn_tty_readmodem() is called periodically from within timer-interrupt.
+ * It tries getting received data from the receive queue an stuff it into
+ * the tty's flip-buffer.
+ */
+void
+isdn_tty_readmodem(void)
+{
+	int resched = 0;
+	int midx;
+	int i;
+	int c;
+	int r;
+	ulong flags;
+	struct tty_struct *tty;
+	modem_info *info;
+
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		if ((midx = dev->m_idx[i]) >= 0) {
+			info = &dev->mdm.info[midx];
+			if (info->online) {
+				r = 0;
+#ifdef CONFIG_ISDN_AUDIO
+				isdn_audio_eval_dtmf(info);
+				if ((info->vonline & 1) && (info->emu.vpar[1]))
+					isdn_audio_eval_silence(info);
+#endif
+				if ((tty = info->tty)) {
+					if (info->mcr & UART_MCR_RTS) {
+						c = TTY_FLIPBUF_SIZE - tty->flip.count;
+						if (c > 0) {
+							save_flags(flags);
+							cli();
+							r = isdn_readbchan(info->isdn_driver, info->isdn_channel,
+									   tty->flip.char_buf_ptr,
+									   tty->flip.flag_buf_ptr, c, 0);
+							/* CISCO AsyncPPP Hack */
+							if (!(info->emu.mdmreg[REG_CPPP] & BIT_CPPP))
+								memset(tty->flip.flag_buf_ptr, 0, r);
+							tty->flip.count += r;
+							tty->flip.flag_buf_ptr += r;
+							tty->flip.char_buf_ptr += r;
+							if (r)
+								queue_task(&tty->flip.tqueue, &tq_timer);
+							restore_flags(flags);
+						}
+					} else
+						r = 1;
+				} else
+					r = 1;
+				if (r) {
+					info->rcvsched = 0;
+					resched = 1;
+				} else
+					info->rcvsched = 1;
+			}
+		}
+	}
+	if (!resched)
+		isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 0);
+}
+
+int
+isdn_tty_rcv_skb(int i, int di, int channel, struct sk_buff *skb)
+{
+	ulong flags;
+	int midx;
+#ifdef CONFIG_ISDN_AUDIO
+	int ifmt;
+#endif
+	modem_info *info;
+
+	if ((midx = dev->m_idx[i]) < 0) {
+		/* if midx is invalid, packet is not for tty */
+		return 0;
+	}
+	info = &dev->mdm.info[midx];
+#ifdef CONFIG_ISDN_AUDIO
+	ifmt = 1;
+	
+	if ((info->vonline) && (!info->emu.vpar[4]))
+		isdn_audio_calc_dtmf(info, skb->data, skb->len, ifmt);
+	if ((info->vonline & 1) && (info->emu.vpar[1]))
+		isdn_audio_calc_silence(info, skb->data, skb->len, ifmt);
+#endif
+	if ((info->online < 2)
+#ifdef CONFIG_ISDN_AUDIO
+	    && (!(info->vonline & 1))
+#endif
+		) {
+		/* If Modem not listening, drop data */
+		kfree_skb(skb);
+		return 1;
+	}
+	if (info->emu.mdmreg[REG_T70] & BIT_T70) {
+		if (info->emu.mdmreg[REG_T70] & BIT_T70_EXT) {
+			/* T.70 decoding: throw away the T.70 header (2 or 4 bytes)   */
+			if (skb->data[0] == 3) /* pure data packet -> 4 byte headers  */
+				skb_pull(skb, 4);
+			else
+				if (skb->data[0] == 1) /* keepalive packet -> 2 byte hdr  */
+					skb_pull(skb, 2);
+		} else
+			/* T.70 decoding: Simply throw away the T.70 header (4 bytes) */
+			if ((skb->data[0] == 1) && ((skb->data[1] == 0) || (skb->data[1] == 1)))
+				skb_pull(skb, 4);
+	}
+#ifdef CONFIG_ISDN_AUDIO
+	if (skb_headroom(skb) < sizeof(isdn_audio_skb)) {
+		printk(KERN_WARNING
+		       "isdn_audio: insufficient skb_headroom, dropping\n");
+		kfree_skb(skb);
+		return 1;
+	}
+	ISDN_AUDIO_SKB_DLECOUNT(skb) = 0;
+	ISDN_AUDIO_SKB_LOCK(skb) = 0;
+	if (info->vonline & 1) {
+		/* voice conversion/compression */
+		switch (info->emu.vpar[3]) {
+			case 2:
+			case 3:
+			case 4:
+				/* adpcm
+				 * Since compressed data takes less
+				 * space, we can overwrite the buffer.
+				 */
+				skb_trim(skb, isdn_audio_xlaw2adpcm(info->adpcmr,
+								    ifmt,
+								    skb->data,
+								    skb->data,
+								    skb->len));
+				break;
+			case 5:
+				/* a-law */
+				if (!ifmt)
+					isdn_audio_ulaw2alaw(skb->data, skb->len);
+				break;
+			case 6:
+				/* u-law */
+				if (ifmt)
+					isdn_audio_alaw2ulaw(skb->data, skb->len);
+				break;
+		}
+		ISDN_AUDIO_SKB_DLECOUNT(skb) =
+			isdn_tty_countDLE(skb->data, skb->len);
+	}
+#ifdef CONFIG_ISDN_TTY_FAX
+	else {
+		if (info->faxonline & 2) {
+			isdn_tty_fax_bitorder(info, skb);
+			ISDN_AUDIO_SKB_DLECOUNT(skb) =
+				isdn_tty_countDLE(skb->data, skb->len);
+		}
+	}
+#endif
+#endif
+	/* Try to deliver directly via tty-flip-buf if queue is empty */
+	save_flags(flags);
+	cli();
+	if (skb_queue_empty(&dev->drv[di]->rpqueue[channel]))
+		if (isdn_tty_try_read(info, skb)) {
+			restore_flags(flags);
+			return 1;
+		}
+	/* Direct deliver failed or queue wasn't empty.
+	 * Queue up for later dequeueing via timer-irq.
+	 */
+	__skb_queue_tail(&dev->drv[di]->rpqueue[channel], skb);
+	dev->drv[di]->rcvcount[channel] +=
+		(skb->len
+#ifdef CONFIG_ISDN_AUDIO
+		 + ISDN_AUDIO_SKB_DLECOUNT(skb)
+#endif
+			);
+	restore_flags(flags);
+	/* Schedule dequeuing */
+	if ((dev->modempoll) && (info->rcvsched))
+		isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
+	return 1;
+}
+
+void
+isdn_tty_cleanup_xmit(modem_info * info)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	skb_queue_purge(&info->xmit_queue);
+#ifdef CONFIG_ISDN_AUDIO
+	skb_queue_purge(&info->dtmf_queue);
+#endif
+	restore_flags(flags);
+}
+
+static void
+isdn_tty_tint(modem_info * info)
+{
+	struct sk_buff *skb = skb_dequeue(&info->xmit_queue);
+	int len,
+	 slen;
+
+	if (!skb)
+		return;
+	len = skb->len;
+	if ((slen = isdn_writebuf_skb_stub(info->isdn_driver,
+					   info->isdn_channel, 1, skb)) == len) {
+		struct tty_struct *tty = info->tty;
+		info->send_outstanding++;
+		info->msr &= ~UART_MSR_CTS;
+		info->lsr &= ~UART_LSR_TEMT;
+		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+		    tty->ldisc.write_wakeup)
+			(tty->ldisc.write_wakeup) (tty);
+		wake_up_interruptible(&tty->write_wait);
+		return;
+	}
+	if (slen < 0) {
+		/* Error: no channel, already shutdown, or wrong parameter */
+		dev_kfree_skb(skb);
+		return;
+	}
+	skb_queue_head(&info->xmit_queue, skb);
+}
+
+#ifdef CONFIG_ISDN_AUDIO
+static int
+isdn_tty_countDLE(unsigned char *buf, int len)
+{
+	int count = 0;
+
+	while (len--)
+		if (*buf++ == DLE)
+			count++;
+	return count;
+}
+
+/* This routine is called from within isdn_tty_write() to perform
+ * DLE-decoding when sending audio-data.
+ */
+static int
+isdn_tty_handleDLEdown(modem_info * info, atemu * m, int len)
+{
+	unsigned char *p = &info->xmit_buf[info->xmit_count];
+	int count = 0;
+
+	while (len > 0) {
+		if (m->lastDLE) {
+			m->lastDLE = 0;
+			switch (*p) {
+				case DLE:
+					/* Escape code */
+					if (len > 1)
+						memmove(p, p + 1, len - 1);
+					p--;
+					count++;
+					break;
+				case ETX:
+					/* End of data */
+					info->vonline |= 4;
+					return count;
+				case DC4:
+					/* Abort RX */
+					info->vonline &= ~1;
+#ifdef ISDN_DEBUG_MODEM_VOICE
+					printk(KERN_DEBUG
+					       "DLEdown: got DLE-DC4, send DLE-ETX on ttyI%d\n",
+					       info->line);
+#endif
+					isdn_tty_at_cout("\020\003", info);
+					if (!info->vonline) {
+#ifdef ISDN_DEBUG_MODEM_VOICE
+						printk(KERN_DEBUG
+						       "DLEdown: send VCON on ttyI%d\n",
+						       info->line);
+#endif
+						isdn_tty_at_cout("\r\nVCON\r\n", info);
+					}
+					/* Fall through */
+				case 'q':
+				case 's':
+					/* Silence */
+					if (len > 1)
+						memmove(p, p + 1, len - 1);
+					p--;
+					break;
+			}
+		} else {
+			if (*p == DLE)
+				m->lastDLE = 1;
+			else
+				count++;
+		}
+		p++;
+		len--;
+	}
+	if (len < 0) {
+		printk(KERN_WARNING "isdn_tty: len<0 in DLEdown\n");
+		return 0;
+	}
+	return count;
+}
+
+/* This routine is called from within isdn_tty_write() when receiving
+ * audio-data. It interrupts receiving, if an character other than
+ * ^S or ^Q is sent.
+ */
+static int
+isdn_tty_end_vrx(const char *buf, int c, int from_user)
+{
+	char ch;
+
+	while (c--) {
+		if (from_user)
+			get_user(ch, buf);
+		else
+			ch = *buf;
+		if ((ch != 0x11) && (ch != 0x13))
+			return 1;
+		buf++;
+	}
+	return 0;
+}
+
+static int voice_cf[7] =
+{0, 0, 4, 3, 2, 0, 0};
+
+#endif                          /* CONFIG_ISDN_AUDIO */
+
+/* isdn_tty_senddown() is called either directly from within isdn_tty_write()
+ * or via timer-interrupt from within isdn_tty_modem_xmit(). It pulls
+ * outgoing data from the tty's xmit-buffer, handles voice-decompression or
+ * T.70 if necessary, and finally queues it up for sending via isdn_tty_tint.
+ */
+static void
+isdn_tty_senddown(modem_info * info)
+{
+	int buflen;
+	int skb_res;
+#ifdef CONFIG_ISDN_AUDIO
+	int audio_len;
+#endif
+	struct sk_buff *skb;
+
+#ifdef CONFIG_ISDN_AUDIO
+	if (info->vonline & 4) {
+		info->vonline &= ~6;
+		if (!info->vonline) {
+#ifdef ISDN_DEBUG_MODEM_VOICE
+			printk(KERN_DEBUG
+			       "senddown: send VCON on ttyI%d\n",
+			       info->line);
+#endif
+			isdn_tty_at_cout("\r\nVCON\r\n", info);
+		}
+	}
+#endif
+	if (!(buflen = info->xmit_count))
+		return;
+ 	if ((info->emu.mdmreg[REG_CTS] & BIT_CTS) != 0)
+		info->msr &= ~UART_MSR_CTS;
+	info->lsr &= ~UART_LSR_TEMT;	
+	/* info->xmit_count is modified here and in isdn_tty_write().
+	 * So we return here if isdn_tty_write() is in the
+	 * critical section.
+	 */
+	atomic_inc(&info->xmit_lock);
+	if (!(atomic_dec_and_test(&info->xmit_lock)))
+		return;
+	if (info->isdn_driver < 0) {
+		info->xmit_count = 0;
+		return;
+	}
+	skb_res = dev->drv[info->isdn_driver]->interface->hl_hdrlen + 4;
+#ifdef CONFIG_ISDN_AUDIO
+	if (info->vonline & 2)
+		audio_len = buflen * voice_cf[info->emu.vpar[3]];
+	else
+		audio_len = 0;
+	skb = dev_alloc_skb(skb_res + buflen + audio_len);
+#else
+	skb = dev_alloc_skb(skb_res + buflen);
+#endif
+	if (!skb) {
+		printk(KERN_WARNING
+		       "isdn_tty: Out of memory in ttyI%d senddown\n",
+		       info->line);
+		return;
+	}
+	skb_reserve(skb, skb_res);
+	memcpy(skb_put(skb, buflen), info->xmit_buf, buflen);
+	info->xmit_count = 0;
+#ifdef CONFIG_ISDN_AUDIO
+	if (info->vonline & 2) {
+		/* For now, ifmt is fixed to 1 (alaw), since this
+		 * is used with ISDN everywhere in the world, except
+		 * US, Canada and Japan.
+		 * Later, when US-ISDN protocols are implemented,
+		 * this setting will depend on the D-channel protocol.
+		 */
+		int ifmt = 1;
+
+		/* voice conversion/decompression */
+		switch (info->emu.vpar[3]) {
+			case 2:
+			case 3:
+			case 4:
+				/* adpcm, compatible to ZyXel 1496 modem
+				 * with ROM revision 6.01
+				 */
+				audio_len = isdn_audio_adpcm2xlaw(info->adpcms,
+								  ifmt,
+								  skb->data,
+						    skb_put(skb, audio_len),
+								  buflen);
+				skb_pull(skb, buflen);
+				skb_trim(skb, audio_len);
+				break;
+			case 5:
+				/* a-law */
+				if (!ifmt)
+					isdn_audio_alaw2ulaw(skb->data,
+							     buflen);
+				break;
+			case 6:
+				/* u-law */
+				if (ifmt)
+					isdn_audio_ulaw2alaw(skb->data,
+							     buflen);
+				break;
+		}
+	}
+#endif                          /* CONFIG_ISDN_AUDIO */
+	if (info->emu.mdmreg[REG_T70] & BIT_T70) {
+		/* Add T.70 simplified header */
+		if (info->emu.mdmreg[REG_T70] & BIT_T70_EXT)
+			memcpy(skb_push(skb, 2), "\1\0", 2);
+		else
+			memcpy(skb_push(skb, 4), "\1\0\1\0", 4);
+	}
+	skb_queue_tail(&info->xmit_queue, skb);
+}
+
+/************************************************************
+ *
+ * Modem-functions
+ *
+ * mostly "stolen" from original Linux-serial.c and friends.
+ *
+ ************************************************************/
+
+/* The next routine is called once from within timer-interrupt
+ * triggered within isdn_tty_modem_ncarrier(). It calls
+ * isdn_tty_modem_result() to stuff a "NO CARRIER" Message
+ * into the tty's flip-buffer.
+ */
+static void
+isdn_tty_modem_do_ncarrier(unsigned long data)
+{
+	modem_info *info = (modem_info *) data;
+	isdn_tty_modem_result(RESULT_NO_CARRIER, info);
+}
+
+/* Next routine is called, whenever the DTR-signal is raised.
+ * It checks the ncarrier-flag, and triggers the above routine
+ * when necessary. The ncarrier-flag is set, whenever DTR goes
+ * low.
+ */
+static void
+isdn_tty_modem_ncarrier(modem_info * info)
+{
+	if (info->ncarrier) {
+		info->nc_timer.expires = jiffies + HZ;
+		info->nc_timer.function = isdn_tty_modem_do_ncarrier;
+		info->nc_timer.data = (unsigned long) info;
+		add_timer(&info->nc_timer);
+	}
+}
+
+/*
+ * return the usage calculated by si and layer 2 protocol
+ */
+int
+isdn_calc_usage(int si, int l2)
+{
+	int usg = ISDN_USAGE_MODEM;
+
+#ifdef CONFIG_ISDN_AUDIO
+	if (si == 1) {
+		switch(l2) {
+			case ISDN_PROTO_L2_MODEM: 
+				usg = ISDN_USAGE_MODEM;
+				break;
+#ifdef CONFIG_ISDN_TTY_FAX
+			case ISDN_PROTO_L2_FAX: 
+				usg = ISDN_USAGE_FAX;
+				break;
+#endif
+			case ISDN_PROTO_L2_TRANS: 
+			default:
+				usg = ISDN_USAGE_VOICE;
+				break;
+		}
+	}
+#endif
+	return(usg);
+}
+
+/* isdn_tty_dial() performs dialing of a tty an the necessary
+ * setup of the lower levels before that.
+ */
+static void
+isdn_tty_dial(char *n, modem_info * info, atemu * m)
+{
+	int usg = ISDN_USAGE_MODEM;
+	int si = 7;
+	int l2 = m->mdmreg[REG_L2PROT];
+	isdn_ctrl cmd;
+	ulong flags;
+	int i;
+	int j;
+
+	for (j = 7; j >= 0; j--)
+		if (m->mdmreg[REG_SI1] & (1 << j)) {
+			si = bit2si[j];
+			break;
+		}
+	usg = isdn_calc_usage(si, l2);
+#ifdef CONFIG_ISDN_AUDIO
+	if ((si == 1) && 
+		(l2 != ISDN_PROTO_L2_MODEM)
+#ifdef CONFIG_ISDN_TTY_FAX
+		&& (l2 != ISDN_PROTO_L2_FAX)
+#endif
+		) {
+		l2 = ISDN_PROTO_L2_TRANS;
+		usg = ISDN_USAGE_VOICE;
+	}
+#endif
+	m->mdmreg[REG_SI1I] = si2bit[si];
+	save_flags(flags);
+	cli();
+	i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1, m->msn);
+	if (i < 0) {
+		restore_flags(flags);
+		isdn_tty_modem_result(RESULT_NO_DIALTONE, info);
+	} else {
+		info->isdn_driver = dev->drvmap[i];
+		info->isdn_channel = dev->chanmap[i];
+		info->drv_index = i;
+		dev->m_idx[i] = info->line;
+		dev->usage[i] |= ISDN_USAGE_OUTGOING;
+		info->last_dir = 1;
+		strcpy(info->last_num, n);
+		isdn_info_update();
+		restore_flags(flags);
+		cmd.driver = info->isdn_driver;
+		cmd.arg = info->isdn_channel;
+		cmd.command = ISDN_CMD_CLREAZ;
+		isdn_command(&cmd);
+		strcpy(cmd.parm.num, isdn_map_eaz2msn(m->msn, info->isdn_driver));
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETEAZ;
+		isdn_command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETL2;
+		info->last_l2 = l2;
+		cmd.arg = info->isdn_channel + (l2 << 8);
+		isdn_command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETL3;
+		cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8);
+#ifdef CONFIG_ISDN_TTY_FAX
+		if (l2 == ISDN_PROTO_L2_FAX) {
+			cmd.parm.fax = info->fax;
+			info->fax->direction = ISDN_TTY_FAX_CONN_OUT;
+		}
+#endif
+		isdn_command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.arg = info->isdn_channel;
+		sprintf(cmd.parm.setup.phone, "%s", n);
+		sprintf(cmd.parm.setup.eazmsn, "%s",
+			isdn_map_eaz2msn(m->msn, info->isdn_driver));
+		cmd.parm.setup.si1 = si;
+		cmd.parm.setup.si2 = m->mdmreg[REG_SI2];
+		cmd.command = ISDN_CMD_DIAL;
+		info->dialing = 1;
+		info->emu.carrierwait = 0;
+		strcpy(dev->num[i], n);
+		isdn_info_update();
+		isdn_command(&cmd);
+		isdn_timer_ctrl(ISDN_TIMER_CARRIER, 1);
+	}
+}
+
+/* isdn_tty_hangup() disassociates a tty from the real
+ * ISDN-line (hangup). The usage-status is cleared
+ * and some cleanup is done also.
+ */
+void
+isdn_tty_modem_hup(modem_info * info, int local)
+{
+	isdn_ctrl cmd;
+	int di, ch;
+
+	if (!info)
+		return;
+
+	di = info->isdn_driver;
+	ch = info->isdn_channel;
+	if (di < 0 || ch < 0)
+		return;
+
+	info->isdn_driver = -1;
+	info->isdn_channel = -1;
+
+#ifdef ISDN_DEBUG_MODEM_HUP
+	printk(KERN_DEBUG "Mhup ttyI%d\n", info->line);
+#endif
+	info->rcvsched = 0;
+	isdn_tty_flush_buffer(info->tty);
+	if (info->online) {
+		info->last_lhup = local;
+		info->online = 0;
+		isdn_tty_modem_result(RESULT_NO_CARRIER, info);
+	}
+#ifdef CONFIG_ISDN_AUDIO
+	info->vonline = 0;
+#ifdef CONFIG_ISDN_TTY_FAX
+	info->faxonline = 0;
+	info->fax->phase = ISDN_FAX_PHASE_IDLE;
+#endif
+	info->emu.vpar[4] = 0;
+	info->emu.vpar[5] = 8;
+	if (info->dtmf_state) {
+		kfree(info->dtmf_state);
+		info->dtmf_state = NULL;
+	}
+	if (info->silence_state) {
+		kfree(info->silence_state);
+		info->silence_state = NULL;
+	}
+	if (info->adpcms) {
+		kfree(info->adpcms);
+		info->adpcms = NULL;
+	}
+	if (info->adpcmr) {
+		kfree(info->adpcmr);
+		info->adpcmr = NULL;
+	}
+#endif
+	if ((info->msr & UART_MSR_RI) &&
+		(info->emu.mdmreg[REG_RUNG] & BIT_RUNG))
+		isdn_tty_modem_result(RESULT_RUNG, info);
+	info->msr &= ~(UART_MSR_DCD | UART_MSR_RI);
+	info->lsr |= UART_LSR_TEMT;
+
+	if (local) {
+		cmd.driver = di;
+		cmd.command = ISDN_CMD_HANGUP;
+		cmd.arg = ch;
+		isdn_command(&cmd);
+	}
+
+	isdn_all_eaz(di, ch);
+	info->emu.mdmreg[REG_RINGCNT] = 0;
+	isdn_free_channel(di, ch, 0);
+
+	if (info->drv_index >= 0) {
+		dev->m_idx[info->drv_index] = -1;
+		info->drv_index = -1;
+	}
+}
+
+/*
+ * Begin of a CAPI like interface, currently used only for 
+ * supplementary service (CAPI 2.0 part III)
+ */
+#include "avmb1/capicmd.h"  /* this should be moved in a common place */
+
+int
+isdn_tty_capi_facility(capi_msg *cm) {
+	return(-1); /* dummy */
+}
+
+/* isdn_tty_suspend() tries to suspend the current tty connection
+ */
+static void
+isdn_tty_suspend(char *id, modem_info * info, atemu * m)
+{
+	isdn_ctrl cmd;
+	
+	int l;
+
+	if (!info)
+		return;
+
+#ifdef ISDN_DEBUG_MODEM_SERVICES
+	printk(KERN_DEBUG "Msusp ttyI%d\n", info->line);
+#endif
+	l = strlen(id);
+	if ((info->isdn_driver >= 0)) {
+		cmd.parm.cmsg.Length = l+18;
+		cmd.parm.cmsg.Command = CAPI_FACILITY;
+		cmd.parm.cmsg.Subcommand = CAPI_REQ;
+		cmd.parm.cmsg.adr.Controller = info->isdn_driver + 1;
+		cmd.parm.cmsg.para[0] = 3; /* 16 bit 0x0003 suplementary service */
+		cmd.parm.cmsg.para[1] = 0;
+		cmd.parm.cmsg.para[2] = l + 3;
+		cmd.parm.cmsg.para[3] = 4; /* 16 bit 0x0004 Suspend */
+		cmd.parm.cmsg.para[4] = 0;
+		cmd.parm.cmsg.para[5] = l;
+		strncpy(&cmd.parm.cmsg.para[6], id, l);
+		cmd.command = CAPI_PUT_MESSAGE;
+		cmd.driver = info->isdn_driver;
+		cmd.arg = info->isdn_channel;
+		isdn_command(&cmd);
+	}
+}
+
+/* isdn_tty_resume() tries to resume a suspended call
+ * setup of the lower levels before that. unfortunatly here is no
+ * checking for compatibility of used protocols implemented by Q931
+ * It does the same things like isdn_tty_dial, the last command
+ * is different, may be we can merge it.
+ */
+
+static void
+isdn_tty_resume(char *id, modem_info * info, atemu * m)
+{
+	int usg = ISDN_USAGE_MODEM;
+	int si = 7;
+	int l2 = m->mdmreg[REG_L2PROT];
+	isdn_ctrl cmd;
+	ulong flags;
+	int i;
+	int j;
+	int l;
+
+	l = strlen(id);
+	for (j = 7; j >= 0; j--)
+		if (m->mdmreg[REG_SI1] & (1 << j)) {
+			si = bit2si[j];
+			break;
+		}
+	usg = isdn_calc_usage(si, l2);
+#ifdef CONFIG_ISDN_AUDIO
+	if ((si == 1) && 
+		(l2 != ISDN_PROTO_L2_MODEM)
+#ifdef CONFIG_ISDN_TTY_FAX
+		&& (l2 != ISDN_PROTO_L2_FAX)
+#endif
+		) {
+		l2 = ISDN_PROTO_L2_TRANS;
+		usg = ISDN_USAGE_VOICE;
+	}
+#endif
+	m->mdmreg[REG_SI1I] = si2bit[si];
+	save_flags(flags);
+	cli();
+	i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1, m->msn);
+	if (i < 0) {
+		restore_flags(flags);
+		isdn_tty_modem_result(RESULT_NO_DIALTONE, info);
+	} else {
+		info->isdn_driver = dev->drvmap[i];
+		info->isdn_channel = dev->chanmap[i];
+		info->drv_index = i;
+		dev->m_idx[i] = info->line;
+		dev->usage[i] |= ISDN_USAGE_OUTGOING;
+		info->last_dir = 1;
+//		strcpy(info->last_num, n);
+		isdn_info_update();
+		restore_flags(flags);
+		cmd.driver = info->isdn_driver;
+		cmd.arg = info->isdn_channel;
+		cmd.command = ISDN_CMD_CLREAZ;
+		isdn_command(&cmd);
+		strcpy(cmd.parm.num, isdn_map_eaz2msn(m->msn, info->isdn_driver));
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETEAZ;
+		isdn_command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETL2;
+		info->last_l2 = l2;
+		cmd.arg = info->isdn_channel + (l2 << 8);
+		isdn_command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETL3;
+		cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8);
+		isdn_command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.arg = info->isdn_channel;
+		cmd.parm.cmsg.Length = l+18;
+		cmd.parm.cmsg.Command = CAPI_FACILITY;
+		cmd.parm.cmsg.Subcommand = CAPI_REQ;
+		cmd.parm.cmsg.adr.Controller = info->isdn_driver + 1;
+		cmd.parm.cmsg.para[0] = 3; /* 16 bit 0x0003 suplementary service */
+		cmd.parm.cmsg.para[1] = 0;
+		cmd.parm.cmsg.para[2] = l+3;
+		cmd.parm.cmsg.para[3] = 5; /* 16 bit 0x0005 Resume */
+		cmd.parm.cmsg.para[4] = 0;
+		cmd.parm.cmsg.para[5] = l;
+		strncpy(&cmd.parm.cmsg.para[6], id, l);
+		cmd.command =CAPI_PUT_MESSAGE;
+		info->dialing = 1;
+//		strcpy(dev->num[i], n);
+		isdn_info_update();
+		isdn_command(&cmd);
+		isdn_timer_ctrl(ISDN_TIMER_CARRIER, 1);
+	}
+}
+
+/* isdn_tty_send_msg() sends a message to a HL driver
+ * This is used for hybrid modem cards to send AT commands to it
+ */
+
+static void
+isdn_tty_send_msg(modem_info * info, atemu * m, char *msg)
+{
+	int usg = ISDN_USAGE_MODEM;
+	int si = 7;
+	int l2 = m->mdmreg[REG_L2PROT];
+	isdn_ctrl cmd;
+	ulong flags;
+	int i;
+	int j;
+	int l;
+
+	l = strlen(msg);
+	if (!l) {
+		isdn_tty_modem_result(RESULT_ERROR, info);
+		return;
+	}
+	for (j = 7; j >= 0; j--)
+		if (m->mdmreg[REG_SI1] & (1 << j)) {
+			si = bit2si[j];
+			break;
+		}
+	usg = isdn_calc_usage(si, l2);
+#ifdef CONFIG_ISDN_AUDIO
+	if ((si == 1) && 
+		(l2 != ISDN_PROTO_L2_MODEM)
+#ifdef CONFIG_ISDN_TTY_FAX
+		&& (l2 != ISDN_PROTO_L2_FAX)
+#endif
+		) {
+		l2 = ISDN_PROTO_L2_TRANS;
+		usg = ISDN_USAGE_VOICE;
+	}
+#endif
+	m->mdmreg[REG_SI1I] = si2bit[si];
+	save_flags(flags);
+	cli();
+	i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1, m->msn);
+	if (i < 0) {
+		restore_flags(flags);
+		isdn_tty_modem_result(RESULT_NO_DIALTONE, info);
+	} else {
+		info->isdn_driver = dev->drvmap[i];
+		info->isdn_channel = dev->chanmap[i];
+		info->drv_index = i;
+		dev->m_idx[i] = info->line;
+		dev->usage[i] |= ISDN_USAGE_OUTGOING;
+		info->last_dir = 1;
+		isdn_info_update();
+		restore_flags(flags);
+		cmd.driver = info->isdn_driver;
+		cmd.arg = info->isdn_channel;
+		cmd.command = ISDN_CMD_CLREAZ;
+		isdn_command(&cmd);
+		strcpy(cmd.parm.num, isdn_map_eaz2msn(m->msn, info->isdn_driver));
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETEAZ;
+		isdn_command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETL2;
+		info->last_l2 = l2;
+		cmd.arg = info->isdn_channel + (l2 << 8);
+		isdn_command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETL3;
+		cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8);
+		isdn_command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.arg = info->isdn_channel;
+		cmd.parm.cmsg.Length = l+14;
+		cmd.parm.cmsg.Command = CAPI_MANUFACTURER;
+		cmd.parm.cmsg.Subcommand = CAPI_REQ;
+		cmd.parm.cmsg.adr.Controller = info->isdn_driver + 1;
+		cmd.parm.cmsg.para[0] = l+1;
+		strncpy(&cmd.parm.cmsg.para[1], msg, l);
+		cmd.parm.cmsg.para[l+1] = 0xd;
+		cmd.command =CAPI_PUT_MESSAGE;
+/*		info->dialing = 1;
+		strcpy(dev->num[i], n);
+		isdn_info_update();
+*/
+		isdn_command(&cmd);
+	}
+}
+
+static inline int
+isdn_tty_paranoia_check(modem_info * info, kdev_t device, const char *routine)
+{
+#ifdef MODEM_PARANOIA_CHECK
+	if (!info) {
+		printk(KERN_WARNING "isdn_tty: null info_struct for (%d, %d) in %s\n",
+		       major(device), minor(device), routine);
+		return 1;
+	}
+	if (info->magic != ISDN_ASYNC_MAGIC) {
+		printk(KERN_WARNING "isdn_tty: bad magic for modem struct (%d, %d) in %s\n",
+		       major(device), minor(device), routine);
+		return 1;
+	}
+#endif
+	return 0;
+}
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void
+isdn_tty_change_speed(modem_info * info)
+{
+	uint cflag,
+	 cval,
+	 fcr,
+	 quot;
+	int i;
+
+	if (!info->tty || !info->tty->termios)
+		return;
+	cflag = info->tty->termios->c_cflag;
+
+	quot = i = cflag & CBAUD;
+	if (i & CBAUDEX) {
+		i &= ~CBAUDEX;
+		if (i < 1 || i > 2)
+			info->tty->termios->c_cflag &= ~CBAUDEX;
+		else
+			i += 15;
+	}
+	if (quot) {
+		info->mcr |= UART_MCR_DTR;
+		isdn_tty_modem_ncarrier(info);
+	} else {
+		info->mcr &= ~UART_MCR_DTR;
+		if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) {
+#ifdef ISDN_DEBUG_MODEM_HUP
+			printk(KERN_DEBUG "Mhup in changespeed\n");
+#endif
+			if (info->online)
+				info->ncarrier = 1;
+			isdn_tty_modem_reset_regs(info, 0);
+			isdn_tty_modem_hup(info, 1);
+		}
+		return;
+	}
+	/* byte size and parity */
+	cval = cflag & (CSIZE | CSTOPB);
+	cval >>= 4;
+	if (cflag & PARENB)
+		cval |= UART_LCR_PARITY;
+	if (!(cflag & PARODD))
+		cval |= UART_LCR_EPAR;
+	fcr = 0;
+
+	/* CTS flow control flag and modem status interrupts */
+	if (cflag & CRTSCTS) {
+		info->flags |= ISDN_ASYNC_CTS_FLOW;
+	} else
+		info->flags &= ~ISDN_ASYNC_CTS_FLOW;
+	if (cflag & CLOCAL)
+		info->flags &= ~ISDN_ASYNC_CHECK_CD;
+	else {
+		info->flags |= ISDN_ASYNC_CHECK_CD;
+	}
+}
+
+static int
+isdn_tty_startup(modem_info * info)
+{
+	ulong flags;
+
+	if (info->flags & ISDN_ASYNC_INITIALIZED)
+		return 0;
+	save_flags(flags);
+	cli();
+	isdn_MOD_INC_USE_COUNT();
+#ifdef ISDN_DEBUG_MODEM_OPEN
+	printk(KERN_DEBUG "starting up ttyi%d ...\n", info->line);
+#endif
+	/*
+	 * Now, initialize the UART
+	 */
+	info->mcr = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
+	if (info->tty)
+		clear_bit(TTY_IO_ERROR, &info->tty->flags);
+	/*
+	 * and set the speed of the serial port
+	 */
+	isdn_tty_change_speed(info);
+
+	info->flags |= ISDN_ASYNC_INITIALIZED;
+	info->msr |= (UART_MSR_DSR | UART_MSR_CTS);
+	info->send_outstanding = 0;
+	restore_flags(flags);
+	return 0;
+}
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void
+isdn_tty_shutdown(modem_info * info)
+{
+	ulong flags;
+
+	if (!(info->flags & ISDN_ASYNC_INITIALIZED))
+		return;
+#ifdef ISDN_DEBUG_MODEM_OPEN
+	printk(KERN_DEBUG "Shutting down isdnmodem port %d ....\n", info->line);
+#endif
+	save_flags(flags);
+	cli();                  /* Disable interrupts */
+	isdn_MOD_DEC_USE_COUNT();
+	info->msr &= ~UART_MSR_RI;
+	if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
+		info->mcr &= ~(UART_MCR_DTR | UART_MCR_RTS);
+		if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) {
+			isdn_tty_modem_reset_regs(info, 0);
+#ifdef ISDN_DEBUG_MODEM_HUP
+			printk(KERN_DEBUG "Mhup in isdn_tty_shutdown\n");
+#endif
+			isdn_tty_modem_hup(info, 1);
+		}
+	}
+	if (info->tty)
+		set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+	info->flags &= ~ISDN_ASYNC_INITIALIZED;
+	restore_flags(flags);
+}
+
+/* isdn_tty_write() is the main send-routine. It is called from the upper
+ * levels within the kernel to perform sending data. Depending on the
+ * online-flag it either directs output to the at-command-interpreter or
+ * to the lower level. Additional tasks done here:
+ *  - If online, check for escape-sequence (+++)
+ *  - If sending audio-data, call isdn_tty_DLEdown() to parse DLE-codes.
+ *  - If receiving audio-data, call isdn_tty_end_vrx() to abort if needed.
+ *  - If dialing, abort dial.
+ */
+static int
+isdn_tty_write(struct tty_struct *tty, int from_user, const u_char * buf, int count)
+{
+	int c;
+	int total = 0;
+	modem_info *info = (modem_info *) tty->driver_data;
+	atemu *m = &info->emu;
+
+	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_write"))
+		return 0;
+	if (from_user)
+		down(&info->write_sem);
+	/* See isdn_tty_senddown() */
+	atomic_inc(&info->xmit_lock);
+	while (1) {
+		c = count;
+		if (c > info->xmit_size - info->xmit_count)
+			c = info->xmit_size - info->xmit_count;
+		if (info->isdn_driver >= 0 && c > dev->drv[info->isdn_driver]->maxbufsize)
+			c = dev->drv[info->isdn_driver]->maxbufsize;
+		if (c <= 0)
+			break;
+		if ((info->online > 1)
+#ifdef CONFIG_ISDN_AUDIO
+		    || (info->vonline & 3)
+#endif
+			) {
+#ifdef CONFIG_ISDN_AUDIO
+			if (!info->vonline)
+#endif
+				isdn_tty_check_esc(buf, m->mdmreg[REG_ESC], c,
+						   &(m->pluscount),
+						   &(m->lastplus),
+						   from_user);
+			if (from_user)
+				copy_from_user(&(info->xmit_buf[info->xmit_count]), buf, c);
+			else
+				memcpy(&(info->xmit_buf[info->xmit_count]), buf, c);
+#ifdef CONFIG_ISDN_AUDIO
+			if (info->vonline) {
+				int cc = isdn_tty_handleDLEdown(info, m, c);
+				if (info->vonline & 2) {
+					if (!cc) {
+						/* If DLE decoding results in zero-transmit, but
+						 * c originally was non-zero, do a wakeup.
+						 */
+						if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+						 tty->ldisc.write_wakeup)
+							(tty->ldisc.write_wakeup) (tty);
+						wake_up_interruptible(&tty->write_wait);
+						info->msr |= UART_MSR_CTS;
+						info->lsr |= UART_LSR_TEMT;
+					}
+					info->xmit_count += cc;
+				}
+				if ((info->vonline & 3) == 1) {
+					/* Do NOT handle Ctrl-Q or Ctrl-S
+					 * when in full-duplex audio mode.
+					 */
+					if (isdn_tty_end_vrx(buf, c, from_user)) {
+						info->vonline &= ~1;
+#ifdef ISDN_DEBUG_MODEM_VOICE
+						printk(KERN_DEBUG
+						       "got !^Q/^S, send DLE-ETX,VCON on ttyI%d\n",
+						       info->line);
+#endif
+						isdn_tty_at_cout("\020\003\r\nVCON\r\n", info);
+					}
+				}
+			} else
+			if (TTY_IS_FCLASS1(info)) {
+				int cc = isdn_tty_handleDLEdown(info, m, c);
+				
+				if (info->vonline & 4) { /* ETX seen */
+					isdn_ctrl c;
+
+					c.command = ISDN_CMD_FAXCMD;
+					c.driver = info->isdn_driver;
+					c.arg = info->isdn_channel;
+					c.parm.aux.cmd = ISDN_FAX_CLASS1_CTRL;
+					c.parm.aux.subcmd = ETX;
+					isdn_command(&c);
+				}
+				info->vonline = 0;
+#ifdef ISDN_DEBUG_MODEM_VOICE
+				printk(KERN_DEBUG "fax dle cc/c %d/%d\n", cc, c);
+#endif
+				info->xmit_count += cc;
+			} else
+#endif
+				info->xmit_count += c;
+		} else {
+			info->msr |= UART_MSR_CTS;
+			info->lsr |= UART_LSR_TEMT;
+			if (info->dialing) {
+				info->dialing = 0;
+#ifdef ISDN_DEBUG_MODEM_HUP
+				printk(KERN_DEBUG "Mhup in isdn_tty_write\n");
+#endif
+				isdn_tty_modem_result(RESULT_NO_CARRIER, info);
+				isdn_tty_modem_hup(info, 1);
+			} else
+				c = isdn_tty_edit_at(buf, c, info, from_user);
+		}
+		buf += c;
+		count -= c;
+		total += c;
+	}
+	atomic_dec(&info->xmit_lock);
+	if ((info->xmit_count) || (skb_queue_len(&info->xmit_queue))) {
+		if (m->mdmreg[REG_DXMT] & BIT_DXMT) {
+			isdn_tty_senddown(info);
+			isdn_tty_tint(info);
+		}
+		isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, 1);
+	}
+	if (from_user)
+		up(&info->write_sem);
+	return total;
+}
+
+static int
+isdn_tty_write_room(struct tty_struct *tty)
+{
+	modem_info *info = (modem_info *) tty->driver_data;
+	int ret;
+
+	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_write_room"))
+		return 0;
+	if (!info->online)
+		return info->xmit_size;
+	ret = info->xmit_size - info->xmit_count;
+	return (ret < 0) ? 0 : ret;
+}
+
+static int
+isdn_tty_chars_in_buffer(struct tty_struct *tty)
+{
+	modem_info *info = (modem_info *) tty->driver_data;
+
+	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_chars_in_buffer"))
+		return 0;
+	if (!info->online)
+		return 0;
+	return (info->xmit_count);
+}
+
+static void
+isdn_tty_flush_buffer(struct tty_struct *tty)
+{
+	modem_info *info;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	if (!tty) {
+		restore_flags(flags);
+		return;
+	}
+	info = (modem_info *) tty->driver_data;
+	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_flush_buffer")) {
+		restore_flags(flags);
+		return;
+	}
+	isdn_tty_cleanup_xmit(info);
+	info->xmit_count = 0;
+	restore_flags(flags);
+	wake_up_interruptible(&tty->write_wait);
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+	    tty->ldisc.write_wakeup)
+		(tty->ldisc.write_wakeup) (tty);
+}
+
+static void
+isdn_tty_flush_chars(struct tty_struct *tty)
+{
+	modem_info *info = (modem_info *) tty->driver_data;
+
+	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_flush_chars"))
+		return;
+	if ((info->xmit_count) || (skb_queue_len(&info->xmit_queue)))
+		isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, 1);
+}
+
+/*
+ * ------------------------------------------------------------
+ * isdn_tty_throttle()
+ *
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ * ------------------------------------------------------------
+ */
+static void
+isdn_tty_throttle(struct tty_struct *tty)
+{
+	modem_info *info = (modem_info *) tty->driver_data;
+
+	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_throttle"))
+		return;
+	if (I_IXOFF(tty))
+		info->x_char = STOP_CHAR(tty);
+	info->mcr &= ~UART_MCR_RTS;
+}
+
+static void
+isdn_tty_unthrottle(struct tty_struct *tty)
+{
+	modem_info *info = (modem_info *) tty->driver_data;
+
+	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_unthrottle"))
+		return;
+	if (I_IXOFF(tty)) {
+		if (info->x_char)
+			info->x_char = 0;
+		else
+			info->x_char = START_CHAR(tty);
+	}
+	info->mcr |= UART_MCR_RTS;
+}
+
+/*
+ * ------------------------------------------------------------
+ * isdn_tty_ioctl() and friends
+ * ------------------------------------------------------------
+ */
+
+/*
+ * isdn_tty_get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ *          is emptied.  On bus types like RS485, the transmitter must
+ *          release the bus after transmitting. This must be done when
+ *          the transmit shift register is empty, not be done when the
+ *          transmit holding register is empty.  This functionality
+ *          allows RS485 driver to be written in user space.
+ */
+static int
+isdn_tty_get_lsr_info(modem_info * info, uint * value)
+{
+	u_char status;
+	uint result;
+	ulong flags;
+
+	save_flags(flags);
+	cli();
+	status = info->lsr;
+	restore_flags(flags);
+	result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
+	return put_user(result, (uint *) value);
+}
+
+
+static int
+isdn_tty_get_modem_info(modem_info * info, uint * value)
+{
+	u_char control,
+	 status;
+	uint result;
+	ulong flags;
+
+	control = info->mcr;
+	save_flags(flags);
+	cli();
+	status = info->msr;
+	restore_flags(flags);
+	result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0)
+	    | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0)
+	    | ((status & UART_MSR_DCD) ? TIOCM_CAR : 0)
+	    | ((status & UART_MSR_RI) ? TIOCM_RNG : 0)
+	    | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0)
+	    | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0);
+	return put_user(result, (uint *) value);
+}
+
+static int
+isdn_tty_set_modem_info(modem_info * info, uint cmd, uint * value)
+{
+	uint arg;
+	int pre_dtr;
+
+	if (get_user(arg, (uint *) value))
+		return -EFAULT;
+	switch (cmd) {
+		case TIOCMBIS:
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+			printk(KERN_DEBUG "ttyI%d ioctl TIOCMBIS\n", info->line);
+#endif
+			if (arg & TIOCM_RTS) {
+				info->mcr |= UART_MCR_RTS;
+			}
+			if (arg & TIOCM_DTR) {
+				info->mcr |= UART_MCR_DTR;
+				isdn_tty_modem_ncarrier(info);
+			}
+			break;
+		case TIOCMBIC:
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+			printk(KERN_DEBUG "ttyI%d ioctl TIOCMBIC\n", info->line);
+#endif
+			if (arg & TIOCM_RTS) {
+				info->mcr &= ~UART_MCR_RTS;
+			}
+			if (arg & TIOCM_DTR) {
+				info->mcr &= ~UART_MCR_DTR;
+				if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) {
+					isdn_tty_modem_reset_regs(info, 0);
+#ifdef ISDN_DEBUG_MODEM_HUP
+					printk(KERN_DEBUG "Mhup in TIOCMBIC\n");
+#endif
+					if (info->online)
+						info->ncarrier = 1;
+					isdn_tty_modem_hup(info, 1);
+				}
+			}
+			break;
+		case TIOCMSET:
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+			printk(KERN_DEBUG "ttyI%d ioctl TIOCMSET\n", info->line);
+#endif
+			pre_dtr = (info->mcr & UART_MCR_DTR);
+			info->mcr = ((info->mcr & ~(UART_MCR_RTS | UART_MCR_DTR))
+				 | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
+			       | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
+			if (pre_dtr |= (info->mcr & UART_MCR_DTR)) {
+				if (!(info->mcr & UART_MCR_DTR)) {
+					if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) {
+						isdn_tty_modem_reset_regs(info, 0);
+#ifdef ISDN_DEBUG_MODEM_HUP
+						printk(KERN_DEBUG "Mhup in TIOCMSET\n");
+#endif
+						if (info->online)
+							info->ncarrier = 1;
+						isdn_tty_modem_hup(info, 1);
+					}
+				} else
+					isdn_tty_modem_ncarrier(info);
+			}
+			break;
+		default:
+			return -EINVAL;
+	}
+	return 0;
+}
+
+static int
+isdn_tty_ioctl(struct tty_struct *tty, struct file *file,
+	       uint cmd, ulong arg)
+{
+	modem_info *info = (modem_info *) tty->driver_data;
+	int retval;
+
+	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_ioctl"))
+		return -ENODEV;
+	if (tty->flags & (1 << TTY_IO_ERROR))
+		return -EIO;
+	switch (cmd) {
+		case TCSBRK:   /* SVID version: non-zero arg --> no break */
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+			printk(KERN_DEBUG "ttyI%d ioctl TCSBRK\n", info->line);
+#endif
+			retval = tty_check_change(tty);
+			if (retval)
+				return retval;
+			tty_wait_until_sent(tty, 0);
+			return 0;
+		case TCSBRKP:  /* support for POSIX tcsendbreak() */
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+			printk(KERN_DEBUG "ttyI%d ioctl TCSBRKP\n", info->line);
+#endif
+			retval = tty_check_change(tty);
+			if (retval)
+				return retval;
+			tty_wait_until_sent(tty, 0);
+			return 0;
+		case TIOCGSOFTCAR:
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+			printk(KERN_DEBUG "ttyI%d ioctl TIOCGSOFTCAR\n", info->line);
+#endif
+			return put_user(C_CLOCAL(tty) ? 1 : 0, (ulong *) arg);
+		case TIOCSSOFTCAR:
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+			printk(KERN_DEBUG "ttyI%d ioctl TIOCSSOFTCAR\n", info->line);
+#endif
+			if (get_user(arg, (ulong *) arg))
+				return -EFAULT;
+			tty->termios->c_cflag =
+			    ((tty->termios->c_cflag & ~CLOCAL) |
+			     (arg ? CLOCAL : 0));
+			return 0;
+		case TIOCMGET:
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+			printk(KERN_DEBUG "ttyI%d ioctl TIOCMGET\n", info->line);
+#endif
+			return isdn_tty_get_modem_info(info, (uint *) arg);
+		case TIOCMBIS:
+		case TIOCMBIC:
+		case TIOCMSET:
+			return isdn_tty_set_modem_info(info, cmd, (uint *) arg);
+		case TIOCSERGETLSR:	/* Get line status register */
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+			printk(KERN_DEBUG "ttyI%d ioctl TIOCSERGETLSR\n", info->line);
+#endif
+			return isdn_tty_get_lsr_info(info, (uint *) arg);
+		default:
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+			printk(KERN_DEBUG "UNKNOWN ioctl 0x%08x on ttyi%d\n", cmd, info->line);
+#endif
+			return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+static void
+isdn_tty_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+	modem_info *info = (modem_info *) tty->driver_data;
+
+	if (!old_termios)
+		isdn_tty_change_speed(info);
+	else {
+		if (tty->termios->c_cflag == old_termios->c_cflag)
+			return;
+		isdn_tty_change_speed(info);
+		if ((old_termios->c_cflag & CRTSCTS) &&
+		    !(tty->termios->c_cflag & CRTSCTS)) {
+			tty->hw_stopped = 0;
+		}
+	}
+}
+
+/*
+ * ------------------------------------------------------------
+ * isdn_tty_open() and friends
+ * ------------------------------------------------------------
+ */
+static int
+isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * info)
+{
+	DECLARE_WAITQUEUE(wait, NULL);
+	int do_clocal = 0;
+	unsigned long flags;
+	int retval;
+
+	/*
+	 * If the device is in the middle of being closed, then block
+	 * until it's done, and then try again.
+	 */
+	if (tty_hung_up_p(filp) ||
+	    (info->flags & ISDN_ASYNC_CLOSING)) {
+		if (info->flags & ISDN_ASYNC_CLOSING)
+			interruptible_sleep_on(&info->close_wait);
+#ifdef MODEM_DO_RESTART
+		if (info->flags & ISDN_ASYNC_HUP_NOTIFY)
+			return -EAGAIN;
+		else
+			return -ERESTARTSYS;
+#else
+		return -EAGAIN;
+#endif
+	}
+	/*
+	 * If this is a callout device, then just make sure the normal
+	 * device isn't being used.
+	 */
+	if (tty->driver.subtype == ISDN_SERIAL_TYPE_CALLOUT) {
+		if (info->flags & ISDN_ASYNC_NORMAL_ACTIVE)
+			return -EBUSY;
+		if ((info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) &&
+		    (info->flags & ISDN_ASYNC_SESSION_LOCKOUT) &&
+		    (info->session != current->session))
+			return -EBUSY;
+		if ((info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) &&
+		    (info->flags & ISDN_ASYNC_PGRP_LOCKOUT) &&
+		    (info->pgrp != current->pgrp))
+			return -EBUSY;
+		info->flags |= ISDN_ASYNC_CALLOUT_ACTIVE;
+		return 0;
+	}
+	/*
+	 * If non-blocking mode is set, then make the check up front
+	 * and then exit.
+	 */
+	if ((filp->f_flags & O_NONBLOCK) ||
+	    (tty->flags & (1 << TTY_IO_ERROR))) {
+		if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE)
+			return -EBUSY;
+		info->flags |= ISDN_ASYNC_NORMAL_ACTIVE;
+		return 0;
+	}
+	if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) {
+		if (info->normal_termios.c_cflag & CLOCAL)
+			do_clocal = 1;
+	} else {
+		if (tty->termios->c_cflag & CLOCAL)
+			do_clocal = 1;
+	}
+	/*
+	 * Block waiting for the carrier detect and the line to become
+	 * free (i.e., not in use by the callout).  While we are in
+	 * this loop, info->count is dropped by one, so that
+	 * isdn_tty_close() knows when to free things.  We restore it upon
+	 * exit, either normal or abnormal.
+	 */
+	retval = 0;
+	add_wait_queue(&info->open_wait, &wait);
+#ifdef ISDN_DEBUG_MODEM_OPEN
+	printk(KERN_DEBUG "isdn_tty_block_til_ready before block: ttyi%d, count = %d\n",
+	       info->line, info->count);
+#endif
+	save_flags(flags);
+	cli();
+	if (!(tty_hung_up_p(filp)))
+		info->count--;
+	restore_flags(flags);
+	info->blocked_open++;
+	while (1) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (tty_hung_up_p(filp) ||
+		    !(info->flags & ISDN_ASYNC_INITIALIZED)) {
+#ifdef MODEM_DO_RESTART
+			if (info->flags & ISDN_ASYNC_HUP_NOTIFY)
+				retval = -EAGAIN;
+			else
+				retval = -ERESTARTSYS;
+#else
+			retval = -EAGAIN;
+#endif
+			break;
+		}
+		if (!(info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) &&
+		    !(info->flags & ISDN_ASYNC_CLOSING) &&
+		    (do_clocal || (info->msr & UART_MSR_DCD))) {
+			break;
+		}
+		if (signal_pending(current)) {
+			retval = -ERESTARTSYS;
+			break;
+		}
+#ifdef ISDN_DEBUG_MODEM_OPEN
+		printk(KERN_DEBUG "isdn_tty_block_til_ready blocking: ttyi%d, count = %d\n",
+		       info->line, info->count);
+#endif
+		schedule();
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&info->open_wait, &wait);
+	if (!tty_hung_up_p(filp))
+		info->count++;
+	info->blocked_open--;
+#ifdef ISDN_DEBUG_MODEM_OPEN
+	printk(KERN_DEBUG "isdn_tty_block_til_ready after blocking: ttyi%d, count = %d\n",
+	       info->line, info->count);
+#endif
+	if (retval)
+		return retval;
+	info->flags |= ISDN_ASYNC_NORMAL_ACTIVE;
+	return 0;
+}
+
+/*
+ * This routine is called whenever a serial port is opened.  It
+ * enables interrupts for a serial port, linking in its async structure into
+ * the IRQ chain.   It also performs the serial-specific
+ * initialization for the tty structure.
+ */
+static int
+isdn_tty_open(struct tty_struct *tty, struct file *filp)
+{
+	modem_info *info;
+	int retval,
+	 line;
+
+	line = minor(tty->device) - tty->driver.minor_start;
+	if (line < 0 || line > ISDN_MAX_CHANNELS)
+		return -ENODEV;
+	info = &dev->mdm.info[line];
+	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_open"))
+		return -ENODEV;
+#ifdef ISDN_DEBUG_MODEM_OPEN
+	printk(KERN_DEBUG "isdn_tty_open %s%d, count = %d\n", tty->driver.name,
+	       info->line, info->count);
+#endif
+	info->count++;
+	tty->driver_data = info;
+	info->tty = tty;
+	/*
+	 * Start up serial port
+	 */
+	retval = isdn_tty_startup(info);
+	if (retval) {
+#ifdef ISDN_DEBUG_MODEM_OPEN
+		printk(KERN_DEBUG "isdn_tty_open return after startup\n");
+#endif
+		return retval;
+	}
+	retval = isdn_tty_block_til_ready(tty, filp, info);
+	if (retval) {
+#ifdef ISDN_DEBUG_MODEM_OPEN
+		printk(KERN_DEBUG "isdn_tty_open return after isdn_tty_block_til_ready \n");
+#endif
+		return retval;
+	}
+	if ((info->count == 1) && (info->flags & ISDN_ASYNC_SPLIT_TERMIOS)) {
+		if (tty->driver.subtype == ISDN_SERIAL_TYPE_NORMAL)
+			*tty->termios = info->normal_termios;
+		else
+			*tty->termios = info->callout_termios;
+		isdn_tty_change_speed(info);
+	}
+	info->session = current->session;
+	info->pgrp = current->pgrp;
+#ifdef ISDN_DEBUG_MODEM_OPEN
+	printk(KERN_DEBUG "isdn_tty_open ttyi%d successful...\n", info->line);
+#endif
+	dev->modempoll++;
+#ifdef ISDN_DEBUG_MODEM_OPEN
+	printk(KERN_DEBUG "isdn_tty_open normal exit\n");
+#endif
+	return 0;
+}
+
+static void
+isdn_tty_close(struct tty_struct *tty, struct file *filp)
+{
+	modem_info *info = (modem_info *) tty->driver_data;
+	ulong flags;
+	ulong timeout;
+
+	if (!info || isdn_tty_paranoia_check(info, tty->device, "isdn_tty_close"))
+		return;
+	save_flags(flags);
+	cli();
+	if (tty_hung_up_p(filp)) {
+		restore_flags(flags);
+#ifdef ISDN_DEBUG_MODEM_OPEN
+		printk(KERN_DEBUG "isdn_tty_close return after tty_hung_up_p\n");
+#endif
+		return;
+	}
+	if ((tty->count == 1) && (info->count != 1)) {
+		/*
+		 * Uh, oh.  tty->count is 1, which means that the tty
+		 * structure will be freed.  Info->count should always
+		 * be one in these conditions.  If it's greater than
+		 * one, we've got real problems, since it means the
+		 * serial port won't be shutdown.
+		 */
+		printk(KERN_ERR "isdn_tty_close: bad port count; tty->count is 1, "
+		       "info->count is %d\n", info->count);
+		info->count = 1;
+	}
+	if (--info->count < 0) {
+		printk(KERN_ERR "isdn_tty_close: bad port count for ttyi%d: %d\n",
+		       info->line, info->count);
+		info->count = 0;
+	}
+	if (info->count) {
+		restore_flags(flags);
+#ifdef ISDN_DEBUG_MODEM_OPEN
+		printk(KERN_DEBUG "isdn_tty_close after info->count != 0\n");
+#endif
+		return;
+	}
+	info->flags |= ISDN_ASYNC_CLOSING;
+	/*
+	 * Save the termios structure, since this port may have
+	 * separate termios for callout and dialin.
+	 */
+	if (info->flags & ISDN_ASYNC_NORMAL_ACTIVE)
+		info->normal_termios = *tty->termios;
+	if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE)
+		info->callout_termios = *tty->termios;
+
+	tty->closing = 1;
+	/*
+	 * At this point we stop accepting input.  To do this, we
+	 * disable the receive line status interrupts, and tell the
+	 * interrupt driver to stop checking the data ready bit in the
+	 * line status register.
+	 */
+	if (info->flags & ISDN_ASYNC_INITIALIZED) {
+		tty_wait_until_sent(tty, 3000);	/* 30 seconds timeout */
+		/*
+		 * Before we drop DTR, make sure the UART transmitter
+		 * has completely drained; this is especially
+		 * important if there is a transmit FIFO!
+		 */
+		timeout = jiffies + HZ;
+		while (!(info->lsr & UART_LSR_TEMT)) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule_timeout(20);
+			if (time_after(jiffies,timeout))
+				break;
+		}
+	}
+	dev->modempoll--;
+	isdn_tty_shutdown(info);
+	if (tty->driver.flush_buffer)
+		tty->driver.flush_buffer(tty);
+	if (tty->ldisc.flush_buffer)
+		tty->ldisc.flush_buffer(tty);
+	info->tty = 0;
+	info->ncarrier = 0;
+	tty->closing = 0;
+	if (info->blocked_open) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(50);
+		wake_up_interruptible(&info->open_wait);
+	}
+	info->flags &= ~(ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE |
+			 ISDN_ASYNC_CLOSING);
+	wake_up_interruptible(&info->close_wait);
+	restore_flags(flags);
+#ifdef ISDN_DEBUG_MODEM_OPEN
+	printk(KERN_DEBUG "isdn_tty_close normal exit\n");
+#endif
+}
+
+/*
+ * isdn_tty_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+static void
+isdn_tty_hangup(struct tty_struct *tty)
+{
+	modem_info *info = (modem_info *) tty->driver_data;
+
+	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_hangup"))
+		return;
+	isdn_tty_shutdown(info);
+	info->count = 0;
+	info->flags &= ~(ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE);
+	info->tty = 0;
+	wake_up_interruptible(&info->open_wait);
+}
+
+/* This routine initializes all emulator-data.
+ */
+static void
+isdn_tty_reset_profile(atemu * m)
+{
+	m->profile[0] = 0;
+	m->profile[1] = 0;
+	m->profile[2] = 43;
+	m->profile[3] = 13;
+	m->profile[4] = 10;
+	m->profile[5] = 8;
+	m->profile[6] = 3;
+	m->profile[7] = 60;
+	m->profile[8] = 2;
+	m->profile[9] = 6;
+	m->profile[10] = 7;
+	m->profile[11] = 70;
+	m->profile[12] = 0x45;
+	m->profile[13] = 4;
+	m->profile[14] = ISDN_PROTO_L2_X75I;
+	m->profile[15] = ISDN_PROTO_L3_TRANS;
+	m->profile[16] = ISDN_SERIAL_XMIT_SIZE / 16;
+	m->profile[17] = ISDN_MODEM_WINSIZE;
+	m->profile[18] = 4;
+	m->profile[19] = 0;
+	m->profile[20] = 0;
+	m->profile[23] = 0;
+	m->pmsn[0] = '\0';
+	m->plmsn[0] = '\0';
+}
+
+#ifdef CONFIG_ISDN_AUDIO
+static void
+isdn_tty_modem_reset_vpar(atemu * m)
+{
+	m->vpar[0] = 2;         /* Voice-device            (2 = phone line) */
+	m->vpar[1] = 0;         /* Silence detection level (0 = none      ) */
+	m->vpar[2] = 70;        /* Silence interval        (7 sec.        ) */
+	m->vpar[3] = 2;         /* Compression type        (1 = ADPCM-2   ) */
+	m->vpar[4] = 0;         /* DTMF detection level    (0 = softcode  ) */
+	m->vpar[5] = 8;         /* DTMF interval           (8 * 5 ms.     ) */
+}
+#endif
+
+#ifdef CONFIG_ISDN_TTY_FAX
+static void
+isdn_tty_modem_reset_faxpar(modem_info * info)
+{
+	T30_s *f = info->fax;
+
+	f->code = 0;
+	f->phase = ISDN_FAX_PHASE_IDLE;
+	f->direction = 0;
+	f->resolution = 1;	/* fine */
+	f->rate = 5;		/* 14400 bit/s */
+	f->width = 0;
+	f->length = 0;
+	f->compression = 0;
+	f->ecm = 0;
+	f->binary = 0;
+	f->scantime = 0;
+	memset(&f->id[0], 32, FAXIDLEN - 1);
+	f->id[FAXIDLEN - 1] = 0;
+	f->badlin = 0;
+	f->badmul = 0;
+	f->bor = 0;
+	f->nbc = 0;
+	f->cq = 0;
+	f->cr = 0;
+	f->ctcrty = 0;
+	f->minsp = 0;
+	f->phcto = 30;
+	f->rel = 0;
+	memset(&f->pollid[0], 32, FAXIDLEN - 1);
+	f->pollid[FAXIDLEN - 1] = 0;
+}
+#endif
+
+static void
+isdn_tty_modem_reset_regs(modem_info * info, int force)
+{
+	atemu *m = &info->emu;
+	if ((m->mdmreg[REG_DTRR] & BIT_DTRR) || force) {
+		memcpy(m->mdmreg, m->profile, ISDN_MODEM_NUMREG);
+		memcpy(m->msn, m->pmsn, ISDN_MSNLEN);
+		memcpy(m->lmsn, m->plmsn, ISDN_LMSNLEN);
+		info->xmit_size = m->mdmreg[REG_PSIZE] * 16;
+	}
+#ifdef CONFIG_ISDN_AUDIO
+	isdn_tty_modem_reset_vpar(m);
+#endif
+#ifdef CONFIG_ISDN_TTY_FAX
+	isdn_tty_modem_reset_faxpar(info);
+#endif
+	m->mdmcmdl = 0;
+}
+
+static void
+modem_write_profile(atemu * m)
+{
+	memcpy(m->profile, m->mdmreg, ISDN_MODEM_NUMREG);
+	memcpy(m->pmsn, m->msn, ISDN_MSNLEN);
+	memcpy(m->plmsn, m->lmsn, ISDN_LMSNLEN);
+	if (dev->profd)
+		send_sig(SIGIO, dev->profd, 1);
+}
+
+int
+isdn_tty_modem_init(void)
+{
+	modem *m;
+	int i;
+	modem_info *info;
+
+	m = &dev->mdm;
+	memset(&m->tty_modem, 0, sizeof(struct tty_driver));
+	m->tty_modem.magic = TTY_DRIVER_MAGIC;
+	m->tty_modem.name = isdn_ttyname_ttyI;
+	m->tty_modem.major = ISDN_TTY_MAJOR;
+	m->tty_modem.minor_start = 0;
+	m->tty_modem.num = ISDN_MAX_CHANNELS;
+	m->tty_modem.type = TTY_DRIVER_TYPE_SERIAL;
+	m->tty_modem.subtype = ISDN_SERIAL_TYPE_NORMAL;
+	m->tty_modem.init_termios = tty_std_termios;
+	m->tty_modem.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+	m->tty_modem.flags = TTY_DRIVER_REAL_RAW;
+	m->tty_modem.refcount = &m->refcount;
+	m->tty_modem.table = m->modem_table;
+	m->tty_modem.termios = m->modem_termios;
+	m->tty_modem.termios_locked = m->modem_termios_locked;
+	m->tty_modem.open = isdn_tty_open;
+	m->tty_modem.close = isdn_tty_close;
+	m->tty_modem.write = isdn_tty_write;
+	m->tty_modem.put_char = NULL;
+	m->tty_modem.flush_chars = isdn_tty_flush_chars;
+	m->tty_modem.write_room = isdn_tty_write_room;
+	m->tty_modem.chars_in_buffer = isdn_tty_chars_in_buffer;
+	m->tty_modem.flush_buffer = isdn_tty_flush_buffer;
+	m->tty_modem.ioctl = isdn_tty_ioctl;
+	m->tty_modem.throttle = isdn_tty_throttle;
+	m->tty_modem.unthrottle = isdn_tty_unthrottle;
+	m->tty_modem.set_termios = isdn_tty_set_termios;
+	m->tty_modem.stop = NULL;
+	m->tty_modem.start = NULL;
+	m->tty_modem.hangup = isdn_tty_hangup;
+	m->tty_modem.driver_name = "isdn_tty";
+	/*
+	 * The callout device is just like normal device except for
+	 * major number and the subtype code.
+	 */
+	m->cua_modem = m->tty_modem;
+	m->cua_modem.name = isdn_ttyname_cui;
+	m->cua_modem.major = ISDN_TTYAUX_MAJOR;
+	m->tty_modem.minor_start = 0;
+	m->cua_modem.subtype = ISDN_SERIAL_TYPE_CALLOUT;
+
+	if (tty_register_driver(&m->tty_modem)) {
+		printk(KERN_WARNING "isdn_tty: Couldn't register modem-device\n");
+		return -1;
+	}
+	if (tty_register_driver(&m->cua_modem)) {
+		printk(KERN_WARNING "isdn_tty: Couldn't register modem-callout-device\n");
+		return -2;
+	}
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		info = &m->info[i];
+#ifdef CONFIG_ISDN_TTY_FAX
+		if (!(info->fax = kmalloc(sizeof(T30_s), GFP_KERNEL))) {
+			printk(KERN_ERR "Could not allocate fax t30-buffer\n");
+			return -3;
+		}
+#endif
+		init_MUTEX(&info->write_sem);
+		sprintf(info->last_cause, "0000");
+		sprintf(info->last_num, "none");
+		info->last_dir = 0;
+		info->last_lhup = 1;
+		info->last_l2 = -1;
+		info->last_si = 0;
+		isdn_tty_reset_profile(&info->emu);
+		isdn_tty_modem_reset_regs(info, 1);
+		info->magic = ISDN_ASYNC_MAGIC;
+		info->line = i;
+		info->tty = 0;
+		info->x_char = 0;
+		info->count = 0;
+		info->blocked_open = 0;
+		info->callout_termios = m->cua_modem.init_termios;
+		info->normal_termios = m->tty_modem.init_termios;
+		init_waitqueue_head(&info->open_wait);
+		init_waitqueue_head(&info->close_wait);
+		info->isdn_driver = -1;
+		info->isdn_channel = -1;
+		info->drv_index = -1;
+		info->xmit_size = ISDN_SERIAL_XMIT_SIZE;
+		skb_queue_head_init(&info->xmit_queue);
+#ifdef CONFIG_ISDN_AUDIO
+		skb_queue_head_init(&info->dtmf_queue);
+#endif
+		if (!(info->xmit_buf = kmalloc(ISDN_SERIAL_XMIT_MAX + 5, GFP_KERNEL))) {
+			printk(KERN_ERR "Could not allocate modem xmit-buffer\n");
+			return -3;
+		}
+		/* Make room for T.70 header */
+		info->xmit_buf += 4;
+	}
+	return 0;
+}
+
+
+/*
+ * isdn_tty_match_icall(char *MSN, atemu *tty_emulator, int dev_idx)
+ *      match the MSN against the MSNs (glob patterns) defined for tty_emulator,
+ *      and return 0 for match, 1 for no match, 2 if MSN could match if longer.
+ */
+
+static int
+isdn_tty_match_icall(char *cid, atemu *emu, int di)
+{
+#ifdef ISDN_DEBUG_MODEM_ICALL
+	printk(KERN_DEBUG "m_fi: msn=%s lmsn=%s mmsn=%s mreg[SI1]=%d mreg[SI2]=%d\n",
+	       emu->msn, emu->lmsn, isdn_map_eaz2msn(emu->msn, di),
+	       emu->mdmreg[REG_SI1], emu->mdmreg[REG_SI2]);
+#endif
+	if (strlen(emu->lmsn)) {
+		char *p = emu->lmsn;
+		char *q;
+		int  tmp;
+		int  ret = 0;
+
+		while (1) {
+			if ((q = strchr(p, ';')))
+				*q = '\0';
+			if ((tmp = isdn_msncmp(cid, isdn_map_eaz2msn(p, di))) > ret)
+				ret = tmp;
+#ifdef ISDN_DEBUG_MODEM_ICALL
+			printk(KERN_DEBUG "m_fi: lmsnX=%s mmsn=%s -> tmp=%d\n",
+			       p, isdn_map_eaz2msn(emu->msn, di), tmp);
+#endif
+			if (q) {
+				*q = ';';
+				p = q;
+				p++;
+			}
+			if (!tmp)
+				return 0;
+			if (!q)
+				break;
+		}
+		return ret;
+	} else {
+		int tmp;
+		tmp = isdn_msncmp(cid, isdn_map_eaz2msn(emu->msn, di));
+#ifdef ISDN_DEBUG_MODEM_ICALL
+			printk(KERN_DEBUG "m_fi: mmsn=%s -> tmp=%d\n",
+			       isdn_map_eaz2msn(emu->msn, di), tmp);
+#endif
+		return tmp;
+	}
+}
+
+/*
+ * An incoming call-request has arrived.
+ * Search the tty-devices for an appropriate device and bind
+ * it to the ISDN-Channel.
+ * Return:
+ *
+ *  0 = No matching device found.
+ *  1 = A matching device found.
+ *  3 = No match found, but eventually would match, if
+ *      CID is longer.
+ */
+int
+isdn_tty_find_icall(int di, int ch, setup_parm *setup)
+{
+	char *eaz;
+	int i;
+	int wret;
+	int idx;
+	int si1;
+	int si2;
+	char *nr;
+	ulong flags;
+
+	if (!setup->phone[0]) {
+		nr = "0";
+		printk(KERN_INFO "isdn_tty: Incoming call without OAD, assuming '0'\n");
+	} else
+		nr = setup->phone;
+	si1 = (int) setup->si1;
+	si2 = (int) setup->si2;
+	if (!setup->eazmsn[0]) {
+		printk(KERN_WARNING "isdn_tty: Incoming call without CPN, assuming '0'\n");
+		eaz = "0";
+	} else
+		eaz = setup->eazmsn;
+#ifdef ISDN_DEBUG_MODEM_ICALL
+	printk(KERN_DEBUG "m_fi: eaz=%s si1=%d si2=%d\n", eaz, si1, si2);
+#endif
+	wret = 0;
+	save_flags(flags);
+	cli();
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		modem_info *info = &dev->mdm.info[i];
+
+                if (info->count == 0)
+                    continue;
+		if ((info->emu.mdmreg[REG_SI1] & si2bit[si1]) &&  /* SI1 is matching */
+		    (info->emu.mdmreg[REG_SI2] == si2))	{         /* SI2 is matching */
+			idx = isdn_dc2minor(di, ch);
+#ifdef ISDN_DEBUG_MODEM_ICALL
+			printk(KERN_DEBUG "m_fi: match1 wret=%d\n", wret);
+			printk(KERN_DEBUG "m_fi: idx=%d flags=%08lx drv=%d ch=%d usg=%d\n", idx,
+			       info->flags, info->isdn_driver, info->isdn_channel,
+			       dev->usage[idx]);
+#endif
+			if (
+#ifndef FIX_FILE_TRANSFER
+				(info->flags & ISDN_ASYNC_NORMAL_ACTIVE) &&
+#endif
+				(info->isdn_driver == -1) &&
+				(info->isdn_channel == -1) &&
+				(USG_NONE(dev->usage[idx]))) {
+				int matchret;
+
+				if ((matchret = isdn_tty_match_icall(eaz, &info->emu, di)) > wret)
+					wret = matchret;
+				if (!matchret) {                  /* EAZ is matching */
+					info->isdn_driver = di;
+					info->isdn_channel = ch;
+					info->drv_index = idx;
+					dev->m_idx[idx] = info->line;
+					dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE;
+					dev->usage[idx] |= isdn_calc_usage(si1, info->emu.mdmreg[REG_L2PROT]); 
+					strcpy(dev->num[idx], nr);
+					strcpy(info->emu.cpn, eaz);
+					info->emu.mdmreg[REG_SI1I] = si2bit[si1];
+					info->emu.mdmreg[REG_PLAN] = setup->plan;
+					info->emu.mdmreg[REG_SCREEN] = setup->screen;
+					isdn_info_update();
+					restore_flags(flags);
+					printk(KERN_INFO "isdn_tty: call from %s, -> RING on ttyI%d\n", nr,
+					       info->line);
+					info->msr |= UART_MSR_RI;
+					isdn_tty_modem_result(RESULT_RING, info);
+					isdn_timer_ctrl(ISDN_TIMER_MODEMRING, 1);
+					return 1;
+				}
+			}
+		}
+	}
+	restore_flags(flags);
+	printk(KERN_INFO "isdn_tty: call from %s -> %s %s\n", nr, eaz,
+	       ((dev->drv[di]->flags & DRV_FLAG_REJBUS) && (wret != 2))? "rejected" : "ignored");
+	return (wret == 2)?3:0;
+}
+
+#define TTY_IS_ACTIVE(info) \
+	(info->flags & (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE))
+
+int
+isdn_tty_stat_callback(int i, isdn_ctrl *c)
+{
+	int mi;
+	modem_info *info;
+	char *e;
+
+	if (i < 0)
+		return 0;
+	if ((mi = dev->m_idx[i]) >= 0) {
+		info = &dev->mdm.info[mi];
+		switch (c->command) {
+                        case ISDN_STAT_CINF:
+                                printk(KERN_DEBUG "CHARGEINFO on ttyI%d: %ld %s\n", info->line, c->arg, c->parm.num);
+                                info->emu.charge = (unsigned) simple_strtoul(c->parm.num, &e, 10);
+                                if (e == (char *)c->parm.num)
+					info->emu.charge = 0;
+				
+                                break;			
+			case ISDN_STAT_BSENT:
+#ifdef ISDN_TTY_STAT_DEBUG
+				printk(KERN_DEBUG "tty_STAT_BSENT ttyI%d\n", info->line);
+#endif
+				if ((info->isdn_driver == c->driver) &&
+				    (info->isdn_channel == c->arg)) {
+					info->msr |= UART_MSR_CTS;
+					if (info->send_outstanding)
+						if (!(--info->send_outstanding))
+							info->lsr |= UART_LSR_TEMT;
+					isdn_tty_tint(info);
+					return 1;
+				}
+				break;
+			case ISDN_STAT_CAUSE:
+#ifdef ISDN_TTY_STAT_DEBUG
+				printk(KERN_DEBUG "tty_STAT_CAUSE ttyI%d\n", info->line);
+#endif
+				/* Signal cause to tty-device */
+				strncpy(info->last_cause, c->parm.num, 5);
+				return 1;
+			case ISDN_STAT_DISPLAY:
+#ifdef ISDN_TTY_STAT_DEBUG
+				printk(KERN_DEBUG "tty_STAT_DISPLAY ttyI%d\n", info->line);
+#endif
+				/* Signal display to tty-device */
+				if ((info->emu.mdmreg[REG_DISPLAY] & BIT_DISPLAY) && 
+					!(info->emu.mdmreg[REG_RESPNUM] & BIT_RESPNUM)) {
+				  isdn_tty_at_cout("\r\n", info);
+				  isdn_tty_at_cout("DISPLAY: ", info);
+				  isdn_tty_at_cout(c->parm.display, info);
+				  isdn_tty_at_cout("\r\n", info);
+				}
+				return 1;
+			case ISDN_STAT_DCONN:
+#ifdef ISDN_TTY_STAT_DEBUG
+				printk(KERN_DEBUG "tty_STAT_DCONN ttyI%d\n", info->line);
+#endif
+				if (TTY_IS_ACTIVE(info)) {
+					if (info->dialing == 1) {
+						info->dialing = 2;
+						return 1;
+					}
+				}
+				break;
+			case ISDN_STAT_DHUP:
+#ifdef ISDN_TTY_STAT_DEBUG
+				printk(KERN_DEBUG "tty_STAT_DHUP ttyI%d\n", info->line);
+#endif
+				if (TTY_IS_ACTIVE(info)) {
+					if (info->dialing == 1) 
+						isdn_tty_modem_result(RESULT_BUSY, info);
+					if (info->dialing > 1) 
+						isdn_tty_modem_result(RESULT_NO_CARRIER, info);
+					info->dialing = 0;
+#ifdef ISDN_DEBUG_MODEM_HUP
+					printk(KERN_DEBUG "Mhup in ISDN_STAT_DHUP\n");
+#endif
+					isdn_tty_modem_hup(info, 0);
+					return 1;
+				}
+				break;
+			case ISDN_STAT_BCONN:
+#ifdef ISDN_TTY_STAT_DEBUG
+				printk(KERN_DEBUG "tty_STAT_BCONN ttyI%d\n", info->line);
+#endif
+				/* Wake up any processes waiting
+				 * for incoming call of this device when
+				 * DCD follow the state of incoming carrier
+				 */
+				if (info->blocked_open &&
+				   (info->emu.mdmreg[REG_DCD] & BIT_DCD)) {
+					wake_up_interruptible(&info->open_wait);
+				}
+
+				/* Schedule CONNECT-Message to any tty
+				 * waiting for it and
+				 * set DCD-bit of its modem-status.
+				 */
+				if (TTY_IS_ACTIVE(info) ||
+				    (info->blocked_open && (info->emu.mdmreg[REG_DCD] & BIT_DCD))) {
+					info->msr |= UART_MSR_DCD;
+					info->emu.charge = 0;
+					if (info->dialing & 0xf)
+						info->last_dir = 1;
+					else
+						info->last_dir = 0;
+					info->dialing = 0;
+					info->rcvsched = 1;
+					if (USG_MODEM(dev->usage[i])) {
+						if (info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM) {
+							strcpy(info->emu.connmsg, c->parm.num);
+							isdn_tty_modem_result(RESULT_CONNECT, info);
+						} else
+							isdn_tty_modem_result(RESULT_CONNECT64000, info);
+					}
+					if (USG_VOICE(dev->usage[i]))
+						isdn_tty_modem_result(RESULT_VCON, info);
+					return 1;
+				}
+				break;
+			case ISDN_STAT_BHUP:
+#ifdef ISDN_TTY_STAT_DEBUG
+				printk(KERN_DEBUG "tty_STAT_BHUP ttyI%d\n", info->line);
+#endif
+				if (TTY_IS_ACTIVE(info)) {
+#ifdef ISDN_DEBUG_MODEM_HUP
+					printk(KERN_DEBUG "Mhup in ISDN_STAT_BHUP\n");
+#endif
+					isdn_tty_modem_hup(info, 0);
+					return 1;
+				}
+				break;
+			case ISDN_STAT_NODCH:
+#ifdef ISDN_TTY_STAT_DEBUG
+				printk(KERN_DEBUG "tty_STAT_NODCH ttyI%d\n", info->line);
+#endif
+				if (TTY_IS_ACTIVE(info)) {
+					if (info->dialing) {
+						info->dialing = 0;
+						info->last_l2 = -1;
+						info->last_si = 0;
+						sprintf(info->last_cause, "0000");
+						isdn_tty_modem_result(RESULT_NO_DIALTONE, info);
+					}
+					isdn_tty_modem_hup(info, 0);
+					return 1;
+				}
+				break;
+			case ISDN_STAT_UNLOAD:
+#ifdef ISDN_TTY_STAT_DEBUG
+				printk(KERN_DEBUG "tty_STAT_UNLOAD ttyI%d\n", info->line);
+#endif
+				for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+					info = &dev->mdm.info[i];
+					if (info->isdn_driver == c->driver) {
+						if (info->online)
+							isdn_tty_modem_hup(info, 1);
+					}
+				}
+				return 1;
+#ifdef CONFIG_ISDN_TTY_FAX
+			case ISDN_STAT_FAXIND:
+				if (TTY_IS_ACTIVE(info)) {
+					isdn_tty_fax_command(info, c); 
+				}
+				break;
+#endif
+#ifdef CONFIG_ISDN_AUDIO
+			case ISDN_STAT_AUDIO:
+				if (TTY_IS_ACTIVE(info)) {
+					switch(c->parm.num[0]) {
+						case ISDN_AUDIO_DTMF:
+							if (info->vonline) {
+								isdn_audio_put_dle_code(info,
+									c->parm.num[1]);
+							}
+							break;
+					}
+				}
+				break;
+#endif
+		}
+	}
+	return 0;
+}
+
+/*********************************************************************
+ Modem-Emulator-Routines
+ *********************************************************************/
+
+#define cmdchar(c) ((c>=' ')&&(c<=0x7f))
+
+/*
+ * Put a message from the AT-emulator into receive-buffer of tty,
+ * convert CR, LF, and BS to values in modem-registers 3, 4 and 5.
+ */
+void
+isdn_tty_at_cout(char *msg, modem_info * info)
+{
+	struct tty_struct *tty;
+	atemu *m = &info->emu;
+	char *p;
+	char c;
+	ulong flags;
+	struct sk_buff *skb = 0;
+	char *sp = 0;
+
+	if (!msg) {
+		printk(KERN_WARNING "isdn_tty: Null-Message in isdn_tty_at_cout\n");
+		return;
+	}
+	save_flags(flags);
+	cli();
+	tty = info->tty;
+	if ((info->flags & ISDN_ASYNC_CLOSING) || (!tty)) {
+		restore_flags(flags);
+		return;
+	}
+
+	/* use queue instead of direct flip, if online and */
+	/* data is in queue or flip buffer is full */
+	if ((info->online) && (((tty->flip.count + strlen(msg)) >= TTY_FLIPBUF_SIZE) ||
+	    (!skb_queue_empty(&dev->drv[info->isdn_driver]->rpqueue[info->isdn_channel])))) {
+		skb = alloc_skb(strlen(msg)
+#ifdef CONFIG_ISDN_AUDIO
+			+ sizeof(isdn_audio_skb)
+#endif
+			, GFP_ATOMIC);
+		if (!skb) {
+			restore_flags(flags);
+			return;
+		}
+#ifdef CONFIG_ISDN_AUDIO
+		skb_reserve(skb, sizeof(isdn_audio_skb));
+#endif
+		sp = skb_put(skb, strlen(msg));
+#ifdef CONFIG_ISDN_AUDIO
+		ISDN_AUDIO_SKB_DLECOUNT(skb) = 0;
+		ISDN_AUDIO_SKB_LOCK(skb) = 0;
+#endif
+	}
+
+	for (p = msg; *p; p++) {
+		switch (*p) {
+			case '\r':
+				c = m->mdmreg[REG_CR];
+				break;
+			case '\n':
+				c = m->mdmreg[REG_LF];
+				break;
+			case '\b':
+				c = m->mdmreg[REG_BS];
+				break;
+			default:
+				c = *p;
+		}
+		if (skb) {
+			*sp++ = c;
+		} else {
+			if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+				break;
+			tty_insert_flip_char(tty, c, 0);
+		}
+	}
+	if (skb) {
+		__skb_queue_tail(&dev->drv[info->isdn_driver]->rpqueue[info->isdn_channel], skb);
+		dev->drv[info->isdn_driver]->rcvcount[info->isdn_channel] += skb->len;
+		restore_flags(flags);
+		/* Schedule dequeuing */
+		if ((dev->modempoll) && (info->rcvsched))
+			isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
+
+	} else {
+		restore_flags(flags);
+		queue_task(&tty->flip.tqueue, &tq_timer);
+	}
+}
+
+/*
+ * Perform ATH Hangup
+ */
+static void
+isdn_tty_on_hook(modem_info * info)
+{
+	if (info->isdn_channel >= 0) {
+#ifdef ISDN_DEBUG_MODEM_HUP
+		printk(KERN_DEBUG "Mhup in isdn_tty_on_hook\n");
+#endif
+		isdn_tty_modem_hup(info, 1);
+	}
+}
+
+static void
+isdn_tty_off_hook(void)
+{
+	printk(KERN_DEBUG "isdn_tty_off_hook\n");
+}
+
+#define PLUSWAIT1 (HZ/2)        /* 0.5 sec. */
+#define PLUSWAIT2 (HZ*3/2)      /* 1.5 sec */
+
+/*
+ * Check Buffer for Modem-escape-sequence, activate timer-callback to
+ * isdn_tty_modem_escape() if sequence found.
+ *
+ * Parameters:
+ *   p          pointer to databuffer
+ *   plus       escape-character
+ *   count      length of buffer
+ *   pluscount  count of valid escape-characters so far
+ *   lastplus   timestamp of last character
+ */
+static void
+isdn_tty_check_esc(const u_char * p, u_char plus, int count, int *pluscount,
+		   int *lastplus, int from_user)
+{
+	char cbuf[3];
+
+	if (plus > 127)
+		return;
+	if (count > 3) {
+		p += count - 3;
+		count = 3;
+		*pluscount = 0;
+	}
+	if (from_user) {
+		copy_from_user(cbuf, p, count);
+		p = cbuf;
+	}
+	while (count > 0) {
+		if (*(p++) == plus) {
+			if ((*pluscount)++) {
+				/* Time since last '+' > 0.5 sec. ? */
+				if (time_after(jiffies, *lastplus + PLUSWAIT1))
+					*pluscount = 1;
+			} else {
+				/* Time since last non-'+' < 1.5 sec. ? */
+				if (time_before(jiffies, *lastplus + PLUSWAIT2))
+					*pluscount = 0;
+			}
+			if ((*pluscount == 3) && (count == 1))
+				isdn_timer_ctrl(ISDN_TIMER_MODEMPLUS, 1);
+			if (*pluscount > 3)
+				*pluscount = 1;
+		} else
+			*pluscount = 0;
+		*lastplus = jiffies;
+		count--;
+	}
+}
+
+/*
+ * Return result of AT-emulator to tty-receive-buffer, depending on
+ * modem-register 12, bit 0 and 1.
+ * For CONNECT-messages also switch to online-mode.
+ * For RING-message handle auto-ATA if register 0 != 0
+ */
+
+static void
+isdn_tty_modem_result(int code, modem_info * info)
+{
+	atemu *m = &info->emu;
+	static char *msg[] =
+	{"OK", "CONNECT", "RING", "NO CARRIER", "ERROR",
+	 "CONNECT 64000", "NO DIALTONE", "BUSY", "NO ANSWER",
+	 "RINGING", "NO MSN/EAZ", "VCON", "RUNG"};
+	ulong flags;
+	char s[ISDN_MSNLEN+10];
+
+	switch (code) {
+		case RESULT_RING:
+			m->mdmreg[REG_RINGCNT]++;
+			if (m->mdmreg[REG_RINGCNT] == m->mdmreg[REG_RINGATA])
+				/* Automatically accept incoming call */
+				isdn_tty_cmd_ATA(info);
+			break;
+		case RESULT_NO_CARRIER:
+#ifdef ISDN_DEBUG_MODEM_HUP
+			printk(KERN_DEBUG "modem_result: NO CARRIER %d %d\n",
+			       (info->flags & ISDN_ASYNC_CLOSING),
+			       (!info->tty));
+#endif
+			save_flags(flags);
+			cli();
+			m->mdmreg[REG_RINGCNT] = 0;
+			del_timer(&info->nc_timer);
+			info->ncarrier = 0;
+			if ((info->flags & ISDN_ASYNC_CLOSING) || (!info->tty)) {
+				restore_flags(flags);
+				return;
+			}
+			restore_flags(flags);
+#ifdef CONFIG_ISDN_AUDIO
+			if (info->vonline & 1) {
+#ifdef ISDN_DEBUG_MODEM_VOICE
+				printk(KERN_DEBUG "res3: send DLE-ETX on ttyI%d\n",
+				       info->line);
+#endif
+				/* voice-recording, add DLE-ETX */
+				isdn_tty_at_cout("\020\003", info);
+			}
+			if (info->vonline & 2) {
+#ifdef ISDN_DEBUG_MODEM_VOICE
+				printk(KERN_DEBUG "res3: send DLE-DC4 on ttyI%d\n",
+				       info->line);
+#endif
+				/* voice-playing, add DLE-DC4 */
+				isdn_tty_at_cout("\020\024", info);
+			}
+#endif
+			break;
+		case RESULT_CONNECT:
+		case RESULT_CONNECT64000:
+			sprintf(info->last_cause, "0000");
+			if (!info->online)
+				info->online = 2;
+			break;
+		case RESULT_VCON:
+#ifdef ISDN_DEBUG_MODEM_VOICE
+			printk(KERN_DEBUG "res3: send VCON on ttyI%d\n",
+			       info->line);
+#endif
+			sprintf(info->last_cause, "0000");
+			if (!info->online)
+				info->online = 1;
+			break;
+	} /* switch(code) */
+
+	if (m->mdmreg[REG_RESP] & BIT_RESP) {
+		/* Show results */
+		if (m->mdmreg[REG_RESPNUM] & BIT_RESPNUM) {
+			/* Show numeric results only */
+			sprintf(s, "\r\n%d\r\n", code);
+			isdn_tty_at_cout(s, info);
+		} else {
+			if (code == RESULT_RING) {
+			    /* return if "show RUNG" and ringcounter>1 */
+			    if ((m->mdmreg[REG_RUNG] & BIT_RUNG) &&
+				    (m->mdmreg[REG_RINGCNT] > 1))
+						return;
+			    /* print CID, _before_ _every_ ring */
+			    if (!(m->mdmreg[REG_CIDONCE] & BIT_CIDONCE)) {
+				    isdn_tty_at_cout("\r\nCALLER NUMBER: ", info);
+				    isdn_tty_at_cout(dev->num[info->drv_index], info);
+				    if (m->mdmreg[REG_CDN] & BIT_CDN) {
+					    isdn_tty_at_cout("\r\nCALLED NUMBER: ", info);
+					    isdn_tty_at_cout(info->emu.cpn, info);
+				    }
+			    }
+			}
+			isdn_tty_at_cout("\r\n", info);
+			isdn_tty_at_cout(msg[code], info);
+			switch (code) {
+				case RESULT_CONNECT:
+					switch (m->mdmreg[REG_L2PROT]) {
+						case ISDN_PROTO_L2_MODEM:
+							isdn_tty_at_cout(" ", info);
+							isdn_tty_at_cout(m->connmsg, info);
+							break;
+					}
+					break;
+				case RESULT_RING:
+					/* Append CPN, if enabled */
+					if ((m->mdmreg[REG_CPN] & BIT_CPN)) {
+						sprintf(s, "/%s", m->cpn);
+						isdn_tty_at_cout(s, info);
+					}
+					/* Print CID only once, _after_ 1st RING */
+					if ((m->mdmreg[REG_CIDONCE] & BIT_CIDONCE) &&
+					    (m->mdmreg[REG_RINGCNT] == 1)) {
+						isdn_tty_at_cout("\r\n", info);
+						isdn_tty_at_cout("CALLER NUMBER: ", info);
+						isdn_tty_at_cout(dev->num[info->drv_index], info);
+						if (m->mdmreg[REG_CDN] & BIT_CDN) {
+							isdn_tty_at_cout("\r\nCALLED NUMBER: ", info);
+							isdn_tty_at_cout(info->emu.cpn, info);
+						}
+					}
+					break;
+				case RESULT_NO_CARRIER:
+				case RESULT_NO_DIALTONE:
+				case RESULT_BUSY:
+				case RESULT_NO_ANSWER:
+					m->mdmreg[REG_RINGCNT] = 0;
+					/* Append Cause-Message if enabled */
+					if (m->mdmreg[REG_RESPXT] & BIT_RESPXT) {
+						sprintf(s, "/%s", info->last_cause);
+						isdn_tty_at_cout(s, info);
+					}
+					break;
+				case RESULT_CONNECT64000:
+					/* Append Protocol to CONNECT message */
+					switch (m->mdmreg[REG_L2PROT]) {
+						case ISDN_PROTO_L2_X75I:
+						case ISDN_PROTO_L2_X75UI:
+						case ISDN_PROTO_L2_X75BUI:
+							isdn_tty_at_cout("/X.75", info);
+							break;
+						case ISDN_PROTO_L2_HDLC:
+							isdn_tty_at_cout("/HDLC", info);
+							break;
+						case ISDN_PROTO_L2_V11096:
+							isdn_tty_at_cout("/V110/9600", info);
+							break;
+						case ISDN_PROTO_L2_V11019:
+							isdn_tty_at_cout("/V110/19200", info);
+							break;
+						case ISDN_PROTO_L2_V11038:
+							isdn_tty_at_cout("/V110/38400", info);
+							break;
+					}
+					if (m->mdmreg[REG_T70] & BIT_T70) {
+						isdn_tty_at_cout("/T.70", info);
+						if (m->mdmreg[REG_T70] & BIT_T70_EXT)
+							isdn_tty_at_cout("+", info);
+					}
+					break;
+			}
+			isdn_tty_at_cout("\r\n", info);
+		}
+	}
+	if (code == RESULT_NO_CARRIER) {
+		save_flags(flags);
+		cli();
+		if ((info->flags & ISDN_ASYNC_CLOSING) || (!info->tty)) {
+			restore_flags(flags);
+			return;
+		}
+		if (info->tty->ldisc.flush_buffer)
+			info->tty->ldisc.flush_buffer(info->tty);
+		if ((info->flags & ISDN_ASYNC_CHECK_CD) &&
+		    (!((info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) &&
+		       (info->flags & ISDN_ASYNC_CALLOUT_NOHUP)))) {
+			tty_hangup(info->tty);
+		}
+		restore_flags(flags);
+	}
+}
+
+
+/*
+ * Display a modem-register-value.
+ */
+static void
+isdn_tty_show_profile(int ridx, modem_info * info)
+{
+	char v[6];
+
+	sprintf(v, "\r\n%d", info->emu.mdmreg[ridx]);
+	isdn_tty_at_cout(v, info);
+}
+
+/*
+ * Get MSN-string from char-pointer, set pointer to end of number
+ */
+static void
+isdn_tty_get_msnstr(char *n, char **p)
+{
+	int limit = ISDN_MSNLEN - 1;
+
+	while (((*p[0] >= '0' && *p[0] <= '9') ||
+		/* Why a comma ??? */
+		(*p[0] == ',') || (*p[0] == ':')) &&
+		(limit--))
+		*n++ = *p[0]++;
+	*n = '\0';
+}
+
+/*
+ * Get phone-number from modem-commandbuffer
+ */
+static void
+isdn_tty_getdial(char *p, char *q,int cnt)
+{
+	int first = 1;
+	int limit = ISDN_MSNLEN - 1;	/* MUST match the size of interface var to avoid
+					buffer overflow */
+
+	while (strchr(" 0123456789,#.*WPTS-", *p) && *p && --cnt>0) {
+		if ((*p >= '0' && *p <= '9') || ((*p == 'S') && first) ||
+		    (*p == '*') || (*p == '#')) {
+			*q++ = *p;
+			limit--;
+		}
+		if(!limit)
+			break;
+		p++;
+		first = 0;
+	}
+	*q = 0;
+}
+
+#define PARSE_ERROR { isdn_tty_modem_result(RESULT_ERROR, info); return; }
+#define PARSE_ERROR1 { isdn_tty_modem_result(RESULT_ERROR, info); return 1; }
+
+static void
+isdn_tty_report(modem_info * info)
+{
+	atemu *m = &info->emu;
+	char s[80];
+
+	isdn_tty_at_cout("\r\nStatistics of last connection:\r\n\r\n", info);
+	sprintf(s, "    Remote Number:    %s\r\n", info->last_num);
+	isdn_tty_at_cout(s, info);
+	sprintf(s, "    Direction:        %s\r\n", info->last_dir ? "outgoing" : "incoming");
+	isdn_tty_at_cout(s, info);
+	isdn_tty_at_cout("    Layer-2 Protocol: ", info);
+	switch (info->last_l2) {
+		case ISDN_PROTO_L2_X75I:
+			isdn_tty_at_cout("X.75i", info);
+			break;
+		case ISDN_PROTO_L2_X75UI:
+			isdn_tty_at_cout("X.75ui", info);
+			break;
+		case ISDN_PROTO_L2_X75BUI:
+			isdn_tty_at_cout("X.75bui", info);
+			break;
+		case ISDN_PROTO_L2_HDLC:
+			isdn_tty_at_cout("HDLC", info);
+			break;
+		case ISDN_PROTO_L2_V11096:
+			isdn_tty_at_cout("V.110 9600 Baud", info);
+			break;
+		case ISDN_PROTO_L2_V11019:
+			isdn_tty_at_cout("V.110 19200 Baud", info);
+			break;
+		case ISDN_PROTO_L2_V11038:
+			isdn_tty_at_cout("V.110 38400 Baud", info);
+			break;
+		case ISDN_PROTO_L2_TRANS:
+			isdn_tty_at_cout("transparent", info);
+			break;
+		case ISDN_PROTO_L2_MODEM:
+			isdn_tty_at_cout("modem", info);
+			break;
+		case ISDN_PROTO_L2_FAX:
+			isdn_tty_at_cout("fax", info);
+			break;
+		default:
+			isdn_tty_at_cout("unknown", info);
+			break;
+	}
+	if (m->mdmreg[REG_T70] & BIT_T70) {
+		isdn_tty_at_cout("/T.70", info);
+		if (m->mdmreg[REG_T70] & BIT_T70_EXT)
+			isdn_tty_at_cout("+", info);
+	}
+	isdn_tty_at_cout("\r\n", info);
+	isdn_tty_at_cout("    Service:          ", info);
+	switch (info->last_si) {
+		case 1:
+			isdn_tty_at_cout("audio\r\n", info);
+			break;
+		case 5:
+			isdn_tty_at_cout("btx\r\n", info);
+			break;
+		case 7:
+			isdn_tty_at_cout("data\r\n", info);
+			break;
+		default:
+			sprintf(s, "%d\r\n", info->last_si);
+			isdn_tty_at_cout(s, info);
+			break;
+	}
+	sprintf(s, "    Hangup location:  %s\r\n", info->last_lhup ? "local" : "remote");
+	isdn_tty_at_cout(s, info);
+	sprintf(s, "    Last cause:       %s\r\n", info->last_cause);
+	isdn_tty_at_cout(s, info);
+}
+
+/*
+ * Parse AT&.. commands.
+ */
+static int
+isdn_tty_cmd_ATand(char **p, modem_info * info)
+{
+	atemu *m = &info->emu;
+	int i;
+	char rb[100];
+
+#define MAXRB (sizeof(rb) - 1)
+
+	switch (*p[0]) {
+		case 'B':
+			/* &B - Set Buffersize */
+			p[0]++;
+			i = isdn_getnum(p);
+			if ((i < 0) || (i > ISDN_SERIAL_XMIT_MAX))
+				PARSE_ERROR1;
+#ifdef CONFIG_ISDN_AUDIO
+			if ((m->mdmreg[REG_SI1] & 1) && (i > VBUF))
+				PARSE_ERROR1;
+#endif
+			m->mdmreg[REG_PSIZE] = i / 16;
+			info->xmit_size = m->mdmreg[REG_PSIZE] * 16;
+			switch (m->mdmreg[REG_L2PROT]) {
+				case ISDN_PROTO_L2_V11096:
+				case ISDN_PROTO_L2_V11019:
+				case ISDN_PROTO_L2_V11038:
+					info->xmit_size /= 10;		
+			}
+			break;
+		case 'C':
+			/* &C - DCD Status */
+			p[0]++;
+			switch (isdn_getnum(p)) {
+				case 0:
+					m->mdmreg[REG_DCD] &= ~BIT_DCD;
+					break;
+				case 1:
+					m->mdmreg[REG_DCD] |= BIT_DCD;
+					break;
+				default:
+					PARSE_ERROR1
+			}
+			break;
+		case 'D':
+			/* &D - Set DTR-Low-behavior */
+			p[0]++;
+			switch (isdn_getnum(p)) {
+				case 0:
+					m->mdmreg[REG_DTRHUP] &= ~BIT_DTRHUP;
+					m->mdmreg[REG_DTRR] &= ~BIT_DTRR;
+					break;
+				case 2:
+					m->mdmreg[REG_DTRHUP] |= BIT_DTRHUP;
+					m->mdmreg[REG_DTRR] &= ~BIT_DTRR;
+					break;
+				case 3:
+					m->mdmreg[REG_DTRHUP] |= BIT_DTRHUP;
+					m->mdmreg[REG_DTRR] |= BIT_DTRR;
+					break;
+				default:
+					PARSE_ERROR1
+			}
+			break;
+		case 'E':
+			/* &E -Set EAZ/MSN */
+			p[0]++;
+			isdn_tty_get_msnstr(m->msn, p);
+			break;
+		case 'F':
+			/* &F -Set Factory-Defaults */
+			p[0]++;
+			if (info->msr & UART_MSR_DCD)
+				PARSE_ERROR1;
+			isdn_tty_reset_profile(m);
+			isdn_tty_modem_reset_regs(info, 1);
+			break;
+#ifdef DUMMY_HAYES_AT
+		case 'K':
+			/* only for be compilant with common scripts */
+			/* &K Flowcontrol - no function */
+			p[0]++;
+			isdn_getnum(p);
+			break;
+#endif
+		case 'L':
+			/* &L -Set Numbers to listen on */
+			p[0]++;
+			i = 0;
+			while (*p[0] && (strchr("0123456789,-*[]?;", *p[0])) &&
+			       (i < ISDN_LMSNLEN))
+				m->lmsn[i++] = *p[0]++;
+			m->lmsn[i] = '\0';
+			break;
+		case 'R':
+			/* &R - Set V.110 bitrate adaption */
+			p[0]++;
+			i = isdn_getnum(p);
+			switch (i) {
+				case 0:
+					/* Switch off V.110, back to X.75 */
+					m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I;
+					m->mdmreg[REG_SI2] = 0;
+					info->xmit_size = m->mdmreg[REG_PSIZE] * 16;
+					break;
+				case 9600:
+					m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_V11096;
+					m->mdmreg[REG_SI2] = 197;
+					info->xmit_size = m->mdmreg[REG_PSIZE] * 16 / 10;
+					break;
+				case 19200:
+					m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_V11019;
+					m->mdmreg[REG_SI2] = 199;
+					info->xmit_size = m->mdmreg[REG_PSIZE] * 16 / 10;
+					break;
+				case 38400:
+					m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_V11038;
+					m->mdmreg[REG_SI2] = 198; /* no existing standard for this */
+					info->xmit_size = m->mdmreg[REG_PSIZE] * 16 / 10;
+					break;
+				default:
+					PARSE_ERROR1;
+			}
+			/* Switch off T.70 */
+			m->mdmreg[REG_T70] &= ~(BIT_T70 | BIT_T70_EXT);
+			/* Set Service 7 */
+			m->mdmreg[REG_SI1] |= 4;
+			break;
+		case 'S':
+			/* &S - Set Windowsize */
+			p[0]++;
+			i = isdn_getnum(p);
+			if ((i > 0) && (i < 9))
+				m->mdmreg[REG_WSIZE] = i;
+			else
+				PARSE_ERROR1;
+			break;
+		case 'V':
+			/* &V - Show registers */
+			p[0]++;
+			isdn_tty_at_cout("\r\n", info);
+			for (i = 0; i < ISDN_MODEM_NUMREG; i++) {
+				sprintf(rb, "S%02d=%03d%s", i,
+					m->mdmreg[i], ((i + 1) % 10) ? " " : "\r\n");
+				isdn_tty_at_cout(rb, info);
+			}
+			sprintf(rb, "\r\nEAZ/MSN: %.50s\r\n",
+				strlen(m->msn) ? m->msn : "None");
+			isdn_tty_at_cout(rb, info);
+			if (strlen(m->lmsn)) {
+				isdn_tty_at_cout("\r\nListen: ", info);
+				isdn_tty_at_cout(m->lmsn, info);
+				isdn_tty_at_cout("\r\n", info);
+			}
+			break;
+		case 'W':
+			/* &W - Write Profile */
+			p[0]++;
+			switch (*p[0]) {
+				case '0':
+					p[0]++;
+					modem_write_profile(m);
+					break;
+				default:
+					PARSE_ERROR1;
+			}
+			break;
+		case 'X':
+			/* &X - Switch to BTX-Mode and T.70 */
+			p[0]++;
+			switch (isdn_getnum(p)) {
+				case 0:
+					m->mdmreg[REG_T70] &= ~(BIT_T70 | BIT_T70_EXT);
+					info->xmit_size = m->mdmreg[REG_PSIZE] * 16;
+					break;
+				case 1:
+					m->mdmreg[REG_T70] |= BIT_T70;
+					m->mdmreg[REG_T70] &= ~BIT_T70_EXT;
+					m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I;
+					info->xmit_size = 112;
+					m->mdmreg[REG_SI1] = 4;
+					m->mdmreg[REG_SI2] = 0;
+					break;
+				case 2:
+					m->mdmreg[REG_T70] |= (BIT_T70 | BIT_T70_EXT);
+					m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I;
+					info->xmit_size = 112;
+					m->mdmreg[REG_SI1] = 4;
+					m->mdmreg[REG_SI2] = 0;
+					break;
+				default:
+					PARSE_ERROR1;
+			}
+			break;
+		default:
+			PARSE_ERROR1;
+	}
+	return 0;
+}
+
+static int
+isdn_tty_check_ats(int mreg, int mval, modem_info * info, atemu * m)
+{
+	/* Some plausibility checks */
+	switch (mreg) {
+		case REG_L2PROT:
+			if (mval > ISDN_PROTO_L2_MAX)
+				return 1;
+			break;
+		case REG_PSIZE:
+			if ((mval * 16) > ISDN_SERIAL_XMIT_MAX)
+				return 1;
+#ifdef CONFIG_ISDN_AUDIO
+			if ((m->mdmreg[REG_SI1] & 1) && (mval > VBUFX))
+				return 1;
+#endif
+			info->xmit_size = mval * 16;
+			switch (m->mdmreg[REG_L2PROT]) {
+				case ISDN_PROTO_L2_V11096:
+				case ISDN_PROTO_L2_V11019:
+				case ISDN_PROTO_L2_V11038:
+					info->xmit_size /= 10;		
+			}
+			break;
+		case REG_SI1I:
+		case REG_PLAN:
+		case REG_SCREEN:
+			/* readonly registers */
+			return 1;
+	}
+	return 0;
+}
+
+/*
+ * Perform ATS command
+ */
+static int
+isdn_tty_cmd_ATS(char **p, modem_info * info)
+{
+	atemu *m = &info->emu;
+	int bitpos;
+	int mreg;
+	int mval;
+	int bval;
+
+	mreg = isdn_getnum(p);
+	if (mreg < 0 || mreg >= ISDN_MODEM_NUMREG)
+		PARSE_ERROR1;
+	switch (*p[0]) {
+		case '=':
+			p[0]++;
+			mval = isdn_getnum(p);
+			if (mval < 0 || mval > 255)
+				PARSE_ERROR1;
+			if (isdn_tty_check_ats(mreg, mval, info, m))
+				PARSE_ERROR1;
+			m->mdmreg[mreg] = mval;
+			break;
+		case '.':
+			/* Set/Clear a single bit */
+			p[0]++;
+			bitpos = isdn_getnum(p);
+			if ((bitpos < 0) || (bitpos > 7))
+				PARSE_ERROR1;
+			switch (*p[0]) {
+				case '=':
+					p[0]++;
+					bval = isdn_getnum(p);
+					if (bval < 0 || bval > 1)
+						PARSE_ERROR1;
+					if (bval)
+						mval = m->mdmreg[mreg] | (1 << bitpos);
+					else
+						mval = m->mdmreg[mreg] & ~(1 << bitpos);
+					if (isdn_tty_check_ats(mreg, mval, info, m))
+						PARSE_ERROR1;
+					m->mdmreg[mreg] = mval;
+					break;
+				case '?':
+					p[0]++;
+					isdn_tty_at_cout("\r\n", info);
+					isdn_tty_at_cout((m->mdmreg[mreg] & (1 << bitpos)) ? "1" : "0",
+							 info);
+					break;
+				default:
+					PARSE_ERROR1;
+			}
+			break;
+		case '?':
+			p[0]++;
+			isdn_tty_show_profile(mreg, info);
+			break;
+		default:
+			PARSE_ERROR1;
+			break;
+	}
+	return 0;
+}
+
+/*
+ * Perform ATA command
+ */
+static void
+isdn_tty_cmd_ATA(modem_info * info)
+{
+	atemu *m = &info->emu;
+	isdn_ctrl cmd;
+	int l2;
+
+	if (info->msr & UART_MSR_RI) {
+		/* Accept incoming call */
+		info->last_dir = 0;
+		strcpy(info->last_num, dev->num[info->drv_index]);
+		m->mdmreg[REG_RINGCNT] = 0;
+		info->msr &= ~UART_MSR_RI;
+		l2 = m->mdmreg[REG_L2PROT];
+#ifdef CONFIG_ISDN_AUDIO
+		/* If more than one bit set in reg18, autoselect Layer2 */
+		if ((m->mdmreg[REG_SI1] & m->mdmreg[REG_SI1I]) != m->mdmreg[REG_SI1]) {
+			if (m->mdmreg[REG_SI1I] == 1) {
+				if ((l2 != ISDN_PROTO_L2_MODEM) && (l2 != ISDN_PROTO_L2_FAX))
+					l2 = ISDN_PROTO_L2_TRANS;
+			} else
+				l2 = ISDN_PROTO_L2_X75I;
+		}
+#endif
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETL2;
+		cmd.arg = info->isdn_channel + (l2 << 8);
+		info->last_l2 = l2;
+		isdn_command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETL3;
+		cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8);
+#ifdef CONFIG_ISDN_TTY_FAX
+		if (l2 == ISDN_PROTO_L2_FAX) {
+			cmd.parm.fax = info->fax;
+			info->fax->direction = ISDN_TTY_FAX_CONN_IN;
+		}
+#endif
+		isdn_command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.arg = info->isdn_channel;
+		cmd.command = ISDN_CMD_ACCEPTD;
+		info->dialing = 16;
+		info->emu.carrierwait = 0;
+		isdn_command(&cmd);
+		isdn_timer_ctrl(ISDN_TIMER_CARRIER, 1);
+	} else
+		isdn_tty_modem_result(RESULT_NO_ANSWER, info);
+}
+
+#ifdef CONFIG_ISDN_AUDIO
+/*
+ * Parse AT+F.. commands
+ */
+static int
+isdn_tty_cmd_PLUSF(char **p, modem_info * info)
+{
+	atemu *m = &info->emu;
+	char rs[20];
+
+	if (!strncmp(p[0], "CLASS", 5)) {
+		p[0] += 5;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				sprintf(rs, "\r\n%d",
+					(m->mdmreg[REG_SI1] & 1) ? 8 : 0);
+#ifdef CONFIG_ISDN_TTY_FAX
+				if (TTY_IS_FCLASS2(info))
+						sprintf(rs, "\r\n2");
+				else if (TTY_IS_FCLASS1(info))
+						sprintf(rs, "\r\n1");
+#endif
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				switch (*p[0]) {
+					case '0':
+						p[0]++;
+						m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I;
+						m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_TRANS;
+						m->mdmreg[REG_SI1] = 4;
+						info->xmit_size =
+						    m->mdmreg[REG_PSIZE] * 16;
+						break;
+#ifdef CONFIG_ISDN_TTY_FAX
+					case '1':
+						p[0]++;
+						if (!(dev->global_features &
+							ISDN_FEATURE_L3_FCLASS1))
+							PARSE_ERROR1;
+						m->mdmreg[REG_SI1] = 1;
+						m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_FAX;
+						m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_FCLASS1;
+						info->xmit_size =
+						    m->mdmreg[REG_PSIZE] * 16;
+						break;
+					case '2':
+						p[0]++;
+						if (!(dev->global_features &
+							ISDN_FEATURE_L3_FCLASS2))
+							PARSE_ERROR1;
+						m->mdmreg[REG_SI1] = 1;
+						m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_FAX;
+						m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_FCLASS2;
+						info->xmit_size =
+						    m->mdmreg[REG_PSIZE] * 16;
+						break;
+#endif
+					case '8':
+						p[0]++;
+						/* L2 will change on dialout with si=1 */
+						m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I;
+						m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_TRANS;
+						m->mdmreg[REG_SI1] = 5;
+						info->xmit_size = VBUF;
+						break;
+					case '?':
+						p[0]++;
+						strcpy(rs, "\r\n0,");
+#ifdef CONFIG_ISDN_TTY_FAX
+						if (dev->global_features &
+							ISDN_FEATURE_L3_FCLASS1)
+							strcat(rs, "1,");
+						if (dev->global_features &
+							ISDN_FEATURE_L3_FCLASS2)
+							strcat(rs, "2,");
+#endif
+						strcat(rs, "8");
+						isdn_tty_at_cout(rs, info);
+						break;
+					default:
+						PARSE_ERROR1;
+				}
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+#ifdef CONFIG_ISDN_TTY_FAX
+	return (isdn_tty_cmd_PLUSF_FAX(p, info));
+#else
+	PARSE_ERROR1;
+#endif
+}
+
+/*
+ * Parse AT+V.. commands
+ */
+static int
+isdn_tty_cmd_PLUSV(char **p, modem_info * info)
+{
+	atemu *m = &info->emu;
+	isdn_ctrl cmd;
+	static char *vcmd[] =
+	{"NH", "IP", "LS", "RX", "SD", "SM", "TX", "DD", NULL};
+	int i;
+	int par1;
+	int par2;
+	char rs[20];
+
+	i = 0;
+	while (vcmd[i]) {
+		if (!strncmp(vcmd[i], p[0], 2)) {
+			p[0] += 2;
+			break;
+		}
+		i++;
+	}
+	switch (i) {
+		case 0:
+			/* AT+VNH - Auto hangup feature */
+			switch (*p[0]) {
+				case '?':
+					p[0]++;
+					isdn_tty_at_cout("\r\n1", info);
+					break;
+				case '=':
+					p[0]++;
+					switch (*p[0]) {
+						case '1':
+							p[0]++;
+							break;
+						case '?':
+							p[0]++;
+							isdn_tty_at_cout("\r\n1", info);
+							break;
+						default:
+							PARSE_ERROR1;
+					}
+					break;
+				default:
+					PARSE_ERROR1;
+			}
+			break;
+		case 1:
+			/* AT+VIP - Reset all voice parameters */
+			isdn_tty_modem_reset_vpar(m);
+			break;
+		case 2:
+			/* AT+VLS - Select device, accept incoming call */
+			switch (*p[0]) {
+				case '?':
+					p[0]++;
+					sprintf(rs, "\r\n%d", m->vpar[0]);
+					isdn_tty_at_cout(rs, info);
+					break;
+				case '=':
+					p[0]++;
+					switch (*p[0]) {
+						case '0':
+							p[0]++;
+							m->vpar[0] = 0;
+							break;
+						case '2':
+							p[0]++;
+							m->vpar[0] = 2;
+							break;
+						case '?':
+							p[0]++;
+							isdn_tty_at_cout("\r\n0,2", info);
+							break;
+						default:
+							PARSE_ERROR1;
+					}
+					break;
+				default:
+					PARSE_ERROR1;
+			}
+			break;
+		case 3:
+			/* AT+VRX - Start recording */
+			if (!m->vpar[0])
+				PARSE_ERROR1;
+			if (info->online != 1) {
+				isdn_tty_modem_result(RESULT_NO_ANSWER, info);
+				return 1;
+			}
+			info->dtmf_state = isdn_audio_dtmf_init(info->dtmf_state);
+			if (!info->dtmf_state) {
+				printk(KERN_WARNING "isdn_tty: Couldn't malloc dtmf state\n");
+				PARSE_ERROR1;
+			}
+			info->silence_state = isdn_audio_silence_init(info->silence_state);
+			if (!info->silence_state) {
+				printk(KERN_WARNING "isdn_tty: Couldn't malloc silence state\n");
+				PARSE_ERROR1;
+			}
+			if (m->vpar[3] < 5) {
+				info->adpcmr = isdn_audio_adpcm_init(info->adpcmr, m->vpar[3]);
+				if (!info->adpcmr) {
+					printk(KERN_WARNING "isdn_tty: Couldn't malloc adpcm state\n");
+					PARSE_ERROR1;
+				}
+			}
+#ifdef ISDN_DEBUG_AT
+			printk(KERN_DEBUG "AT: +VRX\n");
+#endif
+			info->vonline |= 1;
+			isdn_tty_modem_result(RESULT_CONNECT, info);
+			return 0;
+			break;
+		case 4:
+			/* AT+VSD - Silence detection */
+			switch (*p[0]) {
+				case '?':
+					p[0]++;
+					sprintf(rs, "\r\n<%d>,<%d>",
+						m->vpar[1],
+						m->vpar[2]);
+					isdn_tty_at_cout(rs, info);
+					break;
+				case '=':
+					p[0]++;
+					if ((*p[0]>='0') && (*p[0]<='9')) {
+						par1 = isdn_getnum(p);
+						if ((par1 < 0) || (par1 > 31))
+							PARSE_ERROR1;
+						if (*p[0] != ',')
+							PARSE_ERROR1;
+						p[0]++;
+						par2 = isdn_getnum(p);
+						if ((par2 < 0) || (par2 > 255))
+							PARSE_ERROR1;
+						m->vpar[1] = par1;
+						m->vpar[2] = par2;
+						break;
+					} else 
+					if (*p[0] == '?') {
+						p[0]++;
+						isdn_tty_at_cout("\r\n<0-31>,<0-255>",
+							   info);
+						break;
+					} else
+					PARSE_ERROR1;
+					break;
+				default:
+					PARSE_ERROR1;
+			}
+			break;
+		case 5:
+			/* AT+VSM - Select compression */
+			switch (*p[0]) {
+				case '?':
+					p[0]++;
+					sprintf(rs, "\r\n<%d>,<%d><8000>",
+						m->vpar[3],
+						m->vpar[1]);
+					isdn_tty_at_cout(rs, info);
+					break;
+				case '=':
+					p[0]++;
+					switch (*p[0]) {
+						case '2':
+						case '3':
+						case '4':
+						case '5':
+						case '6':
+							par1 = isdn_getnum(p);
+							if ((par1 < 2) || (par1 > 6))
+								PARSE_ERROR1;
+							m->vpar[3] = par1;
+							break;
+						case '?':
+							p[0]++;
+							isdn_tty_at_cout("\r\n2;ADPCM;2;0;(8000)\r\n",
+								   info);
+							isdn_tty_at_cout("3;ADPCM;3;0;(8000)\r\n",
+								   info);
+							isdn_tty_at_cout("4;ADPCM;4;0;(8000)\r\n",
+								   info);
+							isdn_tty_at_cout("5;ALAW;8;0;(8000)\r\n",
+								   info);
+							isdn_tty_at_cout("6;ULAW;8;0;(8000)\r\n",
+								   info);
+							break;
+						default:
+							PARSE_ERROR1;
+					}
+					break;
+				default:
+					PARSE_ERROR1;
+			}
+			break;
+		case 6:
+			/* AT+VTX - Start sending */
+			if (!m->vpar[0])
+				PARSE_ERROR1;
+			if (info->online != 1) {
+				isdn_tty_modem_result(RESULT_NO_ANSWER, info);
+				return 1;
+			}
+			info->dtmf_state = isdn_audio_dtmf_init(info->dtmf_state);
+			if (!info->dtmf_state) {
+				printk(KERN_WARNING "isdn_tty: Couldn't malloc dtmf state\n");
+				PARSE_ERROR1;
+			}
+			if (m->vpar[3] < 5) {
+				info->adpcms = isdn_audio_adpcm_init(info->adpcms, m->vpar[3]);
+				if (!info->adpcms) {
+					printk(KERN_WARNING "isdn_tty: Couldn't malloc adpcm state\n");
+					PARSE_ERROR1;
+				}
+			}
+#ifdef ISDN_DEBUG_AT
+			printk(KERN_DEBUG "AT: +VTX\n");
+#endif
+			m->lastDLE = 0;
+			info->vonline |= 2;
+			isdn_tty_modem_result(RESULT_CONNECT, info);
+			return 0;
+			break;
+		case 7:
+			/* AT+VDD - DTMF detection */
+			switch (*p[0]) {
+				case '?':
+					p[0]++;
+					sprintf(rs, "\r\n<%d>,<%d>",
+						m->vpar[4],
+						m->vpar[5]);
+					isdn_tty_at_cout(rs, info);
+					break;
+				case '=':
+					p[0]++;
+					if ((*p[0]>='0') && (*p[0]<='9')) {
+						if (info->online != 1)
+							PARSE_ERROR1;
+						par1 = isdn_getnum(p);
+						if ((par1 < 0) || (par1 > 15))
+							PARSE_ERROR1;
+						if (*p[0] != ',')
+							PARSE_ERROR1;
+						p[0]++;
+						par2 = isdn_getnum(p);
+						if ((par2 < 0) || (par2 > 255))
+							PARSE_ERROR1;
+						m->vpar[4] = par1;
+						m->vpar[5] = par2;
+						cmd.driver = info->isdn_driver;
+						cmd.command = ISDN_CMD_AUDIO;
+						cmd.arg = info->isdn_channel + (ISDN_AUDIO_SETDD << 8);
+						cmd.parm.num[0] = par1;
+						cmd.parm.num[1] = par2;
+						isdn_command(&cmd);
+						break;
+					} else
+					if (*p[0] == '?') {
+						p[0]++;
+						isdn_tty_at_cout("\r\n<0-15>,<0-255>",
+							info);
+						break;
+					} else
+					PARSE_ERROR1;
+					break;
+				default:
+					PARSE_ERROR1;
+			}
+			break;
+		default:
+			PARSE_ERROR1;
+	}
+	return 0;
+}
+#endif                          /* CONFIG_ISDN_AUDIO */
+
+/*
+ * Parse and perform an AT-command-line.
+ */
+static void
+isdn_tty_parse_at(modem_info * info)
+{
+	atemu *m = &info->emu;
+	char *p;
+	char ds[40];
+
+#ifdef ISDN_DEBUG_AT
+	printk(KERN_DEBUG "AT: '%s'\n", m->mdmcmd);
+#endif
+	for (p = &m->mdmcmd[2]; *p;) {
+		switch (*p) {
+			case ' ':
+				p++;
+				break;
+			case 'A':
+				/* A - Accept incoming call */
+				p++;
+				isdn_tty_cmd_ATA(info);
+				return;
+				break;
+			case 'D':
+				/* D - Dial */
+				if (info->msr & UART_MSR_DCD)
+					PARSE_ERROR;
+				if (info->msr & UART_MSR_RI) {
+					isdn_tty_modem_result(RESULT_NO_CARRIER, info);
+					return;
+				}
+				isdn_tty_getdial(++p, ds, sizeof ds);
+				p += strlen(p);
+				if (!strlen(m->msn))
+					isdn_tty_modem_result(RESULT_NO_MSN_EAZ, info);
+				else if (strlen(ds))
+					isdn_tty_dial(ds, info, m);
+				else
+					PARSE_ERROR;
+				return;
+			case 'E':
+				/* E - Turn Echo on/off */
+				p++;
+				switch (isdn_getnum(&p)) {
+					case 0:
+						m->mdmreg[REG_ECHO] &= ~BIT_ECHO;
+						break;
+					case 1:
+						m->mdmreg[REG_ECHO] |= BIT_ECHO;
+						break;
+					default:
+						PARSE_ERROR;
+				}
+				break;
+			case 'H':
+				/* H - On/Off-hook */
+				p++;
+				switch (*p) {
+					case '0':
+						p++;
+						isdn_tty_on_hook(info);
+						break;
+					case '1':
+						p++;
+						isdn_tty_off_hook();
+						break;
+					default:
+						isdn_tty_on_hook(info);
+						break;
+				}
+				break;
+			case 'I':
+				/* I - Information */
+				p++;
+				isdn_tty_at_cout("\r\nLinux ISDN", info);
+				switch (*p) {
+					case '0':
+					case '1':
+						p++;
+						break;
+					case '2':
+						p++;
+						isdn_tty_report(info);
+						break;
+					case '3':
+                                                p++;
+                                                sprintf(ds, "\r\n%d", info->emu.charge);
+                                                isdn_tty_at_cout(ds, info);
+                                                break;
+					default:;
+				}
+				break;
+#ifdef DUMMY_HAYES_AT
+			case 'L':
+			case 'M':
+				/* only for be compilant with common scripts */
+				/* no function */
+				p++;
+				isdn_getnum(&p);
+				break;
+#endif
+			case 'O':
+				/* O - Go online */
+				p++;
+				if (info->msr & UART_MSR_DCD)
+					/* if B-Channel is up */
+					isdn_tty_modem_result((m->mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM) ? RESULT_CONNECT:RESULT_CONNECT64000, info);
+				else
+					isdn_tty_modem_result(RESULT_NO_CARRIER, info);
+				return;
+			case 'Q':
+				/* Q - Turn Emulator messages on/off */
+				p++;
+				switch (isdn_getnum(&p)) {
+					case 0:
+						m->mdmreg[REG_RESP] |= BIT_RESP;
+						break;
+					case 1:
+						m->mdmreg[REG_RESP] &= ~BIT_RESP;
+						break;
+					default:
+						PARSE_ERROR;
+				}
+				break;
+			case 'S':
+				/* S - Set/Get Register */
+				p++;
+				if (isdn_tty_cmd_ATS(&p, info))
+					return;
+				break;
+			case 'V':
+				/* V - Numeric or ASCII Emulator-messages */
+				p++;
+				switch (isdn_getnum(&p)) {
+					case 0:
+						m->mdmreg[REG_RESP] |= BIT_RESPNUM;
+						break;
+					case 1:
+						m->mdmreg[REG_RESP] &= ~BIT_RESPNUM;
+						break;
+					default:
+						PARSE_ERROR;
+				}
+				break;
+			case 'Z':
+				/* Z - Load Registers from Profile */
+				p++;
+				if (info->msr & UART_MSR_DCD) {
+					info->online = 0;
+					isdn_tty_on_hook(info);
+				}
+				isdn_tty_modem_reset_regs(info, 1);
+				break;
+			case '+':
+				p++;
+				switch (*p) {
+#ifdef CONFIG_ISDN_AUDIO
+					case 'F':
+						p++;
+						if (isdn_tty_cmd_PLUSF(&p, info))
+							return;
+						break;
+					case 'V':
+						if ((!(m->mdmreg[REG_SI1] & 1)) ||
+							(m->mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM))
+							PARSE_ERROR;
+						p++;
+						if (isdn_tty_cmd_PLUSV(&p, info))
+							return;
+						break;
+#endif                          /* CONFIG_ISDN_AUDIO */
+					case 'S':	/* SUSPEND */
+						p++;
+						isdn_tty_get_msnstr(ds, &p);
+						isdn_tty_suspend(ds, info, m);
+						break;
+					case 'R':	/* RESUME */
+						p++;
+						isdn_tty_get_msnstr(ds, &p);
+						isdn_tty_resume(ds, info, m);
+						break;
+					case 'M':	/* MESSAGE */
+						p++;
+						isdn_tty_send_msg(info, m, p);
+						break;
+					default:
+						PARSE_ERROR;
+				}
+				break;
+			case '&':
+				p++;
+				if (isdn_tty_cmd_ATand(&p, info))
+					return;
+				break;
+			default:
+				PARSE_ERROR;
+		}
+	}
+#ifdef CONFIG_ISDN_AUDIO
+	if (!info->vonline)
+#endif
+		isdn_tty_modem_result(RESULT_OK, info);
+}
+
+/* Need own toupper() because standard-toupper is not available
+ * within modules.
+ */
+#define my_toupper(c) (((c>='a')&&(c<='z'))?(c&0xdf):c)
+
+/*
+ * Perform line-editing of AT-commands
+ *
+ * Parameters:
+ *   p        inputbuffer
+ *   count    length of buffer
+ *   channel  index to line (minor-device)
+ *   user     flag: buffer is in userspace
+ */
+static int
+isdn_tty_edit_at(const char *p, int count, modem_info * info, int user)
+{
+	atemu *m = &info->emu;
+	int total = 0;
+	u_char c;
+	char eb[2];
+	int cnt;
+
+	for (cnt = count; cnt > 0; p++, cnt--) {
+		if (user)
+			get_user(c, p);
+		else
+			c = *p;
+		total++;
+		if (c == m->mdmreg[REG_CR] || c == m->mdmreg[REG_LF]) {
+			/* Separator (CR or LF) */
+			m->mdmcmd[m->mdmcmdl] = 0;
+			if (m->mdmreg[REG_ECHO] & BIT_ECHO) {
+				eb[0] = c;
+				eb[1] = 0;
+				isdn_tty_at_cout(eb, info);
+			}
+			if ((m->mdmcmdl >= 2) && (!(strncmp(m->mdmcmd, "AT", 2))))
+				isdn_tty_parse_at(info);
+			m->mdmcmdl = 0;
+			continue;
+		}
+		if (c == m->mdmreg[REG_BS] && m->mdmreg[REG_BS] < 128) {
+			/* Backspace-Function */
+			if ((m->mdmcmdl > 2) || (!m->mdmcmdl)) {
+				if (m->mdmcmdl)
+					m->mdmcmdl--;
+				if (m->mdmreg[REG_ECHO] & BIT_ECHO)
+					isdn_tty_at_cout("\b", info);
+			}
+			continue;
+		}
+		if (cmdchar(c)) {
+			if (m->mdmreg[REG_ECHO] & BIT_ECHO) {
+				eb[0] = c;
+				eb[1] = 0;
+				isdn_tty_at_cout(eb, info);
+			}
+			if (m->mdmcmdl < 255) {
+				c = my_toupper(c);
+				switch (m->mdmcmdl) {
+					case 1:
+						if (c == 'T') {
+							m->mdmcmd[m->mdmcmdl] = c;
+							m->mdmcmd[++m->mdmcmdl] = 0;
+							break;
+						} else
+							m->mdmcmdl = 0;
+						/* Fall through, check for 'A' */
+					case 0:
+						if (c == 'A') {
+							m->mdmcmd[m->mdmcmdl] = c;
+							m->mdmcmd[++m->mdmcmdl] = 0;
+						}
+						break;
+					default:
+						m->mdmcmd[m->mdmcmdl] = c;
+						m->mdmcmd[++m->mdmcmdl] = 0;
+				}
+			}
+		}
+	}
+	return total;
+}
+
+/*
+ * Switch all modem-channels who are online and got a valid
+ * escape-sequence 1.5 seconds ago, to command-mode.
+ * This function is called every second via timer-interrupt from within
+ * timer-dispatcher isdn_timer_function()
+ */
+void
+isdn_tty_modem_escape(void)
+{
+	int ton = 0;
+	int i;
+	int midx;
+
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+		if (USG_MODEM(dev->usage[i]))
+			if ((midx = dev->m_idx[i]) >= 0) {
+				modem_info *info = &dev->mdm.info[midx];
+				if (info->online) {
+					ton = 1;
+					if ((info->emu.pluscount == 3) &&
+					    time_after(jiffies , info->emu.lastplus + PLUSWAIT2)) {
+						info->emu.pluscount = 0;
+						info->online = 0;
+						isdn_tty_modem_result(RESULT_OK, info);
+					}
+				}
+			}
+	isdn_timer_ctrl(ISDN_TIMER_MODEMPLUS, ton);
+}
+
+/*
+ * Put a RING-message to all modem-channels who have the RI-bit set.
+ * This function is called every second via timer-interrupt from within
+ * timer-dispatcher isdn_timer_function()
+ */
+void
+isdn_tty_modem_ring(void)
+{
+	int ton = 0;
+	int i;
+
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		modem_info *info = &dev->mdm.info[i];
+		if (info->msr & UART_MSR_RI) {
+			ton = 1;
+			isdn_tty_modem_result(RESULT_RING, info);
+		}
+	}
+	isdn_timer_ctrl(ISDN_TIMER_MODEMRING, ton);
+}
+
+/*
+ * For all online tty's, try sending data to
+ * the lower levels.
+ */
+void
+isdn_tty_modem_xmit(void)
+{
+	int ton = 1;
+	int i;
+
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		modem_info *info = &dev->mdm.info[i];
+		if (info->online) {
+			ton = 1;
+			isdn_tty_senddown(info);
+			isdn_tty_tint(info);
+		}
+	}
+	isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, ton);
+}
+
+/*
+ * Check all channels if we have a 'no carrier' timeout.
+ * Timeout value is set by Register S7.
+ */
+void
+isdn_tty_carrier_timeout(void)
+{
+	int ton = 0;
+	int i;
+
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		modem_info *info = &dev->mdm.info[i];
+		if (info->dialing) {
+			if (info->emu.carrierwait++ > info->emu.mdmreg[REG_WAITC]) {
+				info->dialing = 0;
+				isdn_tty_modem_result(RESULT_NO_CARRIER, info);
+				isdn_tty_modem_hup(info, 1);
+			}
+			else
+				ton = 1;
+		}
+	}
+	isdn_timer_ctrl(ISDN_TIMER_CARRIER, ton);
+}
diff -Nru a/drivers/isdn/i4l/isdn_tty.h b/drivers/isdn/i4l/isdn_tty.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/i4l/isdn_tty.h	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,121 @@
+/* $Id: isdn_tty.h,v 1.22.6.2 2001/09/23 22:24:32 kai Exp $
+ *
+ * header for Linux ISDN subsystem, tty related functions (linklevel).
+ *
+ * Copyright 1994-1999  by Fritz Elfert (fritz@isdn4linux.de)
+ * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/config.h>
+
+#define DLE 0x10
+#define ETX 0x03
+#define DC4 0x14
+
+
+/*
+ * Definition of some special Registers of AT-Emulator
+ */
+#define REG_RINGATA   0
+#define REG_RINGCNT   1  /* ring counter register */
+#define REG_ESC       2
+#define REG_CR        3
+#define REG_LF        4
+#define REG_BS        5
+
+#define REG_WAITC     7
+
+#define REG_RESP     12  /* show response messages register */
+#define BIT_RESP      1  /* show response messages bit      */
+#define REG_RESPNUM  12  /* show numeric responses register */
+#define BIT_RESPNUM   2  /* show numeric responses bit      */
+#define REG_ECHO     12
+#define BIT_ECHO      4
+#define REG_DCD      12
+#define BIT_DCD       8
+#define REG_CTS      12
+#define BIT_CTS      16
+#define REG_DTRR     12
+#define BIT_DTRR     32
+#define REG_DSR      12
+#define BIT_DSR      64
+#define REG_CPPP     12
+#define BIT_CPPP    128
+
+#define REG_DXMT     13
+#define BIT_DXMT      1
+#define REG_T70      13
+#define BIT_T70       2
+#define BIT_T70_EXT  32
+#define REG_DTRHUP   13
+#define BIT_DTRHUP    4
+#define REG_RESPXT   13
+#define BIT_RESPXT    8
+#define REG_CIDONCE  13
+#define BIT_CIDONCE  16
+#define REG_RUNG     13  /* show RUNG message register      */
+#define BIT_RUNG     64  /* show RUNG message bit           */
+#define REG_DISPLAY  13
+#define BIT_DISPLAY 128
+
+#define REG_L2PROT   14
+#define REG_L3PROT   15
+#define REG_PSIZE    16
+#define REG_WSIZE    17
+#define REG_SI1      18
+#define REG_SI2      19
+#define REG_SI1I     20
+#define REG_PLAN     21
+#define REG_SCREEN   22
+
+#define REG_CPN      23
+#define BIT_CPN       1
+#define REG_CPNFCON  23
+#define BIT_CPNFCON   2
+#define REG_CDN      23
+#define BIT_CDN       4
+
+/* defines for result codes */
+#define RESULT_OK		0
+#define RESULT_CONNECT		1
+#define RESULT_RING		2
+#define RESULT_NO_CARRIER	3
+#define RESULT_ERROR		4
+#define RESULT_CONNECT64000	5
+#define RESULT_NO_DIALTONE	6
+#define RESULT_BUSY		7
+#define RESULT_NO_ANSWER	8
+#define RESULT_RINGING		9
+#define RESULT_NO_MSN_EAZ	10
+#define RESULT_VCON		11
+#define RESULT_RUNG		12
+
+#define TTY_IS_FCLASS1(info) \
+	((info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_FAX) && \
+	 (info->emu.mdmreg[REG_L3PROT] == ISDN_PROTO_L3_FCLASS1))
+#define TTY_IS_FCLASS2(info) \
+	((info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_FAX) && \
+	 (info->emu.mdmreg[REG_L3PROT] == ISDN_PROTO_L3_FCLASS2))
+
+extern void isdn_tty_modem_escape(void);
+extern void isdn_tty_modem_ring(void);
+extern void isdn_tty_carrier_timeout(void);
+extern void isdn_tty_modem_xmit(void);
+extern int isdn_tty_modem_init(void);
+extern void isdn_tty_readmodem(void);
+extern int isdn_tty_find_icall(int, int, setup_parm *);
+extern void isdn_tty_cleanup_xmit(modem_info *);
+extern int isdn_tty_stat_callback(int, isdn_ctrl *);
+extern int isdn_tty_rcv_skb(int, int, int, struct sk_buff *);
+extern int isdn_tty_capi_facility(capi_msg *cm); 
+extern void isdn_tty_at_cout(char *, modem_info *);
+extern void isdn_tty_modem_hup(modem_info *, int);
+#ifdef CONFIG_ISDN_TTY_FAX
+extern int isdn_tty_cmd_PLUSF_FAX(char **, modem_info *);
+extern int isdn_tty_fax_command(modem_info *, isdn_ctrl *);
+extern void isdn_tty_fax_bitorder(modem_info *, struct sk_buff *);
+#endif
diff -Nru a/drivers/isdn/i4l/isdn_ttyfax.c b/drivers/isdn/i4l/isdn_ttyfax.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/i4l/isdn_ttyfax.c	Mon Apr 22 15:32:18 2002
@@ -0,0 +1,1122 @@
+/* $Id: isdn_ttyfax.c,v 1.7.6.2 2001/09/23 22:24:32 kai Exp $
+ *
+ * Linux ISDN subsystem, tty_fax AT-command emulator (linklevel).
+ *
+ * Copyright 1999    by Armin Schindler (mac@melware.de)
+ * Copyright 1999    by Ralf Spachmann (mel@melware.de)
+ * Copyright 1999    by Cytronics & Melware
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#undef ISDN_TTY_FAX_STAT_DEBUG
+#undef ISDN_TTY_FAX_CMD_DEBUG
+
+#include <linux/isdn.h>
+#include "isdn_common.h"
+#include "isdn_tty.h"
+#include "isdn_ttyfax.h"
+
+
+static char *isdn_tty_fax_revision = "$Revision: 1.7.6.2 $";
+
+#define PARSE_ERROR1 { isdn_tty_fax_modem_result(1, info); return 1; }
+
+static char *
+isdn_getrev(const char *revision)
+{
+	char *rev;
+	char *p;
+
+	if ((p = strchr(revision, ':'))) {
+		rev = p + 2;
+		p = strchr(rev, '$');
+		*--p = 0;
+	} else
+		rev = "???";
+	return rev;
+}
+
+/*
+ * Fax Class 2 Modem results
+ *
+ */
+
+static void
+isdn_tty_fax_modem_result(int code, modem_info * info)
+{
+	atemu *m = &info->emu;
+	T30_s *f = info->fax;
+	char rs[50];
+	char rss[50];
+	char *rp;
+	int i;
+	static char *msg[] =
+	{"OK", "ERROR", "+FCON", "+FCSI:", "+FDIS:",
+	 "+FHNG:", "+FDCS:", "CONNECT", "+FTSI:",
+	 "+FCFR", "+FPTS:", "+FET:"};
+
+
+	isdn_tty_at_cout("\r\n", info);
+	isdn_tty_at_cout(msg[code], info);
+
+#ifdef ISDN_TTY_FAX_CMD_DEBUG
+	printk(KERN_DEBUG "isdn_tty: Fax send %s on ttyI%d\n",
+		msg[code], info->line);
+#endif
+	switch (code) {
+		case 0: /* OK */
+			break;
+		case 1: /* ERROR */
+			break;
+		case 2:	/* +FCON */
+			/* Append CPN, if enabled */
+			if ((m->mdmreg[REG_CPNFCON] & BIT_CPNFCON) &&
+				(!(dev->usage[info->isdn_channel] & ISDN_USAGE_OUTGOING))) {
+				sprintf(rs, "/%s", m->cpn);
+				isdn_tty_at_cout(rs, info);
+			}
+			info->online = 1;
+			f->fet = 0;
+			if (f->phase == ISDN_FAX_PHASE_A)
+				f->phase = ISDN_FAX_PHASE_B;
+			break;
+		case 3:	/* +FCSI */
+		case 8:	/* +FTSI */
+			sprintf(rs, "\"%s\"", f->r_id);
+			isdn_tty_at_cout(rs, info);
+			break;
+		case 4:	/* +FDIS */
+			rs[0] = 0;
+			rp = &f->r_resolution;
+			for (i = 0; i < 8; i++) {
+				sprintf(rss, "%c%s", rp[i] + 48,
+					(i < 7) ? "," : "");
+				strcat(rs, rss);
+			}
+			isdn_tty_at_cout(rs, info);
+#ifdef ISDN_TTY_FAX_CMD_DEBUG
+			printk(KERN_DEBUG "isdn_tty: Fax DIS=%s on ttyI%d\n",
+			       rs, info->line);
+#endif
+			break;
+		case 5:	/* +FHNG */
+			sprintf(rs, "%d", f->code);
+			isdn_tty_at_cout(rs, info);
+			info->faxonline = 0;
+			break;
+		case 6:	/* +FDCS */
+			rs[0] = 0;
+			rp = &f->r_resolution;
+			for (i = 0; i < 8; i++) {
+				sprintf(rss, "%c%s", rp[i] + 48,
+					(i < 7) ? "," : "");
+				strcat(rs, rss);
+			}
+			isdn_tty_at_cout(rs, info);
+#ifdef ISDN_TTY_FAX_CMD_DEBUG
+			printk(KERN_DEBUG "isdn_tty: Fax DCS=%s on ttyI%d\n",
+			       rs, info->line);
+#endif
+			break;
+		case 7:	/* CONNECT */
+			info->faxonline |= 2;
+			break;
+		case 9:	/* FCFR */
+			break;
+		case 10:	/* FPTS */
+			isdn_tty_at_cout("1", info);
+			break;
+		case 11:	/* FET */
+			sprintf(rs, "%d", f->fet);
+			isdn_tty_at_cout(rs, info);
+			break;
+	}
+
+	isdn_tty_at_cout("\r\n", info);
+
+	switch (code) {
+		case 7:	/* CONNECT */
+			info->online = 2;
+			if (info->faxonline & 1) {
+				sprintf(rs, "%c", XON);
+				isdn_tty_at_cout(rs, info);
+			}
+			break;
+	}
+}
+
+int
+isdn_tty_fax_command1(modem_info * info, isdn_ctrl * c)
+{
+	static char *msg[] =
+	{"OK", "CONNECT", "NO CARRIER", "ERROR", "FCERROR"};
+
+#ifdef ISDN_TTY_FAX_CMD_DEBUG
+	printk(KERN_DEBUG "isdn_tty: FCLASS1 cmd(%d)\n", c->parm.aux.cmd);
+#endif
+	if (c->parm.aux.cmd < ISDN_FAX_CLASS1_QUERY) {
+		if (info->online)
+			info->online = 1;
+		isdn_tty_at_cout("\r\n", info);
+		isdn_tty_at_cout(msg[c->parm.aux.cmd], info);
+		isdn_tty_at_cout("\r\n", info);
+	}
+	switch (c->parm.aux.cmd) {
+		case ISDN_FAX_CLASS1_CONNECT:
+			info->online = 2;
+			break;
+		case ISDN_FAX_CLASS1_OK:
+		case ISDN_FAX_CLASS1_FCERROR:
+		case ISDN_FAX_CLASS1_ERROR:
+		case ISDN_FAX_CLASS1_NOCARR:
+			break;
+		case ISDN_FAX_CLASS1_QUERY:
+			isdn_tty_at_cout("\r\n", info);
+			if (!c->parm.aux.para[0]) {
+				isdn_tty_at_cout(msg[ISDN_FAX_CLASS1_ERROR], info);
+				isdn_tty_at_cout("\r\n", info);
+			} else {
+				isdn_tty_at_cout(c->parm.aux.para, info);
+				isdn_tty_at_cout("\r\nOK\r\n", info);
+			}
+			break;
+	}
+	return (0);
+}
+
+int
+isdn_tty_fax_command(modem_info * info, isdn_ctrl * c)
+{
+	T30_s *f = info->fax;
+	char rs[10];
+
+	if (TTY_IS_FCLASS1(info))
+		return (isdn_tty_fax_command1(info, c));
+
+#ifdef ISDN_TTY_FAX_CMD_DEBUG
+	printk(KERN_DEBUG "isdn_tty: Fax cmd %d on ttyI%d\n",
+	       f->r_code, info->line);
+#endif
+	switch (f->r_code) {
+		case ISDN_TTY_FAX_FCON:
+			info->faxonline = 1;
+			isdn_tty_fax_modem_result(2, info);	/* +FCON */
+			return (0);
+		case ISDN_TTY_FAX_FCON_I:
+			info->faxonline = 16;
+			isdn_tty_fax_modem_result(2, info);	/* +FCON */
+			return (0);
+		case ISDN_TTY_FAX_RID:
+			if (info->faxonline & 1)
+				isdn_tty_fax_modem_result(3, info);	/* +FCSI */
+			if (info->faxonline & 16)
+				isdn_tty_fax_modem_result(8, info);	/* +FTSI */
+			return (0);
+		case ISDN_TTY_FAX_DIS:
+			isdn_tty_fax_modem_result(4, info);	/* +FDIS */
+			return (0);
+		case ISDN_TTY_FAX_HNG:
+			if (f->phase == ISDN_FAX_PHASE_C) {
+				if (f->direction == ISDN_TTY_FAX_CONN_IN) {
+					sprintf(rs, "%c%c", DLE, ETX);
+					isdn_tty_at_cout(rs, info);
+				} else {
+					sprintf(rs, "%c", 0x18);
+					isdn_tty_at_cout(rs, info);
+				}
+				info->faxonline &= ~2;	/* leave data mode */
+				info->online = 1;
+			}
+			f->phase = ISDN_FAX_PHASE_E;
+			isdn_tty_fax_modem_result(5, info);	/* +FHNG */
+			isdn_tty_fax_modem_result(0, info);	/* OK */
+			return (0);
+		case ISDN_TTY_FAX_DCS:
+			isdn_tty_fax_modem_result(6, info);	/* +FDCS */
+			isdn_tty_fax_modem_result(7, info);	/* CONNECT */
+			f->phase = ISDN_FAX_PHASE_C;
+			return (0);
+		case ISDN_TTY_FAX_TRAIN_OK:
+			isdn_tty_fax_modem_result(6, info);	/* +FDCS */
+			isdn_tty_fax_modem_result(0, info);	/* OK */
+			return (0);
+		case ISDN_TTY_FAX_SENT:
+			isdn_tty_fax_modem_result(0, info);	/* OK */
+			return (0);
+		case ISDN_TTY_FAX_CFR:
+			isdn_tty_fax_modem_result(9, info);	/* +FCFR */
+			return (0);
+		case ISDN_TTY_FAX_ET:
+			sprintf(rs, "%c%c", DLE, ETX);
+			isdn_tty_at_cout(rs, info);
+			isdn_tty_fax_modem_result(10, info);	/* +FPTS */
+			isdn_tty_fax_modem_result(11, info);	/* +FET */
+			isdn_tty_fax_modem_result(0, info);	/* OK */
+			info->faxonline &= ~2;	/* leave data mode */
+			info->online = 1;
+			f->phase = ISDN_FAX_PHASE_D;
+			return (0);
+		case ISDN_TTY_FAX_PTS:
+			isdn_tty_fax_modem_result(10, info);	/* +FPTS */
+			if (f->direction == ISDN_TTY_FAX_CONN_OUT) {
+				if (f->fet == 1)
+					f->phase = ISDN_FAX_PHASE_B;
+				if (f->fet == 0)
+					isdn_tty_fax_modem_result(0, info);	/* OK */
+			}
+			return (0);
+		case ISDN_TTY_FAX_EOP:
+			info->faxonline &= ~2;	/* leave data mode */
+			info->online = 1;
+			f->phase = ISDN_FAX_PHASE_D;
+			return (0);
+
+	}
+	return (-1);
+}
+
+
+void
+isdn_tty_fax_bitorder(modem_info * info, struct sk_buff *skb)
+{
+	__u8 LeftMask;
+	__u8 RightMask;
+	__u8 fBit;
+	__u8 Data;
+	int i;
+
+	if (!info->fax->bor) {
+		for (i = 0; i < skb->len; i++) {
+			Data = skb->data[i];
+			for (
+				    LeftMask = 0x80, RightMask = 0x01;
+				    LeftMask > RightMask;
+				    LeftMask >>= 1, RightMask <<= 1
+			    ) {
+				fBit = (Data & LeftMask);
+				if (Data & RightMask)
+					Data |= LeftMask;
+				else
+					Data &= ~LeftMask;
+				if (fBit)
+					Data |= RightMask;
+				else
+					Data &= ~RightMask;
+
+			}
+			skb->data[i] = Data;
+		}
+	}
+}
+
+/*
+ * Parse AT+F.. FAX class 1 commands
+ */
+
+int
+isdn_tty_cmd_FCLASS1(char **p, modem_info * info)
+{
+	static char *cmd[] =
+	{"AE", "TS", "RS", "TM", "RM", "TH", "RH"};
+	isdn_ctrl c;
+	int par, i;
+	long flags;
+
+	for (c.parm.aux.cmd = 0; c.parm.aux.cmd < 7; c.parm.aux.cmd++)
+		if (!strncmp(p[0], cmd[c.parm.aux.cmd], 2))
+			break;
+
+#ifdef ISDN_TTY_FAX_CMD_DEBUG
+	printk(KERN_DEBUG "isdn_tty_cmd_FCLASS1 (%s,%d)\n", p[0], c.parm.aux.cmd);
+#endif
+	if (c.parm.aux.cmd == 7)
+		PARSE_ERROR1;
+
+	p[0] += 2;
+	switch (*p[0]) {
+		case '?':
+			p[0]++;
+			c.parm.aux.subcmd = AT_QUERY;
+			break;
+		case '=':
+			p[0]++;
+			if (*p[0] == '?') {
+				p[0]++;
+				c.parm.aux.subcmd = AT_EQ_QUERY;
+			} else {
+				par = isdn_getnum(p);
+				if ((par < 0) || (par > 255))
+					PARSE_ERROR1;
+				c.parm.aux.subcmd = AT_EQ_VALUE;
+				c.parm.aux.para[0] = par;
+			}
+			break;
+		case 0:
+			c.parm.aux.subcmd = AT_COMMAND;
+			break;
+		default:
+			PARSE_ERROR1;
+	}
+	c.command = ISDN_CMD_FAXCMD;
+#ifdef ISDN_TTY_FAX_CMD_DEBUG
+	printk(KERN_DEBUG "isdn_tty_cmd_FCLASS1 %d/%d/%d)\n",
+	       c.parm.aux.cmd, c.parm.aux.subcmd, c.parm.aux.para[0]);
+#endif
+	if (info->isdn_driver < 0) {
+		save_flags(flags);
+		cli();
+		if ((c.parm.aux.subcmd == AT_EQ_VALUE) ||
+		    (c.parm.aux.subcmd == AT_COMMAND)) {
+			restore_flags(flags);
+			PARSE_ERROR1;
+		}
+		/* get a temporary connection to the first free fax driver */
+		i = isdn_get_free_channel(ISDN_USAGE_FAX, ISDN_PROTO_L2_FAX,
+					  ISDN_PROTO_L3_FCLASS1, -1, -1, "00");
+		if (i < 0) {
+			restore_flags(flags);
+			PARSE_ERROR1;
+		}
+		info->isdn_driver = dev->drvmap[i];
+		info->isdn_channel = dev->chanmap[i];
+		info->drv_index = i;
+		dev->m_idx[i] = info->line;
+		c.driver = info->isdn_driver;
+		c.arg = info->isdn_channel;
+		isdn_command(&c);
+		isdn_free_channel(info->isdn_driver, info->isdn_channel,
+				  ISDN_USAGE_FAX);
+		info->isdn_driver = -1;
+		info->isdn_channel = -1;
+		if (info->drv_index >= 0) {
+			dev->m_idx[info->drv_index] = -1;
+			info->drv_index = -1;
+		}
+		restore_flags(flags);
+	} else {
+		c.driver = info->isdn_driver;
+		c.arg = info->isdn_channel;
+		isdn_command(&c);
+	}
+	return 1;
+}
+
+/*
+ * Parse AT+F.. FAX class 2 commands
+ */
+
+int
+isdn_tty_cmd_FCLASS2(char **p, modem_info * info)
+{
+	atemu *m = &info->emu;
+	T30_s *f = info->fax;
+	isdn_ctrl cmd;
+	int par;
+	char rs[50];
+	char rss[50];
+	int maxdccval[] =
+	{1, 5, 2, 2, 3, 2, 0, 7};
+
+	/* FAA still unchanged */
+	if (!strncmp(p[0], "AA", 2)) {	/* TODO */
+		p[0] += 2;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				sprintf(rs, "\r\n%d", 0);
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				par = isdn_getnum(p);
+				if ((par < 0) || (par > 255))
+					PARSE_ERROR1;
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+	/* BADLIN=value - dummy 0=disable errorchk disabled, 1-255 nr. of lines for making page bad */
+	if (!strncmp(p[0], "BADLIN", 6)) {
+		p[0] += 6;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				sprintf(rs, "\r\n%d", f->badlin);
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n0-255");
+					isdn_tty_at_cout(rs, info);
+				} else {
+					par = isdn_getnum(p);
+					if ((par < 0) || (par > 255))
+						PARSE_ERROR1;
+					f->badlin = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+					printk(KERN_DEBUG "isdn_tty: Fax FBADLIN=%d\n", par);
+#endif
+				}
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+	/* BADMUL=value - dummy 0=disable errorchk disabled (treshold multiplier) */
+	if (!strncmp(p[0], "BADMUL", 6)) {
+		p[0] += 6;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				sprintf(rs, "\r\n%d", f->badmul);
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n0-255");
+					isdn_tty_at_cout(rs, info);
+				} else {
+					par = isdn_getnum(p);
+					if ((par < 0) || (par > 255))
+						PARSE_ERROR1;
+					f->badmul = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+					printk(KERN_DEBUG "isdn_tty: Fax FBADMUL=%d\n", par);
+#endif
+				}
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+	/* BOR=n - Phase C bit order, 0=direct, 1=reverse */
+	if (!strncmp(p[0], "BOR", 3)) {
+		p[0] += 3;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				sprintf(rs, "\r\n%d", f->bor);
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n0,1");
+					isdn_tty_at_cout(rs, info);
+				} else {
+					par = isdn_getnum(p);
+					if ((par < 0) || (par > 1))
+						PARSE_ERROR1;
+					f->bor = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+					printk(KERN_DEBUG "isdn_tty: Fax FBOR=%d\n", par);
+#endif
+				}
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+	/* NBC=n - No Best Capabilities */
+	if (!strncmp(p[0], "NBC", 3)) {
+		p[0] += 3;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				sprintf(rs, "\r\n%d", f->nbc);
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n0,1");
+					isdn_tty_at_cout(rs, info);
+				} else {
+					par = isdn_getnum(p);
+					if ((par < 0) || (par > 1))
+						PARSE_ERROR1;
+					f->nbc = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+					printk(KERN_DEBUG "isdn_tty: Fax FNBC=%d\n", par);
+#endif
+				}
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+	/* BUF? - Readonly buffersize readout  */
+	if (!strncmp(p[0], "BUF?", 4)) {
+		p[0] += 4;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+		printk(KERN_DEBUG "isdn_tty: Fax FBUF? (%d) \n", (16 * m->mdmreg[REG_PSIZE]));
+#endif
+		p[0]++;
+		sprintf(rs, "\r\n %d ", (16 * m->mdmreg[REG_PSIZE]));
+		isdn_tty_at_cout(rs, info);
+		return 0;
+	}
+	/* CIG=string - local fax station id string for polling rx */
+	if (!strncmp(p[0], "CIG", 3)) {
+		int i, r;
+		p[0] += 3;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				sprintf(rs, "\r\n\"%s\"", f->pollid);
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n\"STRING\"");
+					isdn_tty_at_cout(rs, info);
+				} else {
+					if (*p[0] == '"')
+						p[0]++;
+					for (i = 0; (*p[0]) && i < (FAXIDLEN - 1) && (*p[0] != '"'); i++) {
+						f->pollid[i] = *p[0]++;
+					}
+					if (*p[0] == '"')
+						p[0]++;
+					for (r = i; r < FAXIDLEN; r++) {
+						f->pollid[r] = 32;
+					}
+					f->pollid[FAXIDLEN - 1] = 0;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+					printk(KERN_DEBUG "isdn_tty: Fax local poll ID rx \"%s\"\n", f->pollid);
+#endif
+				}
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+	/* CQ=n - copy qlty chk, 0= no chk, 1=only 1D chk, 2=1D+2D chk */
+	if (!strncmp(p[0], "CQ", 2)) {
+		p[0] += 2;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				sprintf(rs, "\r\n%d", f->cq);
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n0,1,2");
+					isdn_tty_at_cout(rs, info);
+				} else {
+					par = isdn_getnum(p);
+					if ((par < 0) || (par > 2))
+						PARSE_ERROR1;
+					f->cq = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+					printk(KERN_DEBUG "isdn_tty: Fax FCQ=%d\n", par);
+#endif
+				}
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+	/* CR=n - can receive? 0= no data rx or poll remote dev, 1=do receive data or poll remote dev */
+	if (!strncmp(p[0], "CR", 2)) {
+		p[0] += 2;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				sprintf(rs, "\r\n%d", f->cr);	/* read actual value from struct and print */
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n0,1");		/* display online help */
+					isdn_tty_at_cout(rs, info);
+				} else {
+					par = isdn_getnum(p);
+					if ((par < 0) || (par > 1))
+						PARSE_ERROR1;
+					f->cr = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+					printk(KERN_DEBUG "isdn_tty: Fax FCR=%d\n", par);
+#endif
+				}
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+	/* CTCRTY=value - ECM retry count */
+	if (!strncmp(p[0], "CTCRTY", 6)) {
+		p[0] += 6;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				sprintf(rs, "\r\n%d", f->ctcrty);
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n0-255");
+					isdn_tty_at_cout(rs, info);
+				} else {
+					par = isdn_getnum(p);
+					if ((par < 0) || (par > 255))
+						PARSE_ERROR1;
+					f->ctcrty = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+					printk(KERN_DEBUG "isdn_tty: Fax FCTCRTY=%d\n", par);
+#endif
+				}
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+	/* DCC=vr,br,wd,ln,df,ec,bf,st - DCE capabilities parms */
+	if (!strncmp(p[0], "DCC", 3)) {
+		char *rp = &f->resolution;
+		int i;
+
+		p[0] += 3;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				strcpy(rs, "\r\n");
+				for (i = 0; i < 8; i++) {
+					sprintf(rss, "%c%s", rp[i] + 48,
+						(i < 7) ? "," : "");
+					strcat(rs, rss);
+				}
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				if (*p[0] == '?') {
+					isdn_tty_at_cout("\r\n(0,1),(0-5),(0-2),(0-2),(0-3),(0-2),(0),(0-7)", info);
+					p[0]++;
+				} else {
+					for (i = 0; (((*p[0] >= '0') && (*p[0] <= '9')) || (*p[0] == ',')) && (i < 8); i++) {
+						if (*p[0] != ',') {
+							if ((*p[0] - 48) > maxdccval[i]) {
+								PARSE_ERROR1;
+							}
+							rp[i] = *p[0] - 48;
+							p[0]++;
+							if (*p[0] == ',')
+								p[0]++;
+						} else
+							p[0]++;
+					}
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+					printk(KERN_DEBUG "isdn_tty: Fax FDCC capabilities DCE=%d,%d,%d,%d,%d,%d,%d,%d\n",
+					       rp[0], rp[1], rp[2], rp[3], rp[4], rp[5], rp[6], rp[7]);
+#endif
+				}
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+	/* DIS=vr,br,wd,ln,df,ec,bf,st - current session parms */
+	if (!strncmp(p[0], "DIS", 3)) {
+		char *rp = &f->resolution;
+		int i;
+
+		p[0] += 3;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				strcpy(rs, "\r\n");
+				for (i = 0; i < 8; i++) {
+					sprintf(rss, "%c%s", rp[i] + 48,
+						(i < 7) ? "," : "");
+					strcat(rs, rss);
+				}
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				if (*p[0] == '?') {
+					isdn_tty_at_cout("\r\n(0,1),(0-5),(0-2),(0-2),(0-3),(0-2),(0),(0-7)", info);
+					p[0]++;
+				} else {
+					for (i = 0; (((*p[0] >= '0') && (*p[0] <= '9')) || (*p[0] == ',')) && (i < 8); i++) {
+						if (*p[0] != ',') {
+							if ((*p[0] - 48) > maxdccval[i]) {
+								PARSE_ERROR1;
+							}
+							rp[i] = *p[0] - 48;
+							p[0]++;
+							if (*p[0] == ',')
+								p[0]++;
+						} else
+							p[0]++;
+					}
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+					printk(KERN_DEBUG "isdn_tty: Fax FDIS session parms=%d,%d,%d,%d,%d,%d,%d,%d\n",
+					       rp[0], rp[1], rp[2], rp[3], rp[4], rp[5], rp[6], rp[7]);
+#endif
+				}
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+	/* DR - Receive Phase C data command, initiates document reception */
+	if (!strncmp(p[0], "DR", 2)) {
+		p[0] += 2;
+		if ((info->faxonline & 16) &&	/* incoming connection */
+		    ((f->phase == ISDN_FAX_PHASE_B) || (f->phase == ISDN_FAX_PHASE_D))) {
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+			printk(KERN_DEBUG "isdn_tty: Fax FDR\n");
+#endif
+			f->code = ISDN_TTY_FAX_DR;
+			cmd.driver = info->isdn_driver;
+			cmd.arg = info->isdn_channel;
+			cmd.command = ISDN_CMD_FAXCMD;
+			isdn_command(&cmd);
+			if (f->phase == ISDN_FAX_PHASE_B) {
+				f->phase = ISDN_FAX_PHASE_C;
+			} else if (f->phase == ISDN_FAX_PHASE_D) {
+				switch (f->fet) {
+					case 0:	/* next page will be received */
+						f->phase = ISDN_FAX_PHASE_C;
+						isdn_tty_fax_modem_result(7, info);	/* CONNECT */
+						break;
+					case 1:	/* next doc will be received */
+						f->phase = ISDN_FAX_PHASE_B;
+						break;
+					case 2:	/* fax session is terminating */
+						f->phase = ISDN_FAX_PHASE_E;
+						break;
+					default:
+						PARSE_ERROR1;
+				}
+			}
+		} else {
+			PARSE_ERROR1;
+		}
+		return 1;
+	}
+	/* DT=df,vr,wd,ln - TX phase C data command (release DCE to proceed with negotiation) */
+	if (!strncmp(p[0], "DT", 2)) {
+		int i, val[] =
+		{4, 0, 2, 3};
+		char *rp = &f->resolution;
+
+		p[0] += 2;
+		if (!info->faxonline & 1)	/* not outgoing connection */
+			PARSE_ERROR1;
+
+		for (i = 0; (((*p[0] >= '0') && (*p[0] <= '9')) || (*p[0] == ',')) && (i < 4); i++) {
+			if (*p[0] != ',') {
+				if ((*p[0] - 48) > maxdccval[val[i]]) {
+					PARSE_ERROR1;
+				}
+				rp[val[i]] = *p[0] - 48;
+				p[0]++;
+				if (*p[0] == ',')
+					p[0]++;
+			} else
+				p[0]++;
+		}
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+		printk(KERN_DEBUG "isdn_tty: Fax FDT tx data command parms=%d,%d,%d,%d\n",
+		       rp[4], rp[0], rp[2], rp[3]);
+#endif
+		if ((f->phase == ISDN_FAX_PHASE_B) || (f->phase == ISDN_FAX_PHASE_D)) {
+			f->code = ISDN_TTY_FAX_DT;
+			cmd.driver = info->isdn_driver;
+			cmd.arg = info->isdn_channel;
+			cmd.command = ISDN_CMD_FAXCMD;
+			isdn_command(&cmd);
+			if (f->phase == ISDN_FAX_PHASE_D) {
+				f->phase = ISDN_FAX_PHASE_C;
+				isdn_tty_fax_modem_result(7, info);	/* CONNECT */
+			}
+		} else {
+			PARSE_ERROR1;
+		}
+		return 1;
+	}
+	/* ECM=n - Error mode control 0=disabled, 2=enabled, handled by DCE alone incl. buff of partial pages */
+	if (!strncmp(p[0], "ECM", 3)) {
+		p[0] += 3;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				sprintf(rs, "\r\n%d", f->ecm);
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n0,2");
+					isdn_tty_at_cout(rs, info);
+				} else {
+					par = isdn_getnum(p);
+					if ((par != 0) && (par != 2))
+						PARSE_ERROR1;
+					f->ecm = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+					printk(KERN_DEBUG "isdn_tty: Fax FECM=%d\n", par);
+#endif
+				}
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+	/* ET=n - End of page or document */
+	if (!strncmp(p[0], "ET=", 3)) {
+		p[0] += 3;
+		if (*p[0] == '?') {
+			p[0]++;
+			sprintf(rs, "\r\n0-2");
+			isdn_tty_at_cout(rs, info);
+		} else {
+			if ((f->phase != ISDN_FAX_PHASE_D) || (!info->faxonline & 1))
+				PARSE_ERROR1;
+			par = isdn_getnum(p);
+			if ((par < 0) || (par > 2))
+				PARSE_ERROR1;
+			f->fet = par;
+			f->code = ISDN_TTY_FAX_ET;
+			cmd.driver = info->isdn_driver;
+			cmd.arg = info->isdn_channel;
+			cmd.command = ISDN_CMD_FAXCMD;
+			isdn_command(&cmd);
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+			printk(KERN_DEBUG "isdn_tty: Fax FET=%d\n", par);
+#endif
+			return 1;
+		}
+		return 0;
+	}
+	/* K - terminate */
+	if (!strncmp(p[0], "K", 1)) {
+		p[0] += 1;
+		if ((f->phase == ISDN_FAX_PHASE_IDLE) || (f->phase == ISDN_FAX_PHASE_E))
+			PARSE_ERROR1;
+		isdn_tty_modem_hup(info, 1);
+		return 1;
+	}
+	/* LID=string - local fax ID */
+	if (!strncmp(p[0], "LID", 3)) {
+		int i, r;
+		p[0] += 3;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				sprintf(rs, "\r\n\"%s\"", f->id);
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n\"STRING\"");
+					isdn_tty_at_cout(rs, info);
+				} else {
+					if (*p[0] == '"')
+						p[0]++;
+					for (i = 0; (*p[0]) && i < (FAXIDLEN - 1) && (*p[0] != '"'); i++) {
+						f->id[i] = *p[0]++;
+					}
+					if (*p[0] == '"')
+						p[0]++;
+					for (r = i; r < FAXIDLEN; r++) {
+						f->id[r] = 32;
+					}
+					f->id[FAXIDLEN - 1] = 0;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+					printk(KERN_DEBUG "isdn_tty: Fax local ID \"%s\"\n", f->id);
+#endif
+				}
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+
+	/* MDL? - DCE Model       */
+	if (!strncmp(p[0], "MDL?", 4)) {
+		p[0] += 4;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+		printk(KERN_DEBUG "isdn_tty: FMDL?\n");
+#endif
+		isdn_tty_at_cout("\r\nisdn4linux", info);
+		return 0;
+	}
+	/* MFR? - DCE Manufacturer */
+	if (!strncmp(p[0], "MFR?", 4)) {
+		p[0] += 4;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+		printk(KERN_DEBUG "isdn_tty: FMFR?\n");
+#endif
+		isdn_tty_at_cout("\r\nisdn4linux", info);
+		return 0;
+	}
+	/* MINSP=n - Minimum Speed for Phase C */
+	if (!strncmp(p[0], "MINSP", 5)) {
+		p[0] += 5;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				sprintf(rs, "\r\n%d", f->minsp);
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n0-5");
+					isdn_tty_at_cout(rs, info);
+				} else {
+					par = isdn_getnum(p);
+					if ((par < 0) || (par > 5))
+						PARSE_ERROR1;
+					f->minsp = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+					printk(KERN_DEBUG "isdn_tty: Fax FMINSP=%d\n", par);
+#endif
+				}
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+	/* PHCTO=value - DTE phase C timeout */
+	if (!strncmp(p[0], "PHCTO", 5)) {
+		p[0] += 5;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				sprintf(rs, "\r\n%d", f->phcto);
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n0-255");
+					isdn_tty_at_cout(rs, info);
+				} else {
+					par = isdn_getnum(p);
+					if ((par < 0) || (par > 255))
+						PARSE_ERROR1;
+					f->phcto = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+					printk(KERN_DEBUG "isdn_tty: Fax FPHCTO=%d\n", par);
+#endif
+				}
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+
+	/* REL=n - Phase C received EOL alignment */
+	if (!strncmp(p[0], "REL", 3)) {
+		p[0] += 3;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				sprintf(rs, "\r\n%d", f->rel);
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n0,1");
+					isdn_tty_at_cout(rs, info);
+				} else {
+					par = isdn_getnum(p);
+					if ((par < 0) || (par > 1))
+						PARSE_ERROR1;
+					f->rel = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+					printk(KERN_DEBUG "isdn_tty: Fax FREL=%d\n", par);
+#endif
+				}
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+	/* REV? - DCE Revision */
+	if (!strncmp(p[0], "REV?", 4)) {
+		p[0] += 4;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+		printk(KERN_DEBUG "isdn_tty: FREV?\n");
+#endif
+		strcpy(rss, isdn_tty_fax_revision);
+		sprintf(rs, "\r\nRev: %s", isdn_getrev(rss));
+		isdn_tty_at_cout(rs, info);
+		return 0;
+	}
+
+	/* Phase C Transmit Data Block Size */
+	if (!strncmp(p[0], "TBC=", 4)) {	/* dummy, not used */
+		p[0] += 4;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+		printk(KERN_DEBUG "isdn_tty: Fax FTBC=%c\n", *p[0]);
+#endif
+		switch (*p[0]) {
+			case '0':
+				p[0]++;
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+	printk(KERN_DEBUG "isdn_tty: unknown token=>AT+F%s<\n", p[0]);
+	PARSE_ERROR1;
+}
+
+int
+isdn_tty_cmd_PLUSF_FAX(char **p, modem_info * info)
+{
+	if (TTY_IS_FCLASS2(info))
+		return (isdn_tty_cmd_FCLASS2(p, info));
+	else if (TTY_IS_FCLASS1(info))
+		return (isdn_tty_cmd_FCLASS1(p, info));
+	PARSE_ERROR1;
+}
diff -Nru a/drivers/isdn/i4l/isdn_ttyfax.h b/drivers/isdn/i4l/isdn_ttyfax.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/i4l/isdn_ttyfax.h	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,18 @@
+/* $Id: isdn_ttyfax.h,v 1.2.6.1 2001/09/23 22:24:32 kai Exp $
+ *
+ * header for Linux ISDN subsystem, tty_fax related functions (linklevel).
+ *
+ * Copyright 1999   by Armin Schindler (mac@melware.de)
+ * Copyright 1999   by Ralf Spachmann (mel@melware.de)
+ * Copyright 1999   by Cytronics & Melware
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+
+#define XON	0x11
+#define XOFF	0x13
+#define DC2	0x12
+
diff -Nru a/drivers/isdn/i4l/isdn_v110.c b/drivers/isdn/i4l/isdn_v110.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/i4l/isdn_v110.c	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,614 @@
+/* $Id: isdn_v110.c,v 1.5.6.4 2001/09/23 22:24:32 kai Exp $
+ *
+ * Linux ISDN subsystem, V.110 related functions (linklevel).
+ *
+ * Copyright by Thomas Pfeiffer (pfeiffer@pds.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+
+#include <linux/isdn.h>
+#include "isdn_v110.h"
+
+#undef ISDN_V110_DEBUG
+
+char *isdn_v110_revision = "$Revision: 1.5.6.4 $";
+
+#define V110_38400 255
+#define V110_19200  15
+#define V110_9600    3
+
+/* 
+ * The following data are precoded matrices, online and offline matrix 
+ * for 9600, 19200 und 38400, respectively
+ */
+static unsigned char V110_OnMatrix_9600[] =
+{0xfc, 0xfc, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff,
+ 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd,
+ 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff,
+ 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd};
+
+static unsigned char V110_OffMatrix_9600[] =
+{0xfc, 0xfc, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+static unsigned char V110_OnMatrix_19200[] =
+{0xf0, 0xf0, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0xf7,
+ 0xfd, 0xff, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0xf7};
+
+static unsigned char V110_OffMatrix_19200[] =
+{0xf0, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+static unsigned char V110_OnMatrix_38400[] =
+{0x00, 0x7f, 0x7f, 0x7f, 0x7f, 0xfd, 0x7f, 0x7f, 0x7f, 0x7f};
+
+static unsigned char V110_OffMatrix_38400[] =
+{0x00, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff};
+
+/* 
+ * FlipBits reorders sequences of keylen bits in one byte.
+ * E.g. source order 7654321 will be converted to 45670123 when keylen = 4,
+ * and to 67452301 when keylen = 2. This is necessary because ordering on
+ * the isdn line is the other way.
+ */
+static __inline unsigned char
+FlipBits(unsigned char c, int keylen)
+{
+	unsigned char b = c;
+	unsigned char bit = 128;
+	int i;
+	int j;
+	int hunks = (8 / keylen);
+
+	c = 0;
+	for (i = 0; i < hunks; i++) {
+		for (j = 0; j < keylen; j++) {
+			if (b & (bit >> j))
+				c |= bit >> (keylen - j - 1);
+		}
+		bit >>= keylen;
+	}
+	return c;
+}
+
+
+/* isdn_v110_open allocates and initializes private V.110 data
+ * structures and returns a pointer to these.
+ */
+static isdn_v110_stream *
+isdn_v110_open(unsigned char key, int hdrlen, int maxsize)
+{
+	int i;
+	isdn_v110_stream *v;
+
+	if ((v = kmalloc(sizeof(isdn_v110_stream), GFP_ATOMIC)) == NULL)
+		return NULL;
+	memset(v, 0, sizeof(isdn_v110_stream));
+	v->key = key;
+	v->nbits = 0;
+	for (i = 0; key & (1 << i); i++)
+		v->nbits++;
+
+	v->nbytes = 8 / v->nbits;
+	v->decodelen = 0;
+
+	switch (key) {
+		case V110_38400:
+			v->OnlineFrame = V110_OnMatrix_38400;
+			v->OfflineFrame = V110_OffMatrix_38400;
+			break;
+		case V110_19200:
+			v->OnlineFrame = V110_OnMatrix_19200;
+			v->OfflineFrame = V110_OffMatrix_19200;
+			break;
+		default:
+			v->OnlineFrame = V110_OnMatrix_9600;
+			v->OfflineFrame = V110_OffMatrix_9600;
+			break;
+	}
+	v->framelen = v->nbytes * 10;
+	v->SyncInit = 5;
+	v->introducer = 0;
+	v->dbit = 1;
+	v->b = 0;
+	v->skbres = hdrlen;
+	v->maxsize = maxsize - hdrlen;
+	if ((v->encodebuf = kmalloc(maxsize, GFP_ATOMIC)) == NULL) {
+		kfree(v);
+		return NULL;
+	}
+	return v;
+}
+
+/* isdn_v110_close frees private V.110 data structures */
+void
+isdn_v110_close(isdn_v110_stream * v)
+{
+	if (v == NULL)
+		return;
+#ifdef ISDN_V110_DEBUG
+	printk(KERN_DEBUG "v110 close\n");
+#endif
+	kfree(v->encodebuf);
+	kfree(v);
+}
+
+
+/* 
+ * ValidHeaderBytes return the number of valid bytes in v->decodebuf 
+ */
+static int
+ValidHeaderBytes(isdn_v110_stream * v)
+{
+	int i;
+	for (i = 0; (i < v->decodelen) && (i < v->nbytes); i++)
+		if ((v->decodebuf[i] & v->key) != 0)
+			break;
+	return i;
+}
+
+/* 
+ * SyncHeader moves the decodebuf ptr to the next valid header 
+ */
+static void
+SyncHeader(isdn_v110_stream * v)
+{
+	unsigned char *rbuf = v->decodebuf;
+	int len = v->decodelen;
+
+	if (len == 0)
+		return;
+	for (rbuf++, len--; len > 0; len--, rbuf++)	/* such den SyncHeader in buf ! */
+		if ((*rbuf & v->key) == 0)	/* erstes byte gefunden ?       */
+			break;  /* jupp!                        */
+	if (len)
+		memcpy(v->decodebuf, rbuf, len);
+
+	v->decodelen = len;
+#ifdef ISDN_V110_DEBUG
+	printk(KERN_DEBUG "isdn_v110: Header resync\n");
+#endif
+}
+
+/* DecodeMatrix takes n (n>=1) matrices (v110 frames, 10 bytes) where
+   len is the number of matrix-lines. len must be a multiple of 10, i.e.
+   only complete matices must be given.
+   From these, netto data is extracted and returned in buf. The return-value
+   is the bytecount of the decoded data.
+ */
+static int
+DecodeMatrix(isdn_v110_stream * v, unsigned char *m, int len, unsigned char *buf)
+{
+	int line = 0;
+	int buflen = 0;
+	int mbit = 64;
+	int introducer = v->introducer;
+	int dbit = v->dbit;
+	unsigned char b = v->b;
+
+	while (line < len) {    /* Are we done with all lines of the matrix? */
+		if ((line % 10) == 0) {	/* the 0. line of the matrix is always 0 ! */
+			if (m[line] != 0x00) {	/* not 0 ? -> error! */
+#ifdef ISDN_V110_DEBUG
+				printk(KERN_DEBUG "isdn_v110: DecodeMatrix, V110 Bad Header\n");
+				/* returning now is not the right thing, though :-( */
+#endif
+			} 
+			line++; /* next line of matrix */
+			continue;
+		} else if ((line % 10) == 5) {	/* in line 5 there's only e-bits ! */
+			if ((m[line] & 0x70) != 0x30) {	/* 011 has to be at the beginning! */
+#ifdef ISDN_V110_DEBUG
+				printk(KERN_DEBUG "isdn_v110: DecodeMatrix, V110 Bad 5th line\n");
+				/* returning now is not the right thing, though :-( */
+#endif
+			}
+			line++; /* next line */
+			continue;
+		} else if (!introducer) {	/* every byte starts with 10 (stopbit, startbit) */
+			introducer = (m[line] & mbit) ? 0 : 1;	/* current bit of the matrix */
+		      next_byte:
+			if (mbit > 2) {	/* was it the last bit in this line ? */
+				mbit >>= 1;	/* no -> take next */
+				continue;
+			}       /* otherwise start with leftmost bit in the next line */
+			mbit = 64;
+			line++;
+			continue;
+		} else {        /* otherwise we need to set a data bit */
+			if (m[line] & mbit)	/* was that bit set in the matrix ? */
+				b |= dbit;	/* yes -> set it in the data byte */
+			else
+				b &= dbit - 1;	/* no -> clear it in the data byte */
+			if (dbit < 128)	/* is that data byte done ? */
+				dbit <<= 1;	/* no, got the next bit */
+			else {  /* data byte is done */
+				buf[buflen++] = b;	/* copy byte into the output buffer */
+				introducer = b = 0;	/* init of the intro sequence and of the data byte */
+				dbit = 1;	/* next we look for the 0th bit */
+			}
+			goto next_byte;	/* look for next bit in the matrix */
+		}
+	}
+	v->introducer = introducer;
+	v->dbit = dbit;
+	v->b = b;
+	return buflen;          /* return number of bytes in the output buffer */
+}
+
+/* 
+ * DecodeStream receives V.110 coded data from the input stream. It recovers the 
+ * original frames.
+ * The input stream doesn't need to be framed
+ */
+struct sk_buff *
+isdn_v110_decode(isdn_v110_stream * v, struct sk_buff *skb)
+{
+	int i;
+	int j;
+	int len;
+	unsigned char *v110_buf;
+	unsigned char *rbuf;
+
+	if (!skb) {
+		printk(KERN_WARNING "isdn_v110_decode called with NULL skb!\n");
+		return NULL;
+	}
+	rbuf = skb->data;
+	len = skb->len;
+	if (v == NULL) {
+		/* invalid handle, no chance to proceed */
+		printk(KERN_WARNING "isdn_v110_decode called with NULL stream!\n");
+		dev_kfree_skb(skb);
+		return NULL;
+	}
+	if (v->decodelen == 0)  /* cache empty?               */
+		for (; len > 0; len--, rbuf++)	/* scan for SyncHeader in buf */
+			if ((*rbuf & v->key) == 0)
+				break;	/* found first byte           */
+	if (len == 0) {
+		dev_kfree_skb(skb);
+		return NULL;
+	}
+	/* copy new data to decode-buffer */
+	memcpy(&(v->decodebuf[v->decodelen]), rbuf, len);
+	v->decodelen += len;
+      ReSync:
+	if (v->decodelen < v->nbytes) {	/* got a new header ? */
+		dev_kfree_skb(skb);
+		return NULL;    /* no, try later      */
+	}
+	if (ValidHeaderBytes(v) != v->nbytes) {	/* is that a valid header? */
+		SyncHeader(v);  /* no -> look for header */
+		goto ReSync;
+	}
+	len = (v->decodelen - (v->decodelen % (10 * v->nbytes))) / v->nbytes;
+	if ((v110_buf = kmalloc(len, GFP_ATOMIC)) == NULL) {
+		printk(KERN_WARNING "isdn_v110_decode: Couldn't allocate v110_buf\n");
+		dev_kfree_skb(skb);
+		return NULL;
+	}
+	for (i = 0; i < len; i++) {
+		v110_buf[i] = 0;
+		for (j = 0; j < v->nbytes; j++)
+			v110_buf[i] |= (v->decodebuf[(i * v->nbytes) + j] & v->key) << (8 - ((j + 1) * v->nbits));
+		v110_buf[i] = FlipBits(v110_buf[i], v->nbits);
+	}
+	v->decodelen = (v->decodelen % (10 * v->nbytes));
+	memcpy(v->decodebuf, &(v->decodebuf[len * v->nbytes]), v->decodelen);
+
+	skb_trim(skb, DecodeMatrix(v, v110_buf, len, skb->data));
+	kfree(v110_buf);
+	if (skb->len)
+		return skb;
+	else {
+		kfree_skb(skb);
+		return NULL;
+	}
+}
+
+/* EncodeMatrix takes input data in buf, len is the bytecount.
+   Data is encoded into v110 frames in m. Return value is the number of
+   matrix-lines generated.
+ */
+static int
+EncodeMatrix(unsigned char *buf, int len, unsigned char *m, int mlen)
+{
+	int line = 0;
+	int i = 0;
+	int mbit = 128;
+	int dbit = 1;
+	int introducer = 3;
+	int ibit[] = {0, 1, 1};
+
+	while ((i < len) && (line < mlen)) {	/* while we still have input data */
+		switch (line % 10) {	/* in which line of the matrix are we? */
+			case 0:
+				m[line++] = 0x00;	/* line 0 is always 0 */
+				mbit = 128;	/* go on with the 7th bit */
+				break;
+			case 5:
+				m[line++] = 0xbf;	/* line 5 is always 10111111 */
+				mbit = 128;	/* go on with the 7th bit */
+				break;
+		}
+		if (line >= mlen) {
+			printk(KERN_WARNING "isdn_v110 (EncodeMatrix): buffer full!\n");
+			return line;
+		}
+	next_bit:
+		switch (mbit) { /* leftmost or rightmost bit ? */
+			case 1:
+				line++;	/* rightmost -> go to next line */
+				if (line >= mlen) {
+					printk(KERN_WARNING "isdn_v110 (EncodeMatrix): buffer full!\n");
+					return line;
+				}
+			case 128:
+				m[line] = 128;	/* leftmost -> set byte to 1000000 */
+				mbit = 64;	/* current bit in the matrix line */
+				continue;
+		}
+		if (introducer) {	/* set 110 sequence ? */
+			introducer--;	/* set on digit less */
+			m[line] |= ibit[introducer] ? mbit : 0;	/* set corresponding bit */
+			mbit >>= 1;	/* bit of matrix line  >> 1 */
+			goto next_bit;	/* and go on there */
+		}               /* else push data bits into the matrix! */
+		m[line] |= (buf[i] & dbit) ? mbit : 0;	/* set data bit in matrix */
+		if (dbit == 128) {	/* was it the last one? */
+			dbit = 1;	/* then go on with first bit of  */
+			i++;            /* next byte in input buffer */
+			if (i < len)	/* input buffer done ? */
+				introducer = 3;	/* no, write introducer 110 */
+			else {  /* input buffer done ! */
+				m[line] |= (mbit - 1) & 0xfe;	/* set remaining bits in line to 1 */
+				break;
+			}
+		} else          /* not the last data bit */
+			dbit <<= 1;	/* then go to next data bit */
+		mbit >>= 1;     /* go to next bit of matrix */
+		goto next_bit;
+
+	}
+	/* if necessary, generate remaining lines of the matrix... */
+	if ((line) && ((line + 10) < mlen))
+		switch (++line % 10) {
+			case 1:
+				m[line++] = 0xfe;
+			case 2:
+				m[line++] = 0xfe;
+			case 3:
+				m[line++] = 0xfe;
+			case 4:
+				m[line++] = 0xfe;
+			case 5:
+				m[line++] = 0xbf;
+			case 6:
+				m[line++] = 0xfe;
+			case 7:
+				m[line++] = 0xfe;
+			case 8:
+				m[line++] = 0xfe;
+			case 9:
+				m[line++] = 0xfe;
+		}
+	return line;            /* that's how many lines we have */
+}
+
+/*
+ * Build a sync frame.
+ */
+static struct sk_buff *
+isdn_v110_sync(isdn_v110_stream *v)
+{
+	struct sk_buff *skb;
+
+	if (v == NULL) {
+		/* invalid handle, no chance to proceed */
+		printk(KERN_WARNING "isdn_v110_sync called with NULL stream!\n");
+		return NULL;
+	}
+	if ((skb = dev_alloc_skb(v->framelen + v->skbres))) {
+		skb_reserve(skb, v->skbres);
+		memcpy(skb_put(skb, v->framelen), v->OfflineFrame, v->framelen);
+	}
+	return skb;
+}
+
+/*
+ * Build an idle frame.
+ */
+static struct sk_buff *
+isdn_v110_idle(isdn_v110_stream *v)
+{
+	struct sk_buff *skb;
+
+	if (v == NULL) {
+		/* invalid handle, no chance to proceed */
+		printk(KERN_WARNING "isdn_v110_sync called with NULL stream!\n");
+		return NULL;
+	}
+	if ((skb = dev_alloc_skb(v->framelen + v->skbres))) {
+		skb_reserve(skb, v->skbres);
+		memcpy(skb_put(skb, v->framelen), v->OnlineFrame, v->framelen);
+	}
+	return skb;
+}
+
+struct sk_buff *
+isdn_v110_encode(isdn_v110_stream * v, struct sk_buff *skb)
+{
+	int i;
+	int j;
+	int rlen;
+	int mlen;
+	int olen;
+	int size;
+	int sval1;
+	int sval2;
+	int nframes;
+	unsigned char *v110buf;
+	unsigned char *rbuf;
+	struct sk_buff *nskb;
+
+	if (v == NULL) {
+		/* invalid handle, no chance to proceed */
+		printk(KERN_WARNING "isdn_v110_encode called with NULL stream!\n");
+		return NULL;
+	}
+	if (!skb) {
+		/* invalid skb, no chance to proceed */
+		printk(KERN_WARNING "isdn_v110_encode called with NULL skb!\n");
+		return NULL;
+	}
+	rlen = skb->len;
+	nframes = (rlen + 3) / 4;
+	v110buf = v->encodebuf;
+	if ((nframes * 40) > v->maxsize) {
+		size = v->maxsize;
+		rlen = v->maxsize / 40;
+	} else
+		size = nframes * 40;
+	if (!(nskb = dev_alloc_skb(size + v->skbres + sizeof(int)))) {
+		printk(KERN_WARNING "isdn_v110_encode: Couldn't alloc skb\n");
+		return NULL;
+	}
+	skb_reserve(nskb, v->skbres + sizeof(int));
+	if (skb->len == 0) {
+		memcpy(skb_put(nskb, v->framelen), v->OnlineFrame, v->framelen);
+		*((int *)skb_push(nskb, sizeof(int))) = 0;
+		return nskb;
+	}
+	mlen = EncodeMatrix(skb->data, rlen, v110buf, size);
+	/* now distribute 2 or 4 bits each to the output stream! */
+	rbuf = skb_put(nskb, size);
+	olen = 0;
+	sval1 = 8 - v->nbits;
+	sval2 = v->key << sval1;
+	for (i = 0; i < mlen; i++) {
+		v110buf[i] = FlipBits(v110buf[i], v->nbits);
+		for (j = 0; j < v->nbytes; j++) {
+			if (size--)
+				*rbuf++ = ~v->key | (((v110buf[i] << (j * v->nbits)) & sval2) >> sval1);
+			else {
+				printk(KERN_WARNING "isdn_v110_encode: buffers full!\n");
+				goto buffer_full;
+			}
+			olen++;
+		}
+	}
+buffer_full:
+	skb_trim(nskb, olen);
+	*((int *)skb_push(nskb, sizeof(int))) = rlen;
+	return nskb;
+}
+
+int
+isdn_v110_stat_callback(int idx, isdn_ctrl * c)
+{
+	isdn_v110_stream *v = NULL;
+	int i;
+	int ret;
+
+	if (idx < 0)
+		return 0;
+	switch (c->command) {
+		case ISDN_STAT_BSENT:
+                        /* Keep the send-queue of the driver filled
+			 * with frames:
+			 * If number of outstanding frames < 3,
+			 * send down an Idle-Frame (or an Sync-Frame, if
+			 * v->SyncInit != 0). 
+			 */
+			if (!(v = dev->v110[idx]))
+				return 0;
+			atomic_inc(&dev->v110use[idx]);
+			if (v->skbidle > 0) {
+				v->skbidle--;
+				ret = 1;
+			} else {
+				if (v->skbuser > 0)
+					v->skbuser--;
+				ret = 0;
+			}
+			for (i = v->skbuser + v->skbidle; i < 2; i++) {
+				struct sk_buff *skb;
+				if (v->SyncInit > 0)
+					skb = isdn_v110_sync(v);
+				else
+					skb = isdn_v110_idle(v);
+				if (skb) {
+					if (dev->drv[c->driver]->interface->writebuf_skb(c->driver, c->arg, 1, skb) <= 0) {
+						dev_kfree_skb(skb);
+						break;
+					} else {
+						if (v->SyncInit)
+							v->SyncInit--;
+						v->skbidle++;
+					}
+				} else
+					break;
+			}
+			atomic_dec(&dev->v110use[idx]);
+			return ret;
+		case ISDN_STAT_DHUP:
+		case ISDN_STAT_BHUP:
+			while (1) {
+				atomic_inc(&dev->v110use[idx]);
+				if (atomic_dec_and_test(&dev->v110use[idx])) {
+					isdn_v110_close(dev->v110[idx]);
+					dev->v110[idx] = NULL;
+					break;
+				}
+				sti();
+			}
+			break;
+		case ISDN_STAT_BCONN:
+			if (dev->v110emu[idx] && (dev->v110[idx] == NULL)) {
+				int hdrlen = dev->drv[c->driver]->interface->hl_hdrlen;
+				int maxsize = dev->drv[c->driver]->interface->maxbufsize;
+				atomic_inc(&dev->v110use[idx]);
+				switch (dev->v110emu[idx]) {
+					case ISDN_PROTO_L2_V11096:
+						dev->v110[idx] = isdn_v110_open(V110_9600, hdrlen, maxsize);
+						break;
+					case ISDN_PROTO_L2_V11019:
+						dev->v110[idx] = isdn_v110_open(V110_19200, hdrlen, maxsize);
+						break;
+					case ISDN_PROTO_L2_V11038:
+						dev->v110[idx] = isdn_v110_open(V110_38400, hdrlen, maxsize);
+						break;
+					default:;
+				}
+				if ((v = dev->v110[idx])) {
+					while (v->SyncInit) {
+						struct sk_buff *skb = isdn_v110_sync(v);
+						if (dev->drv[c->driver]->interface->writebuf_skb(c->driver, c->arg, 1, skb) <= 0) {
+							dev_kfree_skb(skb);
+							/* Unable to send, try later */
+							break;
+						}
+						v->SyncInit--;
+						v->skbidle++;
+					}
+				} else
+					printk(KERN_WARNING "isdn_v110: Couldn't open stream for chan %d\n", idx);
+				atomic_dec(&dev->v110use[idx]);
+			}
+			break;
+		default:
+			return 0;
+	}
+	return 0;
+}
diff -Nru a/drivers/isdn/i4l/isdn_v110.h b/drivers/isdn/i4l/isdn_v110.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/i4l/isdn_v110.h	Mon Apr 22 15:32:18 2002
@@ -0,0 +1,29 @@
+/* $Id: isdn_v110.h,v 1.4.6.1 2001/09/23 22:24:32 kai Exp $
+ *
+ * Linux ISDN subsystem, V.110 related functions (linklevel).
+ *
+ * Copyright by Thomas Pfeiffer (pfeiffer@pds.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef _isdn_v110_h_
+#define _isdn_v110_h_
+
+/* 
+ * isdn_v110_encode will take raw data and encode it using V.110 
+ */
+extern struct sk_buff *isdn_v110_encode(isdn_v110_stream *, struct sk_buff *);
+
+/* 
+ * isdn_v110_decode receives V.110 coded data from the stream and rebuilds
+ * frames from them. The source stream doesn't need to be framed.
+ */
+extern struct sk_buff *isdn_v110_decode(isdn_v110_stream *, struct sk_buff *);
+
+extern int isdn_v110_stat_callback(int, isdn_ctrl *);
+extern void isdn_v110_close(isdn_v110_stream * v);
+
+#endif
diff -Nru a/drivers/isdn/i4l/isdn_x25iface.c b/drivers/isdn/i4l/isdn_x25iface.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/i4l/isdn_x25iface.c	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,341 @@
+/* $Id: isdn_x25iface.c,v 1.9.6.1 2001/09/23 22:24:32 kai Exp $
+ *
+ * Linux ISDN subsystem, X.25 related functions
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * stuff needed to support the Linux X.25 PLP code on top of devices that
+ * can provide a lab_b service using the concap_proto mechanism.
+ * This module supports a network interface wich provides lapb_sematics
+ * -- as defined in ../../Documentation/networking/x25-iface.txt -- to
+ * the upper layer and assumes that the lower layer provides a reliable
+ * data link service by means of the concap_device_ops callbacks.
+ *
+ * Only protocol specific stuff goes here. Device specific stuff
+ * goes to another -- device related -- concap_proto support source file.
+ *
+ */
+
+/* #include <linux/isdn.h> */
+#include <linux/netdevice.h>
+#include <linux/concap.h>
+#include <linux/wanrouter.h>
+#include "isdn_x25iface.h"
+
+/* for debugging messages not to cause an oops when device pointer is NULL*/
+#define MY_DEVNAME(dev)  ( (dev) ? (dev)->name : "DEVICE UNSPECIFIED" )
+
+
+typedef struct isdn_x25iface_proto_data {
+	int magic;
+	enum wan_states state;
+	/* Private stuff, not to be accessed via proto_data. We provide the
+	   other storage for the concap_proto instance here as well,
+	   enabling us to allocate both with just one kmalloc(): */ 
+	struct concap_proto priv;
+} ix25_pdata_t;
+
+
+
+/* is now in header file (extern): struct concap_proto * isdn_x25iface_proto_new(void); */
+void isdn_x25iface_proto_del( struct concap_proto * );
+int isdn_x25iface_proto_close( struct concap_proto * );
+int isdn_x25iface_proto_restart( struct concap_proto *,
+				 struct net_device *,
+				 struct concap_device_ops *);
+int isdn_x25iface_xmit( struct concap_proto *, struct sk_buff * );
+int isdn_x25iface_receive( struct concap_proto *, struct sk_buff * );
+int isdn_x25iface_connect_ind( struct concap_proto * );
+int isdn_x25iface_disconn_ind( struct concap_proto * );
+
+
+static struct concap_proto_ops ix25_pops = {
+	&isdn_x25iface_proto_new,
+	&isdn_x25iface_proto_del,
+	&isdn_x25iface_proto_restart,
+	&isdn_x25iface_proto_close,
+	&isdn_x25iface_xmit,
+	&isdn_x25iface_receive,
+	&isdn_x25iface_connect_ind,
+	&isdn_x25iface_disconn_ind
+};
+
+/* error message helper function */
+static void illegal_state_warn( unsigned state, unsigned char firstbyte) 
+{
+	printk( KERN_WARNING "isdn_x25iface: firstbyte %x illegal in"
+		"current state %d\n",firstbyte, state );
+}
+
+/* check protocol data field for consistency */
+static int pdata_is_bad( ix25_pdata_t * pda ){
+
+	if( pda  &&  pda -> magic == ISDN_X25IFACE_MAGIC ) return 0;
+	printk( KERN_WARNING
+		"isdn_x25iface_xxx: illegal pointer to proto data\n" );
+	return 1;
+}
+
+/* create a new x25 interface protocol instance
+ */
+struct concap_proto * isdn_x25iface_proto_new()
+{
+	ix25_pdata_t * tmp = kmalloc(sizeof(ix25_pdata_t),GFP_KERNEL);
+	IX25DEBUG("isdn_x25iface_proto_new\n");
+	if( tmp ){
+		tmp -> magic = ISDN_X25IFACE_MAGIC;
+		tmp -> state = WAN_UNCONFIGURED;
+		/* private data space used to hold the concap_proto data.
+		   Only to be accessed via the returned pointer */
+		tmp -> priv.dops       = NULL;
+		tmp -> priv.net_dev    = NULL;
+		tmp -> priv.pops       = &ix25_pops;
+		tmp -> priv.flags      = 0;
+		tmp -> priv.proto_data = tmp;
+		return( &(tmp -> priv) );
+	}
+	return NULL;
+};
+
+/* close the x25iface encapsulation protocol 
+ */
+int isdn_x25iface_proto_close(struct concap_proto *cprot){
+
+	ix25_pdata_t *tmp;
+        int ret = 0;
+	ulong flags;
+
+	if( ! cprot ){
+		printk( KERN_ERR "isdn_x25iface_proto_close: "
+			"invalid concap_proto pointer\n" );
+		return -1;
+	}
+	IX25DEBUG( "isdn_x25iface_proto_close %s \n", MY_DEVNAME(cprot -> net_dev) );
+	save_flags(flags);
+	cli();  /* avoid races with incoming events calling pops methods while
+		 cprot members are inconsistent */  
+	cprot -> dops    = NULL;
+	cprot -> net_dev = NULL;
+	tmp = cprot -> proto_data;
+	if( pdata_is_bad( tmp ) ){
+		ret = -1;
+	} else {
+		tmp -> state = WAN_UNCONFIGURED;
+	}
+	restore_flags(flags);
+
+	return ret;
+}
+
+/* Delete the x25iface encapsulation protocol instance
+ */
+void isdn_x25iface_proto_del(struct concap_proto *cprot){
+
+	ix25_pdata_t * tmp;
+ 
+	IX25DEBUG( "isdn_x25iface_proto_del \n" );
+	if( ! cprot ){
+		printk( KERN_ERR "isdn_x25iface_proto_del: "
+			"concap_proto pointer is NULL\n" );
+		return;
+	}
+	tmp = cprot -> proto_data;
+	if( tmp == NULL ){ 
+		printk( KERN_ERR "isdn_x25iface_proto_del: inconsistent "
+			"proto_data pointer (maybe already deleted?)\n"); 
+		return;
+	}
+	/* close if the protocol is still open */
+	if( cprot -> dops ) isdn_x25iface_proto_close(cprot);
+	/* freeing the storage should be sufficient now. But some additional
+	   settings might help to catch wild pointer bugs */
+	tmp -> magic = 0;
+	cprot -> proto_data = NULL;
+
+	kfree( tmp );
+	return;
+}
+
+/* (re-)initialize the data structures for x25iface encapsulation
+ */
+int isdn_x25iface_proto_restart(struct concap_proto *cprot,
+				struct net_device *ndev, 
+				struct concap_device_ops *dops)
+{
+	ix25_pdata_t * pda = cprot -> proto_data ;
+	ulong flags;
+
+	IX25DEBUG( "isdn_x25iface_proto_restart %s \n", MY_DEVNAME(ndev) );
+
+	if ( pdata_is_bad( pda ) ) return -1;
+
+	if( !( dops  && dops -> data_req && dops -> connect_req 
+	       && dops -> disconn_req )  ){
+		printk( KERN_WARNING "isdn_x25iface_restart: required dops"
+			" missing\n" );
+		isdn_x25iface_proto_close(cprot);
+		return -1;
+	}
+	save_flags(flags);
+	cli();  /* avoid races with incoming events calling pops methods while
+		 cprot members are inconsistent */  
+	cprot -> net_dev = ndev;
+	cprot -> pops = &ix25_pops;
+	cprot -> dops = dops;
+	pda -> state = WAN_DISCONNECTED;
+	restore_flags(flags);
+	return 0;
+}
+
+/* deliver a dl_data frame received from i4l HL driver to the network layer 
+ */
+int isdn_x25iface_receive(struct concap_proto *cprot, struct sk_buff *skb)
+{
+  	IX25DEBUG( "isdn_x25iface_receive %s \n", MY_DEVNAME(cprot->net_dev) );
+	if ( ( (ix25_pdata_t*) (cprot->proto_data) ) 
+	     -> state == WAN_CONNECTED ){
+		skb -> dev = cprot -> net_dev;
+		skb -> protocol = htons(ETH_P_X25);
+		skb -> pkt_type = PACKET_HOST;
+		if( skb_push(skb, 1)){
+			skb -> data[0]=0x00;
+			skb -> mac.raw = skb -> data;
+			netif_rx(skb);
+			return 0;
+		}
+	}
+	printk(KERN_WARNING "isdn_x25iface_receive %s: not connected, skb dropped\n", MY_DEVNAME(cprot->net_dev) );
+	dev_kfree_skb(skb);
+	return -1;
+}
+
+/* a connection set up is indicated by lower layer 
+ */
+int isdn_x25iface_connect_ind(struct concap_proto *cprot)
+{
+	struct sk_buff * skb = dev_alloc_skb(1);
+	enum wan_states *state_p 
+	  = &( ( (ix25_pdata_t*) (cprot->proto_data) ) -> state);
+	IX25DEBUG( "isdn_x25iface_connect_ind %s \n"
+		   , MY_DEVNAME(cprot->net_dev) );
+	if( *state_p == WAN_UNCONFIGURED ){ 
+		printk(KERN_WARNING 
+		       "isdn_x25iface_connect_ind while unconfigured %s\n"
+		       , MY_DEVNAME(cprot->net_dev) );
+		return -1;
+	}
+	*state_p = WAN_CONNECTED;
+	if( skb ){
+		*( skb_put(skb, 1) ) = 0x01;
+		skb -> mac.raw = skb -> data;
+		skb -> dev  = cprot -> net_dev;
+		skb -> protocol = htons(ETH_P_X25);
+		skb -> pkt_type = PACKET_HOST;
+		netif_rx(skb);
+		return 0;
+	} else {
+		printk(KERN_WARNING "isdn_x25iface_connect_ind: "
+		       " out of memory -- disconnecting\n");
+		cprot -> dops -> disconn_req(cprot);
+		return -1;
+	}
+}
+	
+/* a disconnect is indicated by lower layer 
+ */
+int isdn_x25iface_disconn_ind(struct concap_proto *cprot)
+{
+	struct sk_buff *skb;
+	enum wan_states *state_p 
+	  = &( ( (ix25_pdata_t*) (cprot->proto_data) ) -> state);
+	IX25DEBUG( "isdn_x25iface_disconn_ind %s \n", MY_DEVNAME(cprot -> net_dev) );
+	if( *state_p == WAN_UNCONFIGURED ){ 
+		printk(KERN_WARNING 
+		       "isdn_x25iface_disconn_ind while unconfigured\n");
+		return -1;
+	}
+	if(! cprot -> net_dev) return -1;
+	*state_p = WAN_DISCONNECTED;
+	skb = dev_alloc_skb(1);
+	if( skb ){
+		*( skb_put(skb, 1) ) = 0x02;
+		skb -> mac.raw = skb -> data;
+		skb -> dev  = cprot -> net_dev;
+		skb -> protocol = htons(ETH_P_X25);
+		skb -> pkt_type = PACKET_HOST;
+		netif_rx(skb);
+		return 0;
+	} else {
+		printk(KERN_WARNING "isdn_x25iface_disconn_ind:"
+		       " out of memory\n");
+		return -1;
+	}
+}
+
+/* process a frame handed over to us from linux network layer. First byte
+   semantics as defined in ../../Documentation/networking/x25-iface.txt 
+   */
+int isdn_x25iface_xmit(struct concap_proto *cprot, struct sk_buff *skb)
+{
+	unsigned char firstbyte = skb->data[0];
+	unsigned *state = 
+		&( ( (ix25_pdata_t*) (cprot -> proto_data) ) -> state  );
+	int ret = 0;
+	IX25DEBUG( "isdn_x25iface_xmit: %s first=%x state=%d \n", MY_DEVNAME(cprot -> net_dev), firstbyte, *state );
+	switch ( firstbyte ){
+	case 0x00: /* dl_data request */
+		if( *state == WAN_CONNECTED ){
+			skb_pull(skb, 1);
+			cprot -> net_dev -> trans_start = jiffies;
+			ret = ( cprot -> dops -> data_req(cprot, skb) );
+			/* prepare for future retransmissions */
+			if( ret ) skb_push(skb,1);
+			return ret;
+		}
+		illegal_state_warn( *state, firstbyte ); 
+		break;
+	case 0x01: /* dl_connect request */
+		if( *state == WAN_DISCONNECTED ){
+			*state = WAN_CONNECTING;
+		        ret = cprot -> dops -> connect_req(cprot);
+			if(ret){
+				/* reset state and notify upper layer about
+				 * immidiatly failed attempts */
+				isdn_x25iface_disconn_ind(cprot);
+			}
+		} else {
+			illegal_state_warn( *state, firstbyte );
+		}
+		break;
+	case 0x02: /* dl_disconnect request */
+		switch ( *state ){
+		case WAN_DISCONNECTED: 
+			/* Should not happen. However, give upper layer a
+			   chance to recover from inconstistency  but don't
+			   trust the lower layer sending the disconn_confirm
+			   when already disconnected */
+			printk(KERN_WARNING "isdn_x25iface_xmit: disconnect "
+			       " requested while disconnected\n" );
+			isdn_x25iface_disconn_ind(cprot);
+			break; /* prevent infinite loops */
+		case WAN_CONNECTING:
+		case WAN_CONNECTED:
+			*state = WAN_DISCONNECTED;
+			cprot -> dops -> disconn_req(cprot);
+			break;
+		default:
+			illegal_state_warn( *state, firstbyte );
+		}
+		break;
+	case 0x03: /* changing lapb parameters requested */
+		printk(KERN_WARNING "isdn_x25iface_xmit: setting of lapb"
+		       " options not yet supported\n");
+		break;
+	default:
+		printk(KERN_WARNING "isdn_x25iface_xmit: frame with illegal"
+		       " first byte %x ignored:\n", firstbyte);
+	}
+	dev_kfree_skb(skb);
+	return 0;
+}
diff -Nru a/drivers/isdn/i4l/isdn_x25iface.h b/drivers/isdn/i4l/isdn_x25iface.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/i4l/isdn_x25iface.h	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,39 @@
+/* $Id: isdn_x25iface.h,v 1.3.6.1 2001/09/23 22:24:32 kai Exp $
+ *
+ * header for Linux ISDN subsystem, x.25 related functions
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef _LINUX_ISDN_X25IFACE_H
+#define _LINUX_ISDN_X25IFACE_H
+
+#define ISDN_X25IFACE_MAGIC 0x1e75a2b9
+/* #define DEBUG_ISDN_X25 if you want isdn_x25 debugging messages */
+#ifdef DEBUG_ISDN_X25
+#   define IX25DEBUG(fmt,args...) printk(KERN_DEBUG fmt , ## args)
+#else
+#   define IX25DEBUG(fmt,args...)
+#endif
+
+#include <linux/skbuff.h>
+#include <linux/wanrouter.h>
+#include <linux/isdn.h>
+#include <linux/concap.h>
+
+extern struct concap_proto_ops * isdn_x25iface_concap_proto_ops_pt;
+extern struct concap_proto     * isdn_x25iface_proto_new(void);
+
+
+
+#endif
+
+
+
+
+
+
+
+
diff -Nru a/drivers/isdn/icn/Config.help b/drivers/isdn/icn/Config.help
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/icn/Config.help	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,10 @@
+CONFIG_ISDN_DRV_ICN
+  This enables support for two kinds of ISDN-cards made by a German
+  company called ICN.  2B is the standard version for a single ISDN
+  line with two B-channels, 4B supports two ISDN lines.  For running
+  this card, additional firmware is necessary, which has to be
+  downloaded into the card using a utility which is distributed
+  separately.  See <file:Documentation/isdn/README> and
+  <file:Documentation/isdn/README.icn> for more
+  information.
+
diff -Nru a/drivers/isdn/icn/Config.in b/drivers/isdn/icn/Config.in
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/icn/Config.in	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,5 @@
+#
+# Config.in for ICN ISDN driver
+#
+
+dep_tristate       'ICN 2B and 4B support' CONFIG_ISDN_DRV_ICN $CONFIG_ISDN
diff -Nru a/drivers/isdn/icn/Makefile b/drivers/isdn/icn/Makefile
--- a/drivers/isdn/icn/Makefile	Mon Apr 22 15:32:19 2002
+++ b/drivers/isdn/icn/Makefile	Mon Apr 22 15:32:19 2002
@@ -1,4 +1,4 @@
-# Makefile for the act2000 ISDN device driver
+# Makefile for the icn ISDN device driver
 
 # The target object and module list name.
 
diff -Nru a/drivers/isdn/isdn_audio.c b/drivers/isdn/isdn_audio.c
--- a/drivers/isdn/isdn_audio.c	Mon Apr 22 15:32:18 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,720 +0,0 @@
-/* $Id: isdn_audio.c,v 1.21.6.2 2001/09/23 22:24:31 kai Exp $
- *
- * Linux ISDN subsystem, audio conversion and compression (linklevel).
- *
- * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de)
- * DTMF code (c) 1996 by Christian Mock (cm@kukuruz.ping.at)
- * Silence detection (c) 1998 by Armin Schindler (mac@gismo.telekom.de)
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#include <linux/isdn.h>
-#include "isdn_audio.h"
-#include "isdn_common.h"
-
-char *isdn_audio_revision = "$Revision: 1.21.6.2 $";
-
-/*
- * Misc. lookup-tables.
- */
-
-/* ulaw -> signed 16-bit */
-static short isdn_audio_ulaw_to_s16[] =
-{
-	0x8284, 0x8684, 0x8a84, 0x8e84, 0x9284, 0x9684, 0x9a84, 0x9e84,
-	0xa284, 0xa684, 0xaa84, 0xae84, 0xb284, 0xb684, 0xba84, 0xbe84,
-	0xc184, 0xc384, 0xc584, 0xc784, 0xc984, 0xcb84, 0xcd84, 0xcf84,
-	0xd184, 0xd384, 0xd584, 0xd784, 0xd984, 0xdb84, 0xdd84, 0xdf84,
-	0xe104, 0xe204, 0xe304, 0xe404, 0xe504, 0xe604, 0xe704, 0xe804,
-	0xe904, 0xea04, 0xeb04, 0xec04, 0xed04, 0xee04, 0xef04, 0xf004,
-	0xf0c4, 0xf144, 0xf1c4, 0xf244, 0xf2c4, 0xf344, 0xf3c4, 0xf444,
-	0xf4c4, 0xf544, 0xf5c4, 0xf644, 0xf6c4, 0xf744, 0xf7c4, 0xf844,
-	0xf8a4, 0xf8e4, 0xf924, 0xf964, 0xf9a4, 0xf9e4, 0xfa24, 0xfa64,
-	0xfaa4, 0xfae4, 0xfb24, 0xfb64, 0xfba4, 0xfbe4, 0xfc24, 0xfc64,
-	0xfc94, 0xfcb4, 0xfcd4, 0xfcf4, 0xfd14, 0xfd34, 0xfd54, 0xfd74,
-	0xfd94, 0xfdb4, 0xfdd4, 0xfdf4, 0xfe14, 0xfe34, 0xfe54, 0xfe74,
-	0xfe8c, 0xfe9c, 0xfeac, 0xfebc, 0xfecc, 0xfedc, 0xfeec, 0xfefc,
-	0xff0c, 0xff1c, 0xff2c, 0xff3c, 0xff4c, 0xff5c, 0xff6c, 0xff7c,
-	0xff88, 0xff90, 0xff98, 0xffa0, 0xffa8, 0xffb0, 0xffb8, 0xffc0,
-	0xffc8, 0xffd0, 0xffd8, 0xffe0, 0xffe8, 0xfff0, 0xfff8, 0x0000,
-	0x7d7c, 0x797c, 0x757c, 0x717c, 0x6d7c, 0x697c, 0x657c, 0x617c,
-	0x5d7c, 0x597c, 0x557c, 0x517c, 0x4d7c, 0x497c, 0x457c, 0x417c,
-	0x3e7c, 0x3c7c, 0x3a7c, 0x387c, 0x367c, 0x347c, 0x327c, 0x307c,
-	0x2e7c, 0x2c7c, 0x2a7c, 0x287c, 0x267c, 0x247c, 0x227c, 0x207c,
-	0x1efc, 0x1dfc, 0x1cfc, 0x1bfc, 0x1afc, 0x19fc, 0x18fc, 0x17fc,
-	0x16fc, 0x15fc, 0x14fc, 0x13fc, 0x12fc, 0x11fc, 0x10fc, 0x0ffc,
-	0x0f3c, 0x0ebc, 0x0e3c, 0x0dbc, 0x0d3c, 0x0cbc, 0x0c3c, 0x0bbc,
-	0x0b3c, 0x0abc, 0x0a3c, 0x09bc, 0x093c, 0x08bc, 0x083c, 0x07bc,
-	0x075c, 0x071c, 0x06dc, 0x069c, 0x065c, 0x061c, 0x05dc, 0x059c,
-	0x055c, 0x051c, 0x04dc, 0x049c, 0x045c, 0x041c, 0x03dc, 0x039c,
-	0x036c, 0x034c, 0x032c, 0x030c, 0x02ec, 0x02cc, 0x02ac, 0x028c,
-	0x026c, 0x024c, 0x022c, 0x020c, 0x01ec, 0x01cc, 0x01ac, 0x018c,
-	0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104,
-	0x00f4, 0x00e4, 0x00d4, 0x00c4, 0x00b4, 0x00a4, 0x0094, 0x0084,
-	0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040,
-	0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000
-};
-
-/* alaw -> signed 16-bit */
-static short isdn_audio_alaw_to_s16[] =
-{
-	0x13fc, 0xec04, 0x0144, 0xfebc, 0x517c, 0xae84, 0x051c, 0xfae4,
-	0x0a3c, 0xf5c4, 0x0048, 0xffb8, 0x287c, 0xd784, 0x028c, 0xfd74,
-	0x1bfc, 0xe404, 0x01cc, 0xfe34, 0x717c, 0x8e84, 0x071c, 0xf8e4,
-	0x0e3c, 0xf1c4, 0x00c4, 0xff3c, 0x387c, 0xc784, 0x039c, 0xfc64,
-	0x0ffc, 0xf004, 0x0104, 0xfefc, 0x417c, 0xbe84, 0x041c, 0xfbe4,
-	0x083c, 0xf7c4, 0x0008, 0xfff8, 0x207c, 0xdf84, 0x020c, 0xfdf4,
-	0x17fc, 0xe804, 0x018c, 0xfe74, 0x617c, 0x9e84, 0x061c, 0xf9e4,
-	0x0c3c, 0xf3c4, 0x0084, 0xff7c, 0x307c, 0xcf84, 0x030c, 0xfcf4,
-	0x15fc, 0xea04, 0x0164, 0xfe9c, 0x597c, 0xa684, 0x059c, 0xfa64,
-	0x0b3c, 0xf4c4, 0x0068, 0xff98, 0x2c7c, 0xd384, 0x02cc, 0xfd34,
-	0x1dfc, 0xe204, 0x01ec, 0xfe14, 0x797c, 0x8684, 0x07bc, 0xf844,
-	0x0f3c, 0xf0c4, 0x00e4, 0xff1c, 0x3c7c, 0xc384, 0x03dc, 0xfc24,
-	0x11fc, 0xee04, 0x0124, 0xfedc, 0x497c, 0xb684, 0x049c, 0xfb64,
-	0x093c, 0xf6c4, 0x0028, 0xffd8, 0x247c, 0xdb84, 0x024c, 0xfdb4,
-	0x19fc, 0xe604, 0x01ac, 0xfe54, 0x697c, 0x9684, 0x069c, 0xf964,
-	0x0d3c, 0xf2c4, 0x00a4, 0xff5c, 0x347c, 0xcb84, 0x034c, 0xfcb4,
-	0x12fc, 0xed04, 0x0134, 0xfecc, 0x4d7c, 0xb284, 0x04dc, 0xfb24,
-	0x09bc, 0xf644, 0x0038, 0xffc8, 0x267c, 0xd984, 0x026c, 0xfd94,
-	0x1afc, 0xe504, 0x01ac, 0xfe54, 0x6d7c, 0x9284, 0x06dc, 0xf924,
-	0x0dbc, 0xf244, 0x00b4, 0xff4c, 0x367c, 0xc984, 0x036c, 0xfc94,
-	0x0f3c, 0xf0c4, 0x00f4, 0xff0c, 0x3e7c, 0xc184, 0x03dc, 0xfc24,
-	0x07bc, 0xf844, 0x0008, 0xfff8, 0x1efc, 0xe104, 0x01ec, 0xfe14,
-	0x16fc, 0xe904, 0x0174, 0xfe8c, 0x5d7c, 0xa284, 0x05dc, 0xfa24,
-	0x0bbc, 0xf444, 0x0078, 0xff88, 0x2e7c, 0xd184, 0x02ec, 0xfd14,
-	0x14fc, 0xeb04, 0x0154, 0xfeac, 0x557c, 0xaa84, 0x055c, 0xfaa4,
-	0x0abc, 0xf544, 0x0058, 0xffa8, 0x2a7c, 0xd584, 0x02ac, 0xfd54,
-	0x1cfc, 0xe304, 0x01cc, 0xfe34, 0x757c, 0x8a84, 0x075c, 0xf8a4,
-	0x0ebc, 0xf144, 0x00d4, 0xff2c, 0x3a7c, 0xc584, 0x039c, 0xfc64,
-	0x10fc, 0xef04, 0x0114, 0xfeec, 0x457c, 0xba84, 0x045c, 0xfba4,
-	0x08bc, 0xf744, 0x0018, 0xffe8, 0x227c, 0xdd84, 0x022c, 0xfdd4,
-	0x18fc, 0xe704, 0x018c, 0xfe74, 0x657c, 0x9a84, 0x065c, 0xf9a4,
-	0x0cbc, 0xf344, 0x0094, 0xff6c, 0x327c, 0xcd84, 0x032c, 0xfcd4
-};
-
-/* alaw -> ulaw */
-static char isdn_audio_alaw_to_ulaw[] =
-{
-	0xab, 0x2b, 0xe3, 0x63, 0x8b, 0x0b, 0xc9, 0x49,
-	0xba, 0x3a, 0xf6, 0x76, 0x9b, 0x1b, 0xd7, 0x57,
-	0xa3, 0x23, 0xdd, 0x5d, 0x83, 0x03, 0xc1, 0x41,
-	0xb2, 0x32, 0xeb, 0x6b, 0x93, 0x13, 0xcf, 0x4f,
-	0xaf, 0x2f, 0xe7, 0x67, 0x8f, 0x0f, 0xcd, 0x4d,
-	0xbe, 0x3e, 0xfe, 0x7e, 0x9f, 0x1f, 0xdb, 0x5b,
-	0xa7, 0x27, 0xdf, 0x5f, 0x87, 0x07, 0xc5, 0x45,
-	0xb6, 0x36, 0xef, 0x6f, 0x97, 0x17, 0xd3, 0x53,
-	0xa9, 0x29, 0xe1, 0x61, 0x89, 0x09, 0xc7, 0x47,
-	0xb8, 0x38, 0xf2, 0x72, 0x99, 0x19, 0xd5, 0x55,
-	0xa1, 0x21, 0xdc, 0x5c, 0x81, 0x01, 0xbf, 0x3f,
-	0xb0, 0x30, 0xe9, 0x69, 0x91, 0x11, 0xce, 0x4e,
-	0xad, 0x2d, 0xe5, 0x65, 0x8d, 0x0d, 0xcb, 0x4b,
-	0xbc, 0x3c, 0xfa, 0x7a, 0x9d, 0x1d, 0xd9, 0x59,
-	0xa5, 0x25, 0xde, 0x5e, 0x85, 0x05, 0xc3, 0x43,
-	0xb4, 0x34, 0xed, 0x6d, 0x95, 0x15, 0xd1, 0x51,
-	0xac, 0x2c, 0xe4, 0x64, 0x8c, 0x0c, 0xca, 0x4a,
-	0xbb, 0x3b, 0xf8, 0x78, 0x9c, 0x1c, 0xd8, 0x58,
-	0xa4, 0x24, 0xde, 0x5e, 0x84, 0x04, 0xc2, 0x42,
-	0xb3, 0x33, 0xec, 0x6c, 0x94, 0x14, 0xd0, 0x50,
-	0xb0, 0x30, 0xe8, 0x68, 0x90, 0x10, 0xce, 0x4e,
-	0xbf, 0x3f, 0xfe, 0x7e, 0xa0, 0x20, 0xdc, 0x5c,
-	0xa8, 0x28, 0xe0, 0x60, 0x88, 0x08, 0xc6, 0x46,
-	0xb7, 0x37, 0xf0, 0x70, 0x98, 0x18, 0xd4, 0x54,
-	0xaa, 0x2a, 0xe2, 0x62, 0x8a, 0x0a, 0xc8, 0x48,
-	0xb9, 0x39, 0xf4, 0x74, 0x9a, 0x1a, 0xd6, 0x56,
-	0xa2, 0x22, 0xdd, 0x5d, 0x82, 0x02, 0xc0, 0x40,
-	0xb1, 0x31, 0xea, 0x6a, 0x92, 0x12, 0xcf, 0x4f,
-	0xae, 0x2e, 0xe6, 0x66, 0x8e, 0x0e, 0xcc, 0x4c,
-	0xbd, 0x3d, 0xfc, 0x7c, 0x9e, 0x1e, 0xda, 0x5a,
-	0xa6, 0x26, 0xdf, 0x5f, 0x86, 0x06, 0xc4, 0x44,
-	0xb5, 0x35, 0xee, 0x6e, 0x96, 0x16, 0xd2, 0x52
-};
-
-/* ulaw -> alaw */
-static char isdn_audio_ulaw_to_alaw[] =
-{
-	0xab, 0x55, 0xd5, 0x15, 0x95, 0x75, 0xf5, 0x35,
-	0xb5, 0x45, 0xc5, 0x05, 0x85, 0x65, 0xe5, 0x25,
-	0xa5, 0x5d, 0xdd, 0x1d, 0x9d, 0x7d, 0xfd, 0x3d,
-	0xbd, 0x4d, 0xcd, 0x0d, 0x8d, 0x6d, 0xed, 0x2d,
-	0xad, 0x51, 0xd1, 0x11, 0x91, 0x71, 0xf1, 0x31,
-	0xb1, 0x41, 0xc1, 0x01, 0x81, 0x61, 0xe1, 0x21,
-	0x59, 0xd9, 0x19, 0x99, 0x79, 0xf9, 0x39, 0xb9,
-	0x49, 0xc9, 0x09, 0x89, 0x69, 0xe9, 0x29, 0xa9,
-	0xd7, 0x17, 0x97, 0x77, 0xf7, 0x37, 0xb7, 0x47,
-	0xc7, 0x07, 0x87, 0x67, 0xe7, 0x27, 0xa7, 0xdf,
-	0x9f, 0x7f, 0xff, 0x3f, 0xbf, 0x4f, 0xcf, 0x0f,
-	0x8f, 0x6f, 0xef, 0x2f, 0x53, 0x13, 0x73, 0x33,
-	0xb3, 0x43, 0xc3, 0x03, 0x83, 0x63, 0xe3, 0x23,
-	0xa3, 0x5b, 0xdb, 0x1b, 0x9b, 0x7b, 0xfb, 0x3b,
-	0xbb, 0xbb, 0x4b, 0x4b, 0xcb, 0xcb, 0x0b, 0x0b,
-	0x8b, 0x8b, 0x6b, 0x6b, 0xeb, 0xeb, 0x2b, 0x2b,
-	0xab, 0x54, 0xd4, 0x14, 0x94, 0x74, 0xf4, 0x34,
-	0xb4, 0x44, 0xc4, 0x04, 0x84, 0x64, 0xe4, 0x24,
-	0xa4, 0x5c, 0xdc, 0x1c, 0x9c, 0x7c, 0xfc, 0x3c,
-	0xbc, 0x4c, 0xcc, 0x0c, 0x8c, 0x6c, 0xec, 0x2c,
-	0xac, 0x50, 0xd0, 0x10, 0x90, 0x70, 0xf0, 0x30,
-	0xb0, 0x40, 0xc0, 0x00, 0x80, 0x60, 0xe0, 0x20,
-	0x58, 0xd8, 0x18, 0x98, 0x78, 0xf8, 0x38, 0xb8,
-	0x48, 0xc8, 0x08, 0x88, 0x68, 0xe8, 0x28, 0xa8,
-	0xd6, 0x16, 0x96, 0x76, 0xf6, 0x36, 0xb6, 0x46,
-	0xc6, 0x06, 0x86, 0x66, 0xe6, 0x26, 0xa6, 0xde,
-	0x9e, 0x7e, 0xfe, 0x3e, 0xbe, 0x4e, 0xce, 0x0e,
-	0x8e, 0x6e, 0xee, 0x2e, 0x52, 0x12, 0x72, 0x32,
-	0xb2, 0x42, 0xc2, 0x02, 0x82, 0x62, 0xe2, 0x22,
-	0xa2, 0x5a, 0xda, 0x1a, 0x9a, 0x7a, 0xfa, 0x3a,
-	0xba, 0xba, 0x4a, 0x4a, 0xca, 0xca, 0x0a, 0x0a,
-	0x8a, 0x8a, 0x6a, 0x6a, 0xea, 0xea, 0x2a, 0x2a
-};
-
-#define NCOEFF           16     /* number of frequencies to be analyzed       */
-#define DTMF_TRESH    25000     /* above this is dtmf                         */
-#define SILENCE_TRESH   200     /* below this is silence                      */
-#define H2_TRESH      20000     /* 2nd harmonic                               */
-#define AMP_BITS          9     /* bits per sample, reduced to avoid overflow */
-#define LOGRP             0
-#define HIGRP             1
-
-typedef struct {
-	int grp;                /* low/high group     */
-	int k;                  /* k                  */
-	int k2;                 /* k fuer 2. harmonic */
-} dtmf_t;
-
-/* For DTMF recognition:
- * 2 * cos(2 * PI * k / N) precalculated for all k
- */
-static int cos2pik[NCOEFF] =
-{
-	55812, 29528, 53603, 24032, 51193, 14443, 48590, 6517,
-	38113, -21204, 33057, -32186, 25889, -45081, 18332, -55279
-};
-
-static dtmf_t dtmf_tones[8] =
-{
-	{LOGRP, 0, 1},          /*  697 Hz */
-	{LOGRP, 2, 3},          /*  770 Hz */
-	{LOGRP, 4, 5},          /*  852 Hz */
-	{LOGRP, 6, 7},          /*  941 Hz */
-	{HIGRP, 8, 9},          /* 1209 Hz */
-	{HIGRP, 10, 11},        /* 1336 Hz */
-	{HIGRP, 12, 13},        /* 1477 Hz */
-	{HIGRP, 14, 15}         /* 1633 Hz */
-};
-
-static char dtmf_matrix[4][4] =
-{
-	{'1', '2', '3', 'A'},
-	{'4', '5', '6', 'B'},
-	{'7', '8', '9', 'C'},
-	{'*', '0', '#', 'D'}
-};
-
-static inline void
-isdn_audio_tlookup(const u_char *table, u_char *buff, unsigned long n)
-{
-#ifdef __i386__
-	unsigned long d0, d1, d2, d3;
-	__asm__ __volatile__(
-		"cld\n"
-		"1:\tlodsb\n\t"
-		"xlatb\n\t"
-		"stosb\n\t"
-		"loop 1b\n\t"
-	:	"=&b"(d0), "=&c"(d1), "=&D"(d2), "=&S"(d3)
-	:	"0"((long) table), "1"(n), "2"((long) buff), "3"((long) buff)
-	:	"memory", "ax");
-#else
-	while (n--) {
-		*buff = table[*buff];
-		buff++;
-	}
-#endif
-}
-
-void
-isdn_audio_ulaw2alaw(unsigned char *buff, unsigned long len)
-{
-	isdn_audio_tlookup(isdn_audio_ulaw_to_alaw, buff, len);
-}
-
-void
-isdn_audio_alaw2ulaw(unsigned char *buff, unsigned long len)
-{
-	isdn_audio_tlookup(isdn_audio_alaw_to_ulaw, buff, len);
-}
-
-/*
- * linear <-> adpcm conversion stuff
- * Most parts from the mgetty-package.
- * (C) by Gert Doering and Klaus Weidner
- * Used by permission of Gert Doering
- */
-
-
-#define ZEROTRAP                /* turn on the trap as per the MIL-STD */
-#undef ZEROTRAP
-#define BIAS 0x84               /* define the add-in bias for 16 bit samples */
-#define CLIP 32635
-
-static unsigned char
-isdn_audio_linear2ulaw(int sample)
-{
-	static int exp_lut[256] =
-	{
-		0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
-		4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
-		5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
-		5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
-		6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
-		6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
-		6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
-		6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
-		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
-		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
-		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
-		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
-		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
-		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
-		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
-		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
-	};
-	int sign,
-	 exponent,
-	 mantissa;
-	unsigned char ulawbyte;
-
-	/* Get the sample into sign-magnitude. */
-	sign = (sample >> 8) & 0x80;	/* set aside the sign  */
-	if (sign != 0)
-		sample = -sample;	/* get magnitude       */
-	if (sample > CLIP)
-		sample = CLIP;  /* clip the magnitude  */
-
-	/* Convert from 16 bit linear to ulaw. */
-	sample = sample + BIAS;
-	exponent = exp_lut[(sample >> 7) & 0xFF];
-	mantissa = (sample >> (exponent + 3)) & 0x0F;
-	ulawbyte = ~(sign | (exponent << 4) | mantissa);
-#ifdef ZEROTRAP
-	/* optional CCITT trap */
-	if (ulawbyte == 0)
-		ulawbyte = 0x02;
-#endif
-	return (ulawbyte);
-}
-
-
-static int Mx[3][8] =
-{
-	{0x3800, 0x5600, 0, 0, 0, 0, 0, 0},
-	{0x399a, 0x3a9f, 0x4d14, 0x6607, 0, 0, 0, 0},
-	{0x3556, 0x3556, 0x399A, 0x3A9F, 0x4200, 0x4D14, 0x6607, 0x6607},
-};
-
-static int bitmask[9] =
-{
-	0, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff
-};
-
-static int
-isdn_audio_get_bits(adpcm_state * s, unsigned char **in, int *len)
-{
-	while (s->nleft < s->nbits) {
-		int d = *((*in)++);
-		(*len)--;
-		s->word = (s->word << 8) | d;
-		s->nleft += 8;
-	}
-	s->nleft -= s->nbits;
-	return (s->word >> s->nleft) & bitmask[s->nbits];
-}
-
-static void
-isdn_audio_put_bits(int data, int nbits, adpcm_state * s,
-		    unsigned char **out, int *len)
-{
-	s->word = (s->word << nbits) | (data & bitmask[nbits]);
-	s->nleft += nbits;
-	while (s->nleft >= 8) {
-		int d = (s->word >> (s->nleft - 8));
-		*(out[0]++) = d & 255;
-		(*len)++;
-		s->nleft -= 8;
-	}
-}
-
-adpcm_state *
-isdn_audio_adpcm_init(adpcm_state * s, int nbits)
-{
-	if (!s)
-		s = (adpcm_state *) kmalloc(sizeof(adpcm_state), GFP_ATOMIC);
-	if (s) {
-		s->a = 0;
-		s->d = 5;
-		s->word = 0;
-		s->nleft = 0;
-		s->nbits = nbits;
-	}
-	return s;
-}
-
-dtmf_state *
-isdn_audio_dtmf_init(dtmf_state * s)
-{
-	if (!s)
-		s = (dtmf_state *) kmalloc(sizeof(dtmf_state), GFP_ATOMIC);
-	if (s) {
-		s->idx = 0;
-		s->last = ' ';
-	}
-	return s;
-}
-
-/*
- * Decompression of adpcm data to a/u-law
- *
- */
-
-int
-isdn_audio_adpcm2xlaw(adpcm_state * s, int fmt, unsigned char *in,
-		      unsigned char *out, int len)
-{
-	int a = s->a;
-	int d = s->d;
-	int nbits = s->nbits;
-	int olen = 0;
-
-	while (len) {
-		int e = isdn_audio_get_bits(s, &in, &len);
-		int sign;
-
-		if (nbits == 4 && e == 0)
-			d = 4;
-		sign = (e >> (nbits - 1)) ? -1 : 1;
-		e &= bitmask[nbits - 1];
-		a += sign * ((e << 1) + 1) * d >> 1;
-		if (d & 1)
-			a++;
-		if (fmt)
-			*out++ = isdn_audio_ulaw_to_alaw[
-					 isdn_audio_linear2ulaw(a << 2)];
-		else
-			*out++ = isdn_audio_linear2ulaw(a << 2);
-		olen++;
-		d = (d * Mx[nbits - 2][e] + 0x2000) >> 14;
-		if (d < 5)
-			d = 5;
-	}
-	s->a = a;
-	s->d = d;
-	return olen;
-}
-
-int
-isdn_audio_2adpcm_flush(adpcm_state * s, unsigned char *out)
-{
-	int olen = 0;
-
-	if (s->nleft)
-		isdn_audio_put_bits(0, 8 - s->nleft, s, &out, &olen);
-	return olen;
-}
-
-int
-isdn_audio_xlaw2adpcm(adpcm_state * s, int fmt, unsigned char *in,
-		      unsigned char *out, int len)
-{
-	int a = s->a;
-	int d = s->d;
-	int nbits = s->nbits;
-	int olen = 0;
-
-	while (len--) {
-		int e = 0,
-		 nmax = 1 << (nbits - 1);
-		int sign,
-		 delta;
-
-		if (fmt)
-			delta = (isdn_audio_alaw_to_s16[*in++] >> 2) - a;
-		else
-			delta = (isdn_audio_ulaw_to_s16[*in++] >> 2) - a;
-		if (delta < 0) {
-			e = nmax;
-			delta = -delta;
-		}
-		while (--nmax && delta > d) {
-			delta -= d;
-			e++;
-		}
-		if (nbits == 4 && ((e & 0x0f) == 0))
-			e = 8;
-		isdn_audio_put_bits(e, nbits, s, &out, &olen);
-		sign = (e >> (nbits - 1)) ? -1 : 1;
-		e &= bitmask[nbits - 1];
-
-		a += sign * ((e << 1) + 1) * d >> 1;
-		if (d & 1)
-			a++;
-		d = (d * Mx[nbits - 2][e] + 0x2000) >> 14;
-		if (d < 5)
-			d = 5;
-	}
-	s->a = a;
-	s->d = d;
-	return olen;
-}
-
-/*
- * Goertzel algorithm.
- * See http://ptolemy.eecs.berkeley.edu/~pino/Ptolemy/papers/96/dtmf_ict/
- * for more info.
- * Result is stored into an sk_buff and queued up for later
- * evaluation.
- */
-static void
-isdn_audio_goertzel(int *sample, modem_info * info)
-{
-	int sk,
-	 sk1,
-	 sk2;
-	int k,
-	 n;
-	struct sk_buff *skb;
-	int *result;
-
-	skb = dev_alloc_skb(sizeof(int) * NCOEFF);
-	if (!skb) {
-		printk(KERN_WARNING
-		  "isdn_audio: Could not alloc DTMF result for ttyI%d\n",
-		       info->line);
-		return;
-	}
-	result = (int *) skb_put(skb, sizeof(int) * NCOEFF);
-	for (k = 0; k < NCOEFF; k++) {
-		sk = sk1 = sk2 = 0;
-		for (n = 0; n < DTMF_NPOINTS; n++) {
-			sk = sample[n] + ((cos2pik[k] * sk1) >> 15) - sk2;
-			sk2 = sk1;
-			sk1 = sk;
-		}
-		result[k] =
-		    ((sk * sk) >> AMP_BITS) -
-		    ((((cos2pik[k] * sk) >> 15) * sk2) >> AMP_BITS) +
-		    ((sk2 * sk2) >> AMP_BITS);
-	}
-	skb_queue_tail(&info->dtmf_queue, skb);
-	isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
-}
-
-void
-isdn_audio_eval_dtmf(modem_info * info)
-{
-	struct sk_buff *skb;
-	int *result;
-	dtmf_state *s;
-	int silence;
-	int i;
-	int di;
-	int ch;
-	unsigned long flags;
-	int grp[2];
-	char what;
-	char *p;
-
-	while ((skb = skb_dequeue(&info->dtmf_queue))) {
-		result = (int *) skb->data;
-		s = info->dtmf_state;
-		grp[LOGRP] = grp[HIGRP] = -2;
-		silence = 0;
-		for (i = 0; i < 8; i++) {
-			if ((result[dtmf_tones[i].k] > DTMF_TRESH) &&
-			    (result[dtmf_tones[i].k2] < H2_TRESH))
-				grp[dtmf_tones[i].grp] = (grp[dtmf_tones[i].grp] == -2) ? i : -1;
-			else if ((result[dtmf_tones[i].k] < SILENCE_TRESH) &&
-			      (result[dtmf_tones[i].k2] < SILENCE_TRESH))
-				silence++;
-		}
-		if (silence == 8)
-			what = ' ';
-		else {
-			if ((grp[LOGRP] >= 0) && (grp[HIGRP] >= 0)) {
-				what = dtmf_matrix[grp[LOGRP]][grp[HIGRP] - 4];
-				if (s->last != ' ' && s->last != '.')
-					s->last = what;	/* min. 1 non-DTMF between DTMF */
-			} else
-				what = '.';
-		}
-		if ((what != s->last) && (what != ' ') && (what != '.')) {
-			printk(KERN_DEBUG "dtmf: tt='%c'\n", what);
-			p = skb->data;
-			*p++ = 0x10;
-			*p = what;
-			skb_trim(skb, 2);
-			if (skb_headroom(skb) < sizeof(isdn_audio_skb)) {
-				printk(KERN_WARNING
-				       "isdn_audio: insufficient skb_headroom, dropping\n");
-				kfree_skb(skb);
-				return;
-			}
-			ISDN_AUDIO_SKB_DLECOUNT(skb) = 0;
-			ISDN_AUDIO_SKB_LOCK(skb) = 0;
-			save_flags(flags);
-			cli();
-			di = info->isdn_driver;
-			ch = info->isdn_channel;
-			__skb_queue_tail(&dev->drv[di]->rpqueue[ch], skb);
-			dev->drv[di]->rcvcount[ch] += 2;
-			restore_flags(flags);
-			/* Schedule dequeuing */
-			if ((dev->modempoll) && (info->rcvsched))
-				isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
-			wake_up_interruptible(&dev->drv[di]->rcv_waitq[ch]);
-		} else
-			kfree_skb(skb);
-		s->last = what;
-	}
-}
-
-/*
- * Decode DTMF tones, queue result in separate sk_buf for
- * later examination.
- * Parameters:
- *   s    = pointer to state-struct.
- *   buf  = input audio data
- *   len  = size of audio data.
- *   fmt  = audio data format (0 = ulaw, 1 = alaw)
- */
-void
-isdn_audio_calc_dtmf(modem_info * info, unsigned char *buf, int len, int fmt)
-{
-	dtmf_state *s = info->dtmf_state;
-	int i;
-	int c;
-
-	while (len) {
-		c = DTMF_NPOINTS - s->idx;
-		if (c > len)
-			c = len;
-		if (c <= 0)
-			break;
-		for (i = 0; i < c; i++) {
-			if (fmt)
-				s->buf[s->idx++] =
-				    isdn_audio_alaw_to_s16[*buf++] >> (15 - AMP_BITS);
-			else
-				s->buf[s->idx++] =
-				    isdn_audio_ulaw_to_s16[*buf++] >> (15 - AMP_BITS);
-		}
-		if (s->idx == DTMF_NPOINTS) {
-			isdn_audio_goertzel(s->buf, info);
-			s->idx = 0;
-		}
-		len -= c;
-	}
-}
-
-silence_state *
-isdn_audio_silence_init(silence_state * s)
-{
-	if (!s)
-		s = (silence_state *) kmalloc(sizeof(silence_state), GFP_ATOMIC);
-	if (s) {
-		s->idx = 0;
-		s->state = 0;
-	}
-	return s;
-}
-
-void
-isdn_audio_calc_silence(modem_info * info, unsigned char *buf, int len, int fmt)
-{
-	silence_state *s = info->silence_state;
-	int i;
-	signed char c;
-
-	if (!info->emu.vpar[1]) return;
-
-	for (i = 0; i < len; i++) {
-		if (fmt)
-		    c = isdn_audio_alaw_to_ulaw[*buf++];
-			else
-		    c = *buf++;
-
-		if (c > 0) c -= 128;
-		c = abs(c);
-
-		if (c > (info->emu.vpar[1] * 4)) { 
-			s->idx = 0;
-			s->state = 1; 
-		} else {
-			if (s->idx < 210000) s->idx++; 
-		}
-	}
-}
-
-void
-isdn_audio_put_dle_code(modem_info * info, u_char code)
-{
-	struct sk_buff *skb;
-	unsigned long flags;
-	int di;
-	int ch;
-	char *p;
-
-	skb = dev_alloc_skb(2);
-	if (!skb) {
-		printk(KERN_WARNING
-		  "isdn_audio: Could not alloc skb for ttyI%d\n",
-		       info->line);
-		return;
-	}
-	p = (char *) skb_put(skb, 2);
-	p[0] = 0x10;
-	p[1] = code;
-	if (skb_headroom(skb) < sizeof(isdn_audio_skb)) {
-		printk(KERN_WARNING
-		       "isdn_audio: insufficient skb_headroom, dropping\n");
-		kfree_skb(skb);
-		return;
-	}
-	ISDN_AUDIO_SKB_DLECOUNT(skb) = 0;
-	ISDN_AUDIO_SKB_LOCK(skb) = 0;
-	save_flags(flags);
-	cli();
-	di = info->isdn_driver;
-	ch = info->isdn_channel;
-	__skb_queue_tail(&dev->drv[di]->rpqueue[ch], skb);
-	dev->drv[di]->rcvcount[ch] += 2;
-	restore_flags(flags);
-	/* Schedule dequeuing */
-	if ((dev->modempoll) && (info->rcvsched))
-		isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
-	wake_up_interruptible(&dev->drv[di]->rcv_waitq[ch]);
-}
-
-void
-isdn_audio_eval_silence(modem_info * info)
-{
-	silence_state *s = info->silence_state;
-	char what;
-
-	what = ' ';
-
-	if (s->idx > (info->emu.vpar[2] * 800)) { 
-		s->idx = 0;
-		if (!s->state) {	/* silence from beginning of rec */ 
-			what = 's';
-		} else {
-			what = 'q';
-		}
-	}
-		if ((what == 's') || (what == 'q')) {
-			printk(KERN_DEBUG "ttyI%d: %s\n", info->line,
-				(what=='s') ? "silence":"quiet");
-			isdn_audio_put_dle_code(info, what);
-		} 
-}
diff -Nru a/drivers/isdn/isdn_audio.h b/drivers/isdn/isdn_audio.h
--- a/drivers/isdn/isdn_audio.h	Mon Apr 22 15:32:19 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,44 +0,0 @@
-/* $Id: isdn_audio.h,v 1.9.6.1 2001/09/23 22:24:31 kai Exp $
- *
- * Linux ISDN subsystem, audio conversion and compression (linklevel).
- *
- * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de)
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#define DTMF_NPOINTS 205        /* Number of samples for DTMF recognition */
-typedef struct adpcm_state {
-	int a;
-	int d;
-	int word;
-	int nleft;
-	int nbits;
-} adpcm_state;
-
-typedef struct dtmf_state {
-	char last;
-	int idx;
-	int buf[DTMF_NPOINTS];
-} dtmf_state;
-
-typedef struct silence_state {
-	int state;
-	unsigned int idx;
-} silence_state;
-
-extern void isdn_audio_ulaw2alaw(unsigned char *, unsigned long);
-extern void isdn_audio_alaw2ulaw(unsigned char *, unsigned long);
-extern adpcm_state *isdn_audio_adpcm_init(adpcm_state *, int);
-extern int isdn_audio_adpcm2xlaw(adpcm_state *, int, unsigned char *, unsigned char *, int);
-extern int isdn_audio_xlaw2adpcm(adpcm_state *, int, unsigned char *, unsigned char *, int);
-extern int isdn_audio_2adpcm_flush(adpcm_state * s, unsigned char *out);
-extern void isdn_audio_calc_dtmf(modem_info *, unsigned char *, int, int);
-extern void isdn_audio_eval_dtmf(modem_info *);
-dtmf_state *isdn_audio_dtmf_init(dtmf_state *);
-extern void isdn_audio_calc_silence(modem_info *, unsigned char *, int, int);
-extern void isdn_audio_eval_silence(modem_info *);
-silence_state *isdn_audio_silence_init(silence_state *);
-extern void isdn_audio_put_dle_code(modem_info *, u_char);
diff -Nru a/drivers/isdn/isdn_bsdcomp.c b/drivers/isdn/isdn_bsdcomp.c
--- a/drivers/isdn/isdn_bsdcomp.c	Mon Apr 22 15:32:19 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,940 +0,0 @@
-/*
- * BSD compression module
- *
- * Patched version for ISDN syncPPP written 1997/1998 by Michael Hipp
- * The whole module is now SKB based.
- *
- */
-
-/*
- * Update: The Berkeley copyright was changed, and the change 
- * is retroactive to all "true" BSD software (ie everything
- * from UCB as opposed to other peoples code that just carried
- * the same license). The new copyright doesn't clash with the
- * GPL, so the module-only restriction has been removed..
- */
-
-/*
- * Original copyright notice:
- *
- * Copyright (c) 1985, 1986 The Regents of the University of California.
- * All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * James A. Woods, derived from original work by Spencer Thomas
- * and Joseph Orost.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *	This product includes software developed by the University of
- *	California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
-#include <linux/slab.h>
-#include <linux/tty.h>
-#include <linux/errno.h>
-#include <linux/string.h>	/* used in new tty drivers */
-#include <linux/signal.h>	/* used in new tty drivers */
-
-#include <asm/system.h>
-#include <asm/bitops.h>
-#include <asm/byteorder.h>
-#include <asm/types.h>
-
-#include <linux/if.h>
-
-#include <linux/if_ether.h>
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <linux/inet.h>
-#include <linux/ioctl.h>
-#include <linux/vmalloc.h>
-
-#include <linux/ppp_defs.h>
-
-#include <linux/isdn.h>
-#include <linux/isdn_ppp.h>
-#include <linux/ip.h>
-#include <linux/tcp.h>
-#include <linux/if_arp.h>
-#include <linux/ppp-comp.h>
-
-#include "isdn_ppp.h"
-
-MODULE_DESCRIPTION("ISDN4Linux: BSD Compression for PPP over ISDN");
-MODULE_LICENSE("Dual BSD/GPL");
-
-#define BSD_VERSION(x)	((x) >> 5)
-#define BSD_NBITS(x)	((x) & 0x1F)
-
-#define BSD_CURRENT_VERSION	1
-
-#define DEBUG 1
-
-/*
- * A dictionary for doing BSD compress.
- */
-
-struct bsd_dict {
-	u32 fcode;
-	u16 codem1;		/* output of hash table -1 */
-	u16 cptr;		/* map code to hash table entry */
-};
-
-struct bsd_db {
-	int            totlen;		/* length of this structure */
-	unsigned int   hsize;		/* size of the hash table */
-	unsigned char  hshift;		/* used in hash function */
-	unsigned char  n_bits;		/* current bits/code */
-	unsigned char  maxbits;		/* maximum bits/code */
-	unsigned char  debug;		/* non-zero if debug desired */
-	unsigned char  unit;		/* ppp unit number */
-	u16 seqno;          		/* sequence # of next packet */
-	unsigned int   mru;		/* size of receive (decompress) bufr */
-	unsigned int   maxmaxcode;	/* largest valid code */
-	unsigned int   max_ent;		/* largest code in use */
-	unsigned int   in_count;	/* uncompressed bytes, aged */
-	unsigned int   bytes_out;	/* compressed bytes, aged */
-	unsigned int   ratio;		/* recent compression ratio */
-	unsigned int   checkpoint;	/* when to next check the ratio */
-	unsigned int   clear_count;	/* times dictionary cleared */
-	unsigned int   incomp_count;	/* incompressible packets */
-	unsigned int   incomp_bytes;	/* incompressible bytes */
-	unsigned int   uncomp_count;	/* uncompressed packets */
-	unsigned int   uncomp_bytes;	/* uncompressed bytes */
-	unsigned int   comp_count;	/* compressed packets */
-	unsigned int   comp_bytes;	/* compressed bytes */
-	unsigned short  *lens;		/* array of lengths of codes */
-	struct bsd_dict *dict;		/* dictionary */
-	int xmit;
-};
-
-#define BSD_OVHD	2		/* BSD compress overhead/packet */
-#define MIN_BSD_BITS	9
-#define BSD_INIT_BITS	MIN_BSD_BITS
-#define MAX_BSD_BITS	15
-
-/*
- * the next two codes should not be changed lightly, as they must not
- * lie within the contiguous general code space.
- */
-#define CLEAR	256			/* table clear output code */
-#define FIRST	257			/* first free entry */
-#define LAST	255
-
-#define MAXCODE(b)	((1 << (b)) - 1)
-#define BADCODEM1	MAXCODE(MAX_BSD_BITS);
-
-#define BSD_HASH(prefix,suffix,hshift) ((((unsigned long)(suffix))<<(hshift)) \
-					 ^ (unsigned long)(prefix))
-#define BSD_KEY(prefix,suffix)		((((unsigned long)(suffix)) << 16) \
-					 + (unsigned long)(prefix))
-
-#define CHECK_GAP	10000		/* Ratio check interval */
-
-#define RATIO_SCALE_LOG	8
-#define RATIO_SCALE	(1<<RATIO_SCALE_LOG)
-#define RATIO_MAX	(0x7fffffff>>RATIO_SCALE_LOG)
-
-/*
- * clear the dictionary
- */
-
-static void bsd_clear(struct bsd_db *db)
-{
-	db->clear_count++;
-	db->max_ent      = FIRST-1;
-	db->n_bits       = BSD_INIT_BITS;
-	db->bytes_out    = 0;
-	db->in_count     = 0;
-	db->incomp_count = 0;
-	db->ratio	     = 0;
-	db->checkpoint   = CHECK_GAP;
-}
-
-/*
- * If the dictionary is full, then see if it is time to reset it.
- *
- * Compute the compression ratio using fixed-point arithmetic
- * with 8 fractional bits.
- *
- * Since we have an infinite stream instead of a single file,
- * watch only the local compression ratio.
- *
- * Since both peers must reset the dictionary at the same time even in
- * the absence of CLEAR codes (while packets are incompressible), they
- * must compute the same ratio.
- */
-static int bsd_check (struct bsd_db *db)	/* 1=output CLEAR */
-{
-    unsigned int new_ratio;
-
-    if (db->in_count >= db->checkpoint)
-      {
-	/* age the ratio by limiting the size of the counts */
-	if (db->in_count >= RATIO_MAX || db->bytes_out >= RATIO_MAX)
-	  {
-	    db->in_count  -= (db->in_count  >> 2);
-	    db->bytes_out -= (db->bytes_out >> 2);
-	  }
-	
-	db->checkpoint = db->in_count + CHECK_GAP;
-	
-	if (db->max_ent >= db->maxmaxcode)
-	  {
-	    /* Reset the dictionary only if the ratio is worse,
-	     * or if it looks as if it has been poisoned
-	     * by incompressible data.
-	     *
-	     * This does not overflow, because
-	     *	db->in_count <= RATIO_MAX.
-	     */
-
-	    new_ratio = db->in_count << RATIO_SCALE_LOG;
-	    if (db->bytes_out != 0)
-	      {
-		new_ratio /= db->bytes_out;
-	      }
-	    
-	    if (new_ratio < db->ratio || new_ratio < 1 * RATIO_SCALE)
-	      {
-		bsd_clear (db);
-		return 1;
-	      }
-	    db->ratio = new_ratio;
-	  }
-      }
-    return 0;
-}
-
-/*
- * Return statistics.
- */
-
-static void bsd_stats (void *state, struct compstat *stats)
-{
-	struct bsd_db *db = (struct bsd_db *) state;
-    
-	stats->unc_bytes    = db->uncomp_bytes;
-	stats->unc_packets  = db->uncomp_count;
-	stats->comp_bytes   = db->comp_bytes;
-	stats->comp_packets = db->comp_count;
-	stats->inc_bytes    = db->incomp_bytes;
-	stats->inc_packets  = db->incomp_count;
-	stats->in_count     = db->in_count;
-	stats->bytes_out    = db->bytes_out;
-}
-
-/*
- * Reset state, as on a CCP ResetReq.
- */
-static void bsd_reset (void *state,unsigned char code, unsigned char id,
-			unsigned char *data, unsigned len,
-			struct isdn_ppp_resetparams *rsparm)
-{
-	struct bsd_db *db = (struct bsd_db *) state;
-
-	bsd_clear(db);
-	db->seqno       = 0;
-	db->clear_count = 0;
-}
-
-/*
- * Release the compression structure
- */
-static void bsd_free (void *state)
-{
-	struct bsd_db *db = (struct bsd_db *) state;
-
-	if (db) {
-		/*
-		 * Release the dictionary
-		 */
-		if (db->dict) {
-			vfree (db->dict);
-			db->dict = NULL;
-		}
-
-		/*
-		 * Release the string buffer
-		 */
-		if (db->lens) {
-			vfree (db->lens);
-			db->lens = NULL;
-		}
-
-		/*
-		 * Finally release the structure itself.
-		 */
-		kfree (db);
-		MOD_DEC_USE_COUNT;
-	}
-}
-
-
-/*
- * Allocate space for a (de) compressor.
- */
-static void *bsd_alloc (struct isdn_ppp_comp_data *data)
-{
-	int bits;
-	unsigned int hsize, hshift, maxmaxcode;
-	struct bsd_db *db;
-	int decomp;
-
-	static unsigned int htab[][2] = {
-		{ 5003 , 4 } , { 5003 , 4 } , { 5003 , 4 } , { 5003 , 4 } , 
-		{ 9001 , 5 } , { 18013 , 6 } , { 35023 , 7 } , { 69001 , 8 } 
-	};
-		
-	if (data->optlen != 1 || data->num != CI_BSD_COMPRESS
-		|| BSD_VERSION(data->options[0]) != BSD_CURRENT_VERSION)
-		return NULL;
-
-	bits = BSD_NBITS(data->options[0]);
-
-	if(bits < 9 || bits > 15)
-		return NULL;
-
-	hsize = htab[bits-9][0];
-	hshift = htab[bits-9][1];
-	
-	/*
-	 * Allocate the main control structure for this instance.
-	 */
-	maxmaxcode = MAXCODE(bits);
-	db = (struct bsd_db *) kmalloc (sizeof (struct bsd_db),GFP_KERNEL);
-	if (!db)
-		return NULL;
-
-	memset (db, 0, sizeof(struct bsd_db));
-
-	db->xmit = data->flags & IPPP_COMP_FLAG_XMIT;
-	decomp = db->xmit ? 0 : 1;
-
-	/*
-	 * Allocate space for the dictionary. This may be more than one page in
-	 * length.
-	 */
-	db->dict = (struct bsd_dict *) vmalloc (hsize * sizeof (struct bsd_dict));
-	if (!db->dict) {
-		bsd_free (db);
-		return NULL;
-	}
-
-	MOD_INC_USE_COUNT;
-
-	/*
-	 * If this is the compression buffer then there is no length data.
-	 * For decompression, the length information is needed as well.
-	 */
-	if (!decomp)
-		db->lens = NULL;
-	else {
-		db->lens = (unsigned short *) vmalloc ((maxmaxcode + 1) *
-			sizeof (db->lens[0]));
-		if (!db->lens) {
-			bsd_free (db); /* calls MOD_DEC_USE_COUNT; */
-			return (NULL);
-		}
-	}
-
-	/*
-	 * Initialize the data information for the compression code
-	 */
-	db->totlen     = sizeof (struct bsd_db) + (sizeof (struct bsd_dict) * hsize);
-	db->hsize      = hsize;
-	db->hshift     = hshift;
-	db->maxmaxcode = maxmaxcode;
-	db->maxbits    = bits;
-
-	return (void *) db;
-}
-
-/*
- * Initialize the database.
- */
-static int bsd_init (void *state, struct isdn_ppp_comp_data *data, int unit, int debug)
-{
-	struct bsd_db *db = state;
-	int indx;
-	int decomp;
-
-	if(!state || !data) {
-		printk(KERN_ERR "isdn_bsd_init: [%d] ERR, state %lx data %lx\n",unit,(long)state,(long)data);
-		return 0;
-	}
-
-	decomp = db->xmit ? 0 : 1;
-    
-	if (data->optlen != 1 || data->num != CI_BSD_COMPRESS
-		|| (BSD_VERSION(data->options[0]) != BSD_CURRENT_VERSION)
-		|| (BSD_NBITS(data->options[0]) != db->maxbits)
-		|| (decomp && db->lens == NULL)) {
-		printk(KERN_ERR "isdn_bsd: %d %d %d %d %lx\n",data->optlen,data->num,data->options[0],decomp,(unsigned long)db->lens);
-		return 0;
-	}
-
-	if (decomp)
-		for(indx=LAST;indx>=0;indx--)
-			db->lens[indx] = 1;
-
-	indx = db->hsize;
-	while (indx-- != 0) {
-		db->dict[indx].codem1 = BADCODEM1;
-		db->dict[indx].cptr   = 0;
-	}
-
-	db->unit = unit;
-	db->mru  = 0;
-
-	db->debug = 1;
-    
-	bsd_reset(db,0,0,NULL,0,NULL);
-    
-	return 1;
-}
-
-/*
- * Obtain pointers to the various structures in the compression tables
- */
-
-#define dict_ptrx(p,idx) &(p->dict[idx])
-#define lens_ptrx(p,idx) &(p->lens[idx])
-
-#ifdef DEBUG
-static unsigned short *lens_ptr(struct bsd_db *db, int idx)
-{
-	if ((unsigned int) idx > (unsigned int) db->maxmaxcode) {
-		printk (KERN_DEBUG "<9>ppp: lens_ptr(%d) > max\n", idx);
-		idx = 0;
-	}
-	return lens_ptrx (db, idx);
-}
-
-static struct bsd_dict *dict_ptr(struct bsd_db *db, int idx)
-{
-	if ((unsigned int) idx >= (unsigned int) db->hsize) {
-		printk (KERN_DEBUG "<9>ppp: dict_ptr(%d) > max\n", idx);
-		idx = 0;
-	}
-	return dict_ptrx (db, idx);
-}
-
-#else
-#define lens_ptr(db,idx) lens_ptrx(db,idx)
-#define dict_ptr(db,idx) dict_ptrx(db,idx)
-#endif
-
-/*
- * compress a packet
- */
-static int bsd_compress (void *state, struct sk_buff *skb_in, struct sk_buff *skb_out,int proto)
-{
-	struct bsd_db *db;
-	int hshift;
-	unsigned int max_ent;
-	unsigned int n_bits;
-	unsigned int bitno;
-	unsigned long accm;
-	int ent;
-	unsigned long fcode;
-	struct bsd_dict *dictp;
-	unsigned char c;
-	int hval,disp,ilen,mxcode;
-	unsigned char *rptr = skb_in->data;
-	int isize = skb_in->len;
-
-#define OUTPUT(ent)			\
-  {					\
-    bitno -= n_bits;			\
-    accm |= ((ent) << bitno);		\
-    do	{				\
-        if(skb_out && skb_tailroom(skb_out) > 0) 	\
-      		*(skb_put(skb_out,1)) = (unsigned char) (accm>>24); \
-	accm <<= 8;			\
-	bitno += 8;			\
-    } while (bitno <= 24);		\
-  }
-
-	/*
-	 * If the protocol is not in the range we're interested in,
-	 * just return without compressing the packet.  If it is,
-	 * the protocol becomes the first byte to compress.
-	 */
-	printk(KERN_DEBUG "bsd_compress called with %x\n",proto);
-	
-	ent = proto;
-	if (proto < 0x21 || proto > 0xf9 || !(proto & 0x1) )
-		return 0;
-
-	db      = (struct bsd_db *) state;
-	hshift  = db->hshift;
-	max_ent = db->max_ent;
-	n_bits  = db->n_bits;
-	bitno   = 32;
-	accm    = 0;
-	mxcode  = MAXCODE (n_bits);
-	
-	/* This is the PPP header information */
-	if(skb_out && skb_tailroom(skb_out) >= 2) {
-		char *v = skb_put(skb_out,2);
-		/* we only push our own data on the header,
-		  AC,PC and protos is pushed by caller  */
-		v[0] = db->seqno >> 8;
-		v[1] = db->seqno;
-	}
-
-	ilen   = ++isize; /* This is off by one, but that is what is in draft! */
-
-	while (--ilen > 0) {
-		c     = *rptr++;
-		fcode = BSD_KEY  (ent, c);
-		hval  = BSD_HASH (ent, c, hshift);
-		dictp = dict_ptr (db, hval);
-	
-		/* Validate and then check the entry. */
-		if (dictp->codem1 >= max_ent)
-			goto nomatch;
-
-		if (dictp->fcode == fcode) {
-			ent = dictp->codem1 + 1;
-			continue;	/* found (prefix,suffix) */
-		}
-	
-		/* continue probing until a match or invalid entry */
-		disp = (hval == 0) ? 1 : hval;
-
-		do {
-			hval += disp;
-			if (hval >= db->hsize)
-				hval -= db->hsize;
-			dictp = dict_ptr (db, hval);
-			if (dictp->codem1 >= max_ent)
-				goto nomatch;
-		} while (dictp->fcode != fcode);
-
-		ent = dictp->codem1 + 1;	/* finally found (prefix,suffix) */
-		continue;
-	
-nomatch:
-		OUTPUT(ent);		/* output the prefix */
-	
-		/* code -> hashtable */
-		if (max_ent < db->maxmaxcode) {
-			struct bsd_dict *dictp2;
-			struct bsd_dict *dictp3;
-			int indx;
-
-			/* expand code size if needed */
-			if (max_ent >= mxcode) {
-				db->n_bits = ++n_bits;
-				mxcode = MAXCODE (n_bits);
-			}
-	    
-			/* 
-			 * Invalidate old hash table entry using
-			 * this code, and then take it over.
-			 */
-			dictp2 = dict_ptr (db, max_ent + 1);
-			indx   = dictp2->cptr;
-			dictp3 = dict_ptr (db, indx);
-
-			if (dictp3->codem1 == max_ent)
-				dictp3->codem1 = BADCODEM1;
-
-			dictp2->cptr   = hval;
-			dictp->codem1  = max_ent;
-			dictp->fcode = fcode;
-			db->max_ent    = ++max_ent;
-
-			if (db->lens) {
-				unsigned short *len1 = lens_ptr (db, max_ent);
-				unsigned short *len2 = lens_ptr (db, ent);
-				*len1 = *len2 + 1;
-			}
-		}
-		ent = c;
-	}
-    
-	OUTPUT(ent);		/* output the last code */
-
-	if(skb_out)
-		db->bytes_out    += skb_out->len; /* Do not count bytes from here */
-	db->uncomp_bytes += isize;
-	db->in_count     += isize;
-	++db->uncomp_count;
-	++db->seqno;
-
-	if (bitno < 32)
-		++db->bytes_out; /* must be set before calling bsd_check */
-
-	/*
-	 * Generate the clear command if needed
-	 */
-
-	if (bsd_check(db))
-		OUTPUT (CLEAR);
-
-	/*
-	 * Pad dribble bits of last code with ones.
-	 * Do not emit a completely useless byte of ones.
-	 */
-	if (bitno < 32 && skb_out && skb_tailroom(skb_out) > 0) 
-		*(skb_put(skb_out,1)) = (unsigned char) ((accm | (0xff << (bitno-8))) >> 24);
-    
-	/*
-	 * Increase code size if we would have without the packet
-	 * boundary because the decompressor will do so.
-	 */
-	if (max_ent >= mxcode && max_ent < db->maxmaxcode)
-		db->n_bits++;
-
-	/* If output length is too large then this is an incompressible frame. */
-	if (!skb_out || (skb_out && skb_out->len >= skb_in->len) ) {
-		++db->incomp_count;
-		db->incomp_bytes += isize;
-		return 0;
-	}
-
-	/* Count the number of compressed frames */
-	++db->comp_count;
-	db->comp_bytes += skb_out->len;
-	return skb_out->len;
-
-#undef OUTPUT
-}
-
-/*
- * Update the "BSD Compress" dictionary on the receiver for
- * incompressible data by pretending to compress the incoming data.
- */
-static void bsd_incomp (void *state, struct sk_buff *skb_in,int proto)
-{
-	bsd_compress (state, skb_in, NULL, proto);
-}
-
-/*
- * Decompress "BSD Compress".
- */
-static int bsd_decompress (void *state, struct sk_buff *skb_in, struct sk_buff *skb_out,
-			   struct isdn_ppp_resetparams *rsparm)
-{
-	struct bsd_db *db;
-	unsigned int max_ent;
-	unsigned long accm;
-	unsigned int bitno;		/* 1st valid bit in accm */
-	unsigned int n_bits;
-	unsigned int tgtbitno;	/* bitno when we have a code */
-	struct bsd_dict *dictp;
-	int seq;
-	unsigned int incode;
-	unsigned int oldcode;
-	unsigned int finchar;
-	unsigned char *p,*ibuf;
-	int ilen;
-	int codelen;
-	int extra;
-
-	db       = (struct bsd_db *) state;
-	max_ent  = db->max_ent;
-	accm     = 0;
-	bitno    = 32;		/* 1st valid bit in accm */
-	n_bits   = db->n_bits;
-	tgtbitno = 32 - n_bits;	/* bitno when we have a code */
-
-	printk(KERN_DEBUG "bsd_decompress called\n");
-
-	if(!skb_in || !skb_out) {
-		printk(KERN_ERR "bsd_decompress called with NULL parameter\n");
-		return DECOMP_ERROR;
-	}
-    
-	/*
-	 * Get the sequence number.
-	 */
-	if( (p = skb_pull(skb_in,2)) == NULL) {
-		return DECOMP_ERROR;
-	}
-	p-=2;
-	seq   = (p[0] << 8) + p[1];
-	ilen  = skb_in->len;
-	ibuf = skb_in->data;
-
-	/*
-	 * Check the sequence number and give up if it differs from
-	 * the value we're expecting.
-	 */
-	if (seq != db->seqno) {
-		if (db->debug) {
-			printk(KERN_DEBUG "bsd_decomp%d: bad sequence # %d, expected %d\n",
-				db->unit, seq, db->seqno - 1);
-		}
-		return DECOMP_ERROR;
-	}
-
-	++db->seqno;
-	db->bytes_out += ilen;
-
-	if(skb_tailroom(skb_out) > 0)
-		*(skb_put(skb_out,1)) = 0;
-	else
-		return DECOMP_ERR_NOMEM;
-    
-	oldcode = CLEAR;
-
-	/*
-	 * Keep the checkpoint correctly so that incompressible packets
-	 * clear the dictionary at the proper times.
-	 */
-
-	for (;;) {
-		if (ilen-- <= 0) {
-			db->in_count += (skb_out->len - 1); /* don't count the header */
-			break;
-		}
-
-		/*
-		 * Accumulate bytes until we have a complete code.
-		 * Then get the next code, relying on the 32-bit,
-		 * unsigned accm to mask the result.
-		 */
-
-		bitno -= 8;
-		accm  |= *ibuf++ << bitno;
-		if (tgtbitno < bitno)
-			continue;
-
-		incode = accm >> tgtbitno;
-		accm <<= n_bits;
-		bitno += n_bits;
-
-		/*
-		 * The dictionary must only be cleared at the end of a packet.
-		 */
-	
-		if (incode == CLEAR) {
-			if (ilen > 0) {
-				if (db->debug)
-					printk(KERN_DEBUG "bsd_decomp%d: bad CLEAR\n", db->unit);
-				return DECOMP_FATALERROR;	/* probably a bug */
-			}
-			bsd_clear(db);
-			break;
-		}
-
-		if ((incode > max_ent + 2) || (incode > db->maxmaxcode)
-			|| (incode > max_ent && oldcode == CLEAR)) {
-			if (db->debug) {
-				printk(KERN_DEBUG "bsd_decomp%d: bad code 0x%x oldcode=0x%x ",
-					db->unit, incode, oldcode);
-				printk(KERN_DEBUG "max_ent=0x%x skb->Len=%d seqno=%d\n",
-					max_ent, skb_out->len, db->seqno);
-			}
-			return DECOMP_FATALERROR;	/* probably a bug */
-		}
-	
-		/* Special case for KwKwK string. */
-		if (incode > max_ent) {
-			finchar = oldcode;
-			extra   = 1;
-		} else {
-			finchar = incode;
-			extra   = 0;
-		}
-
-		codelen = *(lens_ptr (db, finchar));
-		if( skb_tailroom(skb_out) < codelen + extra) {
-			if (db->debug) {
-				printk(KERN_DEBUG "bsd_decomp%d: ran out of mru\n", db->unit);
-#ifdef DEBUG
-				printk(KERN_DEBUG "  len=%d, finchar=0x%x, codelen=%d,skblen=%d\n",
-					ilen, finchar, codelen, skb_out->len);
-#endif
-			}
-			return DECOMP_FATALERROR;
-		}
-
-		/*
-		 * Decode this code and install it in the decompressed buffer.
-		 */
-
-		p     = skb_put(skb_out,codelen);
-		p += codelen;
-		while (finchar > LAST) {
-			struct bsd_dict *dictp2 = dict_ptr (db, finchar);
-	    
-			dictp = dict_ptr (db, dictp2->cptr);
-
-#ifdef DEBUG
-			if (--codelen <= 0 || dictp->codem1 != finchar-1) {
-				if (codelen <= 0) {
-					printk(KERN_ERR "bsd_decomp%d: fell off end of chain ", db->unit);
-					printk(KERN_ERR "0x%x at 0x%x by 0x%x, max_ent=0x%x\n", incode, finchar, dictp2->cptr, max_ent);
-				} else {
-					if (dictp->codem1 != finchar-1) {
-						printk(KERN_ERR "bsd_decomp%d: bad code chain 0x%x finchar=0x%x ",db->unit, incode, finchar);
-						printk(KERN_ERR "oldcode=0x%x cptr=0x%x codem1=0x%x\n", oldcode, dictp2->cptr, dictp->codem1);
-					}
-				}
-				return DECOMP_FATALERROR;
-			}
-#endif
-
-			{
-				u32 fcode = dictp->fcode;
-				*--p    = (fcode >> 16) & 0xff;
-				finchar = fcode & 0xffff;
-			}
-		}
-		*--p = finchar;
-	
-#ifdef DEBUG
-		if (--codelen != 0)
-			printk(KERN_ERR "bsd_decomp%d: short by %d after code 0x%x, max_ent=0x%x\n", db->unit, codelen, incode, max_ent);
-#endif
-	
-		if (extra)		/* the KwKwK case again */
-			*(skb_put(skb_out,1)) = finchar;
-	
-		/*
-		 * If not first code in a packet, and
-		 * if not out of code space, then allocate a new code.
-		 *
-		 * Keep the hash table correct so it can be used
-		 * with uncompressed packets.
-		 */
-		if (oldcode != CLEAR && max_ent < db->maxmaxcode) {
-			struct bsd_dict *dictp2, *dictp3;
-			u16  *lens1,  *lens2;
-			unsigned long fcode;
-			int hval, disp, indx;
-	    
-			fcode = BSD_KEY(oldcode,finchar);
-			hval  = BSD_HASH(oldcode,finchar,db->hshift);
-			dictp = dict_ptr (db, hval);
-	    
-			/* look for a free hash table entry */
-			if (dictp->codem1 < max_ent) {
-				disp = (hval == 0) ? 1 : hval;
-				do {
-					hval += disp;
-					if (hval >= db->hsize)
-						hval -= db->hsize;
-					dictp = dict_ptr (db, hval);
-				} while (dictp->codem1 < max_ent);
-			}
-	    
-			/*
-			 * Invalidate previous hash table entry
-			 * assigned this code, and then take it over
-			 */
-
-			dictp2 = dict_ptr (db, max_ent + 1);
-			indx   = dictp2->cptr;
-			dictp3 = dict_ptr (db, indx);
-
-			if (dictp3->codem1 == max_ent)
-				dictp3->codem1 = BADCODEM1;
-
-			dictp2->cptr   = hval;
-			dictp->codem1  = max_ent;
-			dictp->fcode = fcode;
-			db->max_ent    = ++max_ent;
-
-			/* Update the length of this string. */
-			lens1  = lens_ptr (db, max_ent);
-			lens2  = lens_ptr (db, oldcode);
-			*lens1 = *lens2 + 1;
-	    
-			/* Expand code size if needed. */
-			if (max_ent >= MAXCODE(n_bits) && max_ent < db->maxmaxcode) {
-				db->n_bits = ++n_bits;
-				tgtbitno   = 32-n_bits;
-			}
-		}
-		oldcode = incode;
-	}
-
-	++db->comp_count;
-	++db->uncomp_count;
-	db->comp_bytes   += skb_in->len - BSD_OVHD;
-	db->uncomp_bytes += skb_out->len;
-
-	if (bsd_check(db)) {
-		if (db->debug)
-			printk(KERN_DEBUG "bsd_decomp%d: peer should have cleared dictionary on %d\n",
-				db->unit, db->seqno - 1);
-	}
-	return skb_out->len;
-}
-
-/*************************************************************
- * Table of addresses for the BSD compression module
- *************************************************************/
-
-static struct isdn_ppp_compressor ippp_bsd_compress = {
-	NULL,NULL,		/* prev,next: overwritten by isdn_ppp */
-	CI_BSD_COMPRESS,	/* compress_proto */
-	bsd_alloc,		/* alloc */
-	bsd_free,		/* free */
-	bsd_init,		/* init */
-	bsd_reset,		/* reset */
-	bsd_compress,		/* compress */
-	bsd_decompress,		/* decompress */
-	bsd_incomp,		/* incomp */
-	bsd_stats		/* comp_stat */
-};
-
-/*************************************************************
- * Module support routines
- *************************************************************/
-
-static int __init isdn_bsdcomp_init(void)
-{
-	int answer = isdn_ppp_register_compressor (&ippp_bsd_compress);
-	if (answer == 0)
-		printk (KERN_INFO "PPP BSD Compression module registered\n");
-	return answer;
-}
-
-static void __exit isdn_bsdcomp_exit(void)
-{
-	isdn_ppp_unregister_compressor (&ippp_bsd_compress);
-}
-
-module_init(isdn_bsdcomp_init);
-module_exit(isdn_bsdcomp_exit);
diff -Nru a/drivers/isdn/isdn_common.c b/drivers/isdn/isdn_common.c
--- a/drivers/isdn/isdn_common.c	Mon Apr 22 15:32:19 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,2371 +0,0 @@
-/* $Id: isdn_common.c,v 1.114.6.16 2001/11/06 20:58:28 kai Exp $
- *
- * Linux ISDN subsystem, common used functions (linklevel).
- *
- * Copyright 1994-1999  by Fritz Elfert (fritz@isdn4linux.de)
- * Copyright 1995,96    Thinking Objects Software GmbH Wuerzburg
- * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/version.h>
-#include <linux/poll.h>
-#include <linux/vmalloc.h>
-#include <linux/isdn.h>
-#include <linux/smp_lock.h>
-#include "isdn_common.h"
-#include "isdn_tty.h"
-#include "isdn_net.h"
-#include "isdn_ppp.h"
-#ifdef CONFIG_ISDN_AUDIO
-#include "isdn_audio.h"
-#endif
-#ifdef CONFIG_ISDN_DIVERSION_MODULE
-#define CONFIG_ISDN_DIVERSION
-#endif
-#ifdef CONFIG_ISDN_DIVERSION
-#include <linux/isdn_divertif.h>
-#endif /* CONFIG_ISDN_DIVERSION */
-#include "isdn_v110.h"
-#include <linux/devfs_fs_kernel.h>
-
-/* Debugflags */
-#undef ISDN_DEBUG_STATCALLB
-
-MODULE_DESCRIPTION("ISDN4Linux: link layer");
-MODULE_AUTHOR("Fritz Elfert");
-MODULE_LICENSE("GPL");
-
-isdn_dev *dev;
-
-static char *isdn_revision = "$Revision: 1.114.6.16 $";
-
-extern char *isdn_net_revision;
-extern char *isdn_tty_revision;
-#ifdef CONFIG_ISDN_PPP
-extern char *isdn_ppp_revision;
-#else
-static char *isdn_ppp_revision = ": none $";
-#endif
-#ifdef CONFIG_ISDN_AUDIO
-extern char *isdn_audio_revision;
-#else
-static char *isdn_audio_revision = ": none $";
-#endif
-extern char *isdn_v110_revision;
-
-#ifdef CONFIG_ISDN_DIVERSION
-static isdn_divert_if *divert_if; /* = NULL */
-#endif /* CONFIG_ISDN_DIVERSION */
-
-
-static void set_global_features(void);
-static void isdn_register_devfs(int);
-static void isdn_unregister_devfs(int);
-static int isdn_wildmat(char *s, char *p);
-
-void
-isdn_lock_drivers(void)
-{
-	int i;
-
-	for (i = 0; i < dev->drivers; i++) {
-		isdn_ctrl cmd;
-
-		cmd.driver = i;
-		cmd.arg = 0;
-		cmd.command = ISDN_CMD_LOCK;
-		isdn_command(&cmd);
-		dev->drv[i]->locks++;
-	}
-}
-
-void
-isdn_MOD_INC_USE_COUNT(void)
-{
-	MOD_INC_USE_COUNT;
-	isdn_lock_drivers();
-}
-
-void
-isdn_unlock_drivers(void)
-{
-	int i;
-
-	for (i = 0; i < dev->drivers; i++)
-		if (dev->drv[i]->locks > 0) {
-			isdn_ctrl cmd;
-
-			cmd.driver = i;
-			cmd.arg = 0;
-			cmd.command = ISDN_CMD_UNLOCK;
-			isdn_command(&cmd);
-			dev->drv[i]->locks--;
-		}
-}
-
-void
-isdn_MOD_DEC_USE_COUNT(void)
-{
-	MOD_DEC_USE_COUNT;
-	isdn_unlock_drivers();
-}
-
-#if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP)
-void
-isdn_dumppkt(char *s, u_char * p, int len, int dumplen)
-{
-	int dumpc;
-
-	printk(KERN_DEBUG "%s(%d) ", s, len);
-	for (dumpc = 0; (dumpc < dumplen) && (len); len--, dumpc++)
-		printk(" %02x", *p++);
-	printk("\n");
-}
-#endif
-
-/*
- * I picked the pattern-matching-functions from an old GNU-tar version (1.10)
- * It was originally written and put to PD by rs@mirror.TMC.COM (Rich Salz)
- */
-static int
-isdn_star(char *s, char *p)
-{
-	while (isdn_wildmat(s, p)) {
-		if (*++s == '\0')
-			return (2);
-	}
-	return (0);
-}
-
-/*
- * Shell-type Pattern-matching for incoming caller-Ids
- * This function gets a string in s and checks, if it matches the pattern
- * given in p.
- *
- * Return:
- *   0 = match.
- *   1 = no match.
- *   2 = no match. Would eventually match, if s would be longer.
- *
- * Possible Patterns:
- *
- * '?'     matches one character
- * '*'     matches zero or more characters
- * [xyz]   matches the set of characters in brackets.
- * [^xyz]  matches any single character not in the set of characters
- */
-
-static int
-isdn_wildmat(char *s, char *p)
-{
-	register int last;
-	register int matched;
-	register int reverse;
-	register int nostar = 1;
-
-	if (!(*s) && !(*p))
-		return(1);
-	for (; *p; s++, p++)
-		switch (*p) {
-			case '\\':
-				/*
-				 * Literal match with following character,
-				 * fall through.
-				 */
-				p++;
-			default:
-				if (*s != *p)
-					return (*s == '\0')?2:1;
-				continue;
-			case '?':
-				/* Match anything. */
-				if (*s == '\0')
-					return (2);
-				continue;
-			case '*':
-				nostar = 0;	
-				/* Trailing star matches everything. */
-				return (*++p ? isdn_star(s, p) : 0);
-			case '[':
-				/* [^....] means inverse character class. */
-				if ((reverse = (p[1] == '^')))
-					p++;
-				for (last = 0, matched = 0; *++p && (*p != ']'); last = *p)
-					/* This next line requires a good C compiler. */
-					if (*p == '-' ? *s <= *++p && *s >= last : *s == *p)
-						matched = 1;
-				if (matched == reverse)
-					return (1);
-				continue;
-		}
-	return (*s == '\0')?0:nostar;
-}
-
-int isdn_msncmp( const char * msn1, const char * msn2 )
-{
-	char TmpMsn1[ ISDN_MSNLEN ];
-	char TmpMsn2[ ISDN_MSNLEN ];
-	char *p;
-
-	for ( p = TmpMsn1; *msn1 && *msn1 != ':'; )  // Strip off a SPID
-		*p++ = *msn1++;
-	*p = '\0';
-
-	for ( p = TmpMsn2; *msn2 && *msn2 != ':'; )  // Strip off a SPID
-		*p++ = *msn2++;
-	*p = '\0';
-
-	return isdn_wildmat( TmpMsn1, TmpMsn2 );
-}
-
-int
-isdn_dc2minor(int di, int ch)
-{
-	int i;
-	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
-		if (dev->chanmap[i] == ch && dev->drvmap[i] == di)
-			return i;
-	return -1;
-}
-
-static int isdn_timer_cnt1 = 0;
-static int isdn_timer_cnt2 = 0;
-static int isdn_timer_cnt3 = 0;
-
-static void
-isdn_timer_funct(ulong dummy)
-{
-	int tf = dev->tflags;
-	if (tf & ISDN_TIMER_FAST) {
-		if (tf & ISDN_TIMER_MODEMREAD)
-			isdn_tty_readmodem();
-		if (tf & ISDN_TIMER_MODEMPLUS)
-			isdn_tty_modem_escape();
-		if (tf & ISDN_TIMER_MODEMXMIT)
-			isdn_tty_modem_xmit();
-	}
-	if (tf & ISDN_TIMER_SLOW) {
-		if (++isdn_timer_cnt1 >= ISDN_TIMER_02SEC) {
-			isdn_timer_cnt1 = 0;
-			if (tf & ISDN_TIMER_NETDIAL)
-				isdn_net_dial();
-		}
-		if (++isdn_timer_cnt2 >= ISDN_TIMER_1SEC) {
-			isdn_timer_cnt2 = 0;
-			if (tf & ISDN_TIMER_NETHANGUP)
-				isdn_net_autohup();
-			if (++isdn_timer_cnt3 >= ISDN_TIMER_RINGING) {
-				isdn_timer_cnt3 = 0;
-				if (tf & ISDN_TIMER_MODEMRING)
-					isdn_tty_modem_ring();
-			}
-			if (tf & ISDN_TIMER_CARRIER)
-				isdn_tty_carrier_timeout();
-		}
-	}
-	if (tf) 
-	{
-		unsigned long flags;
-
-		save_flags(flags);
-		cli();
-		mod_timer(&dev->timer, jiffies+ISDN_TIMER_RES);
-		restore_flags(flags);
-	}
-}
-
-void
-isdn_timer_ctrl(int tf, int onoff)
-{
-	unsigned long flags;
-	int old_tflags;
-
-	save_flags(flags);
-	cli();
-	if ((tf & ISDN_TIMER_SLOW) && (!(dev->tflags & ISDN_TIMER_SLOW))) {
-		/* If the slow-timer wasn't activated until now */
-		isdn_timer_cnt1 = 0;
-		isdn_timer_cnt2 = 0;
-	}
-	old_tflags = dev->tflags;
-	if (onoff)
-		dev->tflags |= tf;
-	else
-		dev->tflags &= ~tf;
-	if (dev->tflags && !old_tflags)
-		mod_timer(&dev->timer, jiffies+ISDN_TIMER_RES);
-	restore_flags(flags);
-}
-
-/*
- * Receive a packet from B-Channel. (Called from low-level-module)
- */
-static void
-isdn_receive_skb_callback(int di, int channel, struct sk_buff *skb)
-{
-	int i;
-
-	if ((i = isdn_dc2minor(di, channel)) == -1) {
-		dev_kfree_skb(skb);
-		return;
-	}
-	/* Update statistics */
-	dev->ibytes[i] += skb->len;
-	
-	/* First, try to deliver data to network-device */
-	if (isdn_net_rcv_skb(i, skb))
-		return;
-
-	/* V.110 handling
-	 * makes sense for async streams only, so it is
-	 * called after possible net-device delivery.
-	 */
-	if (dev->v110[i]) {
-		atomic_inc(&dev->v110use[i]);
-		skb = isdn_v110_decode(dev->v110[i], skb);
-		atomic_dec(&dev->v110use[i]);
-		if (!skb)
-			return;
-	}
-
-	/* No network-device found, deliver to tty or raw-channel */
-	if (skb->len) {
-		if (isdn_tty_rcv_skb(i, di, channel, skb))
-			return;
-		wake_up_interruptible(&dev->drv[di]->rcv_waitq[channel]);
-	} else
-		dev_kfree_skb(skb);
-}
-
-/*
- * Intercept command from Linklevel to Lowlevel.
- * If layer 2 protocol is V.110 and this is not supported by current
- * lowlevel-driver, use driver's transparent mode and handle V.110 in
- * linklevel instead.
- */
-int
-isdn_command(isdn_ctrl *cmd)
-{
-	if (cmd->driver == -1) {
-		printk(KERN_WARNING "isdn_command command(%x) driver -1\n", cmd->command);
-		return(1);
-	}
-	if (cmd->command == ISDN_CMD_SETL2) {
-		int idx = isdn_dc2minor(cmd->driver, cmd->arg & 255);
-		unsigned long l2prot = (cmd->arg >> 8) & 255;
-		unsigned long features = (dev->drv[cmd->driver]->interface->features
-						>> ISDN_FEATURE_L2_SHIFT) &
-						ISDN_FEATURE_L2_MASK;
-		unsigned long l2_feature = (1 << l2prot);
-
-		switch (l2prot) {
-			case ISDN_PROTO_L2_V11096:
-			case ISDN_PROTO_L2_V11019:
-			case ISDN_PROTO_L2_V11038:
-			/* If V.110 requested, but not supported by
-			 * HL-driver, set emulator-flag and change
-			 * Layer-2 to transparent
-			 */
-				if (!(features & l2_feature)) {
-					dev->v110emu[idx] = l2prot;
-					cmd->arg = (cmd->arg & 255) |
-						(ISDN_PROTO_L2_TRANS << 8);
-				} else
-					dev->v110emu[idx] = 0;
-		}
-	}
-	return dev->drv[cmd->driver]->interface->command(cmd);
-}
-
-void
-isdn_all_eaz(int di, int ch)
-{
-	isdn_ctrl cmd;
-
-	if (di < 0)
-		return;
-	cmd.driver = di;
-	cmd.arg = ch;
-	cmd.command = ISDN_CMD_SETEAZ;
-	cmd.parm.num[0] = '\0';
-	isdn_command(&cmd);
-}
-
-/*
- * Begin of a CAPI like LL<->HL interface, currently used only for 
- * supplementary service (CAPI 2.0 part III)
- */
-#include "avmb1/capicmd.h"  /* this should be moved in a common place */
-
-int
-isdn_capi_rec_hl_msg(capi_msg *cm) {
-	
-	int di;
-	int ch;
-	
-	di = (cm->adr.Controller & 0x7f) -1;
-	ch = isdn_dc2minor(di, (cm->adr.Controller>>8)& 0x7f);
-	switch(cm->Command) {
-		case CAPI_FACILITY:
-			/* in the moment only handled in tty */
-			return(isdn_tty_capi_facility(cm));
-		default:
-			return(-1);
-	}
-}
-
-static int
-isdn_status_callback(isdn_ctrl * c)
-{
-	int di;
-	ulong flags;
-	int i;
-	int r;
-	int retval = 0;
-	isdn_ctrl cmd;
-	isdn_net_dev *p;
-
-	di = c->driver;
-	i = isdn_dc2minor(di, c->arg);
-	switch (c->command) {
-		case ISDN_STAT_BSENT:
-			if (i < 0)
-				return -1;
-			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
-				return 0;
-			if (isdn_net_stat_callback(i, c))
-				return 0;
-			if (isdn_v110_stat_callback(i, c))
-				return 0;
-			if (isdn_tty_stat_callback(i, c))
-				return 0;
-			wake_up_interruptible(&dev->drv[di]->snd_waitq[c->arg]);
-			break;
-		case ISDN_STAT_STAVAIL:
-			save_flags(flags);
-			cli();
-			dev->drv[di]->stavail += c->arg;
-			restore_flags(flags);
-			wake_up_interruptible(&dev->drv[di]->st_waitq);
-			break;
-		case ISDN_STAT_RUN:
-			dev->drv[di]->flags |= DRV_FLAG_RUNNING;
-			for (i = 0; i < ISDN_MAX_CHANNELS; i++)
-				if (dev->drvmap[i] == di)
-					isdn_all_eaz(di, dev->chanmap[i]);
-			set_global_features();
-			break;
-		case ISDN_STAT_STOP:
-			dev->drv[di]->flags &= ~DRV_FLAG_RUNNING;
-			break;
-		case ISDN_STAT_ICALL:
-			if (i < 0)
-				return -1;
-#ifdef ISDN_DEBUG_STATCALLB
-			printk(KERN_DEBUG "ICALL (net): %d %ld %s\n", di, c->arg, c->parm.num);
-#endif
-			if (dev->global_flags & ISDN_GLOBAL_STOPPED) {
-				cmd.driver = di;
-				cmd.arg = c->arg;
-				cmd.command = ISDN_CMD_HANGUP;
-				isdn_command(&cmd);
-				return 0;
-			}
-			/* Try to find a network-interface which will accept incoming call */
-			r = ((c->command == ISDN_STAT_ICALLW) ? 0 : isdn_net_find_icall(di, c->arg, i, &c->parm.setup));
-			switch (r) {
-				case 0:
-					/* No network-device replies.
-					 * Try ttyI's.
-					 * These return 0 on no match, 1 on match and
-					 * 3 on eventually match, if CID is longer.
-					 */
-                                        if (c->command == ISDN_STAT_ICALL)
-					  if ((retval = isdn_tty_find_icall(di, c->arg, &c->parm.setup))) return(retval);
-#ifdef CONFIG_ISDN_DIVERSION 
-                                         if (divert_if)
-                 	                  if ((retval = divert_if->stat_callback(c))) 
-					    return(retval); /* processed */
-#endif /* CONFIG_ISDN_DIVERSION */                       
-					if ((!retval) && (dev->drv[di]->flags & DRV_FLAG_REJBUS)) {
-						/* No tty responding */
-						cmd.driver = di;
-						cmd.arg = c->arg;
-						cmd.command = ISDN_CMD_HANGUP;
-						isdn_command(&cmd);
-						retval = 2;
-					}
-					break;
-				case 1:
-					/* Schedule connection-setup */
-					isdn_net_dial();
-					cmd.driver = di;
-					cmd.arg = c->arg;
-					cmd.command = ISDN_CMD_ACCEPTD;
-					for ( p = dev->netdev; p; p = p->next )
-						if ( p->local->isdn_channel == cmd.arg )
-						{
-							strcpy( cmd.parm.setup.eazmsn, p->local->msn );
-							isdn_command(&cmd);
-							retval = 1;
-							break;
-						}
-					break;
-
-				case 2:	/* For calling back, first reject incoming call ... */
-				case 3:	/* Interface found, but down, reject call actively  */
-					retval = 2;
-					printk(KERN_INFO "isdn: Rejecting Call\n");
-					cmd.driver = di;
-					cmd.arg = c->arg;
-					cmd.command = ISDN_CMD_HANGUP;
-					isdn_command(&cmd);
-					if (r == 3)
-						break;
-					/* Fall through */
-				case 4:
-					/* ... then start callback. */
-					isdn_net_dial();
-					break;
-				case 5:
-					/* Number would eventually match, if longer */
-					retval = 3;
-					break;
-			}
-#ifdef ISDN_DEBUG_STATCALLB
-			printk(KERN_DEBUG "ICALL: ret=%d\n", retval);
-#endif
-			return retval;
-			break;
-		case ISDN_STAT_CINF:
-			if (i < 0)
-				return -1;
-#ifdef ISDN_DEBUG_STATCALLB
-			printk(KERN_DEBUG "CINF: %ld %s\n", c->arg, c->parm.num);
-#endif
-			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
-				return 0;
-			if (strcmp(c->parm.num, "0"))
-				isdn_net_stat_callback(i, c);
-			isdn_tty_stat_callback(i, c);
-			break;
-		case ISDN_STAT_CAUSE:
-#ifdef ISDN_DEBUG_STATCALLB
-			printk(KERN_DEBUG "CAUSE: %ld %s\n", c->arg, c->parm.num);
-#endif
-			printk(KERN_INFO "isdn: %s,ch%ld cause: %s\n",
-			       dev->drvid[di], c->arg, c->parm.num);
-			isdn_tty_stat_callback(i, c);
-#ifdef CONFIG_ISDN_DIVERSION
-                        if (divert_if)
-                         divert_if->stat_callback(c); 
-#endif /* CONFIG_ISDN_DIVERSION */
-			break;
-		case ISDN_STAT_DISPLAY:
-#ifdef ISDN_DEBUG_STATCALLB
-			printk(KERN_DEBUG "DISPLAY: %ld %s\n", c->arg, c->parm.display);
-#endif
-			isdn_tty_stat_callback(i, c);
-#ifdef CONFIG_ISDN_DIVERSION
-                        if (divert_if)
-                         divert_if->stat_callback(c); 
-#endif /* CONFIG_ISDN_DIVERSION */
-			break;
-		case ISDN_STAT_DCONN:
-			if (i < 0)
-				return -1;
-#ifdef ISDN_DEBUG_STATCALLB
-			printk(KERN_DEBUG "DCONN: %ld\n", c->arg);
-#endif
-			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
-				return 0;
-			/* Find any net-device, waiting for D-channel setup */
-			if (isdn_net_stat_callback(i, c))
-				break;
-			isdn_v110_stat_callback(i, c);
-			/* Find any ttyI, waiting for D-channel setup */
-			if (isdn_tty_stat_callback(i, c)) {
-				cmd.driver = di;
-				cmd.arg = c->arg;
-				cmd.command = ISDN_CMD_ACCEPTB;
-				isdn_command(&cmd);
-				break;
-			}
-			break;
-		case ISDN_STAT_DHUP:
-			if (i < 0)
-				return -1;
-#ifdef ISDN_DEBUG_STATCALLB
-			printk(KERN_DEBUG "DHUP: %ld\n", c->arg);
-#endif
-			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
-				return 0;
-			dev->drv[di]->online &= ~(1 << (c->arg));
-			isdn_info_update();
-			/* Signal hangup to network-devices */
-			if (isdn_net_stat_callback(i, c))
-				break;
-			isdn_v110_stat_callback(i, c);
-			if (isdn_tty_stat_callback(i, c))
-				break;
-#ifdef CONFIG_ISDN_DIVERSION
-                        if (divert_if)
-                         divert_if->stat_callback(c); 
-#endif /* CONFIG_ISDN_DIVERSION */
-			break;
-			break;
-		case ISDN_STAT_BCONN:
-			if (i < 0)
-				return -1;
-#ifdef ISDN_DEBUG_STATCALLB
-			printk(KERN_DEBUG "BCONN: %ld\n", c->arg);
-#endif
-			/* Signal B-channel-connect to network-devices */
-			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
-				return 0;
-			dev->drv[di]->online |= (1 << (c->arg));
-			isdn_info_update();
-			if (isdn_net_stat_callback(i, c))
-				break;
-			isdn_v110_stat_callback(i, c);
-			if (isdn_tty_stat_callback(i, c))
-				break;
-			break;
-		case ISDN_STAT_BHUP:
-			if (i < 0)
-				return -1;
-#ifdef ISDN_DEBUG_STATCALLB
-			printk(KERN_DEBUG "BHUP: %ld\n", c->arg);
-#endif
-			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
-				return 0;
-			dev->drv[di]->online &= ~(1 << (c->arg));
-			isdn_info_update();
-#ifdef CONFIG_ISDN_X25
-			/* Signal hangup to network-devices */
-			if (isdn_net_stat_callback(i, c))
-				break;
-#endif
-			isdn_v110_stat_callback(i, c);
-			if (isdn_tty_stat_callback(i, c))
-				break;
-			break;
-		case ISDN_STAT_NODCH:
-			if (i < 0)
-				return -1;
-#ifdef ISDN_DEBUG_STATCALLB
-			printk(KERN_DEBUG "NODCH: %ld\n", c->arg);
-#endif
-			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
-				return 0;
-			if (isdn_net_stat_callback(i, c))
-				break;
-			if (isdn_tty_stat_callback(i, c))
-				break;
-			break;
-		case ISDN_STAT_ADDCH:
-			if (isdn_add_channels(dev->drv[di], di, c->arg, 1))
-				return -1;
-			isdn_info_update();
-			break;
-		case ISDN_STAT_DISCH:
-			save_flags(flags);
-			cli();
-			for (i = 0; i < ISDN_MAX_CHANNELS; i++)
-				if ((dev->drvmap[i] == di) &&
-				    (dev->chanmap[i] == c->arg)) {
-				    if (c->parm.num[0])
-				      dev->usage[i] &= ~ISDN_USAGE_DISABLED;
-				    else
-				      if (USG_NONE(dev->usage[i])) {
-					dev->usage[i] |= ISDN_USAGE_DISABLED;
-				      }
-				      else 
-					retval = -1;
-				    break;
-				}
-			restore_flags(flags);
-			isdn_info_update();
-			break;
-		case ISDN_STAT_UNLOAD:
-			while (dev->drv[di]->locks > 0) {
-				isdn_ctrl cmd;
-				cmd.driver = di;
-				cmd.arg = 0;
-				cmd.command = ISDN_CMD_UNLOCK;
-				isdn_command(&cmd);
-				dev->drv[di]->locks--;
-			}
-			save_flags(flags);
-			cli();
-			isdn_tty_stat_callback(i, c);
-			for (i = 0; i < ISDN_MAX_CHANNELS; i++)
-				if (dev->drvmap[i] == di) {
-					dev->drvmap[i] = -1;
-					dev->chanmap[i] = -1;
-					dev->usage[i] &= ~ISDN_USAGE_DISABLED;
-					isdn_unregister_devfs(i);
-				}
-			dev->drivers--;
-			dev->channels -= dev->drv[di]->channels;
-			kfree(dev->drv[di]->rcverr);
-			kfree(dev->drv[di]->rcvcount);
-			for (i = 0; i < dev->drv[di]->channels; i++)
-				skb_queue_purge(&dev->drv[di]->rpqueue[i]);
-			kfree(dev->drv[di]->rpqueue);
-			kfree(dev->drv[di]->rcv_waitq);
-			kfree(dev->drv[di]);
-			dev->drv[di] = NULL;
-			dev->drvid[di][0] = '\0';
-			isdn_info_update();
-			set_global_features();
-			restore_flags(flags);
-			return 0;
-		case ISDN_STAT_L1ERR:
-			break;
-		case CAPI_PUT_MESSAGE:
-			return(isdn_capi_rec_hl_msg(&c->parm.cmsg));
-#ifdef CONFIG_ISDN_TTY_FAX
-		case ISDN_STAT_FAXIND:
-			isdn_tty_stat_callback(i, c);
-			break;
-#endif
-#ifdef CONFIG_ISDN_AUDIO
-		case ISDN_STAT_AUDIO:
-			isdn_tty_stat_callback(i, c);
-			break;
-#endif
-#ifdef CONFIG_ISDN_DIVERSION
-	        case ISDN_STAT_PROT:
-	        case ISDN_STAT_REDIR:
-                        if (divert_if)
-                          return(divert_if->stat_callback(c));
-#endif /* CONFIG_ISDN_DIVERSION */
-		default:
-			return -1;
-	}
-	return 0;
-}
-
-/*
- * Get integer from char-pointer, set pointer to end of number
- */
-int
-isdn_getnum(char **p)
-{
-	int v = -1;
-
-	while (*p[0] >= '0' && *p[0] <= '9')
-		v = ((v < 0) ? 0 : (v * 10)) + (int) ((*p[0]++) - '0');
-	return v;
-}
-
-#define DLE 0x10
-
-/*
- * isdn_readbchan() tries to get data from the read-queue.
- * It MUST be called with interrupts off.
- *
- * Be aware that this is not an atomic operation when sleep != 0, even though 
- * interrupts are turned off! Well, like that we are currently only called
- * on behalf of a read system call on raw device files (which are documented
- * to be dangerous and for for debugging purpose only). The inode semaphore
- * takes care that this is not called for the same minor device number while
- * we are sleeping, but access is not serialized against simultaneous read()
- * from the corresponding ttyI device. Can other ugly events, like changes
- * of the mapping (di,ch)<->minor, happen during the sleep? --he 
- */
-int
-isdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, wait_queue_head_t *sleep)
-{
-	int count;
-	int count_pull;
-	int count_put;
-	int dflag;
-	struct sk_buff *skb;
-	u_char *cp;
-
-	if (!dev->drv[di])
-		return 0;
-	if (skb_queue_empty(&dev->drv[di]->rpqueue[channel])) {
-		if (sleep)
-			interruptible_sleep_on(sleep);
-		else
-			return 0;
-	}
-	if (len > dev->drv[di]->rcvcount[channel])
-		len = dev->drv[di]->rcvcount[channel];
-	cp = buf;
-	count = 0;
-	while (len) {
-		if (!(skb = skb_peek(&dev->drv[di]->rpqueue[channel])))
-			break;
-#ifdef CONFIG_ISDN_AUDIO
-		if (ISDN_AUDIO_SKB_LOCK(skb))
-			break;
-		ISDN_AUDIO_SKB_LOCK(skb) = 1;
-		if ((ISDN_AUDIO_SKB_DLECOUNT(skb)) || (dev->drv[di]->DLEflag & (1 << channel))) {
-			char *p = skb->data;
-			unsigned long DLEmask = (1 << channel);
-
-			dflag = 0;
-			count_pull = count_put = 0;
-			while ((count_pull < skb->len) && (len > 0)) {
-				len--;
-				if (dev->drv[di]->DLEflag & DLEmask) {
-					*cp++ = DLE;
-					dev->drv[di]->DLEflag &= ~DLEmask;
-				} else {
-					*cp++ = *p;
-					if (*p == DLE) {
-						dev->drv[di]->DLEflag |= DLEmask;
-						(ISDN_AUDIO_SKB_DLECOUNT(skb))--;
-					}
-					p++;
-					count_pull++;
-				}
-				count_put++;
-			}
-			if (count_pull >= skb->len)
-				dflag = 1;
-		} else {
-#endif
-			/* No DLE's in buff, so simply copy it */
-			dflag = 1;
-			if ((count_pull = skb->len) > len) {
-				count_pull = len;
-				dflag = 0;
-			}
-			count_put = count_pull;
-			memcpy(cp, skb->data, count_put);
-			cp += count_put;
-			len -= count_put;
-#ifdef CONFIG_ISDN_AUDIO
-		}
-#endif
-		count += count_put;
-		if (fp) {
-			memset(fp, 0, count_put);
-			fp += count_put;
-		}
-		if (dflag) {
-			/* We got all the data in this buff.
-			 * Now we can dequeue it.
-			 */
-			if (fp)
-				*(fp - 1) = 0xff;
-#ifdef CONFIG_ISDN_AUDIO
-			ISDN_AUDIO_SKB_LOCK(skb) = 0;
-#endif
-			skb = skb_dequeue(&dev->drv[di]->rpqueue[channel]);
-			dev_kfree_skb(skb);
-		} else {
-			/* Not yet emptied this buff, so it
-			 * must stay in the queue, for further calls
-			 * but we pull off the data we got until now.
-			 */
-			skb_pull(skb, count_pull);
-#ifdef CONFIG_ISDN_AUDIO
-			ISDN_AUDIO_SKB_LOCK(skb) = 0;
-#endif
-		}
-		dev->drv[di]->rcvcount[channel] -= count_put;
-	}
-	return count;
-}
-
-static __inline int
-isdn_minor2drv(int minor)
-{
-	return (dev->drvmap[minor]);
-}
-
-static __inline int
-isdn_minor2chan(int minor)
-{
-	return (dev->chanmap[minor]);
-}
-
-static char *
-isdn_statstr(void)
-{
-	static char istatbuf[2048];
-	char *p;
-	int i;
-
-	sprintf(istatbuf, "idmap:\t");
-	p = istatbuf + strlen(istatbuf);
-	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-		sprintf(p, "%s ", (dev->drvmap[i] < 0) ? "-" : dev->drvid[dev->drvmap[i]]);
-		p = istatbuf + strlen(istatbuf);
-	}
-	sprintf(p, "\nchmap:\t");
-	p = istatbuf + strlen(istatbuf);
-	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-		sprintf(p, "%d ", dev->chanmap[i]);
-		p = istatbuf + strlen(istatbuf);
-	}
-	sprintf(p, "\ndrmap:\t");
-	p = istatbuf + strlen(istatbuf);
-	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-		sprintf(p, "%d ", dev->drvmap[i]);
-		p = istatbuf + strlen(istatbuf);
-	}
-	sprintf(p, "\nusage:\t");
-	p = istatbuf + strlen(istatbuf);
-	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-		sprintf(p, "%d ", dev->usage[i]);
-		p = istatbuf + strlen(istatbuf);
-	}
-	sprintf(p, "\nflags:\t");
-	p = istatbuf + strlen(istatbuf);
-	for (i = 0; i < ISDN_MAX_DRIVERS; i++) {
-		if (dev->drv[i]) {
-			sprintf(p, "%ld ", dev->drv[i]->online);
-			p = istatbuf + strlen(istatbuf);
-		} else {
-			sprintf(p, "? ");
-			p = istatbuf + strlen(istatbuf);
-		}
-	}
-	sprintf(p, "\nphone:\t");
-	p = istatbuf + strlen(istatbuf);
-	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-		sprintf(p, "%s ", dev->num[i]);
-		p = istatbuf + strlen(istatbuf);
-	}
-	sprintf(p, "\n");
-	return istatbuf;
-}
-
-/* 
- * /dev/isdninfo
- */
-
-void
-isdn_info_update(void)
-{
-	infostruct *p = dev->infochain;
-
-	while (p) {
-		*(p->private) = 1;
-		p = (infostruct *) p->next;
-	}
-	wake_up_interruptible(&(dev->info_waitq));
-}
-
-static int
-isdn_status_open(struct inode *ino, struct file *filep)
-{
-	infostruct *p;
-	
-	p = kmalloc(sizeof(infostruct), GFP_KERNEL);
-	if (!p)
-		return -ENOMEM;
-
-	p->next = (char *) dev->infochain;
-	p->private = (char *) &(filep->private_data);
-	dev->infochain = p;
-	/* At opening we allow a single update */
-	filep->private_data = (char *) 1;
-
-	return 0;
-}
-
-static int
-isdn_status_release(struct inode *ino, struct file *filep)
-{
-	infostruct *p = dev->infochain;
-	infostruct *q = NULL;
-	
-	lock_kernel();
-
-	while (p) {
-		if (p->private == (char *) &(filep->private_data)) {
-			if (q)
-				q->next = p->next;
-			else
-				dev->infochain = (infostruct *) (p->next);
-			kfree(p);
-			goto out;
-		}
-		q = p;
-		p = (infostruct *) (p->next);
-	}
-	printk(KERN_WARNING "isdn: No private data while closing isdnctrl\n");
-
- out:
-	unlock_kernel();
-	return 0;
-}
-
-static ssize_t
-isdn_status_read(struct file *file, char *buf, size_t count, loff_t * off)
-{
-	int retval;
-	int len = 0;
-	char *p;
-
-	if (off != &file->f_pos)
-		return -ESPIPE;
-
-	lock_kernel();
-	if (!file->private_data) {
-		if (file->f_flags & O_NONBLOCK) {
-			retval = -EAGAIN;
-			goto out;
-		}
-		interruptible_sleep_on(&(dev->info_waitq));
-	}
-	p = isdn_statstr();
-	file->private_data = 0;
-	if ((len = strlen(p)) <= count) {
-		if (copy_to_user(buf, p, len)) {
-			retval = -EFAULT;
-			goto out;
-		}
-		*off += len;
-		retval = len;
-		goto out;
-	}
-	retval = 0;
-	goto out;
-
- out:
-	unlock_kernel();
-	return retval;
-}
-
-static ssize_t
-isdn_status_write(struct file *file, const char *buf, size_t count, loff_t *off)
-{
-	return -EPERM;
-}
-
-static unsigned int
-isdn_status_poll(struct file *file, poll_table *wait)
-{
-	unsigned int mask = 0;
-
-	lock_kernel();
-
-	poll_wait(file, &(dev->info_waitq), wait);
-	if (file->private_data)
-		mask |= POLLIN | POLLRDNORM;
-
-	unlock_kernel();
-	return mask;
-}
-
-static int
-isdn_status_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
-{
-	int ret;
-	union iocpar {
-		char name[10];
-		char bname[22];
-		isdn_ioctl_struct iocts;
-		isdn_net_ioctl_phone phone;
-		isdn_net_ioctl_cfg cfg;
-	} iocpar;
-
-#define name  iocpar.name
-#define bname iocpar.bname
-#define iocts iocpar.iocts
-#define phone iocpar.phone
-#define cfg   iocpar.cfg
-
-	switch (cmd) {
-	case IIOCGETDVR:
-		return (TTY_DV +
-			(NET_DV << 8) +
-			(INF_DV << 16));
-	case IIOCGETCPS:
-		if (arg) {
-			ulong *p = (ulong *) arg;
-			int i;
-			if ((ret = verify_area(VERIFY_WRITE, (void *) arg,
-					       sizeof(ulong) * ISDN_MAX_CHANNELS * 2)))
-				return ret;
-			for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-				put_user(dev->ibytes[i], p++);
-				put_user(dev->obytes[i], p++);
-			}
-			return 0;
-		} else
-			return -EINVAL;
-		break;
-#ifdef CONFIG_NETDEVICES
-	case IIOCNETGPN:
-		/* Get peer phone number of a connected 
-		 * isdn network interface */
-		if (arg) {
-			if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone)))
-				return -EFAULT;
-			return isdn_net_getpeer(&phone, (isdn_net_ioctl_phone *) arg);
-		} else
-			return -EINVAL;
-#endif
-	default:
-		return -EINVAL;
-	}
-
-#undef name
-#undef bname
-#undef iocts
-#undef phone
-#undef cfg
-}
-
-static struct file_operations isdn_status_fops =
-{
-	owner:		THIS_MODULE,
-	llseek:		no_llseek,
-	read:		isdn_status_read,
-	write:		isdn_status_write,
-	poll:		isdn_status_poll,
-	ioctl:		isdn_status_ioctl,
-	open:		isdn_status_open,
-	release:	isdn_status_release,
-};
-
-/*
- * /dev/isdnctrlX
- */
-
-static int
-isdn_ctrl_open(struct inode *ino, struct file *filep)
-{
-	uint minor = minor(ino->i_rdev);
-	int drvidx;
-
-	drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
-	if (drvidx < 0)
-		return -ENODEV;
-
-	isdn_lock_drivers();
-	return 0;
-}
-
-static int
-isdn_ctrl_release(struct inode *ino, struct file *filep)
-{
-	lock_kernel();
-
-	if (dev->profd == current)
-		dev->profd = NULL;
-
-	isdn_unlock_drivers();
-
-	unlock_kernel();
-	return 0;
-}
-
-static ssize_t
-isdn_ctrl_read(struct file *file, char *buf, size_t count, loff_t * off)
-{
-	uint minor = minor(file->f_dentry->d_inode->i_rdev);
-	ulong flags;
-	int len = 0;
-	int drvidx;
-	int retval;
-
-	if (off != &file->f_pos)
-		return -ESPIPE;
-
-	lock_kernel();
-
-	drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
-	if (drvidx < 0) {
-		retval = -ENODEV;
-		goto out;
-	}
-	if (!dev->drv[drvidx]->stavail) {
-		if (file->f_flags & O_NONBLOCK) {
-			retval = -EAGAIN;
-			goto out;
-		}
-		interruptible_sleep_on(&(dev->drv[drvidx]->st_waitq));
-	}
-	if (dev->drv[drvidx]->interface->readstat) {
-		if (count > dev->drv[drvidx]->stavail)
-			count = dev->drv[drvidx]->stavail;
-		len = dev->drv[drvidx]->interface->
-			readstat(buf, count, 1, drvidx,
-				 isdn_minor2chan(minor));
-	} else {
-		len = 0;
-	}
-	save_flags(flags);
-	cli();
-	if (len)
-		dev->drv[drvidx]->stavail -= len;
-	else
-		dev->drv[drvidx]->stavail = 0;
-	restore_flags(flags);
-	*off += len;
-	retval = len;
-	
- out:
-	unlock_kernel();
-	return retval;
-}
-
-static ssize_t
-isdn_ctrl_write(struct file *file, const char *buf, size_t count, loff_t *off)
-{
-	uint minor = minor(file->f_dentry->d_inode->i_rdev);
-	int drvidx;
-	int retval;
-
-	if (off != &file->f_pos)
-		return -ESPIPE;
-
-	lock_kernel();
-
-	drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
-	if (drvidx < 0) {
-		retval = -ENODEV;
-		goto out;
-	}
-	if (!dev->drv[drvidx]->interface->writecmd) {
-		retval = -EINVAL;
-		goto out;
-	}
-	retval = dev->drv[drvidx]->interface->
-		writecmd(buf, count, 1, drvidx, isdn_minor2chan(minor - ISDN_MINOR_CTRL));
-
- out:
-	unlock_kernel();
-	return retval;
-}
-
-static unsigned int
-isdn_ctrl_poll(struct file *file, poll_table *wait)
-{
-	unsigned int mask = 0;
-	unsigned int minor = minor(file->f_dentry->d_inode->i_rdev);
-	int drvidx;
-
-	drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
-	if (drvidx < 0)
-		/* driver deregistered while file open */
-		return POLLHUP;
-
-	lock_kernel();
-
-	poll_wait(file, &(dev->drv[drvidx]->st_waitq), wait);
-	mask = POLLOUT | POLLWRNORM;
-	if (dev->drv[drvidx]->stavail)
-		mask |= POLLIN | POLLRDNORM;
-
-	unlock_kernel();
-	return mask;
-}
-
-
-static int
-isdn_ctrl_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
-{
-	isdn_ctrl c;
-	int drvidx;
-	int ret;
-	int i;
-	char *p;
-	char *s;
-	union iocpar {
-		char name[10];
-		char bname[22];
-		isdn_ioctl_struct iocts;
-		isdn_net_ioctl_phone phone;
-		isdn_net_ioctl_cfg cfg;
-	} iocpar;
-
-#define name  iocpar.name
-#define bname iocpar.bname
-#define iocts iocpar.iocts
-#define phone iocpar.phone
-#define cfg   iocpar.cfg
-/*
- * isdn net devices manage lots of configuration variables as linked lists.
- * Those lists must only be manipulated from user space. Some of the ioctl's
- * service routines access user space and are not atomic. Therefor, ioctl's
- * manipulating the lists and ioctl's sleeping while accessing the lists
- * are serialized by means of a semaphore.
- */
-	switch (cmd) {
-	case IIOCNETDWRSET:
-		printk(KERN_INFO "INFO: ISDN_DW_ABC_EXTENSION not enabled\n");
-		return(-EINVAL);
-	case IIOCNETLCR:
-		printk(KERN_INFO "INFO: ISDN_ABC_LCR_SUPPORT not enabled\n");
-		return -ENODEV;
-#ifdef CONFIG_NETDEVICES
-	case IIOCNETAIF:
-		/* Add a network-interface */
-		if (arg) {
-			if (copy_from_user(name, (char *) arg, sizeof(name)))
-				return -EFAULT;
-			s = name;
-		} else {
-			s = NULL;
-		}
-		ret = down_interruptible(&dev->sem);
-		if( ret ) return ret;
-		if ((s = isdn_net_new(s, NULL))) {
-			if (copy_to_user((char *) arg, s, strlen(s) + 1)){
-				ret = -EFAULT;
-			} else {
-				ret = 0;
-			}
-		} else
-			ret = -ENODEV;
-		up(&dev->sem);
-		return ret;
-	case IIOCNETASL:
-		/* Add a slave to a network-interface */
-		if (arg) {
-			if (copy_from_user(bname, (char *) arg, sizeof(bname) - 1))
-				return -EFAULT;
-		} else
-			return -EINVAL;
-		ret = down_interruptible(&dev->sem);
-		if( ret ) return ret;
-		if ((s = isdn_net_newslave(bname))) {
-			if (copy_to_user((char *) arg, s, strlen(s) + 1)){
-				ret = -EFAULT;
-			} else {
-				ret = 0;
-			}
-		} else
-			ret = -ENODEV;
-		up(&dev->sem);
-		return ret;
-	case IIOCNETDIF:
-		/* Delete a network-interface */
-		if (arg) {
-			if (copy_from_user(name, (char *) arg, sizeof(name)))
-				return -EFAULT;
-			ret = down_interruptible(&dev->sem);
-			if( ret ) return ret;
-			ret = isdn_net_rm(name);
-			up(&dev->sem);
-			return ret;
-		} else
-			return -EINVAL;
-	case IIOCNETSCF:
-		/* Set configurable parameters of a network-interface */
-		if (arg) {
-			if (copy_from_user((char *) &cfg, (char *) arg, sizeof(cfg)))
-				return -EFAULT;
-			return isdn_net_setcfg(&cfg);
-		} else
-			return -EINVAL;
-	case IIOCNETGCF:
-		/* Get configurable parameters of a network-interface */
-		if (arg) {
-			if (copy_from_user((char *) &cfg, (char *) arg, sizeof(cfg)))
-				return -EFAULT;
-			if (!(ret = isdn_net_getcfg(&cfg))) {
-				if (copy_to_user((char *) arg, (char *) &cfg, sizeof(cfg)))
-					return -EFAULT;
-			}
-			return ret;
-		} else
-			return -EINVAL;
-	case IIOCNETANM:
-		/* Add a phone-number to a network-interface */
-		if (arg) {
-			if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone)))
-				return -EFAULT;
-			ret = down_interruptible(&dev->sem);
-			if( ret ) return ret;
-			ret = isdn_net_addphone(&phone);
-			up(&dev->sem);
-			return ret;
-		} else
-			return -EINVAL;
-	case IIOCNETGNM:
-		/* Get list of phone-numbers of a network-interface */
-		if (arg) {
-			if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone)))
-				return -EFAULT;
-			ret = down_interruptible(&dev->sem);
-			if( ret ) return ret;
-			ret = isdn_net_getphones(&phone, (char *) arg);
-			up(&dev->sem);
-			return ret;
-		} else
-			return -EINVAL;
-	case IIOCNETDNM:
-		/* Delete a phone-number of a network-interface */
-		if (arg) {
-			if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone)))
-				return -EFAULT;
-			ret = down_interruptible(&dev->sem);
-			if( ret ) return ret;
-			ret = isdn_net_delphone(&phone);
-			up(&dev->sem);
-			return ret;
-		} else
-			return -EINVAL;
-	case IIOCNETDIL:
-		/* Force dialing of a network-interface */
-		if (arg) {
-			if (copy_from_user(name, (char *) arg, sizeof(name)))
-				return -EFAULT;
-			return isdn_net_force_dial(name);
-		} else
-			return -EINVAL;
-#ifdef CONFIG_ISDN_PPP
-	case IIOCNETALN:
-		if (!arg)
-			return -EINVAL;
-		if (copy_from_user(name, (char *) arg, sizeof(name)))
-			return -EFAULT;
-		return isdn_ppp_dial_slave(name);
-	case IIOCNETDLN:
-		if (!arg)
-			return -EINVAL;
-		if (copy_from_user(name, (char *) arg, sizeof(name)))
-			return -EFAULT;
-		return isdn_ppp_hangup_slave(name);
-#endif
-	case IIOCNETHUP:
-		/* Force hangup of a network-interface */
-		if (!arg)
-			return -EINVAL;
-		if (copy_from_user(name, (char *) arg, sizeof(name)))
-			return -EFAULT;
-		return isdn_net_force_hangup(name);
-		break;
-#endif                          /* CONFIG_NETDEVICES */
-	case IIOCSETVER:
-		dev->net_verbose = arg;
-		printk(KERN_INFO "isdn: Verbose-Level is %d\n", dev->net_verbose);
-		return 0;
-	case IIOCSETGST:
-		if (arg)
-			dev->global_flags |= ISDN_GLOBAL_STOPPED;
-		else
-			dev->global_flags &= ~ISDN_GLOBAL_STOPPED;
-		printk(KERN_INFO "isdn: Global Mode %s\n",
-		       (dev->global_flags & ISDN_GLOBAL_STOPPED) ? "stopped" : "running");
-		return 0;
-	case IIOCSETBRJ:
-		drvidx = -1;
-		if (arg) {
-			int i;
-			char *p;
-			if (copy_from_user((char *) &iocts, (char *) arg,
-					   sizeof(isdn_ioctl_struct)))
-				return -EFAULT;
-			if (strlen(iocts.drvid)) {
-				if ((p = strchr(iocts.drvid, ',')))
-					*p = 0;
-				drvidx = -1;
-				for (i = 0; i < ISDN_MAX_DRIVERS; i++)
-					if (!(strcmp(dev->drvid[i], iocts.drvid))) {
-						drvidx = i;
-						break;
-					}
-			}
-		}
-		if (drvidx == -1)
-			return -ENODEV;
-		if (iocts.arg)
-			dev->drv[drvidx]->flags |= DRV_FLAG_REJBUS;
-		else
-			dev->drv[drvidx]->flags &= ~DRV_FLAG_REJBUS;
-		return 0;
-	case IIOCSIGPRF:
-		dev->profd = current;
-		return 0;
-		break;
-	case IIOCGETPRF:
-		/* Get all Modem-Profiles */
-		if (arg) {
-			char *p = (char *) arg;
-			int i;
-
-			if ((ret = verify_area(VERIFY_WRITE, (void *) arg,
-					       (ISDN_MODEM_NUMREG + ISDN_MSNLEN + ISDN_LMSNLEN)
-					       * ISDN_MAX_CHANNELS)))
-				return ret;
-
-			for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-				if (copy_to_user(p, dev->mdm.info[i].emu.profile,
-						 ISDN_MODEM_NUMREG))
-					return -EFAULT;
-				p += ISDN_MODEM_NUMREG;
-				if (copy_to_user(p, dev->mdm.info[i].emu.pmsn, ISDN_MSNLEN))
-					return -EFAULT;
-				p += ISDN_MSNLEN;
-				if (copy_to_user(p, dev->mdm.info[i].emu.plmsn, ISDN_LMSNLEN))
-					return -EFAULT;
-				p += ISDN_LMSNLEN;
-			}
-			return (ISDN_MODEM_NUMREG + ISDN_MSNLEN + ISDN_LMSNLEN) * ISDN_MAX_CHANNELS;
-		} else
-			return -EINVAL;
-		break;
-	case IIOCSETPRF:
-		/* Set all Modem-Profiles */
-		if (arg) {
-			char *p = (char *) arg;
-			int i;
-
-			if ((ret = verify_area(VERIFY_READ, (void *) arg,
-					       (ISDN_MODEM_NUMREG + ISDN_MSNLEN + ISDN_LMSNLEN)
-					       * ISDN_MAX_CHANNELS)))
-				return ret;
-
-			for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-				if (copy_from_user(dev->mdm.info[i].emu.profile, p,
-						   ISDN_MODEM_NUMREG))
-					return -EFAULT;
-				p += ISDN_MODEM_NUMREG;
-				if (copy_from_user(dev->mdm.info[i].emu.plmsn, p, ISDN_LMSNLEN))
-					return -EFAULT;
-				p += ISDN_LMSNLEN;
-				if (copy_from_user(dev->mdm.info[i].emu.pmsn, p, ISDN_MSNLEN))
-					return -EFAULT;
-				p += ISDN_MSNLEN;
-			}
-			return 0;
-		} else
-			return -EINVAL;
-		break;
-	case IIOCSETMAP:
-	case IIOCGETMAP:
-		/* Set/Get MSN->EAZ-Mapping for a driver */
-		if (arg) {
-
-			if (copy_from_user((char *) &iocts,
-					   (char *) arg,
-					   sizeof(isdn_ioctl_struct)))
-				return -EFAULT;
-			if (strlen(iocts.drvid)) {
-				drvidx = -1;
-				for (i = 0; i < ISDN_MAX_DRIVERS; i++)
-					if (!(strcmp(dev->drvid[i], iocts.drvid))) {
-						drvidx = i;
-						break;
-					}
-			} else
-				drvidx = 0;
-			if (drvidx == -1)
-				return -ENODEV;
-			if (cmd == IIOCSETMAP) {
-				int loop = 1;
-
-				p = (char *) iocts.arg;
-				i = 0;
-				while (loop) {
-					int j = 0;
-
-					while (1) {
-						if ((ret = verify_area(VERIFY_READ, p, 1)))
-							return ret;
-						get_user(bname[j], p++);
-						switch (bname[j]) {
-						case '\0':
-							loop = 0;
-							/* Fall through */
-						case ',':
-							bname[j] = '\0';
-							strcpy(dev->drv[drvidx]->msn2eaz[i], bname);
-							j = ISDN_MSNLEN;
-							break;
-						default:
-							j++;
-						}
-						if (j >= ISDN_MSNLEN)
-							break;
-					}
-					if (++i > 9)
-						break;
-				}
-			} else {
-				p = (char *) iocts.arg;
-				for (i = 0; i < 10; i++) {
-					sprintf(bname, "%s%s",
-						strlen(dev->drv[drvidx]->msn2eaz[i]) ?
-						dev->drv[drvidx]->msn2eaz[i] : "_",
-						(i < 9) ? "," : "\0");
-					if (copy_to_user(p, bname, strlen(bname) + 1))
-						return -EFAULT;
-					p += strlen(bname);
-				}
-			}
-			return 0;
-		} else
-			return -EINVAL;
-	case IIOCDBGVAR:
-		if (arg) {
-			if (copy_to_user((char *) arg, (char *) &dev, sizeof(ulong)))
-				return -EFAULT;
-			return 0;
-		} else
-			return -EINVAL;
-		break;
-	default:
-		if ((cmd & IIOCDRVCTL) == IIOCDRVCTL)
-			cmd = ((cmd >> _IOC_NRSHIFT) & _IOC_NRMASK) & ISDN_DRVIOCTL_MASK;
-		else
-			return -EINVAL;
-		if (arg) {
-			int i;
-			char *p;
-			if (copy_from_user((char *) &iocts, (char *) arg, sizeof(isdn_ioctl_struct)))
-				return -EFAULT;
-			if (strlen(iocts.drvid)) {
-				if ((p = strchr(iocts.drvid, ',')))
-					*p = 0;
-				drvidx = -1;
-				for (i = 0; i < ISDN_MAX_DRIVERS; i++)
-					if (!(strcmp(dev->drvid[i], iocts.drvid))) {
-						drvidx = i;
-						break;
-					}
-			} else
-				drvidx = 0;
-			if (drvidx == -1)
-				return -ENODEV;
-			if ((ret = verify_area(VERIFY_WRITE, (void *) arg,
-					       sizeof(isdn_ioctl_struct))))
-				return ret;
-			c.driver = drvidx;
-			c.command = ISDN_CMD_IOCTL;
-			c.arg = cmd;
-			memcpy(c.parm.num, (char *) &iocts.arg, sizeof(ulong));
-			ret = isdn_command(&c);
-			memcpy((char *) &iocts.arg, c.parm.num, sizeof(ulong));
-			if (copy_to_user((char *) arg, &iocts, sizeof(isdn_ioctl_struct)))
-				return -EFAULT;
-			return ret;
-		} else
-			return -EINVAL;
-	}
-
-#undef name
-#undef bname
-#undef iocts
-#undef phone
-#undef cfg
-}
-
-static struct file_operations isdn_ctrl_fops =
-{
-	owner:		THIS_MODULE,
-	llseek:		no_llseek,
-	read:		isdn_ctrl_read,
-	write:		isdn_ctrl_write,
-	poll:		isdn_ctrl_poll,
-	ioctl:		isdn_ctrl_ioctl,
-	open:		isdn_ctrl_open,
-	release:	isdn_ctrl_release,
-};
-
-/*
- * file_operations for major 43, /dev/isdn*
- * stolen from drivers/char/misc.c
- */
-
-static int
-isdn_open(struct inode * inode, struct file * file)
-{
-	int minor = minor(inode->i_rdev);
-	int err = -ENODEV;
-	struct file_operations *old_fops, *new_fops = NULL;
-	
-	if (minor >= ISDN_MINOR_CTRL && minor <= ISDN_MINOR_CTRLMAX)
-		new_fops = fops_get(&isdn_ctrl_fops);
-#ifdef CONFIG_ISDN_PPP
-	else if (minor >= ISDN_MINOR_PPP && minor <= ISDN_MINOR_PPPMAX)
-		new_fops = fops_get(&isdn_ppp_fops);
-#endif
-	else if (minor == ISDN_MINOR_STATUS)
-		new_fops = fops_get(&isdn_status_fops);
-
-	if (!new_fops)
-		goto out;
-
-	err = 0;
-	old_fops = file->f_op;
-	file->f_op = new_fops;
-	if (file->f_op->open) {
-		err = file->f_op->open(inode,file);
-		if (err) {
-			fops_put(file->f_op);
-			file->f_op = fops_get(old_fops);
-		}
-	}
-	fops_put(old_fops);
-	
- out:
-	return err;
-}
-
-static struct file_operations isdn_fops =
-{
-	owner:		THIS_MODULE,
-	open:		isdn_open,
-};
-
-char *
-isdn_map_eaz2msn(char *msn, int di)
-{
-	driver *this = dev->drv[di];
-	int i;
-
-	if (strlen(msn) == 1) {
-		i = msn[0] - '0';
-		if ((i >= 0) && (i <= 9))
-			if (strlen(this->msn2eaz[i]))
-				return (this->msn2eaz[i]);
-	}
-	return (msn);
-}
-
-/*
- * Find an unused ISDN-channel, whose feature-flags match the
- * given L2- and L3-protocols.
- */
-#define L2V (~(ISDN_FEATURE_L2_V11096|ISDN_FEATURE_L2_V11019|ISDN_FEATURE_L2_V11038))
-
-int
-isdn_get_free_channel(int usage, int l2_proto, int l3_proto, int pre_dev
-		      ,int pre_chan, char *msn)
-{
-	int i;
-	ulong flags;
-	ulong features;
-	ulong vfeatures;
-
-	save_flags(flags);
-	cli();
-	features = ((1 << l2_proto) | (0x10000 << l3_proto));
-	vfeatures = (((1 << l2_proto) | (0x10000 << l3_proto)) &
-		     ~(ISDN_FEATURE_L2_V11096|ISDN_FEATURE_L2_V11019|ISDN_FEATURE_L2_V11038));
-	/* If Layer-2 protocol is V.110, accept drivers with
-	 * transparent feature even if these don't support V.110
-	 * because we can emulate this in linklevel.
-	 */
-	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
-		if (USG_NONE(dev->usage[i]) &&
-		    (dev->drvmap[i] != -1)) {
-			int d = dev->drvmap[i];
-			if ((dev->usage[i] & ISDN_USAGE_EXCLUSIVE) &&
-			((pre_dev != d) || (pre_chan != dev->chanmap[i])))
-				continue;
-			if (!strcmp(isdn_map_eaz2msn(msn, d), "-"))
-				continue;
-			if (dev->usage[i] & ISDN_USAGE_DISABLED)
-			        continue; /* usage not allowed */
-			if (dev->drv[d]->flags & DRV_FLAG_RUNNING) {
-				if (((dev->drv[d]->interface->features & features) == features) ||
-				    (((dev->drv[d]->interface->features & vfeatures) == vfeatures) &&
-				     (dev->drv[d]->interface->features & ISDN_FEATURE_L2_TRANS))) {
-					if ((pre_dev < 0) || (pre_chan < 0)) {
-						dev->usage[i] &= ISDN_USAGE_EXCLUSIVE;
-						dev->usage[i] |= usage;
-						isdn_info_update();
-						restore_flags(flags);
-						return i;
-					} else {
-						if ((pre_dev == d) && (pre_chan == dev->chanmap[i])) {
-							dev->usage[i] &= ISDN_USAGE_EXCLUSIVE;
-							dev->usage[i] |= usage;
-							isdn_info_update();
-							restore_flags(flags);
-							return i;
-						}
-					}
-				}
-			}
-		}
-	restore_flags(flags);
-	return -1;
-}
-
-/*
- * Set state of ISDN-channel to 'unused'
- */
-void
-isdn_free_channel(int di, int ch, int usage)
-{
-	int i;
-	ulong flags;
-
-	save_flags(flags);
-	cli();
-	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
-		if (((!usage) || ((dev->usage[i] & ISDN_USAGE_MASK) == usage)) &&
-		    (dev->drvmap[i] == di) &&
-		    (dev->chanmap[i] == ch)) {
-			dev->usage[i] &= (ISDN_USAGE_NONE | ISDN_USAGE_EXCLUSIVE);
-			strcpy(dev->num[i], "???");
-			dev->ibytes[i] = 0;
-			dev->obytes[i] = 0;
-// 20.10.99 JIM, try to reinitialize v110 !
-			dev->v110emu[i] = 0;
-			atomic_set(&(dev->v110use[i]), 0);
-			isdn_v110_close(dev->v110[i]);
-			dev->v110[i] = NULL;
-// 20.10.99 JIM, try to reinitialize v110 !
-			isdn_info_update();
-			skb_queue_purge(&dev->drv[di]->rpqueue[ch]);
-		}
-	restore_flags(flags);
-}
-
-/*
- * Cancel Exclusive-Flag for ISDN-channel
- */
-void
-isdn_unexclusive_channel(int di, int ch)
-{
-	int i;
-	ulong flags;
-
-	save_flags(flags);
-	cli();
-	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
-		if ((dev->drvmap[i] == di) &&
-		    (dev->chanmap[i] == ch)) {
-			dev->usage[i] &= ~ISDN_USAGE_EXCLUSIVE;
-			isdn_info_update();
-			restore_flags(flags);
-			return;
-		}
-	restore_flags(flags);
-}
-
-/*
- * Return: length of data on success, -ERRcode on failure.
- */
-int
-isdn_writebuf_skb_stub(int drvidx, int chan, int ack, struct sk_buff *skb)
-{
-	int ret;
-	struct sk_buff *nskb = NULL;
-	int v110_ret = skb->len;
-	int idx = isdn_dc2minor(drvidx, chan);
-
-	if (dev->v110[idx]) {
-		atomic_inc(&dev->v110use[idx]);
-		nskb = isdn_v110_encode(dev->v110[idx], skb);
-		atomic_dec(&dev->v110use[idx]);
-		if (!nskb)
-			return 0;
-		v110_ret = *((int *)nskb->data);
-		skb_pull(nskb, sizeof(int));
-		if (!nskb->len) {
-			dev_kfree_skb(nskb);
-			return v110_ret;
-		}
-		/* V.110 must always be acknowledged */
-		ack = 1;
-		ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, ack, nskb);
-	} else {
-		int hl = dev->drv[drvidx]->interface->hl_hdrlen;
-
-		if( skb_headroom(skb) < hl ){
-			/* 
-			 * This should only occur when new HL driver with
-			 * increased hl_hdrlen was loaded after netdevice
-			 * was created and connected to the new driver.
-			 *
-			 * The V.110 branch (re-allocates on its own) does
-			 * not need this
-			 */
-			struct sk_buff * skb_tmp;
-
-			skb_tmp = skb_realloc_headroom(skb, hl);
-			printk(KERN_DEBUG "isdn_writebuf_skb_stub: reallocating headroom%s\n", skb_tmp ? "" : " failed");
-			if (!skb_tmp) return -ENOMEM; /* 0 better? */
-			ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, ack, skb_tmp);
-			if( ret > 0 ){
-				dev_kfree_skb(skb);
-			} else {
-				dev_kfree_skb(skb_tmp);
-			}
-		} else {
-			ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, ack, skb);
-		}
-	}
-	if (ret > 0) {
-		dev->obytes[idx] += ret;
-		if (dev->v110[idx]) {
-			atomic_inc(&dev->v110use[idx]);
-			dev->v110[idx]->skbuser++;
-			atomic_dec(&dev->v110use[idx]);
-			/* For V.110 return unencoded data length */
-			ret = v110_ret;
-			/* if the complete frame was send we free the skb;
-			   if not upper function will requeue the skb */ 
-			if (ret == skb->len)
-				dev_kfree_skb(skb);
-		}
-	} else
-		if (dev->v110[idx])
-			dev_kfree_skb(nskb);
-	return ret;
-}
-
-int
-isdn_add_channels(driver *d, int drvidx, int n, int adding)
-{
-	int j, k, m;
-	ulong flags;
-
-	init_waitqueue_head(&d->st_waitq);
-	if (d->flags & DRV_FLAG_RUNNING)
-		return -1;
-       	if (n < 1) return 0;
-
-	m = (adding) ? d->channels + n : n;
-
-	if (dev->channels + n > ISDN_MAX_CHANNELS) {
-		printk(KERN_WARNING "register_isdn: Max. %d channels supported\n",
-		       ISDN_MAX_CHANNELS);
-		return -1;
-	}
-
-	if ((adding) && (d->rcverr))
-		kfree(d->rcverr);
-	if (!(d->rcverr = kmalloc(sizeof(int) * m, GFP_KERNEL))) {
-		printk(KERN_WARNING "register_isdn: Could not alloc rcverr\n");
-		return -1;
-	}
-	memset((char *) d->rcverr, 0, sizeof(int) * m);
-
-	if ((adding) && (d->rcvcount))
-		kfree(d->rcvcount);
-	if (!(d->rcvcount = kmalloc(sizeof(int) * m, GFP_KERNEL))) {
-		printk(KERN_WARNING "register_isdn: Could not alloc rcvcount\n");
-		if (!adding) kfree(d->rcverr);
-		return -1;
-	}
-	memset((char *) d->rcvcount, 0, sizeof(int) * m);
-
-	if ((adding) && (d->rpqueue)) {
-		for (j = 0; j < d->channels; j++)
-			skb_queue_purge(&d->rpqueue[j]);
-		kfree(d->rpqueue);
-	}
-	if (!(d->rpqueue = kmalloc(sizeof(struct sk_buff_head) * m, GFP_KERNEL))) {
-		printk(KERN_WARNING "register_isdn: Could not alloc rpqueue\n");
-		if (!adding) {
-			kfree(d->rcvcount);
-			kfree(d->rcverr);
-		}
-		return -1; 
-	}
-	for (j = 0; j < m; j++) {
-		skb_queue_head_init(&d->rpqueue[j]);
-	}
-
-	if ((adding) && (d->rcv_waitq))
-		kfree(d->rcv_waitq);
-	d->rcv_waitq = kmalloc(sizeof(wait_queue_head_t) * 2 * m, GFP_KERNEL);
-	if (!d->rcv_waitq) {
-		printk(KERN_WARNING "register_isdn: Could not alloc rcv_waitq\n");
-		if (!adding) {
-			kfree(d->rpqueue);
-			kfree(d->rcvcount);
-			kfree(d->rcverr);
-		}
-		return -1;
-	}
-	d->snd_waitq = d->rcv_waitq + m;
-	for (j = 0; j < m; j++) {
-		init_waitqueue_head(&d->rcv_waitq[j]);
-		init_waitqueue_head(&d->snd_waitq[j]);
-	}
-
-	dev->channels += n;
-	save_flags(flags);
-	cli();
-	for (j = d->channels; j < m; j++)
-		for (k = 0; k < ISDN_MAX_CHANNELS; k++)
-			if (dev->chanmap[k] < 0) {
-				dev->chanmap[k] = j;
-				dev->drvmap[k] = drvidx;
-				isdn_register_devfs(k);
-				break;
-			}
-	restore_flags(flags);
-	d->channels = m;
-	return 0;
-}
-
-/*
- * Low-level-driver registration
- */
-
-static void
-set_global_features(void)
-{
-	int drvidx;
-
-	dev->global_features = 0;
-	for (drvidx = 0; drvidx < ISDN_MAX_DRIVERS; drvidx++) {
-		if (!dev->drv[drvidx])
-			continue;
-		if (dev->drv[drvidx]->interface)
-			dev->global_features |= dev->drv[drvidx]->interface->features;
-	}
-}
-
-#ifdef CONFIG_ISDN_DIVERSION
-
-static char *map_drvname(int di)
-{
-  if ((di < 0) || (di >= ISDN_MAX_DRIVERS)) 
-    return(NULL);
-  return(dev->drvid[di]); /* driver name */
-} /* map_drvname */
-
-static int map_namedrv(char *id)
-{  int i;
-
-   for (i = 0; i < ISDN_MAX_DRIVERS; i++)
-    { if (!strcmp(dev->drvid[i],id)) 
-        return(i);
-    }
-   return(-1);
-} /* map_namedrv */
-
-int DIVERT_REG_NAME(isdn_divert_if *i_div)
-{
-  if (i_div->if_magic != DIVERT_IF_MAGIC) 
-    return(DIVERT_VER_ERR);
-  switch (i_div->cmd)
-    {
-      case DIVERT_CMD_REL:
-        if (divert_if != i_div) 
-          return(DIVERT_REL_ERR);
-        divert_if = NULL; /* free interface */
-        MOD_DEC_USE_COUNT;
-        return(DIVERT_NO_ERR);
-
-      case DIVERT_CMD_REG:
-        if (divert_if) 
-          return(DIVERT_REG_ERR);
-        i_div->ll_cmd = isdn_command; /* set command function */
-        i_div->drv_to_name = map_drvname; 
-        i_div->name_to_drv = map_namedrv; 
-        MOD_INC_USE_COUNT;
-        divert_if = i_div; /* remember interface */
-        return(DIVERT_NO_ERR);
-
-      default:
-        return(DIVERT_CMD_ERR);   
-    }
-} /* DIVERT_REG_NAME */
-
-EXPORT_SYMBOL(DIVERT_REG_NAME);
-
-#endif /* CONFIG_ISDN_DIVERSION */
-
-
-EXPORT_SYMBOL(register_isdn);
-#ifdef CONFIG_ISDN_PPP
-EXPORT_SYMBOL(isdn_ppp_register_compressor);
-EXPORT_SYMBOL(isdn_ppp_unregister_compressor);
-#endif
-
-int
-register_isdn(isdn_if * i)
-{
-	driver *d;
-	int j;
-	ulong flags;
-	int drvidx;
-
-	if (dev->drivers >= ISDN_MAX_DRIVERS) {
-		printk(KERN_WARNING "register_isdn: Max. %d drivers supported\n",
-		       ISDN_MAX_DRIVERS);
-		return 0;
-	}
-	if (!i->writebuf_skb) {
-		printk(KERN_WARNING "register_isdn: No write routine given.\n");
-		return 0;
-	}
-	if (!(d = kmalloc(sizeof(driver), GFP_KERNEL))) {
-		printk(KERN_WARNING "register_isdn: Could not alloc driver-struct\n");
-		return 0;
-	}
-	memset((char *) d, 0, sizeof(driver));
-
-	d->maxbufsize = i->maxbufsize;
-	d->pktcount = 0;
-	d->stavail = 0;
-	d->flags = DRV_FLAG_LOADED;
-	d->online = 0;
-	d->interface = i;
-	d->channels = 0;
-	for (drvidx = 0; drvidx < ISDN_MAX_DRIVERS; drvidx++)
-		if (!dev->drv[drvidx])
-			break;
-	if (isdn_add_channels(d, drvidx, i->channels, 0)) {
-		kfree(d);
-		return 0;
-	}
-	i->channels = drvidx;
-	i->rcvcallb_skb = isdn_receive_skb_callback;
-	i->statcallb = isdn_status_callback;
-	if (!strlen(i->id))
-		sprintf(i->id, "line%d", drvidx);
-	save_flags(flags);
-	cli();
-	for (j = 0; j < drvidx; j++)
-		if (!strcmp(i->id, dev->drvid[j]))
-			sprintf(i->id, "line%d", drvidx);
-	dev->drv[drvidx] = d;
-	strcpy(dev->drvid[drvidx], i->id);
-	isdn_info_update();
-	dev->drivers++;
-	set_global_features();
-	restore_flags(flags);
-	return 1;
-}
-
-/*
- *****************************************************************************
- * And now the modules code.
- *****************************************************************************
- */
-
-static char *
-isdn_getrev(const char *revision)
-{
-	char *rev;
-	char *p;
-
-	if ((p = strchr(revision, ':'))) {
-		rev = p + 2;
-		p = strchr(rev, '$');
-		*--p = 0;
-	} else
-		rev = "???";
-	return rev;
-}
-
-#ifdef CONFIG_DEVFS_FS
-
-static devfs_handle_t devfs_handle;
-
-static void isdn_register_devfs(int k)
-{
-	char buf[11];
-
-	sprintf (buf, "isdnctrl%d", k);
-	dev->devfs_handle_isdnctrlX[k] =
-	    devfs_register (devfs_handle, buf, DEVFS_FL_DEFAULT,
-			    ISDN_MAJOR, ISDN_MINOR_CTRL + k, 0600 | S_IFCHR,
-			    &isdn_fops, NULL);
-}
-
-static void isdn_unregister_devfs(int k)
-{
-	devfs_unregister (dev->devfs_handle_isdnctrlX[k]);
-}
-
-static void isdn_init_devfs(void)
-{
-#  ifdef CONFIG_ISDN_PPP
-	int i;
-#  endif
-
-	devfs_handle = devfs_mk_dir (NULL, "isdn", NULL);
-#  ifdef CONFIG_ISDN_PPP
-	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-		char buf[8];
-
-		sprintf (buf, "ippp%d", i);
-		dev->devfs_handle_ipppX[i] =
-		    devfs_register (devfs_handle, buf, DEVFS_FL_DEFAULT,
-				    ISDN_MAJOR, ISDN_MINOR_PPP + i,
-				    0600 | S_IFCHR, &isdn_fops, NULL);
-	}
-#  endif
-
-	dev->devfs_handle_isdninfo =
-	    devfs_register (devfs_handle, "isdninfo", DEVFS_FL_DEFAULT,
-			    ISDN_MAJOR, ISDN_MINOR_STATUS, 0600 | S_IFCHR,
-			    &isdn_fops, NULL);
-	dev->devfs_handle_isdnctrl =
-	    devfs_register (devfs_handle, "isdnctrl", DEVFS_FL_DEFAULT,
-			    ISDN_MAJOR, ISDN_MINOR_CTRL, 0600 | S_IFCHR, 
-			    &isdn_fops, NULL);
-}
-
-static void isdn_cleanup_devfs(void)
-{
-#  ifdef CONFIG_ISDN_PPP
-	int i;
-	for (i = 0; i < ISDN_MAX_CHANNELS; i++) 
-		devfs_unregister (dev->devfs_handle_ipppX[i]);
-#  endif
-	devfs_unregister (dev->devfs_handle_isdninfo);
-	devfs_unregister (dev->devfs_handle_isdnctrl);
-	devfs_unregister (devfs_handle);
-}
-
-#else   /* CONFIG_DEVFS_FS */
-static void isdn_register_devfs(int dummy)
-{
-}
-
-static void isdn_unregister_devfs(int dummy)
-{
-}
-
-static void isdn_init_devfs(void)
-{
-}
-
-static void isdn_cleanup_devfs(void)
-{
-}
-
-#endif  /* CONFIG_DEVFS_FS */
-
-/*
- * Allocate and initialize all data, register modem-devices
- */
-static int __init isdn_init(void)
-{
-	int i;
-	char tmprev[50];
-
-	if (!(dev = (isdn_dev *) vmalloc(sizeof(isdn_dev)))) {
-		printk(KERN_WARNING "isdn: Could not allocate device-struct.\n");
-		return -EIO;
-	}
-	memset((char *) dev, 0, sizeof(isdn_dev));
-	init_timer(&dev->timer);
-	dev->timer.function = isdn_timer_funct;
-	init_MUTEX(&dev->sem);
-	init_waitqueue_head(&dev->info_waitq);
-	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-		dev->drvmap[i] = -1;
-		dev->chanmap[i] = -1;
-		dev->m_idx[i] = -1;
-		strcpy(dev->num[i], "???");
-		init_waitqueue_head(&dev->mdm.info[i].open_wait);
-		init_waitqueue_head(&dev->mdm.info[i].close_wait);
-	}
-	if (devfs_register_chrdev(ISDN_MAJOR, "isdn", &isdn_fops)) {
-		printk(KERN_WARNING "isdn: Could not register control devices\n");
-		vfree(dev);
-		return -EIO;
-	}
-	isdn_init_devfs();
-	if ((i = isdn_tty_modem_init()) < 0) {
-		printk(KERN_WARNING "isdn: Could not register tty devices\n");
-		if (i == -3)
-			tty_unregister_driver(&dev->mdm.cua_modem);
-		if (i <= -2)
-			tty_unregister_driver(&dev->mdm.tty_modem);
-		vfree(dev);
-		isdn_cleanup_devfs();
-		devfs_unregister_chrdev(ISDN_MAJOR, "isdn");
-		return -EIO;
-	}
-#ifdef CONFIG_ISDN_PPP
-	if (isdn_ppp_init() < 0) {
-		printk(KERN_WARNING "isdn: Could not create PPP-device-structs\n");
-		tty_unregister_driver(&dev->mdm.tty_modem);
-		tty_unregister_driver(&dev->mdm.cua_modem);
-		for (i = 0; i < ISDN_MAX_CHANNELS; i++)
-			kfree(dev->mdm.info[i].xmit_buf - 4);
-		isdn_cleanup_devfs();
-		devfs_unregister_chrdev(ISDN_MAJOR, "isdn");
-		vfree(dev);
-		return -EIO;
-	}
-#endif                          /* CONFIG_ISDN_PPP */
-
-	strcpy(tmprev, isdn_revision);
-	printk(KERN_NOTICE "ISDN subsystem Rev: %s/", isdn_getrev(tmprev));
-	strcpy(tmprev, isdn_tty_revision);
-	printk("%s/", isdn_getrev(tmprev));
-	strcpy(tmprev, isdn_net_revision);
-	printk("%s/", isdn_getrev(tmprev));
-	strcpy(tmprev, isdn_ppp_revision);
-	printk("%s/", isdn_getrev(tmprev));
-	strcpy(tmprev, isdn_audio_revision);
-	printk("%s/", isdn_getrev(tmprev));
-	strcpy(tmprev, isdn_v110_revision);
-	printk("%s", isdn_getrev(tmprev));
-
-#ifdef MODULE
-	printk(" loaded\n");
-#else
-	printk("\n");
-#endif
-	isdn_info_update();
-	return 0;
-}
-
-/*
- * Unload module
- */
-static void __exit isdn_exit(void)
-{
-	unsigned long flags;
-	int i;
-
-#ifdef CONFIG_ISDN_PPP
-	isdn_ppp_cleanup();
-#endif
-	save_flags(flags);
-	cli();
-	if (isdn_net_rmall() < 0) {
-		printk(KERN_WARNING "isdn: net-device busy, remove cancelled\n");
-		restore_flags(flags);
-		return;
-	}
-	if (tty_unregister_driver(&dev->mdm.tty_modem)) {
-		printk(KERN_WARNING "isdn: ttyI-device busy, remove cancelled\n");
-		restore_flags(flags);
-		return;
-	}
-	if (tty_unregister_driver(&dev->mdm.cua_modem)) {
-		printk(KERN_WARNING "isdn: cui-device busy, remove cancelled\n");
-		restore_flags(flags);
-		return;
-	}
-	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-		isdn_tty_cleanup_xmit(&dev->mdm.info[i]);
-		kfree(dev->mdm.info[i].xmit_buf - 4);
-#ifdef CONFIG_ISDN_TTY_FAX
-		kfree(dev->mdm.info[i].fax);
-#endif
-	}
-	if (devfs_unregister_chrdev(ISDN_MAJOR, "isdn") != 0) {
-		printk(KERN_WARNING "isdn: controldevice busy, remove cancelled\n");
-		restore_flags(flags);
-	} else {
-		isdn_cleanup_devfs();
-		del_timer(&dev->timer);
-		restore_flags(flags);
-		/* call vfree with interrupts enabled, else it will hang */
-		vfree(dev);
-		printk(KERN_NOTICE "ISDN-subsystem unloaded\n");
-	}
-}
-
-module_init(isdn_init);
-module_exit(isdn_exit);
diff -Nru a/drivers/isdn/isdn_common.h b/drivers/isdn/isdn_common.h
--- a/drivers/isdn/isdn_common.h	Mon Apr 22 15:32:18 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,49 +0,0 @@
-/* $Id: isdn_common.h,v 1.21.6.1 2001/09/23 22:24:31 kai Exp $
- *
- * header for Linux ISDN subsystem
- * common used functions and debugging-switches (linklevel).
- *
- * Copyright 1994-1999  by Fritz Elfert (fritz@isdn4linux.de)
- * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
- * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#undef  ISDN_DEBUG_MODEM_OPEN
-#undef  ISDN_DEBUG_MODEM_IOCTL
-#undef  ISDN_DEBUG_MODEM_WAITSENT
-#undef  ISDN_DEBUG_MODEM_HUP
-#undef  ISDN_DEBUG_MODEM_ICALL
-#undef  ISDN_DEBUG_MODEM_DUMP
-#undef  ISDN_DEBUG_MODEM_VOICE
-#undef  ISDN_DEBUG_AT
-#undef  ISDN_DEBUG_NET_DUMP
-#undef  ISDN_DEBUG_NET_DIAL
-#undef  ISDN_DEBUG_NET_ICALL
-
-/* Prototypes */
-extern void isdn_MOD_INC_USE_COUNT(void);
-extern void isdn_MOD_DEC_USE_COUNT(void);
-extern void isdn_lock_drivers(void);
-extern void isdn_unlock_drivers(void);
-extern void isdn_free_channel(int di, int ch, int usage);
-extern void isdn_all_eaz(int di, int ch);
-extern int isdn_command(isdn_ctrl *);
-extern int isdn_dc2minor(int di, int ch);
-extern void isdn_info_update(void);
-extern char *isdn_map_eaz2msn(char *msn, int di);
-extern void isdn_timer_ctrl(int tf, int onoff);
-extern void isdn_unexclusive_channel(int di, int ch);
-extern int isdn_getnum(char **);
-extern int isdn_readbchan(int, int, u_char *, u_char *, int, wait_queue_head_t *);
-extern int isdn_get_free_channel(int, int, int, int, int, char *);
-extern int isdn_writebuf_skb_stub(int, int, int, struct sk_buff *);
-extern int register_isdn(isdn_if * i);
-extern int isdn_msncmp( const char *,  const char *);
-extern int isdn_add_channels(driver *, int, int, int);
-#if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP)
-extern void isdn_dumppkt(char *, u_char *, int, int);
-#endif
diff -Nru a/drivers/isdn/isdn_concap.c b/drivers/isdn/isdn_concap.c
--- a/drivers/isdn/isdn_concap.c	Mon Apr 22 15:32:19 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,108 +0,0 @@
-/* $Id: isdn_concap.c,v 1.8.6.1 2001/09/23 22:24:31 kai Exp $
- * 
- * Linux ISDN subsystem, protocol encapsulation
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-/* Stuff to support the concap_proto by isdn4linux. isdn4linux - specific
- * stuff goes here. Stuff that depends only on the concap protocol goes to
- * another -- protocol specific -- source file.
- *
- */
-
-
-#include <linux/isdn.h>
-#include "isdn_x25iface.h"
-#include "isdn_net.h"
-#include <linux/concap.h>
-#include "isdn_concap.h"
-
-
-/* The following set of device service operations are for encapsulation
-   protocols that require for reliable datalink semantics. That means:
-
-   - before any data is to be submitted the connection must explicitly
-     be set up.
-   - after the successful set up of the connection is signalled the
-     connection is considered to be reliably up.
-
-   Auto-dialing ist not compatible with this requirements. Thus, auto-dialing 
-   is completely bypassed.
-
-   It might be possible to implement a (non standardized) datalink protocol
-   that provides a reliable data link service while using some auto dialing
-   mechanism. Such a protocol would need an auxiliary channel (i.e. user-user-
-   signaling on the D-channel) while the B-channel is down.
-   */
-
-
-int isdn_concap_dl_data_req(struct concap_proto *concap, struct sk_buff *skb)
-{
-	struct net_device *ndev = concap -> net_dev;
-	isdn_net_dev *nd = ((isdn_net_local *) ndev->priv)->netdev;
-	isdn_net_local *lp = isdn_net_get_locked_lp(nd);
-
-	IX25DEBUG( "isdn_concap_dl_data_req: %s \n", concap->net_dev->name);
-	if (!lp) {
-		IX25DEBUG( "isdn_concap_dl_data_req: %s : isdn_net_send_skb returned %d\n", concap -> net_dev -> name, 1);
-		return 1;
-	}
-	lp->huptimer = 0;
-	isdn_net_writebuf_skb(lp, skb);
-	spin_unlock_bh(&lp->xmit_lock);
-	IX25DEBUG( "isdn_concap_dl_data_req: %s : isdn_net_send_skb returned %d\n", concap -> net_dev -> name, 0);
-	return 0;
-}
-
-
-int isdn_concap_dl_connect_req(struct concap_proto *concap)
-{
-	struct net_device *ndev = concap -> net_dev;
-	isdn_net_local *lp = (isdn_net_local *) ndev->priv;
-	int ret;
-	IX25DEBUG( "isdn_concap_dl_connect_req: %s \n", ndev -> name);
-
-	/* dial ... */
-	ret = isdn_net_dial_req( lp );
-	if ( ret ) IX25DEBUG("dialing failed\n");
-	return ret;
-}
-
-int isdn_concap_dl_disconn_req(struct concap_proto *concap)
-{
-	IX25DEBUG( "isdn_concap_dl_disconn_req: %s \n", concap -> net_dev -> name);
-
-	isdn_net_hangup( concap -> net_dev );
-	return 0;
-}
-
-struct concap_device_ops isdn_concap_reliable_dl_dops = {
-	&isdn_concap_dl_data_req,
-	&isdn_concap_dl_connect_req,
-	&isdn_concap_dl_disconn_req
-};
-
-struct concap_device_ops isdn_concap_demand_dial_dops = {
-	NULL, /* set this first entry to something like &isdn_net_start_xmit,
-		 but the entry part of the current isdn_net_start_xmit must be
-		 separated first. */
-	/* no connection control for demand dial semantics */
-	NULL,
-	NULL,
-};
-
-/* The following should better go into a dedicated source file such that
-   this sourcefile does not need to include any protocol specific header
-   files. For now:
-   */
-struct concap_proto * isdn_concap_new( int encap )
-{
-	switch ( encap ) {
-	case ISDN_NET_ENCAP_X25IFACE:
-		return isdn_x25iface_proto_new();
-	}
-	return NULL;
-}
diff -Nru a/drivers/isdn/isdn_concap.h b/drivers/isdn/isdn_concap.h
--- a/drivers/isdn/isdn_concap.h	Mon Apr 22 15:32:18 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,14 +0,0 @@
-/* $Id: isdn_concap.h,v 1.3.6.1 2001/09/23 22:24:31 kai Exp $
- *
- * Linux ISDN subsystem, protocol encapsulation
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-extern struct concap_device_ops isdn_concap_reliable_dl_dops;
-extern struct concap_device_ops isdn_concap_demand_dial_dops;
-extern struct concap_proto * isdn_concap_new( int );
-
-
diff -Nru a/drivers/isdn/isdn_net.c b/drivers/isdn/isdn_net.c
--- a/drivers/isdn/isdn_net.c	Mon Apr 22 15:32:19 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,3238 +0,0 @@
-/* $Id: isdn_net.c,v 1.140.6.11 2001/11/06 20:58:28 kai Exp $
- *
- * Linux ISDN subsystem, network interfaces and related functions (linklevel).
- *
- * Copyright 1994-1998  by Fritz Elfert (fritz@isdn4linux.de)
- * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
- * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- * Jan 2001: fix CISCO HDLC      Bjoern A. Zeeb <i4l@zabbadoz.net>
- *           for info on the protocol, see 
- *           http://i4l.zabbadoz.net/i4l/cisco-hdlc.txt
- */
-
-#include <linux/config.h>
-#include <linux/isdn.h>
-#include <net/arp.h>
-#include <net/dst.h>
-#include <net/pkt_sched.h>
-#include <linux/inetdevice.h>
-#include "isdn_common.h"
-#include "isdn_net.h"
-#ifdef CONFIG_ISDN_PPP
-#include "isdn_ppp.h"
-#endif
-#ifdef CONFIG_ISDN_X25
-#include <linux/concap.h>
-#include "isdn_concap.h"
-#endif
-
-
-/*
- * Outline of new tbusy handling: 
- *
- * Old method, roughly spoken, consisted of setting tbusy when entering
- * isdn_net_start_xmit() and at several other locations and clearing
- * it from isdn_net_start_xmit() thread when sending was successful.
- *
- * With 2.3.x multithreaded network core, to prevent problems, tbusy should
- * only be set by the isdn_net_start_xmit() thread and only when a tx-busy
- * condition is detected. Other threads (in particular isdn_net_stat_callb())
- * are only allowed to clear tbusy.
- *
- * -HE
- */
-
-/*
- * About SOFTNET:
- * Most of the changes were pretty obvious and basically done by HE already.
- *
- * One problem of the isdn net device code is that is uses struct net_device
- * for masters and slaves. However, only master interface are registered to 
- * the network layer, and therefore, it only makes sense to call netif_* 
- * functions on them.
- *
- * --KG
- */
-
-/* 
- * Find out if the netdevice has been ifup-ed yet.
- * For slaves, look at the corresponding master.
- */
-static __inline__ int isdn_net_device_started(isdn_net_dev *n)
-{
-	isdn_net_local *lp = n->local;
-	struct net_device *dev;
-	
-	if (lp->master) 
-		dev = lp->master;
-	else
-		dev = &n->dev;
-	return netif_running(dev);
-}
-
-/*
- * wake up the network -> net_device queue.
- * For slaves, wake the corresponding master interface.
- */
-static __inline__ void isdn_net_device_wake_queue(isdn_net_local *lp)
-{
-	if (lp->master) 
-		netif_wake_queue(lp->master);
-	else
-		netif_wake_queue(&lp->netdev->dev);
-}
-
-/*
- * stop the network -> net_device queue.
- * For slaves, stop the corresponding master interface.
- */
-static __inline__ void isdn_net_device_stop_queue(isdn_net_local *lp)
-{
-	if (lp->master)
-		netif_stop_queue(lp->master);
-	else
-		netif_stop_queue(&lp->netdev->dev);
-}
-
-/*
- * find out if the net_device which this lp belongs to (lp can be
- * master or slave) is busy. It's busy iff all (master and slave) 
- * queues are busy
- */
-static __inline__ int isdn_net_device_busy(isdn_net_local *lp)
-{
-	isdn_net_local *nlp;
-	isdn_net_dev *nd;
-	unsigned long flags;
-
-	if (!isdn_net_lp_busy(lp))
-		return 0;
-
-	if (lp->master)
-		nd = ((isdn_net_local *) lp->master->priv)->netdev;
-	else
-		nd = lp->netdev;
-	
-	spin_lock_irqsave(&nd->queue_lock, flags);
-	nlp = lp->next;
-	while (nlp != lp) {
-		if (!isdn_net_lp_busy(nlp)) {
-			spin_unlock_irqrestore(&nd->queue_lock, flags);
-			return 0;
-		}
-		nlp = nlp->next;
-	}
-	spin_unlock_irqrestore(&nd->queue_lock, flags);
-	return 1;
-}
-
-static __inline__ void isdn_net_inc_frame_cnt(isdn_net_local *lp)
-{
-	atomic_inc(&lp->frame_cnt);
-	if (isdn_net_device_busy(lp))
-		isdn_net_device_stop_queue(lp);
-}
-
-static __inline__ void isdn_net_dec_frame_cnt(isdn_net_local *lp)
-{
-	atomic_dec(&lp->frame_cnt);
-
-	if (!(isdn_net_device_busy(lp))) {
-		if (!skb_queue_empty(&lp->super_tx_queue)) {
-			queue_task(&lp->tqueue, &tq_immediate);
-			mark_bh(IMMEDIATE_BH);
-		} else {
-			isdn_net_device_wake_queue(lp);
-		}
-       }                                                                      
-}
-
-static __inline__ void isdn_net_zero_frame_cnt(isdn_net_local *lp)
-{
-	atomic_set(&lp->frame_cnt, 0);
-}
-
-/* For 2.2.x we leave the transmitter busy timeout at 2 secs, just 
- * to be safe.
- * For 2.3.x we push it up to 20 secs, because call establishment
- * (in particular callback) may take such a long time, and we 
- * don't want confusing messages in the log. However, there is a slight
- * possibility that this large timeout will break other things like MPPP,
- * which might rely on the tx timeout. If so, we'll find out this way...
- */
-
-#define ISDN_NET_TX_TIMEOUT (20*HZ) 
-
-/* Prototypes */
-
-int isdn_net_force_dial_lp(isdn_net_local *);
-static int isdn_net_start_xmit(struct sk_buff *, struct net_device *);
-
-static void isdn_net_ciscohdlck_connected(isdn_net_local *lp);
-static void isdn_net_ciscohdlck_disconnected(isdn_net_local *lp);
-
-char *isdn_net_revision = "$Revision: 1.140.6.11 $";
-
- /*
-  * Code for raw-networking over ISDN
-  */
-
-static void
-isdn_net_unreachable(struct net_device *dev, struct sk_buff *skb, char *reason)
-{
-	if(skb) {
-
-		u_short proto = ntohs(skb->protocol);
-
-		printk(KERN_DEBUG "isdn_net: %s: %s, signalling dst_link_failure %s\n",
-		       dev->name,
-		       (reason != NULL) ? reason : "unknown",
-		       (proto != ETH_P_IP) ? "Protocol != ETH_P_IP" : "");
-		
-		dst_link_failure(skb);
-	}
-	else {  /* dial not triggered by rawIP packet */
-		printk(KERN_DEBUG "isdn_net: %s: %s\n",
-			   dev->name,
-			   (reason != NULL) ? reason : "reason unknown");
-	}
-}
-
-static void
-isdn_net_reset(struct net_device *dev)
-{
-#ifdef CONFIG_ISDN_X25
-	struct concap_device_ops * dops =
-		( (isdn_net_local *) dev->priv ) -> dops;
-	struct concap_proto * cprot =
-		( (isdn_net_local *) dev->priv ) -> netdev -> cprot;
-#endif
-	ulong flags;
-
-	/* not sure if the cli() is needed at all --KG */
-	save_flags(flags);
-	cli();                  /* Avoid glitch on writes to CMD regs */
-#ifdef CONFIG_ISDN_X25
-	if( cprot && cprot -> pops && dops )
-		cprot -> pops -> restart ( cprot, dev, dops );
-#endif
-	restore_flags(flags);
-}
-
-/* Open/initialize the board. */
-static int
-isdn_net_open(struct net_device *dev)
-{
-	int i;
-	struct net_device *p;
-	struct in_device *in_dev;
-
-	/* moved here from isdn_net_reset, because only the master has an
-	   interface associated which is supposed to be started. BTW:
-	   we need to call netif_start_queue, not netif_wake_queue here */
-	netif_start_queue(dev);
-
-	isdn_net_reset(dev);
-	/* Fill in the MAC-level header (not needed, but for compatibility... */
-	for (i = 0; i < ETH_ALEN - sizeof(u32); i++)
-		dev->dev_addr[i] = 0xfc;
-	if ((in_dev = dev->ip_ptr) != NULL) {
-		/*
-		 *      Any address will do - we take the first
-		 */
-		struct in_ifaddr *ifa = in_dev->ifa_list;
-		if (ifa != NULL)
-			memcpy(dev->dev_addr+2, &ifa->ifa_local, 4);
-	}
-
-	/* If this interface has slaves, start them also */
-
-	if ((p = (((isdn_net_local *) dev->priv)->slave))) {
-		while (p) {
-			isdn_net_reset(p);
-			p = (((isdn_net_local *) p->priv)->slave);
-		}
-	}
-	isdn_MOD_INC_USE_COUNT();
-	return 0;
-}
-
-/*
- * Assign an ISDN-channel to a net-interface
- */
-static void
-isdn_net_bind_channel(isdn_net_local * lp, int idx)
-{
-	ulong flags;
-
-	save_flags(flags);
-	cli();
-	lp->flags |= ISDN_NET_CONNECTED;
-	lp->isdn_device = dev->drvmap[idx];
-	lp->isdn_channel = dev->chanmap[idx];
-	dev->rx_netdev[idx] = lp->netdev;
-	dev->st_netdev[idx] = lp->netdev;
-	restore_flags(flags);
-}
-
-/*
- * unbind a net-interface (resets interface after an error)
- */
-static void
-isdn_net_unbind_channel(isdn_net_local * lp)
-{
-	ulong flags;
-
-	save_flags(flags);
-	cli();
-	skb_queue_purge(&lp->super_tx_queue);
-
-	if (!lp->master) {	/* reset only master device */
-		/* Moral equivalent of dev_purge_queues():
-		   BEWARE! This chunk of code cannot be called from hardware
-		   interrupt handler. I hope it is true. --ANK
-		 */
-		qdisc_reset(lp->netdev->dev.qdisc);
-	}
-	lp->dialstate = 0;
-	dev->rx_netdev[isdn_dc2minor(lp->isdn_device, lp->isdn_channel)] = NULL;
-	dev->st_netdev[isdn_dc2minor(lp->isdn_device, lp->isdn_channel)] = NULL;
-	isdn_free_channel(lp->isdn_device, lp->isdn_channel, ISDN_USAGE_NET);
-	lp->flags &= ~ISDN_NET_CONNECTED;
-	lp->isdn_device = -1;
-	lp->isdn_channel = -1;
-
-	restore_flags(flags);
-}
-
-/*
- * Perform auto-hangup and cps-calculation for net-interfaces.
- *
- * auto-hangup:
- * Increment idle-counter (this counter is reset on any incoming or
- * outgoing packet), if counter exceeds configured limit either do a
- * hangup immediately or - if configured - wait until just before the next
- * charge-info.
- *
- * cps-calculation (needed for dynamic channel-bundling):
- * Since this function is called every second, simply reset the
- * byte-counter of the interface after copying it to the cps-variable.
- */
-unsigned long last_jiffies = -HZ;
-
-void
-isdn_net_autohup()
-{
-	isdn_net_dev *p = dev->netdev;
-	int anymore;
-
-	anymore = 0;
-	while (p) {
-		isdn_net_local *l = p->local;
-		if (jiffies == last_jiffies)
-			l->cps = l->transcount;
-		else
-			l->cps = (l->transcount * HZ) / (jiffies - last_jiffies);
-		l->transcount = 0;
-		if (dev->net_verbose > 3)
-			printk(KERN_DEBUG "%s: %d bogocps\n", l->name, l->cps);
-		if ((l->flags & ISDN_NET_CONNECTED) && (!l->dialstate)) {
-			anymore = 1;
-			l->huptimer++;
-			/*
-			 * if there is some dialmode where timeout-hangup
-			 * should _not_ be done, check for that here
-			 */
-			if ((l->onhtime) &&
-			    (l->huptimer > l->onhtime))
-			{
-				if (l->hupflags & ISDN_MANCHARGE &&
-				    l->hupflags & ISDN_CHARGEHUP) {
-					while (time_after(jiffies, l->chargetime + l->chargeint))
-						l->chargetime += l->chargeint;
-					if (time_after(jiffies, l->chargetime + l->chargeint - 2 * HZ))
-						if (l->outgoing || l->hupflags & ISDN_INHUP)
-							isdn_net_hangup(&p->dev);
-				} else if (l->outgoing) {
-					if (l->hupflags & ISDN_CHARGEHUP) {
-						if (l->hupflags & ISDN_WAITCHARGE) {
-							printk(KERN_DEBUG "isdn_net: Hupflags of %s are %X\n",
-							       l->name, l->hupflags);
-							isdn_net_hangup(&p->dev);
-						} else if (time_after(jiffies, l->chargetime + l->chargeint)) {
-							printk(KERN_DEBUG
-							       "isdn_net: %s: chtime = %lu, chint = %d\n",
-							       l->name, l->chargetime, l->chargeint);
-							isdn_net_hangup(&p->dev);
-						}
-					}
-				} else if (l->hupflags & ISDN_INHUP)
-					isdn_net_hangup(&p->dev);
-			}
-
-			if(dev->global_flags & ISDN_GLOBAL_STOPPED || (ISDN_NET_DIALMODE(*l) == ISDN_NET_DM_OFF)) {
-				isdn_net_hangup(&p->dev);
-				break;
-			}
-		}
-		p = (isdn_net_dev *) p->next;
-	}
-	last_jiffies = jiffies;
-	isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, anymore);
-}
-
-static void isdn_net_lp_disconnected(isdn_net_local *lp)
-{
-	isdn_net_rm_from_bundle(lp);
-}
-
-/*
- * Handle status-messages from ISDN-interfacecard.
- * This function is called from within the main-status-dispatcher
- * isdn_status_callback, which itself is called from the low-level driver.
- * Return: 1 = Event handled, 0 = not for us or unknown Event.
- */
-int
-isdn_net_stat_callback(int idx, isdn_ctrl *c)
-{
-	isdn_net_dev *p = dev->st_netdev[idx];
-	int cmd = c->command;
-
-	if (p) {
-		isdn_net_local *lp = p->local;
-#ifdef CONFIG_ISDN_X25
-		struct concap_proto *cprot = lp -> netdev -> cprot;
-		struct concap_proto_ops *pops = cprot ? cprot -> pops : 0;
-#endif
-		switch (cmd) {
-			case ISDN_STAT_BSENT:
-				/* A packet has successfully been sent out */
-				if ((lp->flags & ISDN_NET_CONNECTED) &&
-				    (!lp->dialstate)) {
-					isdn_net_dec_frame_cnt(lp);
-					lp->stats.tx_packets++;
-					lp->stats.tx_bytes += c->parm.length;
-				}
-				return 1;
-			case ISDN_STAT_DCONN:
-				/* D-Channel is up */
-				switch (lp->dialstate) {
-					case 4:
-					case 7:
-					case 8:
-						lp->dialstate++;
-						return 1;
-					case 12:
-						lp->dialstate = 5;
-						return 1;
-				}
-				break;
-			case ISDN_STAT_DHUP:
-				/* Either D-Channel-hangup or error during dialout */
-#ifdef CONFIG_ISDN_X25
-				/* If we are not connencted then dialing had
-				   failed. If there are generic encap protocol
-				   receiver routines signal the closure of
-				   the link*/
-
-				if( !(lp->flags & ISDN_NET_CONNECTED)
-				    && pops && pops -> disconn_ind )
-					pops -> disconn_ind(cprot);
-#endif /* CONFIG_ISDN_X25 */
-				if ((!lp->dialstate) && (lp->flags & ISDN_NET_CONNECTED)) {
-					if (lp->p_encap == ISDN_NET_ENCAP_CISCOHDLCK)
-						isdn_net_ciscohdlck_disconnected(lp);
-#ifdef CONFIG_ISDN_PPP
-					if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
-						isdn_ppp_free(lp);
-#endif
-					isdn_net_lp_disconnected(lp);
-					isdn_all_eaz(lp->isdn_device, lp->isdn_channel);
-					printk(KERN_INFO "%s: remote hangup\n", lp->name);
-					printk(KERN_INFO "%s: Chargesum is %d\n", lp->name,
-					       lp->charge);
-					isdn_net_unbind_channel(lp);
-					return 1;
-				}
-				break;
-#ifdef CONFIG_ISDN_X25
-			case ISDN_STAT_BHUP:
-				/* B-Channel-hangup */
-				/* try if there are generic encap protocol
-				   receiver routines and signal the closure of
-				   the link */
-				if( pops  &&  pops -> disconn_ind ){
-						pops -> disconn_ind(cprot);
-						return 1;
-					}
-				break;
-#endif /* CONFIG_ISDN_X25 */
-			case ISDN_STAT_BCONN:
-				/* B-Channel is up */
-				isdn_net_zero_frame_cnt(lp);
-				switch (lp->dialstate) {
-					case 5:
-					case 6:
-					case 7:
-					case 8:
-					case 9:
-					case 10:
-					case 12:
-						if (lp->dialstate <= 6) {
-							dev->usage[idx] |= ISDN_USAGE_OUTGOING;
-							isdn_info_update();
-						} else
-							dev->rx_netdev[idx] = p;
-						lp->dialstate = 0;
-						isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 1);
-						if (lp->p_encap == ISDN_NET_ENCAP_CISCOHDLCK)
-							isdn_net_ciscohdlck_connected(lp);
-						if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP) {
-							if (lp->master) { /* is lp a slave? */
-								isdn_net_dev *nd = ((isdn_net_local *)lp->master->priv)->netdev;
-								isdn_net_add_to_bundle(nd, lp);
-							}
-						}
-						printk(KERN_INFO "isdn_net: %s connected\n", lp->name);
-						/* If first Chargeinfo comes before B-Channel connect,
-						 * we correct the timestamp here.
-						 */
-						lp->chargetime = jiffies;
-
-						/* reset dial-timeout */
-						lp->dialstarted = 0;
-						lp->dialwait_timer = 0;
-
-#ifdef CONFIG_ISDN_PPP
-						if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
-							isdn_ppp_wakeup_daemon(lp);
-#endif
-#ifdef CONFIG_ISDN_X25
-						/* try if there are generic concap receiver routines */
-						if( pops )
-							if( pops->connect_ind)
-								pops->connect_ind(cprot);
-#endif /* CONFIG_ISDN_X25 */
-						/* ppp needs to do negotiations first */
-						if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP)
-							isdn_net_device_wake_queue(lp);
-						return 1;
-				}
-				break;
-			case ISDN_STAT_NODCH:
-				/* No D-Channel avail. */
-				if (lp->dialstate == 4) {
-					lp->dialstate--;
-					return 1;
-				}
-				break;
-			case ISDN_STAT_CINF:
-				/* Charge-info from TelCo. Calculate interval between
-				 * charge-infos and set timestamp for last info for
-				 * usage by isdn_net_autohup()
-				 */
-				lp->charge++;
-				if (lp->hupflags & ISDN_HAVECHARGE) {
-					lp->hupflags &= ~ISDN_WAITCHARGE;
-					lp->chargeint = jiffies - lp->chargetime - (2 * HZ);
-				}
-				if (lp->hupflags & ISDN_WAITCHARGE)
-					lp->hupflags |= ISDN_HAVECHARGE;
-				lp->chargetime = jiffies;
-				printk(KERN_DEBUG "isdn_net: Got CINF chargetime of %s now %lu\n",
-				       lp->name, lp->chargetime);
-				return 1;
-		}
-	}
-	return 0;
-}
-
-/*
- * Perform dialout for net-interfaces and timeout-handling for
- * D-Channel-up and B-Channel-up Messages.
- * This function is initially called from within isdn_net_start_xmit() or
- * or isdn_net_find_icall() after initializing the dialstate for an
- * interface. If further calls are needed, the function schedules itself
- * for a timer-callback via isdn_timer_function().
- * The dialstate is also affected by incoming status-messages from
- * the ISDN-Channel which are handled in isdn_net_stat_callback() above.
- */
-void
-isdn_net_dial(void)
-{
-	isdn_net_dev *p = dev->netdev;
-	int anymore = 0;
-	int i;
-	unsigned long flags;
-	isdn_ctrl cmd;
-
-	while (p) {
-		isdn_net_local *lp = p->local;
-
-#ifdef ISDN_DEBUG_NET_DIAL
-		if (lp->dialstate)
-			printk(KERN_DEBUG "%s: dialstate=%d\n", lp->name, lp->dialstate);
-#endif
-		switch (lp->dialstate) {
-			case 0:
-				/* Nothing to do for this interface */
-				break;
-			case 1:
-				/* Initiate dialout. Set phone-number-pointer to first number
-				 * of interface.
-				 */
-				save_flags(flags);
-				cli();
-				lp->dial = lp->phone[1];
-				restore_flags(flags);
-				if (!lp->dial) {
-					printk(KERN_WARNING "%s: phone number deleted?\n",
-					       lp->name);
-					isdn_net_hangup(&p->dev);
-					break;
-				}
-				anymore = 1;
-
-				if(lp->dialtimeout > 0)
-					if(lp->dialstarted == 0 || time_after(jiffies, lp->dialstarted + lp->dialtimeout + lp->dialwait)) {
-						lp->dialstarted = jiffies;
-						lp->dialwait_timer = 0;
-					}
-
-				lp->dialstate++;
-				/* Fall through */
-			case 2:
-				/* Prepare dialing. Clear EAZ, then set EAZ. */
-				cmd.driver = lp->isdn_device;
-				cmd.arg = lp->isdn_channel;
-				cmd.command = ISDN_CMD_CLREAZ;
-				isdn_command(&cmd);
-				sprintf(cmd.parm.num, "%s", isdn_map_eaz2msn(lp->msn, cmd.driver));
-				cmd.command = ISDN_CMD_SETEAZ;
-				isdn_command(&cmd);
-				lp->dialretry = 0;
-				anymore = 1;
-				lp->dialstate++;
-				/* Fall through */
-			case 3:
-				/* Setup interface, dial current phone-number, switch to next number.
-				 * If list of phone-numbers is exhausted, increment
-				 * retry-counter.
-				 */
-				if(dev->global_flags & ISDN_GLOBAL_STOPPED || (ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_OFF)) {
-					char *s;
-					if (dev->global_flags & ISDN_GLOBAL_STOPPED)
-						s = "dial suppressed: isdn system stopped";
-					else
-						s = "dial suppressed: dialmode `off'";
-					isdn_net_unreachable(&p->dev, 0, s);
-					isdn_net_hangup(&p->dev);
-					break;
-				}
-				cmd.driver = lp->isdn_device;
-				cmd.command = ISDN_CMD_SETL2;
-				cmd.arg = lp->isdn_channel + (lp->l2_proto << 8);
-				isdn_command(&cmd);
-				cmd.driver = lp->isdn_device;
-				cmd.command = ISDN_CMD_SETL3;
-				cmd.arg = lp->isdn_channel + (lp->l3_proto << 8);
-				isdn_command(&cmd);
-				cmd.driver = lp->isdn_device;
-				cmd.arg = lp->isdn_channel;
-				save_flags(flags);
-				cli();
-				if (!lp->dial) {
-					restore_flags(flags);
-					printk(KERN_WARNING "%s: phone number deleted?\n",
-					       lp->name);
-					isdn_net_hangup(&p->dev);
-					break;
-				}
-				if (!strncmp(lp->dial->num, "LEASED", strlen("LEASED"))) {
-					restore_flags(flags);
-					lp->dialstate = 4;
-					printk(KERN_INFO "%s: Open leased line ...\n", lp->name);
-				} else {
-					if(lp->dialtimeout > 0)
-						if (time_after(jiffies, lp->dialstarted + lp->dialtimeout)) {
-							restore_flags(flags);
-							lp->dialwait_timer = jiffies + lp->dialwait;
-							lp->dialstarted = 0;
-							isdn_net_unreachable(&p->dev, 0, "dial: timed out");
-							isdn_net_hangup(&p->dev);
-							break;
-						}
-
-					sprintf(cmd.parm.setup.phone, "%s", lp->dial->num);
-					/*
-					 * Switch to next number or back to start if at end of list.
-					 */
-					if (!(lp->dial = (isdn_net_phone *) lp->dial->next)) {
-						lp->dial = lp->phone[1];
-						lp->dialretry++;
-
-						if (lp->dialretry > lp->dialmax) {
-							restore_flags(flags);
-							if (lp->dialtimeout == 0) {
-								lp->dialwait_timer = jiffies + lp->dialwait;
-								lp->dialstarted = 0;
-								isdn_net_unreachable(&p->dev, 0, "dial: tried all numbers dialmax times");
-							}
-							isdn_net_hangup(&p->dev);
-							break;
-						}
-					}
-					restore_flags(flags);
-					cmd.driver = lp->isdn_device;
-					cmd.command = ISDN_CMD_DIAL;
-					cmd.parm.setup.si1 = 7;
-					cmd.parm.setup.si2 = 0;
-					sprintf(cmd.parm.setup.eazmsn, "%s",
-						isdn_map_eaz2msn(lp->msn, cmd.driver));
-					i = isdn_dc2minor(lp->isdn_device, lp->isdn_channel);
-					if (i >= 0) {
-						strcpy(dev->num[i], cmd.parm.setup.phone);
-						dev->usage[i] |= ISDN_USAGE_OUTGOING;
-						isdn_info_update();
-					}
-					printk(KERN_INFO "%s: dialing %d %s...\n", lp->name,
-					       lp->dialretry, cmd.parm.setup.phone);
-					lp->dtimer = 0;
-#ifdef ISDN_DEBUG_NET_DIAL
-					printk(KERN_DEBUG "dial: d=%d c=%d\n", lp->isdn_device,
-					       lp->isdn_channel);
-#endif
-					isdn_command(&cmd);
-				}
-				lp->huptimer = 0;
-				lp->outgoing = 1;
-				if (lp->chargeint) {
-					lp->hupflags |= ISDN_HAVECHARGE;
-					lp->hupflags &= ~ISDN_WAITCHARGE;
-				} else {
-					lp->hupflags |= ISDN_WAITCHARGE;
-					lp->hupflags &= ~ISDN_HAVECHARGE;
-				}
-				anymore = 1;
-				lp->dialstate =
-				    (lp->cbdelay &&
-				     (lp->flags & ISDN_NET_CBOUT)) ? 12 : 4;
-				break;
-			case 4:
-				/* Wait for D-Channel-connect.
-				 * If timeout, switch back to state 3.
-				 * Dialmax-handling moved to state 3.
-				 */
-				if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT10)
-					lp->dialstate = 3;
-				anymore = 1;
-				break;
-			case 5:
-				/* Got D-Channel-Connect, send B-Channel-request */
-				cmd.driver = lp->isdn_device;
-				cmd.arg = lp->isdn_channel;
-				cmd.command = ISDN_CMD_ACCEPTB;
-				anymore = 1;
-				lp->dtimer = 0;
-				lp->dialstate++;
-				isdn_command(&cmd);
-				break;
-			case 6:
-				/* Wait for B- or D-Channel-connect. If timeout,
-				 * switch back to state 3.
-				 */
-#ifdef ISDN_DEBUG_NET_DIAL
-				printk(KERN_DEBUG "dialtimer2: %d\n", lp->dtimer);
-#endif
-				if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT10)
-					lp->dialstate = 3;
-				anymore = 1;
-				break;
-			case 7:
-				/* Got incoming Call, setup L2 and L3 protocols,
-				 * then wait for D-Channel-connect
-				 */
-#ifdef ISDN_DEBUG_NET_DIAL
-				printk(KERN_DEBUG "dialtimer4: %d\n", lp->dtimer);
-#endif
-				cmd.driver = lp->isdn_device;
-				cmd.command = ISDN_CMD_SETL2;
-				cmd.arg = lp->isdn_channel + (lp->l2_proto << 8);
-				isdn_command(&cmd);
-				cmd.driver = lp->isdn_device;
-				cmd.command = ISDN_CMD_SETL3;
-				cmd.arg = lp->isdn_channel + (lp->l3_proto << 8);
-				isdn_command(&cmd);
-				if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT15)
-					isdn_net_hangup(&p->dev);
-				else {
-					anymore = 1;
-					lp->dialstate++;
-				}
-				break;
-			case 9:
-				/* Got incoming D-Channel-Connect, send B-Channel-request */
-				cmd.driver = lp->isdn_device;
-				cmd.arg = lp->isdn_channel;
-				cmd.command = ISDN_CMD_ACCEPTB;
-				isdn_command(&cmd);
-				anymore = 1;
-				lp->dtimer = 0;
-				lp->dialstate++;
-				break;
-			case 8:
-			case 10:
-				/*  Wait for B- or D-channel-connect */
-#ifdef ISDN_DEBUG_NET_DIAL
-				printk(KERN_DEBUG "dialtimer4: %d\n", lp->dtimer);
-#endif
-				if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT10)
-					isdn_net_hangup(&p->dev);
-				else
-					anymore = 1;
-				break;
-			case 11:
-				/* Callback Delay */
-				if (lp->dtimer++ > lp->cbdelay)
-					lp->dialstate = 1;
-				anymore = 1;
-				break;
-			case 12:
-				/* Remote does callback. Hangup after cbdelay, then wait for incoming
-				 * call (in state 4).
-				 */
-				if (lp->dtimer++ > lp->cbdelay)
-				{
-					printk(KERN_INFO "%s: hangup waiting for callback ...\n", lp->name);
-					lp->dtimer = 0;
-					lp->dialstate = 4;
-					cmd.driver = lp->isdn_device;
-					cmd.command = ISDN_CMD_HANGUP;
-					cmd.arg = lp->isdn_channel;
-					isdn_command(&cmd);
-					isdn_all_eaz(lp->isdn_device, lp->isdn_channel);
-				}
-				anymore = 1;
-				break;
-			default:
-				printk(KERN_WARNING "isdn_net: Illegal dialstate %d for device %s\n",
-				       lp->dialstate, lp->name);
-		}
-		p = (isdn_net_dev *) p->next;
-	}
-	isdn_timer_ctrl(ISDN_TIMER_NETDIAL, anymore);
-}
-
-/*
- * Perform hangup for a net-interface.
- */
-void
-isdn_net_hangup(struct net_device *d)
-{
-	isdn_net_local *lp = (isdn_net_local *) d->priv;
-	isdn_ctrl cmd;
-#ifdef CONFIG_ISDN_X25
-	struct concap_proto *cprot = lp -> netdev -> cprot;
-	struct concap_proto_ops *pops = cprot ? cprot -> pops : 0;
-#endif
-
-	if (lp->flags & ISDN_NET_CONNECTED) {
-		if (lp->slave != NULL) {
-			isdn_net_local *slp = (isdn_net_local *)lp->slave->priv;
-			if (slp->flags & ISDN_NET_CONNECTED) {
-				printk(KERN_INFO
-					"isdn_net: hang up slave %s before %s\n",
-					slp->name, lp->name);
-				isdn_net_hangup(lp->slave);
-			}
-		}
-		printk(KERN_INFO "isdn_net: local hangup %s\n", lp->name);
-#ifdef CONFIG_ISDN_PPP
-		if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
-			isdn_ppp_free(lp);
-#endif
-		isdn_net_lp_disconnected(lp);
-#ifdef CONFIG_ISDN_X25
-		/* try if there are generic encap protocol
-		   receiver routines and signal the closure of
-		   the link */
-		if( pops && pops -> disconn_ind )
-		  pops -> disconn_ind(cprot);
-#endif /* CONFIG_ISDN_X25 */
-
-		cmd.driver = lp->isdn_device;
-		cmd.command = ISDN_CMD_HANGUP;
-		cmd.arg = lp->isdn_channel;
-		isdn_command(&cmd);
-		printk(KERN_INFO "%s: Chargesum is %d\n", lp->name, lp->charge);
-		isdn_all_eaz(lp->isdn_device, lp->isdn_channel);
-	}
-	isdn_net_unbind_channel(lp);
-}
-
-typedef struct {
-	unsigned short source;
-	unsigned short dest;
-} ip_ports;
-
-static void
-isdn_net_log_skb(struct sk_buff * skb, isdn_net_local * lp)
-{
-	u_char *p = skb->nh.raw; /* hopefully, this was set correctly */
-	unsigned short proto = ntohs(skb->protocol);
-	int data_ofs;
-	ip_ports *ipp;
-	char addinfo[100];
-
-	addinfo[0] = '\0';
-	/* This check stolen from 2.1.72 dev_queue_xmit_nit() */
-	if (skb->nh.raw < skb->data || skb->nh.raw >= skb->tail) {
-		/* fall back to old isdn_net_log_packet method() */
-		char * buf = skb->data;
-
-		printk(KERN_DEBUG "isdn_net: protocol %04x is buggy, dev %s\n", skb->protocol, lp->name);
-		p = buf;
-		proto = ETH_P_IP;
-		switch (lp->p_encap) {
-			case ISDN_NET_ENCAP_IPTYP:
-				proto = ntohs(*(unsigned short *) &buf[0]);
-				p = &buf[2];
-				break;
-			case ISDN_NET_ENCAP_ETHER:
-				proto = ntohs(*(unsigned short *) &buf[12]);
-				p = &buf[14];
-				break;
-			case ISDN_NET_ENCAP_CISCOHDLC:
-				proto = ntohs(*(unsigned short *) &buf[2]);
-				p = &buf[4];
-				break;
-#ifdef CONFIG_ISDN_PPP
-			case ISDN_NET_ENCAP_SYNCPPP:
-				proto = ntohs(skb->protocol);
-				p = &buf[IPPP_MAX_HEADER];
-				break;
-#endif
-		}
-	}
-	data_ofs = ((p[0] & 15) * 4);
-	switch (proto) {
-		case ETH_P_IP:
-			switch (p[9]) {
-				case 1:
-					strcpy(addinfo, " ICMP");
-					break;
-				case 2:
-					strcpy(addinfo, " IGMP");
-					break;
-				case 4:
-					strcpy(addinfo, " IPIP");
-					break;
-				case 6:
-					ipp = (ip_ports *) (&p[data_ofs]);
-					sprintf(addinfo, " TCP, port: %d -> %d", ntohs(ipp->source),
-						ntohs(ipp->dest));
-					break;
-				case 8:
-					strcpy(addinfo, " EGP");
-					break;
-				case 12:
-					strcpy(addinfo, " PUP");
-					break;
-				case 17:
-					ipp = (ip_ports *) (&p[data_ofs]);
-					sprintf(addinfo, " UDP, port: %d -> %d", ntohs(ipp->source),
-						ntohs(ipp->dest));
-					break;
-				case 22:
-					strcpy(addinfo, " IDP");
-					break;
-			}
-			printk(KERN_INFO
-				"OPEN: %d.%d.%d.%d -> %d.%d.%d.%d%s\n",
-
-			       p[12], p[13], p[14], p[15],
-			       p[16], p[17], p[18], p[19],
-			       addinfo);
-			break;
-		case ETH_P_ARP:
-			printk(KERN_INFO
-				"OPEN: ARP %d.%d.%d.%d -> *.*.*.* ?%d.%d.%d.%d\n",
-			       p[14], p[15], p[16], p[17],
-			       p[24], p[25], p[26], p[27]);
-			break;
-	}
-}
-
-/*
- * this function is used to send supervisory data, i.e. data which was
- * not received from the network layer, but e.g. frames from ipppd, CCP
- * reset frames etc.
- */
-void isdn_net_write_super(isdn_net_local *lp, struct sk_buff *skb)
-{
-	if (in_irq()) {
-		// we can't grab the lock from irq context, 
-		// so we just queue the packet
-		skb_queue_tail(&lp->super_tx_queue, skb); 
-		queue_task(&lp->tqueue, &tq_immediate);
-		mark_bh(IMMEDIATE_BH);
-		return;
-	}
-
-	spin_lock_bh(&lp->xmit_lock);
-	if (!isdn_net_lp_busy(lp)) {
-		isdn_net_writebuf_skb(lp, skb);
-	} else {
-		skb_queue_tail(&lp->super_tx_queue, skb);
-	}
-	spin_unlock_bh(&lp->xmit_lock);
-}
-
-/*
- * called from tq_immediate
- */
-static void isdn_net_softint(void *private)
-{
-	isdn_net_local *lp = private;
-	struct sk_buff *skb;
-
-	spin_lock_bh(&lp->xmit_lock);
-	while (!isdn_net_lp_busy(lp)) {
-		skb = skb_dequeue(&lp->super_tx_queue);
-		if (!skb)
-			break;
-		isdn_net_writebuf_skb(lp, skb);                                
-	}
-	spin_unlock_bh(&lp->xmit_lock);
-}
-
-/* 
- * all frames sent from the (net) LL to a HL driver should go via this function
- * it's serialized by the caller holding the lp->xmit_lock spinlock
- */
-void isdn_net_writebuf_skb(isdn_net_local *lp, struct sk_buff *skb)
-{
-	int ret;
-	int len = skb->len;     /* save len */
-
-	/* before obtaining the lock the caller should have checked that
-	   the lp isn't busy */
-	if (isdn_net_lp_busy(lp)) {
-		printk("isdn BUG at %s:%d!\n", __FILE__, __LINE__);
-		goto error;
-	}
-
-	if (!(lp->flags & ISDN_NET_CONNECTED)) {
-		printk("isdn BUG at %s:%d!\n", __FILE__, __LINE__);
-		goto error;
-	}
-	ret = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, 1, skb);
-	if (ret != len) {
-		/* we should never get here */
-		printk(KERN_WARNING "%s: HL driver queue full\n", lp->name);
-		goto error;
-	}
-	
-	lp->transcount += len;
-	isdn_net_inc_frame_cnt(lp);
-	return;
-
- error:
-	dev_kfree_skb(skb);
-	lp->stats.tx_errors++;
-
-}
-
-
-/*
- *  Helper function for isdn_net_start_xmit.
- *  When called, the connection is already established.
- *  Based on cps-calculation, check if device is overloaded.
- *  If so, and if a slave exists, trigger dialing for it.
- *  If any slave is online, deliver packets using a simple round robin
- *  scheme.
- *
- *  Return: 0 on success, !0 on failure.
- */
-
-static int
-isdn_net_xmit(struct net_device *ndev, struct sk_buff *skb)
-{
-	isdn_net_dev *nd;
-	isdn_net_local *slp;
-	isdn_net_local *lp = (isdn_net_local *) ndev->priv;
-	int retv = 0;
-
-	if (((isdn_net_local *) (ndev->priv))->master) {
-		printk("isdn BUG at %s:%d!\n", __FILE__, __LINE__);
-		dev_kfree_skb(skb);
-		return 0;
-	}
-
-	/* For the other encaps the header has already been built */
-#ifdef CONFIG_ISDN_PPP
-	if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) {
-		return isdn_ppp_xmit(skb, ndev);
-	}
-#endif
-	nd = ((isdn_net_local *) ndev->priv)->netdev;
-	lp = isdn_net_get_locked_lp(nd);
-	if (!lp) {
-		printk(KERN_WARNING "%s: all channels busy - requeuing!\n", ndev->name);
-		return 1;
-	}
-	/* we have our lp locked from now on */
-
-	/* Reset hangup-timeout */
-	lp->huptimer = 0; // FIXME?
-	isdn_net_writebuf_skb(lp, skb);
-	spin_unlock_bh(&lp->xmit_lock);
-
-	/* the following stuff is here for backwards compatibility.
-	 * in future, start-up and hangup of slaves (based on current load)
-	 * should move to userspace and get based on an overall cps
-	 * calculation
-	 */
-	if (lp->cps > lp->triggercps) {
-		if (lp->slave) {
-			if (!lp->sqfull) {
-				/* First time overload: set timestamp only */
-				lp->sqfull = 1;
-				lp->sqfull_stamp = jiffies;
-			} else {
-				/* subsequent overload: if slavedelay exceeded, start dialing */
-				if (time_after(jiffies, lp->sqfull_stamp + lp->slavedelay)) {
-					slp = lp->slave->priv;
-					if (!(slp->flags & ISDN_NET_CONNECTED)) {
-						isdn_net_force_dial_lp((isdn_net_local *) lp->slave->priv);
-					}
-				}
-			}
-		}
-	} else {
-		if (lp->sqfull && time_after(jiffies, lp->sqfull_stamp + lp->slavedelay + (10 * HZ))) {
-			lp->sqfull = 0;
-		}
-		/* this is a hack to allow auto-hangup for slaves on moderate loads */
-		nd->queue = nd->local;
-	}
-
-	return retv;
-
-}
-
-static void
-isdn_net_adjust_hdr(struct sk_buff *skb, struct net_device *dev)
-{
-	isdn_net_local *lp = (isdn_net_local *) dev->priv;
-	if (!skb)
-		return;
-	if (lp->p_encap == ISDN_NET_ENCAP_ETHER) {
-		int pullsize = (ulong)skb->nh.raw - (ulong)skb->data - ETH_HLEN;
-		if (pullsize > 0) {
-			printk(KERN_DEBUG "isdn_net: Pull junk %d\n", pullsize);
-			skb_pull(skb, pullsize);
-		}
-	}
-}
-
-
-void isdn_net_tx_timeout(struct net_device * ndev)
-{
-	isdn_net_local *lp = (isdn_net_local *) ndev->priv;
-
-	printk(KERN_WARNING "isdn_tx_timeout dev %s dialstate %d\n", ndev->name, lp->dialstate);
-	if (!lp->dialstate){
-		lp->stats.tx_errors++;
-                /*
-		 * There is a certain probability that this currently
-		 * works at all because if we always wake up the interface,
-		 * then upper layer will try to send the next packet
-		 * immediately. And then, the old clean_up logic in the
-		 * driver will hopefully continue to work as it used to do.
-		 *
-		 * This is rather primitive right know, we better should
-		 * clean internal queues here, in particular for multilink and
-		 * ppp, and reset HL driver's channel, too.   --HE
-		 *
-		 * actually, this may not matter at all, because ISDN hardware
-		 * should not see transmitter hangs at all IMO
-		 * changed KERN_DEBUG to KERN_WARNING to find out if this is 
-		 * ever called   --KG
-		 */
-	}
-	ndev->trans_start = jiffies;
-	netif_wake_queue(ndev);
-}
-
-/*
- * Try sending a packet.
- * If this interface isn't connected to a ISDN-Channel, find a free channel,
- * and start dialing.
- */
-static int
-isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev)
-{
-	isdn_net_local *lp = (isdn_net_local *) ndev->priv;
-#ifdef CONFIG_ISDN_X25
-	struct concap_proto * cprot = lp -> netdev -> cprot;
-#endif
-#ifdef CONFIG_ISDN_X25
-/* At this point hard_start_xmit() passes control to the encapsulation
-   protocol (if present).
-   For X.25 auto-dialing is completly bypassed because:
-   - It does not conform with the semantics of a reliable datalink
-     service as needed by X.25 PLP.
-   - I don't want that the interface starts dialing when the network layer
-     sends a message which requests to disconnect the lapb link (or if it
-     sends any other message not resulting in data transmission).
-   Instead, dialing will be initiated by the encapsulation protocol entity
-   when a dl_establish request is received from the upper layer.
-*/
-	if( cprot ) {
-		int ret = cprot -> pops -> encap_and_xmit ( cprot , skb);
-		if(ret) netif_stop_queue(ndev);
-		return ret;
-	} else
-#endif
-	/* auto-dialing xmit function */
-	{
-#ifdef ISDN_DEBUG_NET_DUMP
-		u_char *buf;
-#endif
-		isdn_net_adjust_hdr(skb, ndev);
-#ifdef ISDN_DEBUG_NET_DUMP
-		buf = skb->data;
-		isdn_dumppkt("S:", buf, skb->len, 40);
-#endif
-
-		if (!(lp->flags & ISDN_NET_CONNECTED)) {
-			int chi;
-			/* only do autodial if allowed by config */
-			if (!(ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_AUTO)) {
-				isdn_net_unreachable(ndev, skb, "dial rejected: interface not in dialmode `auto'");
-				dev_kfree_skb(skb);
-				return 0;
-			}
-			if (lp->phone[1]) {
-				ulong flags;
-				save_flags(flags);
-				cli();
-
-				if(lp->dialwait_timer <= 0)
-					if(lp->dialstarted > 0 && lp->dialtimeout > 0 && time_before(jiffies, lp->dialstarted + lp->dialtimeout + lp->dialwait))
-						lp->dialwait_timer = lp->dialstarted + lp->dialtimeout + lp->dialwait;
-
-				if(lp->dialwait_timer > 0) {
-					if(time_before(jiffies, lp->dialwait_timer)) {
-						isdn_net_unreachable(ndev, skb, "dial rejected: retry-time not reached");
-						dev_kfree_skb(skb);
-						restore_flags(flags);
-						return 0;
-					} else
-						lp->dialwait_timer = 0;
-				}
-				/* Grab a free ISDN-Channel */
-				if (((chi =
-				     isdn_get_free_channel(
-					 		ISDN_USAGE_NET,
-							lp->l2_proto,
-							lp->l3_proto,
-							lp->pre_device,
-						 	lp->pre_channel,
-							lp->msn)
-							) < 0) &&
-					((chi =
-				     isdn_get_free_channel(
-					 		ISDN_USAGE_NET,
-							lp->l2_proto,
-							lp->l3_proto,
-							lp->pre_device,
-							lp->pre_channel^1,
-							lp->msn)
-							) < 0)) {
-					restore_flags(flags);
-					isdn_net_unreachable(ndev, skb,
-							   "No channel");
-					dev_kfree_skb(skb);
-					return 0;
-				}
-				/* Log packet, which triggered dialing */
-				if (dev->net_verbose)
-					isdn_net_log_skb(skb, lp);
-				lp->dialstate = 1;
-				/* Connect interface with channel */
-				isdn_net_bind_channel(lp, chi);
-#ifdef CONFIG_ISDN_PPP
-				if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) {
-					/* no 'first_skb' handling for syncPPP */
-					if (isdn_ppp_bind(lp) < 0) {
-						dev_kfree_skb(skb);
-						isdn_net_unbind_channel(lp);
-						restore_flags(flags);
-						return 0;	/* STN (skb to nirvana) ;) */
-					}
-					restore_flags(flags);
-					isdn_net_dial();	/* Initiate dialing */
-					netif_stop_queue(ndev);
-					return 1;	/* let upper layer requeue skb packet */
-				}
-#endif
-				/* Initiate dialing */
-				restore_flags(flags);
-				isdn_net_dial();
-				isdn_net_device_stop_queue(lp);
-				return 1;
-			} else {
-				isdn_net_unreachable(ndev, skb,
-						     "No phone number");
-				dev_kfree_skb(skb);
-				return 0;
-			}
-		} else {
-			/* Device is connected to an ISDN channel */ 
-			ndev->trans_start = jiffies;
-			if (!lp->dialstate) {
-				/* ISDN connection is established, try sending */
-				int ret;
-				ret = (isdn_net_xmit(ndev, skb));
-				if(ret) netif_stop_queue(ndev);
-				return ret;
-			} else
-				netif_stop_queue(ndev);
-		}
-	}
-	return 1;
-}
-
-/*
- * Shutdown a net-interface.
- */
-static int
-isdn_net_close(struct net_device *dev)
-{
-	struct net_device *p;
-#ifdef CONFIG_ISDN_X25
-	struct concap_proto * cprot =
-		( (isdn_net_local *) dev->priv ) -> netdev -> cprot;
-	/* printk(KERN_DEBUG "isdn_net_close %s\n" , dev-> name ); */
-#endif
-
-#ifdef CONFIG_ISDN_X25
-	if( cprot && cprot -> pops ) cprot -> pops -> close( cprot );
-#endif
-	netif_stop_queue(dev);
-	if ((p = (((isdn_net_local *) dev->priv)->slave))) {
-		/* If this interface has slaves, stop them also */
-		while (p) {
-#ifdef CONFIG_ISDN_X25
-			cprot = ( (isdn_net_local *) p->priv )
-				-> netdev -> cprot;
-			if( cprot && cprot -> pops )
-				cprot -> pops -> close( cprot );
-#endif
-			isdn_net_hangup(p);
-			p = (((isdn_net_local *) p->priv)->slave);
-		}
-	}
-	isdn_net_hangup(dev);
-	isdn_MOD_DEC_USE_COUNT();
-	return 0;
-}
-
-/*
- * Get statistics
- */
-static struct net_device_stats *
-isdn_net_get_stats(struct net_device *dev)
-{
-	isdn_net_local *lp = (isdn_net_local *) dev->priv;
-	return &lp->stats;
-}
-
-/*      This is simply a copy from std. eth.c EXCEPT we pull ETH_HLEN
- *      instead of dev->hard_header_len off. This is done because the
- *      lowlevel-driver has already pulled off its stuff when we get
- *      here and this routine only gets called with p_encap == ETHER.
- *      Determine the packet's protocol ID. The rule here is that we
- *      assume 802.3 if the type field is short enough to be a length.
- *      This is normal practice and works for any 'now in use' protocol.
- */
-
-static unsigned short
-isdn_net_type_trans(struct sk_buff *skb, struct net_device *dev)
-{
-	struct ethhdr *eth;
-	unsigned char *rawp;
-
-	skb->mac.raw = skb->data;
-	skb_pull(skb, ETH_HLEN);
-	eth = skb->mac.ethernet;
-
-	if (*eth->h_dest & 1) {
-		if (memcmp(eth->h_dest, dev->broadcast, ETH_ALEN) == 0)
-			skb->pkt_type = PACKET_BROADCAST;
-		else
-			skb->pkt_type = PACKET_MULTICAST;
-	}
-	/*
-	 *      This ALLMULTI check should be redundant by 1.4
-	 *      so don't forget to remove it.
-	 */
-
-	else if (dev->flags & (IFF_PROMISC /*| IFF_ALLMULTI*/)) {
-		if (memcmp(eth->h_dest, dev->dev_addr, ETH_ALEN))
-			skb->pkt_type = PACKET_OTHERHOST;
-	}
-	if (ntohs(eth->h_proto) >= 1536)
-		return eth->h_proto;
-
-	rawp = skb->data;
-
-	/*
-	 *      This is a magic hack to spot IPX packets. Older Novell breaks
-	 *      the protocol design and runs IPX over 802.3 without an 802.2 LLC
-	 *      layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This
-	 *      won't work for fault tolerant netware but does for the rest.
-	 */
-	if (*(unsigned short *) rawp == 0xFFFF)
-		return htons(ETH_P_802_3);
-	/*
-	 *      Real 802.2 LLC
-	 */
-	return htons(ETH_P_802_2);
-}
-
-
-/* 
- * CISCO HDLC keepalive specific stuff
- */
-static struct sk_buff*
-isdn_net_ciscohdlck_alloc_skb(isdn_net_local *lp, int len)
-{
-	unsigned short hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen;
-	struct sk_buff *skb;
-
-	skb = alloc_skb(hl + len, GFP_ATOMIC);
-	if (!skb) {
-		printk("isdn out of mem at %s:%d!\n", __FILE__, __LINE__);
-		return 0;
-	}
-	skb_reserve(skb, hl);
-	return skb;
-}
-
-/* cisco hdlck device private ioctls */
-int
-isdn_ciscohdlck_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
-{
-	isdn_net_local *lp = (isdn_net_local *) dev->priv;
-	unsigned long len = 0;
-	unsigned long expires = 0;
-	int tmp = 0;
-	int period = lp->cisco_keepalive_period;
-	char debserint = lp->cisco_debserint;
-	int rc = 0;
-
-	if (lp->p_encap != ISDN_NET_ENCAP_CISCOHDLCK)
-		return -EINVAL;
-
-	switch (cmd) {
-		/* get/set keepalive period */
-		case SIOCGKEEPPERIOD:
-			len = (unsigned long)sizeof(lp->cisco_keepalive_period);
-			if (copy_to_user((char *)ifr->ifr_ifru.ifru_data,
-				(int *)&lp->cisco_keepalive_period, len))
-				rc = -EFAULT;
-			break;
-		case SIOCSKEEPPERIOD:
-			tmp = lp->cisco_keepalive_period;
-			len = (unsigned long)sizeof(lp->cisco_keepalive_period);
-			if (copy_from_user((int *)&period,
-				(char *)ifr->ifr_ifru.ifru_data, len))
-				rc = -EFAULT;
-			if ((period > 0) && (period <= 32767))
-				lp->cisco_keepalive_period = period;
-			else
-				rc = -EINVAL;
-			if (!rc && (tmp != lp->cisco_keepalive_period)) {
-				expires = (unsigned long)(jiffies +
-					lp->cisco_keepalive_period * HZ);
-				mod_timer(&lp->cisco_timer, expires);
-				printk(KERN_INFO "%s: Keepalive period set "
-					"to %d seconds.\n",
-					lp->name, lp->cisco_keepalive_period);
-			}
-			break;
-
-		/* get/set debugging */
-		case SIOCGDEBSERINT:
-			len = (unsigned long)sizeof(lp->cisco_debserint);
-			if (copy_to_user((char *)ifr->ifr_ifru.ifru_data,
-				(char *)&lp->cisco_debserint, len))
-				rc = -EFAULT;
-			break;
-		case SIOCSDEBSERINT:
-			len = (unsigned long)sizeof(lp->cisco_debserint);
-			if (copy_from_user((char *)&debserint,
-				(char *)ifr->ifr_ifru.ifru_data, len))
-				rc = -EFAULT;
-			if ((debserint >= 0) && (debserint <= 64))
-				lp->cisco_debserint = debserint;
-			else
-				rc = -EINVAL;
-			break;
-
-		default:
-			rc = -EINVAL;
-			break;
-	}
-	return (rc);
-}
-
-/* called via cisco_timer.function */
-static void
-isdn_net_ciscohdlck_slarp_send_keepalive(unsigned long data)
-{
-	isdn_net_local *lp = (isdn_net_local *) data;
-	struct sk_buff *skb;
-	unsigned char *p;
-	unsigned long last_cisco_myseq = lp->cisco_myseq;
-	int myseq_diff = 0;
-
-	if (!(lp->flags & ISDN_NET_CONNECTED) || lp->dialstate) {
-		printk("isdn BUG at %s:%d!\n", __FILE__, __LINE__);
-		return;
-	}
-	lp->cisco_myseq++;
-
-	myseq_diff = (lp->cisco_myseq - lp->cisco_mineseen);
-	if ((lp->cisco_line_state) && ((myseq_diff >= 3)||(myseq_diff <= -3))) {
-		/* line up -> down */
-		lp->cisco_line_state = 0;
-		printk (KERN_WARNING
-				"UPDOWN: Line protocol on Interface %s,"
-				" changed state to down\n", lp->name);
-		/* should stop routing higher-level data accross */
-	} else if ((!lp->cisco_line_state) &&
-		(myseq_diff >= 0) && (myseq_diff <= 2)) {
-		/* line down -> up */
-		lp->cisco_line_state = 1;
-		printk (KERN_WARNING
-				"UPDOWN: Line protocol on Interface %s,"
-				" changed state to up\n", lp->name);
-		/* restart routing higher-level data accross */
-	}
-
-	if (lp->cisco_debserint)
-		printk (KERN_DEBUG "%s: HDLC "
-			"myseq %lu, mineseen %lu%c, yourseen %lu, %s\n",
-			lp->name, last_cisco_myseq, lp->cisco_mineseen,
-			((last_cisco_myseq == lp->cisco_mineseen) ? '*' : 040),
-			lp->cisco_yourseq,
-			((lp->cisco_line_state) ? "line up" : "line down"));
-
-	skb = isdn_net_ciscohdlck_alloc_skb(lp, 4 + 14);
-	if (!skb)
-		return;
-
-	p = skb_put(skb, 4 + 14);
-
-	/* cisco header */
-	p += put_u8 (p, CISCO_ADDR_UNICAST);
-	p += put_u8 (p, CISCO_CTRL);
-	p += put_u16(p, CISCO_TYPE_SLARP);
-
-	/* slarp keepalive */
-	p += put_u32(p, CISCO_SLARP_KEEPALIVE);
-	p += put_u32(p, lp->cisco_myseq);
-	p += put_u32(p, lp->cisco_yourseq);
-	p += put_u16(p, 0xffff); // reliablity, always 0xffff
-
-	isdn_net_write_super(lp, skb);
-
-	lp->cisco_timer.expires = jiffies + lp->cisco_keepalive_period * HZ;
-	
-	add_timer(&lp->cisco_timer);
-}
-
-static void
-isdn_net_ciscohdlck_slarp_send_request(isdn_net_local *lp)
-{
-	struct sk_buff *skb;
-	unsigned char *p;
-
-	skb = isdn_net_ciscohdlck_alloc_skb(lp, 4 + 14);
-	if (!skb)
-		return;
-
-	p = skb_put(skb, 4 + 14);
-
-	/* cisco header */
-	p += put_u8 (p, CISCO_ADDR_UNICAST);
-	p += put_u8 (p, CISCO_CTRL);
-	p += put_u16(p, CISCO_TYPE_SLARP);
-
-	/* slarp request */
-	p += put_u32(p, CISCO_SLARP_REQUEST);
-	p += put_u32(p, 0); // address
-	p += put_u32(p, 0); // netmask
-	p += put_u16(p, 0); // unused
-
-	isdn_net_write_super(lp, skb);
-}
-
-static void 
-isdn_net_ciscohdlck_connected(isdn_net_local *lp)
-{
-	lp->cisco_myseq = 0;
-	lp->cisco_mineseen = 0;
-	lp->cisco_yourseq = 0;
-	lp->cisco_keepalive_period = ISDN_TIMER_KEEPINT;
-	lp->cisco_last_slarp_in = 0;
-	lp->cisco_line_state = 0;
-	lp->cisco_debserint = 0;
-
-	/* send slarp request because interface/seq.no.s reset */
-	isdn_net_ciscohdlck_slarp_send_request(lp);
-
-	init_timer(&lp->cisco_timer);
-	lp->cisco_timer.data = (unsigned long) lp;
-	lp->cisco_timer.function = isdn_net_ciscohdlck_slarp_send_keepalive;
-	lp->cisco_timer.expires = jiffies + lp->cisco_keepalive_period * HZ;
-	add_timer(&lp->cisco_timer);
-}
-
-static void 
-isdn_net_ciscohdlck_disconnected(isdn_net_local *lp)
-{
-	del_timer(&lp->cisco_timer);
-}
-
-static void
-isdn_net_ciscohdlck_slarp_send_reply(isdn_net_local *lp)
-{
-	struct sk_buff *skb;
-	unsigned char *p;
-	struct in_device *in_dev = NULL;
-	u32 addr = 0;		/* local ipv4 address */
-	u32 mask = 0;		/* local netmask */
-
-	if ((in_dev = lp->netdev->dev.ip_ptr) != NULL) {
-		/* take primary(first) address of interface */
-		struct in_ifaddr *ifa = in_dev->ifa_list;
-		if (ifa != NULL) {
-			addr = ifa->ifa_local;
-			mask = ifa->ifa_mask;
-		}
-	}
-
-	skb = isdn_net_ciscohdlck_alloc_skb(lp, 4 + 14);
-	if (!skb)
-		return;
-
-	p = skb_put(skb, 4 + 14);
-
-	/* cisco header */
-	p += put_u8 (p, CISCO_ADDR_UNICAST);
-	p += put_u8 (p, CISCO_CTRL);
-	p += put_u16(p, CISCO_TYPE_SLARP);
-
-	/* slarp reply, send own ip/netmask; if values are nonsense remote
-	 * should think we are unable to provide it with an address via SLARP */
-	p += put_u32(p, CISCO_SLARP_REPLY);
-	p += put_u32(p, addr);	// address
-	p += put_u32(p, mask);	// netmask
-	p += put_u16(p, 0);	// unused
-
-	isdn_net_write_super(lp, skb);
-}
-
-static void
-isdn_net_ciscohdlck_slarp_in(isdn_net_local *lp, struct sk_buff *skb)
-{
-	unsigned char *p;
-	int period;
-	u32 code;
-	u32 my_seq, addr;
-	u32 your_seq, mask;
-	u32 local;
-	u16 unused;
-
-	if (skb->len < 14)
-		return;
-
-	p = skb->data;
-	p += get_u32(p, &code);
-	
-	switch (code) {
-	case CISCO_SLARP_REQUEST:
-		lp->cisco_yourseq = 0;
-		isdn_net_ciscohdlck_slarp_send_reply(lp);
-		break;
-	case CISCO_SLARP_REPLY:
-		addr = ntohl(*(u32 *)p);
-		mask = ntohl(*(u32 *)(p+4));
-		if (mask != 0xfffffffc)
-			goto slarp_reply_out;
-		if ((addr & 3) == 0 || (addr & 3) == 3)
-			goto slarp_reply_out;
-		local = addr ^ 3;
-		printk(KERN_INFO "%s: got slarp reply: "
-			"remote ip: %d.%d.%d.%d, "
-			"local ip: %d.%d.%d.%d "
-			"mask: %d.%d.%d.%d\n",
-		       lp->name,
-		       HIPQUAD(addr),
-		       HIPQUAD(local),
-		       HIPQUAD(mask));
-		break;
-  slarp_reply_out:
-		 printk(KERN_INFO "%s: got invalid slarp "
-				 "reply (%d.%d.%d.%d/%d.%d.%d.%d) "
-				 "- ignored\n", lp->name,
-				 HIPQUAD(addr), HIPQUAD(mask));
-		break;
-	case CISCO_SLARP_KEEPALIVE:
-		period = (int)((jiffies - lp->cisco_last_slarp_in
-				+ HZ/2 - 1) / HZ);
-		if (lp->cisco_debserint &&
-				(period != lp->cisco_keepalive_period) &&
-				lp->cisco_last_slarp_in) {
-			printk(KERN_DEBUG "%s: Keepalive period mismatch - "
-				"is %d but should be %d.\n",
-				lp->name, period, lp->cisco_keepalive_period);
-		}
-		lp->cisco_last_slarp_in = jiffies;
-		p += get_u32(p, &my_seq);
-		p += get_u32(p, &your_seq);
-		p += get_u16(p, &unused);
-		lp->cisco_yourseq = my_seq;
-		lp->cisco_mineseen = your_seq;
-		break;
-	}
-}
-
-static void
-isdn_net_ciscohdlck_receive(isdn_net_local *lp, struct sk_buff *skb)
-{
-	unsigned char *p;
- 	u8 addr;
- 	u8 ctrl;
- 	u16 type;
-	
-	if (skb->len < 4)
-		goto out_free;
-
-	p = skb->data;
-	p += get_u8 (p, &addr);
-	p += get_u8 (p, &ctrl);
-	p += get_u16(p, &type);
-	skb_pull(skb, 4);
-	
-	if (addr != CISCO_ADDR_UNICAST && addr != CISCO_ADDR_BROADCAST) {
-		printk(KERN_WARNING "%s: Unknown Cisco addr 0x%02x\n",
-		       lp->name, addr);
-		goto out_free;
-	}
-	if (ctrl != CISCO_CTRL) {
-		printk(KERN_WARNING "%s: Unknown Cisco ctrl 0x%02x\n",
-		       lp->name, ctrl);
-		goto out_free;
-	}
-
-	switch (type) {
-	case CISCO_TYPE_INET:
-		skb->protocol = htons(ETH_P_IP);
-		netif_rx(skb);
-		break;
-	case CISCO_TYPE_SLARP:
-		isdn_net_ciscohdlck_slarp_in(lp, skb);
-		goto out_free;
-	case CISCO_TYPE_CDP:
-		if (lp->cisco_debserint)
-			printk(KERN_DEBUG "%s: Received CDP packet. use "
-				"\"no cdp enable\" on cisco.\n", lp->name);
-		goto out_free;
-	default:
-		printk(KERN_WARNING "%s: Unknown Cisco type 0x%04x\n",
-		       lp->name, type);
-		goto out_free;
-	}
-	return;
-
- out_free:
-	kfree_skb(skb);
-}
-
-/*
- * Got a packet from ISDN-Channel.
- */
-static void
-isdn_net_receive(struct net_device *ndev, struct sk_buff *skb)
-{
-	isdn_net_local *lp = (isdn_net_local *) ndev->priv;
-	isdn_net_local *olp = lp;	/* original 'lp' */
-#ifdef CONFIG_ISDN_PPP
-	int proto = PPP_PROTOCOL(skb->data);
-#endif
-#ifdef CONFIG_ISDN_X25
-	struct concap_proto *cprot = lp -> netdev -> cprot;
-#endif
-	lp->transcount += skb->len;
-
-	lp->stats.rx_packets++;
-	lp->stats.rx_bytes += skb->len;
-	if (lp->master) {
-		/* Bundling: If device is a slave-device, deliver to master, also
-		 * handle master's statistics and hangup-timeout
-		 */
-		ndev = lp->master;
-		lp = (isdn_net_local *) ndev->priv;
-		lp->stats.rx_packets++;
-		lp->stats.rx_bytes += skb->len;
-	}
-	skb->dev = ndev;
-	skb->pkt_type = PACKET_HOST;
-	skb->mac.raw = skb->data;
-#ifdef ISDN_DEBUG_NET_DUMP
-	isdn_dumppkt("R:", skb->data, skb->len, 40);
-#endif
-	switch (lp->p_encap) {
-		case ISDN_NET_ENCAP_ETHER:
-			/* Ethernet over ISDN */
-			olp->huptimer = 0;
-			lp->huptimer = 0;
-			skb->protocol = isdn_net_type_trans(skb, ndev);
-			break;
-		case ISDN_NET_ENCAP_UIHDLC:
-			/* HDLC with UI-frame (for ispa with -h1 option) */
-			olp->huptimer = 0;
-			lp->huptimer = 0;
-			skb_pull(skb, 2);
-			/* Fall through */
-		case ISDN_NET_ENCAP_RAWIP:
-			/* RAW-IP without MAC-Header */
-			olp->huptimer = 0;
-			lp->huptimer = 0;
-			skb->protocol = htons(ETH_P_IP);
-			break;
-		case ISDN_NET_ENCAP_CISCOHDLCK:
-			isdn_net_ciscohdlck_receive(lp, skb);
-			return;
-		case ISDN_NET_ENCAP_CISCOHDLC:
-			/* CISCO-HDLC IP with type field and  fake I-frame-header */
-			skb_pull(skb, 2);
-			/* Fall through */
-		case ISDN_NET_ENCAP_IPTYP:
-			/* IP with type field */
-			olp->huptimer = 0;
-			lp->huptimer = 0;
-			skb->protocol = *(unsigned short *) &(skb->data[0]);
-			skb_pull(skb, 2);
-			if (*(unsigned short *) skb->data == 0xFFFF)
-				skb->protocol = htons(ETH_P_802_3);
-			break;
-#ifdef CONFIG_ISDN_PPP
-		case ISDN_NET_ENCAP_SYNCPPP:
-			/*
-			 * If encapsulation is syncppp, don't reset
-			 * huptimer on LCP packets.
-			 */
-			if (proto != PPP_LCP) {
-				olp->huptimer = 0;
-				lp->huptimer = 0;
-			}
-			isdn_ppp_receive(lp->netdev, olp, skb);
-			return;
-#endif
-
-		default:
-#ifdef CONFIG_ISDN_X25
-		  /* try if there are generic sync_device receiver routines */
-			if(cprot) if(cprot -> pops)
-				if( cprot -> pops -> data_ind){
-					cprot -> pops -> data_ind(cprot,skb);
-					return;
-				};
-#endif /* CONFIG_ISDN_X25 */
-			printk(KERN_WARNING "%s: unknown encapsulation, dropping\n",
-			       lp->name);
-			kfree_skb(skb);
-			return;
-	}
-
-	netif_rx(skb);
-	return;
-}
-
-/*
- * A packet arrived via ISDN. Search interface-chain for a corresponding
- * interface. If found, deliver packet to receiver-function and return 1,
- * else return 0.
- */
-int
-isdn_net_rcv_skb(int idx, struct sk_buff *skb)
-{
-	isdn_net_dev *p = dev->rx_netdev[idx];
-
-	if (p) {
-		isdn_net_local *lp = p->local;
-		if ((lp->flags & ISDN_NET_CONNECTED) &&
-		    (!lp->dialstate)) {
-			isdn_net_receive(&p->dev, skb);
-			return 1;
-		}
-	}
-	return 0;
-}
-
-static int
-my_eth_header(struct sk_buff *skb, struct net_device *dev, unsigned short type,
-	      void *daddr, void *saddr, unsigned len)
-{
-	struct ethhdr *eth = (struct ethhdr *) skb_push(skb, ETH_HLEN);
-
-	/*
-	 * Set the protocol type. For a packet of type ETH_P_802_3 we
-	 * put the length here instead. It is up to the 802.2 layer to
-	 * carry protocol information.
-	 */
-
-	if (type != ETH_P_802_3)
-		eth->h_proto = htons(type);
-	else
-		eth->h_proto = htons(len);
-
-	/*
-	 * Set the source hardware address.
-	 */
-	if (saddr)
-		memcpy(eth->h_source, saddr, dev->addr_len);
-	else
-		memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
-
-	/*
-	 * Anyway, the loopback-device should never use this function...
-	 */
-
-	if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) {
-		memset(eth->h_dest, 0, dev->addr_len);
-		return ETH_HLEN /*(dev->hard_header_len)*/;
-	}
-	if (daddr) {
-		memcpy(eth->h_dest, daddr, dev->addr_len);
-		return ETH_HLEN /*dev->hard_header_len*/;
-	}
-	return -ETH_HLEN /*dev->hard_header_len*/;
-}
-
-/*
- *  build an header
- *  depends on encaps that is being used.
- */
-
-static int
-isdn_net_header(struct sk_buff *skb, struct net_device *dev, unsigned short type,
-		void *daddr, void *saddr, unsigned plen)
-{
-	isdn_net_local *lp = dev->priv;
-	unsigned char *p;
-	ushort len = 0;
-
-	switch (lp->p_encap) {
-		case ISDN_NET_ENCAP_ETHER:
-			len = my_eth_header(skb, dev, type, daddr, saddr, plen);
-			break;
-#ifdef CONFIG_ISDN_PPP
-		case ISDN_NET_ENCAP_SYNCPPP:
-			/* stick on a fake header to keep fragmentation code happy. */
-			len = IPPP_MAX_HEADER;
-			skb_push(skb,len);
-			break;
-#endif
-		case ISDN_NET_ENCAP_RAWIP:
-			printk(KERN_WARNING "isdn_net_header called with RAW_IP!\n");
-			len = 0;
-			break;
-		case ISDN_NET_ENCAP_IPTYP:
-			/* ethernet type field */
-			*((ushort *) skb_push(skb, 2)) = htons(type);
-			len = 2;
-			break;
-		case ISDN_NET_ENCAP_UIHDLC:
-			/* HDLC with UI-Frames (for ispa with -h1 option) */
-			*((ushort *) skb_push(skb, 2)) = htons(0x0103);
-			len = 2;
-			break;
-		case ISDN_NET_ENCAP_CISCOHDLC:
-		case ISDN_NET_ENCAP_CISCOHDLCK:
-			p = skb_push(skb, 4);
-			p += put_u8 (p, CISCO_ADDR_UNICAST);
-			p += put_u8 (p, CISCO_CTRL);
-			p += put_u16(p, type);
-			len = 4;
-			break;
-#ifdef CONFIG_ISDN_X25
-		default:
-		  /* try if there are generic concap protocol routines */
-			if( lp-> netdev -> cprot ){
-				printk(KERN_WARNING "isdn_net_header called with concap_proto!\n");
-				len = 0;
-				break;
-			}
-			break;
-#endif /* CONFIG_ISDN_X25 */
-	}
-	return len;
-}
-
-/* We don't need to send arp, because we have point-to-point connections. */
-static int
-isdn_net_rebuild_header(struct sk_buff *skb)
-{
-	struct net_device *dev = skb->dev;
-	isdn_net_local *lp = dev->priv;
-	int ret = 0;
-
-	if (lp->p_encap == ISDN_NET_ENCAP_ETHER) {
-		struct ethhdr *eth = (struct ethhdr *) skb->data;
-
-		/*
-		 *      Only ARP/IP is currently supported
-		 */
-
-		if (eth->h_proto != htons(ETH_P_IP)) {
-			printk(KERN_WARNING
-			       "isdn_net: %s don't know how to resolve type %d addresses?\n",
-			       dev->name, (int) eth->h_proto);
-			memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
-			return 0;
-		}
-		/*
-		 *      Try to get ARP to resolve the header.
-		 */
-#ifdef CONFIG_INET
-		ret = arp_find(eth->h_dest, skb);
-#endif
-	}
-	return ret;
-}
-
-/*
- * Interface-setup. (just after registering a new interface)
- */
-static int
-isdn_net_init(struct net_device *ndev)
-{
-	ushort max_hlhdr_len = 0;
-	isdn_net_local *lp = (isdn_net_local *) ndev->priv;
-	int drvidx, i;
-
-	ether_setup(ndev);
-	lp->org_hhc = ndev->hard_header_cache;
-	lp->org_hcu = ndev->header_cache_update;
-
-	/* Setup the generic properties */
-
-	ndev->hard_header = NULL;
-	ndev->hard_header_cache = NULL;
-	ndev->header_cache_update = NULL;
-	ndev->mtu = 1500;
-	ndev->flags = IFF_NOARP|IFF_POINTOPOINT;
-	ndev->type = ARPHRD_ETHER;
-	ndev->addr_len = ETH_ALEN;
-
-	/* for clients with MPPP maybe higher values better */
-	ndev->tx_queue_len = 30;
-
-	for (i = 0; i < ETH_ALEN; i++)
-		ndev->broadcast[i] = 0xff;
-
-	/* The ISDN-specific entries in the device structure. */
-	ndev->open = &isdn_net_open;
-	ndev->hard_start_xmit = &isdn_net_start_xmit;
-
-	/*
-	 *  up till binding we ask the protocol layer to reserve as much
-	 *  as we might need for HL layer
-	 */
-
-	for (drvidx = 0; drvidx < ISDN_MAX_DRIVERS; drvidx++)
-		if (dev->drv[drvidx])
-			if (max_hlhdr_len < dev->drv[drvidx]->interface->hl_hdrlen)
-				max_hlhdr_len = dev->drv[drvidx]->interface->hl_hdrlen;
-
-	ndev->hard_header_len = ETH_HLEN + max_hlhdr_len;
-	ndev->stop = &isdn_net_close;
-	ndev->get_stats = &isdn_net_get_stats;
-	ndev->rebuild_header = &isdn_net_rebuild_header;
-	ndev->do_ioctl = NULL;
-	return 0;
-}
-
-static void
-isdn_net_swapbind(int drvidx)
-{
-	isdn_net_dev *p;
-
-#ifdef ISDN_DEBUG_NET_ICALL
-	printk(KERN_DEBUG "n_fi: swapping ch of %d\n", drvidx);
-#endif
-	p = dev->netdev;
-	while (p) {
-		if (p->local->pre_device == drvidx)
-			switch (p->local->pre_channel) {
-				case 0:
-					p->local->pre_channel = 1;
-					break;
-				case 1:
-					p->local->pre_channel = 0;
-					break;
-			}
-		p = (isdn_net_dev *) p->next;
-	}
-}
-
-static void
-isdn_net_swap_usage(int i1, int i2)
-{
-	int u1 = dev->usage[i1] & ISDN_USAGE_EXCLUSIVE;
-	int u2 = dev->usage[i2] & ISDN_USAGE_EXCLUSIVE;
-
-#ifdef ISDN_DEBUG_NET_ICALL
-	printk(KERN_DEBUG "n_fi: usage of %d and %d\n", i1, i2);
-#endif
-	dev->usage[i1] &= ~ISDN_USAGE_EXCLUSIVE;
-	dev->usage[i1] |= u2;
-	dev->usage[i2] &= ~ISDN_USAGE_EXCLUSIVE;
-	dev->usage[i2] |= u1;
-	isdn_info_update();
-}
-
-/*
- * An incoming call-request has arrived.
- * Search the interface-chain for an appropriate interface.
- * If found, connect the interface to the ISDN-channel and initiate
- * D- and B-Channel-setup. If secure-flag is set, accept only
- * configured phone-numbers. If callback-flag is set, initiate
- * callback-dialing.
- *
- * Return-Value: 0 = No appropriate interface for this call.
- *               1 = Call accepted
- *               2 = Reject call, wait cbdelay, then call back
- *               3 = Reject call
- *               4 = Wait cbdelay, then call back
- *               5 = No appropriate interface for this call,
- *                   would eventually match if CID was longer.
- */
-int
-isdn_net_find_icall(int di, int ch, int idx, setup_parm *setup)
-{
-	char *eaz;
-	int si1;
-	int si2;
-	int ematch;
-	int wret;
-	int swapped;
-	int sidx = 0;
-	isdn_net_dev *p;
-	isdn_net_phone *n;
-	ulong flags;
-	char nr[32];
-	/* Search name in netdev-chain */
-	save_flags(flags);
-	cli();
-	if (!setup->phone[0]) {
-		nr[0] = '0';
-		nr[1] = '\0';
-		printk(KERN_INFO "isdn_net: Incoming call without OAD, assuming '0'\n");
-	} else
-		strcpy(nr, setup->phone);
-	si1 = (int) setup->si1;
-	si2 = (int) setup->si2;
-	if (!setup->eazmsn[0]) {
-		printk(KERN_WARNING "isdn_net: Incoming call without CPN, assuming '0'\n");
-		eaz = "0";
-	} else
-		eaz = setup->eazmsn;
-	if (dev->net_verbose > 1)
-		printk(KERN_INFO "isdn_net: call from %s,%d,%d -> %s\n", nr, si1, si2, eaz);
-	/* Accept only calls with Si1 = 7 (Data-Transmission) */
-	if (si1 != 7) {
-		restore_flags(flags);
-		if (dev->net_verbose > 1)
-			printk(KERN_INFO "isdn_net: Service-Indicator not 7, ignored\n");
-		return 0;
-	}
-	n = (isdn_net_phone *) 0;
-	p = dev->netdev;
-	ematch = wret = swapped = 0;
-#ifdef ISDN_DEBUG_NET_ICALL
-	printk(KERN_DEBUG "n_fi: di=%d ch=%d idx=%d usg=%d\n", di, ch, idx,
-	       dev->usage[idx]);
-#endif
-	while (p) {
-		int matchret;
-		isdn_net_local *lp = p->local;
-
-		/* If last check has triggered as binding-swap, revert it */
-		switch (swapped) {
-			case 2:
-				isdn_net_swap_usage(idx, sidx);
-				/* fall through */
-			case 1:
-				isdn_net_swapbind(di);
-				break;
-		}
-		swapped = 0;
-		if (!(matchret = isdn_msncmp(eaz, isdn_map_eaz2msn(lp->msn, di))))
-			ematch = 1;
-		/* Remember if more numbers eventually can match */
-		if (matchret > wret)
-			wret = matchret;
-#ifdef ISDN_DEBUG_NET_ICALL
-		printk(KERN_DEBUG "n_fi: if='%s', l.msn=%s, l.flags=%d, l.dstate=%d\n",
-		       lp->name, lp->msn, lp->flags, lp->dialstate);
-#endif
-		if ((!matchret) &&                                        /* EAZ is matching   */
-		    (((!(lp->flags & ISDN_NET_CONNECTED)) &&              /* but not connected */
-		      (USG_NONE(dev->usage[idx]))) ||                     /* and ch. unused or */
-		     ((((lp->dialstate == 4) || (lp->dialstate == 12)) && /* if dialing        */
-		       (!(lp->flags & ISDN_NET_CALLBACK)))                /* but no callback   */
-		     )))
-			 {
-#ifdef ISDN_DEBUG_NET_ICALL
-			printk(KERN_DEBUG "n_fi: match1, pdev=%d pch=%d\n",
-			       lp->pre_device, lp->pre_channel);
-#endif
-			if (dev->usage[idx] & ISDN_USAGE_EXCLUSIVE) {
-				if ((lp->pre_channel != ch) ||
-				    (lp->pre_device != di)) {
-					/* Here we got a problem:
-					 * If using an ICN-Card, an incoming call is always signaled on
-					 * on the first channel of the card, if both channels are
-					 * down. However this channel may be bound exclusive. If the
-					 * second channel is free, this call should be accepted.
-					 * The solution is horribly but it runs, so what:
-					 * We exchange the exclusive bindings of the two channels, the
-					 * corresponding variables in the interface-structs.
-					 */
-					if (ch == 0) {
-						sidx = isdn_dc2minor(di, 1);
-#ifdef ISDN_DEBUG_NET_ICALL
-						printk(KERN_DEBUG "n_fi: ch is 0\n");
-#endif
-						if (USG_NONE(dev->usage[sidx])) {
-							/* Second Channel is free, now see if it is bound
-							 * exclusive too. */
-							if (dev->usage[sidx] & ISDN_USAGE_EXCLUSIVE) {
-#ifdef ISDN_DEBUG_NET_ICALL
-								printk(KERN_DEBUG "n_fi: 2nd channel is down and bound\n");
-#endif
-								/* Yes, swap bindings only, if the original
-								 * binding is bound to channel 1 of this driver */
-								if ((lp->pre_device == di) &&
-								    (lp->pre_channel == 1)) {
-									isdn_net_swapbind(di);
-									swapped = 1;
-								} else {
-									/* ... else iterate next device */
-									p = (isdn_net_dev *) p->next;
-									continue;
-								}
-							} else {
-#ifdef ISDN_DEBUG_NET_ICALL
-								printk(KERN_DEBUG "n_fi: 2nd channel is down and unbound\n");
-#endif
-								/* No, swap always and swap excl-usage also */
-								isdn_net_swap_usage(idx, sidx);
-								isdn_net_swapbind(di);
-								swapped = 2;
-							}
-							/* Now check for exclusive binding again */
-#ifdef ISDN_DEBUG_NET_ICALL
-							printk(KERN_DEBUG "n_fi: final check\n");
-#endif
-							if ((dev->usage[idx] & ISDN_USAGE_EXCLUSIVE) &&
-							    ((lp->pre_channel != ch) ||
-							     (lp->pre_device != di))) {
-#ifdef ISDN_DEBUG_NET_ICALL
-								printk(KERN_DEBUG "n_fi: final check failed\n");
-#endif
-								p = (isdn_net_dev *) p->next;
-								continue;
-							}
-						}
-					} else {
-						/* We are already on the second channel, so nothing to do */
-#ifdef ISDN_DEBUG_NET_ICALL
-						printk(KERN_DEBUG "n_fi: already on 2nd channel\n");
-#endif
-					}
-				}
-			}
-#ifdef ISDN_DEBUG_NET_ICALL
-			printk(KERN_DEBUG "n_fi: match2\n");
-#endif
-			n = lp->phone[0];
-			if (lp->flags & ISDN_NET_SECURE) {
-				while (n) {
-					if (!isdn_msncmp(nr, n->num))
-						break;
-					n = (isdn_net_phone *) n->next;
-				}
-			}
-			if (n || (!(lp->flags & ISDN_NET_SECURE))) {
-#ifdef ISDN_DEBUG_NET_ICALL
-				printk(KERN_DEBUG "n_fi: match3\n");
-#endif
-				/* matching interface found */
-
-				/*
-				 * Is the state STOPPED?
-				 * If so, no dialin is allowed,
-				 * so reject actively.
-				 * */
-				if (ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_OFF) {
-					restore_flags(flags);
-					printk(KERN_INFO "incoming call, interface %s `stopped' -> rejected\n",
-					       lp->name);
-					return 3;
-				}
-				/*
-				 * Is the interface up?
-				 * If not, reject the call actively.
-				 */
-				if (!isdn_net_device_started(p)) {
-					restore_flags(flags);
-					printk(KERN_INFO "%s: incoming call, interface down -> rejected\n",
-					       lp->name);
-					return 3;
-				}
-				/* Interface is up, now see if it's a slave. If so, see if
-				 * it's master and parent slave is online. If not, reject the call.
-				 */
-				if (lp->master) {
-					isdn_net_local *mlp = (isdn_net_local *) lp->master->priv;
-					printk(KERN_DEBUG "ICALLslv: %s\n", lp->name);
-					printk(KERN_DEBUG "master=%s\n", mlp->name);
-					if (mlp->flags & ISDN_NET_CONNECTED) {
-						printk(KERN_DEBUG "master online\n");
-						/* Master is online, find parent-slave (master if first slave) */
-						while (mlp->slave) {
-							if ((isdn_net_local *) mlp->slave->priv == lp)
-								break;
-							mlp = (isdn_net_local *) mlp->slave->priv;
-						}
-					} else
-						printk(KERN_DEBUG "master offline\n");
-					/* Found parent, if it's offline iterate next device */
-					printk(KERN_DEBUG "mlpf: %d\n", mlp->flags & ISDN_NET_CONNECTED);
-					if (!(mlp->flags & ISDN_NET_CONNECTED)) {
-						p = (isdn_net_dev *) p->next;
-						continue;
-					}
-				} 
-				if (lp->flags & ISDN_NET_CALLBACK) {
-					int chi;
-					/*
-					 * Is the state MANUAL?
-					 * If so, no callback can be made,
-					 * so reject actively.
-					 * */
-					if (ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_OFF) {
-						restore_flags(flags);
-						printk(KERN_INFO "incoming call for callback, interface %s `off' -> rejected\n",
-						       lp->name);
-						return 3;
-					}
-					printk(KERN_DEBUG "%s: call from %s -> %s, start callback\n",
-					       lp->name, nr, eaz);
-					if (lp->phone[1]) {
-						/* Grab a free ISDN-Channel */
-						if ((chi = 
-							isdn_get_free_channel(
-								ISDN_USAGE_NET,
-								lp->l2_proto,
-								lp->l3_proto,
-							  	lp->pre_device,
-						 		lp->pre_channel,
-						 		lp->msn)
-								) < 0) {
-
-							printk(KERN_WARNING "isdn_net_find_icall: No channel for %s\n", lp->name);
-							restore_flags(flags);
-							return 0;
-						}
-						/* Setup dialstate. */
-						lp->dtimer = 0;
-						lp->dialstate = 11;
-						/* Connect interface with channel */
-						isdn_net_bind_channel(lp, chi);
-#ifdef CONFIG_ISDN_PPP
-						if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
-							if (isdn_ppp_bind(lp) < 0) {
-								isdn_net_unbind_channel(lp);
-								restore_flags(flags);
-								return 0;
-							}
-#endif
-						/* Initiate dialing by returning 2 or 4 */
-						restore_flags(flags);
-						return (lp->flags & ISDN_NET_CBHUP) ? 2 : 4;
-					} else
-						printk(KERN_WARNING "isdn_net: %s: No phone number\n", lp->name);
-					restore_flags(flags);
-					return 0;
-				} else {
-					printk(KERN_DEBUG "%s: call from %s -> %s accepted\n", lp->name, nr,
-					       eaz);
-					/* if this interface is dialing, it does it probably on a different
-					   device, so free this device */
-					if ((lp->dialstate == 4) || (lp->dialstate == 12)) {
-#ifdef CONFIG_ISDN_PPP
-						if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
-							isdn_ppp_free(lp);
-#endif
-						isdn_net_lp_disconnected(lp);
-						isdn_free_channel(lp->isdn_device, lp->isdn_channel,
-							 ISDN_USAGE_NET);
-					}
-					dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE;
-					dev->usage[idx] |= ISDN_USAGE_NET;
-					strcpy(dev->num[idx], nr);
-					isdn_info_update();
-					dev->st_netdev[idx] = lp->netdev;
-					lp->isdn_device = di;
-					lp->isdn_channel = ch;
-					lp->ppp_slot = -1;
-					lp->flags |= ISDN_NET_CONNECTED;
-					lp->dialstate = 7;
-					lp->dtimer = 0;
-					lp->outgoing = 0;
-					lp->huptimer = 0;
-					lp->hupflags |= ISDN_WAITCHARGE;
-					lp->hupflags &= ~ISDN_HAVECHARGE;
-#ifdef CONFIG_ISDN_PPP
-					if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
-						if (isdn_ppp_bind(lp) < 0) {
-							isdn_net_unbind_channel(lp);
-							restore_flags(flags);
-							return 0;
-						}
-#endif
-					restore_flags(flags);
-					return 1;
-				}
-			}
-		}
-		p = (isdn_net_dev *) p->next;
-	}
-	/* If none of configured EAZ/MSN matched and not verbose, be silent */
-	if (!ematch || dev->net_verbose)
-		printk(KERN_INFO "isdn_net: call from %s -> %d %s ignored\n", nr, di, eaz);
-	restore_flags(flags);
-	return (wret == 2)?5:0;
-}
-
-/*
- * Search list of net-interfaces for an interface with given name.
- */
-isdn_net_dev *
-isdn_net_findif(char *name)
-{
-	isdn_net_dev *p = dev->netdev;
-
-	while (p) {
-		if (!strcmp(p->local->name, name))
-			return p;
-		p = (isdn_net_dev *) p->next;
-	}
-	return (isdn_net_dev *) NULL;
-}
-
-/*
- * Force a net-interface to dial out.
- * This is called from the userlevel-routine below or
- * from isdn_net_start_xmit().
- */
-int
-isdn_net_force_dial_lp(isdn_net_local * lp)
-{
-	if ((!(lp->flags & ISDN_NET_CONNECTED)) && !lp->dialstate) {
-		int chi;
-		if (lp->phone[1]) {
-			ulong flags;
-			save_flags(flags);
-			cli();
-
-			/* Grab a free ISDN-Channel */
-			if ((chi = 
-						isdn_get_free_channel(
-							ISDN_USAGE_NET,
-							lp->l2_proto,
-							lp->l3_proto,
-							lp->pre_device,
-						 	lp->pre_channel,
-							lp->msn)
-							) < 0) {
-				printk(KERN_WARNING "isdn_net_force_dial: No channel for %s\n", lp->name);
-				restore_flags(flags);
-				return -EAGAIN;
-			}
-			lp->dialstate = 1;
-			/* Connect interface with channel */
-			isdn_net_bind_channel(lp, chi);
-#ifdef CONFIG_ISDN_PPP
-			if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
-				if (isdn_ppp_bind(lp) < 0) {
-					isdn_net_unbind_channel(lp);
-					restore_flags(flags);
-					return -EAGAIN;
-				}
-#endif
-			/* Initiate dialing */
-			restore_flags(flags);
-			isdn_net_dial();
-			return 0;
-		} else
-			return -EINVAL;
-	} else
-		return -EBUSY;
-}
-
-/*
- * This is called from certain upper protocol layers (multilink ppp
- * and x25iface encapsulation module) that want to initiate dialing
- * themselves.
- */
-int
-isdn_net_dial_req(isdn_net_local * lp)
-{
-	/* is there a better error code? */
-	if (!(ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_AUTO)) return -EBUSY;
-
-	return isdn_net_force_dial_lp(lp);
-}
-
-/*
- * Force a net-interface to dial out.
- * This is always called from within userspace (ISDN_IOCTL_NET_DIAL).
- */
-int
-isdn_net_force_dial(char *name)
-{
-	isdn_net_dev *p = isdn_net_findif(name);
-
-	if (!p)
-		return -ENODEV;
-	return (isdn_net_force_dial_lp(p->local));
-}
-
-/*
- * Allocate a new network-interface and initialize its data structures.
- */
-char *
-isdn_net_new(char *name, struct net_device *master)
-{
-	isdn_net_dev *netdev;
-
-	/* Avoid creating an existing interface */
-	if (isdn_net_findif(name)) {
-		printk(KERN_WARNING "isdn_net: interface %s already exists\n", name);
-		return NULL;
-	}
-	if (!(netdev = (isdn_net_dev *) kmalloc(sizeof(isdn_net_dev), GFP_KERNEL))) {
-		printk(KERN_WARNING "isdn_net: Could not allocate net-device\n");
-		return NULL;
-	}
-	memset(netdev, 0, sizeof(isdn_net_dev));
-	if (!(netdev->local = (isdn_net_local *) kmalloc(sizeof(isdn_net_local), GFP_KERNEL))) {
-		printk(KERN_WARNING "isdn_net: Could not allocate device locals\n");
-		kfree(netdev);
-		return NULL;
-	}
-	memset(netdev->local, 0, sizeof(isdn_net_local));
-	if (name == NULL)
-		strcpy(netdev->local->name, "         ");
-	else
-		strcpy(netdev->local->name, name);
-	strcpy(netdev->dev.name, netdev->local->name);
-	netdev->dev.priv = netdev->local;
-	netdev->dev.init = isdn_net_init;
-	netdev->local->p_encap = ISDN_NET_ENCAP_RAWIP;
-	if (master) {
-		/* Device shall be a slave */
-		struct net_device *p = (((isdn_net_local *) master->priv)->slave);
-		struct net_device *q = master;
-
-		netdev->local->master = master;
-		/* Put device at end of slave-chain */
-		while (p) {
-			q = p;
-			p = (((isdn_net_local *) p->priv)->slave);
-		}
-		((isdn_net_local *) q->priv)->slave = &(netdev->dev);
-	} else {
-		/* Device shall be a master */
-		/*
-		 * Watchdog timer (currently) for master only.
-		 */
-		netdev->dev.tx_timeout = isdn_net_tx_timeout;
-		netdev->dev.watchdog_timeo = ISDN_NET_TX_TIMEOUT;
-		if (register_netdev(&netdev->dev) != 0) {
-			printk(KERN_WARNING "isdn_net: Could not register net-device\n");
-			kfree(netdev->local);
-			kfree(netdev);
-			return NULL;
-		}
-	}
-	netdev->local->magic = ISDN_NET_MAGIC;
-
-	netdev->queue = netdev->local;
-	spin_lock_init(&netdev->queue_lock);
-
-	netdev->local->last = netdev->local;
-	netdev->local->netdev = netdev;
-	netdev->local->next = netdev->local;
-
-	netdev->local->tqueue.sync = 0;
-	netdev->local->tqueue.routine = isdn_net_softint;
-	netdev->local->tqueue.data = netdev->local;
-	spin_lock_init(&netdev->local->xmit_lock);
-
-	netdev->local->isdn_device = -1;
-	netdev->local->isdn_channel = -1;
-	netdev->local->pre_device = -1;
-	netdev->local->pre_channel = -1;
-	netdev->local->exclusive = -1;
-	netdev->local->ppp_slot = -1;
-	netdev->local->pppbind = -1;
-	skb_queue_head_init(&netdev->local->super_tx_queue);
-	netdev->local->l2_proto = ISDN_PROTO_L2_X75I;
-	netdev->local->l3_proto = ISDN_PROTO_L3_TRANS;
-	netdev->local->triggercps = 6000;
-	netdev->local->slavedelay = 10 * HZ;
-	netdev->local->hupflags = ISDN_INHUP;	/* Do hangup even on incoming calls */
-	netdev->local->onhtime = 10;	/* Default hangup-time for saving costs
-	   of those who forget configuring this */
-	netdev->local->dialmax = 1;
-	netdev->local->flags = ISDN_NET_CBHUP | ISDN_NET_DM_MANUAL;	/* Hangup before Callback, manual dial */
-	netdev->local->cbdelay = 25;	/* Wait 5 secs before Callback */
-	netdev->local->dialtimeout = -1;  /* Infinite Dial-Timeout */
-	netdev->local->dialwait = 5 * HZ; /* Wait 5 sec. after failed dial */
-	netdev->local->dialstarted = 0;   /* Jiffies of last dial-start */
-	netdev->local->dialwait_timer = 0;  /* Jiffies of earliest next dial-start */
-
-	/* Put into to netdev-chain */
-	netdev->next = (void *) dev->netdev;
-	dev->netdev = netdev;
-	return netdev->dev.name;
-}
-
-char *
-isdn_net_newslave(char *parm)
-{
-	char *p = strchr(parm, ',');
-	isdn_net_dev *n;
-	char newname[10];
-
-	if (p) {
-		/* Slave-Name MUST not be empty */
-		if (!strlen(p + 1))
-			return NULL;
-		strcpy(newname, p + 1);
-		*p = 0;
-		/* Master must already exist */
-		if (!(n = isdn_net_findif(parm)))
-			return NULL;
-		/* Master must be a real interface, not a slave */
-		if (n->local->master)
-			return NULL;
-		/* Master must not be started yet */
-		if (isdn_net_device_started(n)) 
-			return NULL;
-		return (isdn_net_new(newname, &(n->dev)));
-	}
-	return NULL;
-}
-
-/*
- * Set interface-parameters.
- * Always set all parameters, so the user-level application is responsible
- * for not overwriting existing setups. It has to get the current
- * setup first, if only selected parameters are to be changed.
- */
-int
-isdn_net_setcfg(isdn_net_ioctl_cfg * cfg)
-{
-	isdn_net_dev *p = isdn_net_findif(cfg->name);
-	ulong features;
-	int i;
-	int drvidx;
-	int chidx;
-	char drvid[25];
-#ifdef CONFIG_ISDN_X25
-	ulong flags;
-#endif
-	if (p) {
-		isdn_net_local *lp = p->local;
-
-		/* See if any registered driver supports the features we want */
-		features = ((1 << cfg->l2_proto) << ISDN_FEATURE_L2_SHIFT) |
-			((1 << cfg->l3_proto) << ISDN_FEATURE_L3_SHIFT);
-		for (i = 0; i < ISDN_MAX_DRIVERS; i++)
-			if (dev->drv[i])
-				if ((dev->drv[i]->interface->features & features) == features)
-					break;
-		if (i == ISDN_MAX_DRIVERS) {
-			printk(KERN_WARNING "isdn_net: No driver with selected features\n");
-			return -ENODEV;
-		}
-		if (lp->p_encap != cfg->p_encap){
-#ifdef CONFIG_ISDN_X25
-			struct concap_proto * cprot = p -> cprot;
-#endif
-			if (isdn_net_device_started(p)) {
-				printk(KERN_WARNING "%s: cannot change encap when if is up\n",
-				       lp->name);
-				return -EBUSY;
-			}
-#ifdef CONFIG_ISDN_X25
-			/* delete old encapsulation protocol if present ... */
-			save_flags(flags);
-			cli(); /* avoid races with incoming events trying to
-				  call cprot->pops methods */
-			if( cprot && cprot -> pops )
-				cprot -> pops -> proto_del ( cprot );
-			p -> cprot = NULL;
-			lp -> dops = NULL;
-			restore_flags(flags);
-			/* ... ,  prepare for configuration of new one ... */
-			switch ( cfg -> p_encap ){
-			case ISDN_NET_ENCAP_X25IFACE:
-				lp -> dops = &isdn_concap_reliable_dl_dops;
-			}
-			/* ... and allocate new one ... */
-			p -> cprot = isdn_concap_new( cfg -> p_encap );
-			/* p -> cprot == NULL now if p_encap is not supported
-			   by means of the concap_proto mechanism */
-			/* the protocol is not configured yet; this will
-			   happen later when isdn_net_reset() is called */
-#endif
-		}
-		switch ( cfg->p_encap ) {
-		case ISDN_NET_ENCAP_SYNCPPP:
-#ifndef CONFIG_ISDN_PPP
-			printk(KERN_WARNING "%s: SyncPPP support not configured\n",
-			       lp->name);
-			return -EINVAL;
-#else
-			p->dev.type = ARPHRD_PPP;	/* change ARP type */
-			p->dev.addr_len = 0;
-			p->dev.do_ioctl = isdn_ppp_dev_ioctl;
-#endif
-			break;
-		case ISDN_NET_ENCAP_X25IFACE:
-#ifndef CONFIG_ISDN_X25
-			printk(KERN_WARNING "%s: isdn-x25 support not configured\n",
-			       p->local->name);
-			return -EINVAL;
-#else
-			p->dev.type = ARPHRD_X25;	/* change ARP type */
-			p->dev.addr_len = 0;
-#endif
-			break;
-		case ISDN_NET_ENCAP_CISCOHDLCK:
-			p->dev.do_ioctl = isdn_ciscohdlck_dev_ioctl;
-			break;
-		default:
-			if( cfg->p_encap >= 0 &&
-			    cfg->p_encap <= ISDN_NET_ENCAP_MAX_ENCAP )
-				break;
-			printk(KERN_WARNING
-			       "%s: encapsulation protocol %d not supported\n",
-			       p->local->name, cfg->p_encap);
-			return -EINVAL;
-		}
-		if (strlen(cfg->drvid)) {
-			/* A bind has been requested ... */
-			char *c,
-			*e;
-
-			drvidx = -1;
-			chidx = -1;
-			strcpy(drvid, cfg->drvid);
-			if ((c = strchr(drvid, ','))) {
-				/* The channel-number is appended to the driver-Id with a comma */
-				chidx = (int) simple_strtoul(c + 1, &e, 10);
-				if (e == c)
-					chidx = -1;
-				*c = '\0';
-			}
-			for (i = 0; i < ISDN_MAX_DRIVERS; i++)
-				/* Lookup driver-Id in array */
-				if (!(strcmp(dev->drvid[i], drvid))) {
-					drvidx = i;
-					break;
-				}
-			if ((drvidx == -1) || (chidx == -1))
-				/* Either driver-Id or channel-number invalid */
-				return -ENODEV;
-		} else {
-			/* Parameters are valid, so get them */
-			drvidx = lp->pre_device;
-			chidx = lp->pre_channel;
-		}
-		if (cfg->exclusive > 0) {
-			unsigned long flags;
-
-			/* If binding is exclusive, try to grab the channel */
-			save_flags(flags);
-			if ((i = isdn_get_free_channel(ISDN_USAGE_NET,
-				lp->l2_proto, lp->l3_proto, drvidx,
-				chidx, lp->msn)) < 0) {
-				/* Grab failed, because desired channel is in use */
-				lp->exclusive = -1;
-				restore_flags(flags);
-				return -EBUSY;
-			}
-			/* All went ok, so update isdninfo */
-			dev->usage[i] = ISDN_USAGE_EXCLUSIVE;
-			isdn_info_update();
-			restore_flags(flags);
-			lp->exclusive = i;
-		} else {
-			/* Non-exclusive binding or unbind. */
-			lp->exclusive = -1;
-			if ((lp->pre_device != -1) && (cfg->exclusive == -1)) {
-				isdn_unexclusive_channel(lp->pre_device, lp->pre_channel);
-				isdn_free_channel(lp->pre_device, lp->pre_channel, ISDN_USAGE_NET);
-				drvidx = -1;
-				chidx = -1;
-			}
-		}
-		strcpy(lp->msn, cfg->eaz);
-		lp->pre_device = drvidx;
-		lp->pre_channel = chidx;
-		lp->onhtime = cfg->onhtime;
-		lp->charge = cfg->charge;
-		lp->l2_proto = cfg->l2_proto;
-		lp->l3_proto = cfg->l3_proto;
-		lp->cbdelay = cfg->cbdelay;
-		lp->dialmax = cfg->dialmax;
-		lp->triggercps = cfg->triggercps;
-		lp->slavedelay = cfg->slavedelay * HZ;
-		lp->pppbind = cfg->pppbind;
-		lp->dialtimeout = cfg->dialtimeout >= 0 ? cfg->dialtimeout * HZ : -1;
-		lp->dialwait = cfg->dialwait * HZ;
-		if (cfg->secure)
-			lp->flags |= ISDN_NET_SECURE;
-		else
-			lp->flags &= ~ISDN_NET_SECURE;
-		if (cfg->cbhup)
-			lp->flags |= ISDN_NET_CBHUP;
-		else
-			lp->flags &= ~ISDN_NET_CBHUP;
-		switch (cfg->callback) {
-			case 0:
-				lp->flags &= ~(ISDN_NET_CALLBACK | ISDN_NET_CBOUT);
-				break;
-			case 1:
-				lp->flags |= ISDN_NET_CALLBACK;
-				lp->flags &= ~ISDN_NET_CBOUT;
-				break;
-			case 2:
-				lp->flags |= ISDN_NET_CBOUT;
-				lp->flags &= ~ISDN_NET_CALLBACK;
-				break;
-		}
-		lp->flags &= ~ISDN_NET_DIALMODE_MASK;	/* first all bits off */
-		if (cfg->dialmode && !(cfg->dialmode & ISDN_NET_DIALMODE_MASK)) {
-			/* old isdnctrl version, where only 0 or 1 is given */
-			printk(KERN_WARNING
-			     "Old isdnctrl version detected! Please update.\n");
-			lp->flags |= ISDN_NET_DM_OFF; /* turn on `off' bit */
-		}
-		else {
-			lp->flags |= cfg->dialmode;  /* turn on selected bits */
-		}
-		if (cfg->chargehup)
-			lp->hupflags |= ISDN_CHARGEHUP;
-		else
-			lp->hupflags &= ~ISDN_CHARGEHUP;
-		if (cfg->ihup)
-			lp->hupflags |= ISDN_INHUP;
-		else
-			lp->hupflags &= ~ISDN_INHUP;
-		if (cfg->chargeint > 10) {
-			lp->hupflags |= ISDN_HAVECHARGE | ISDN_MANCHARGE;
-			lp->chargeint = cfg->chargeint * HZ;
-		}
-		if (cfg->p_encap != lp->p_encap) {
-			if (cfg->p_encap == ISDN_NET_ENCAP_RAWIP) {
-				p->dev.hard_header = NULL;
-				p->dev.hard_header_cache = NULL;
-				p->dev.header_cache_update = NULL;
-				p->dev.flags = IFF_NOARP|IFF_POINTOPOINT;
-			} else {
-				p->dev.hard_header = isdn_net_header;
-				if (cfg->p_encap == ISDN_NET_ENCAP_ETHER) {
-					p->dev.hard_header_cache = lp->org_hhc;
-					p->dev.header_cache_update = lp->org_hcu;
-					p->dev.flags = IFF_BROADCAST | IFF_MULTICAST;
-				} else {
-					p->dev.hard_header_cache = NULL;
-					p->dev.header_cache_update = NULL;
-					p->dev.flags = IFF_NOARP|IFF_POINTOPOINT;
-				}
-			}
-		}
-		lp->p_encap = cfg->p_encap;
-		return 0;
-	}
-	return -ENODEV;
-}
-
-/*
- * Perform get-interface-parameters.ioctl
- */
-int
-isdn_net_getcfg(isdn_net_ioctl_cfg * cfg)
-{
-	isdn_net_dev *p = isdn_net_findif(cfg->name);
-
-	if (p) {
-		isdn_net_local *lp = p->local;
-
-		strcpy(cfg->eaz, lp->msn);
-		cfg->exclusive = lp->exclusive;
-		if (lp->pre_device >= 0) {
-			sprintf(cfg->drvid, "%s,%d", dev->drvid[lp->pre_device],
-				lp->pre_channel);
-		} else
-			cfg->drvid[0] = '\0';
-		cfg->onhtime = lp->onhtime;
-		cfg->charge = lp->charge;
-		cfg->l2_proto = lp->l2_proto;
-		cfg->l3_proto = lp->l3_proto;
-		cfg->p_encap = lp->p_encap;
-		cfg->secure = (lp->flags & ISDN_NET_SECURE) ? 1 : 0;
-		cfg->callback = 0;
-		if (lp->flags & ISDN_NET_CALLBACK)
-			cfg->callback = 1;
-		if (lp->flags & ISDN_NET_CBOUT)
-			cfg->callback = 2;
-		cfg->cbhup = (lp->flags & ISDN_NET_CBHUP) ? 1 : 0;
-		cfg->dialmode = lp->flags & ISDN_NET_DIALMODE_MASK;
-		cfg->chargehup = (lp->hupflags & 4) ? 1 : 0;
-		cfg->ihup = (lp->hupflags & 8) ? 1 : 0;
-		cfg->cbdelay = lp->cbdelay;
-		cfg->dialmax = lp->dialmax;
-		cfg->triggercps = lp->triggercps;
-		cfg->slavedelay = lp->slavedelay / HZ;
-		cfg->chargeint = (lp->hupflags & ISDN_CHARGEHUP) ?
-		    (lp->chargeint / HZ) : 0;
-		cfg->pppbind = lp->pppbind;
-		cfg->dialtimeout = lp->dialtimeout >= 0 ? lp->dialtimeout / HZ : -1;
-		cfg->dialwait = lp->dialwait / HZ;
-		if (lp->slave)
-			strcpy(cfg->slave, ((isdn_net_local *) lp->slave->priv)->name);
-		else
-			cfg->slave[0] = '\0';
-		if (lp->master)
-			strcpy(cfg->master, ((isdn_net_local *) lp->master->priv)->name);
-		else
-			cfg->master[0] = '\0';
-		return 0;
-	}
-	return -ENODEV;
-}
-
-/*
- * Add a phone-number to an interface.
- */
-int
-isdn_net_addphone(isdn_net_ioctl_phone * phone)
-{
-	isdn_net_dev *p = isdn_net_findif(phone->name);
-	isdn_net_phone *n;
-
-	if (p) {
-		if (!(n = (isdn_net_phone *) kmalloc(sizeof(isdn_net_phone), GFP_KERNEL)))
-			return -ENOMEM;
-		strcpy(n->num, phone->phone);
-		n->next = p->local->phone[phone->outgoing & 1];
-		p->local->phone[phone->outgoing & 1] = n;
-		return 0;
-	}
-	return -ENODEV;
-}
-
-/*
- * Copy a string of all phone-numbers of an interface to user space.
- * This might sleep and must be called with the isdn semaphore down.
- */
-int
-isdn_net_getphones(isdn_net_ioctl_phone * phone, char *phones)
-{
-	isdn_net_dev *p = isdn_net_findif(phone->name);
-	int inout = phone->outgoing & 1;
-	int more = 0;
-	int count = 0;
-	isdn_net_phone *n;
-
-	if (!p)
-		return -ENODEV;
-	inout &= 1;
-	for (n = p->local->phone[inout]; n; n = n->next) {
-		if (more) {
-			put_user(' ', phones++);
-			count++;
-		}
-		if (copy_to_user(phones, n->num, strlen(n->num) + 1)) {
-			return -EFAULT;
-		}
-		phones += strlen(n->num);
-		count += strlen(n->num);
-		more = 1;
-	}
-	put_user(0, phones);
-	count++;
-	return count;
-}
-
-/*
- * Copy a string containing the peer's phone number of a connected interface
- * to user space.
- */
-int
-isdn_net_getpeer(isdn_net_ioctl_phone *phone, isdn_net_ioctl_phone *peer)
-{
-	isdn_net_dev *p = isdn_net_findif(phone->name);
-	int ch, dv, idx;
-
-	if (!p) return -ENODEV;
-	/*
-	 * Theoretical race: while this executes, the remote number might
-	 * become invalid (hang up) or change (new connection), resulting
-         * in (partially) wrong number copied to user. This race
-	 * currently ignored.
-	 */
-	ch = p->local->isdn_channel;
-	dv = p->local->isdn_device;
-	if(ch<0 && dv<0) return -ENOTCONN;
-	idx = isdn_dc2minor(dv, ch);
-	if (idx<0) return -ENODEV;
-	/* for pre-bound channels, we need this extra check */
-	if ( strncmp(dev->num[idx],"???",3) == 0 ) return -ENOTCONN;
-	strncpy(phone->phone,dev->num[idx],ISDN_MSNLEN);
-	phone->outgoing=USG_OUTGOING(dev->usage[idx]);
-	if ( copy_to_user(peer,phone,sizeof(*peer)) ) return -EFAULT;
-	return 0;
-}
-/*
- * Delete a phone-number from an interface.
- */
-int
-isdn_net_delphone(isdn_net_ioctl_phone * phone)
-{
-	isdn_net_dev *p = isdn_net_findif(phone->name);
-	int inout = phone->outgoing & 1;
-	isdn_net_phone *n;
-	isdn_net_phone *m;
-	unsigned long flags;
-
-	if (p) {
-		save_flags(flags);
-		cli();
-		n = p->local->phone[inout];
-		m = NULL;
-		while (n) {
-			if (!strcmp(n->num, phone->phone)) {
-				if (p->local->dial == n)
-					p->local->dial = n->next;
-				if (m)
-					m->next = n->next;
-				else
-					p->local->phone[inout] = n->next;
-				kfree(n);
-				restore_flags(flags);
-				return 0;
-			}
-			m = n;
-			n = (isdn_net_phone *) n->next;
-		}
-		restore_flags(flags);
-		return -EINVAL;
-	}
-	return -ENODEV;
-}
-
-/*
- * Delete all phone-numbers of an interface.
- */
-static int
-isdn_net_rmallphone(isdn_net_dev * p)
-{
-	isdn_net_phone *n;
-	isdn_net_phone *m;
-	unsigned long flags;
-	int i;
-
-	save_flags(flags);
-	cli();
-	for (i = 0; i < 2; i++) {
-		n = p->local->phone[i];
-		while (n) {
-			m = n->next;
-			kfree(n);
-			n = m;
-		}
-		p->local->phone[i] = NULL;
-	}
-	p->local->dial = NULL;
-	restore_flags(flags);
-	return 0;
-}
-
-/*
- * Force a hangup of a network-interface.
- */
-int
-isdn_net_force_hangup(char *name)
-{
-	isdn_net_dev *p = isdn_net_findif(name);
-	struct net_device *q;
-
-	if (p) {
-		if (p->local->isdn_device < 0)
-			return 1;
-		q = p->local->slave;
-		/* If this interface has slaves, do a hangup for them also. */
-		while (q) {
-			isdn_net_hangup(q);
-			q = (((isdn_net_local *) q->priv)->slave);
-		}
-		isdn_net_hangup(&p->dev);
-		return 0;
-	}
-	return -ENODEV;
-}
-
-/*
- * Helper-function for isdn_net_rm: Do the real work.
- */
-static int
-isdn_net_realrm(isdn_net_dev * p, isdn_net_dev * q)
-{
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-	if (isdn_net_device_started(p)) {
-		restore_flags(flags);
-		return -EBUSY;
-	}
-#ifdef CONFIG_ISDN_X25
-	if( p -> cprot && p -> cprot -> pops )
-		p -> cprot -> pops -> proto_del ( p -> cprot );
-#endif
-	/* Free all phone-entries */
-	isdn_net_rmallphone(p);
-	/* If interface is bound exclusive, free channel-usage */
-	if (p->local->exclusive != -1)
-		isdn_unexclusive_channel(p->local->pre_device, p->local->pre_channel);
-	if (p->local->master) {
-		/* It's a slave-device, so update master's slave-pointer if necessary */
-		if (((isdn_net_local *) (p->local->master->priv))->slave == &p->dev)
-			((isdn_net_local *) (p->local->master->priv))->slave = p->local->slave;
-	} else {
-		/* Unregister only if it's a master-device */
-		p->dev.hard_header_cache = p->local->org_hhc;
-		p->dev.header_cache_update = p->local->org_hcu;
-		unregister_netdev(&p->dev);
-	}
-	/* Unlink device from chain */
-	if (q)
-		q->next = p->next;
-	else
-		dev->netdev = p->next;
-	if (p->local->slave) {
-		/* If this interface has a slave, remove it also */
-		char *slavename = ((isdn_net_local *) (p->local->slave->priv))->name;
-		isdn_net_dev *n = dev->netdev;
-		q = NULL;
-		while (n) {
-			if (!strcmp(n->local->name, slavename)) {
-				isdn_net_realrm(n, q);
-				break;
-			}
-			q = n;
-			n = (isdn_net_dev *) n->next;
-		}
-	}
-	/* If no more net-devices remain, disable auto-hangup timer */
-	if (dev->netdev == NULL)
-		isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 0);
-	restore_flags(flags);
-	kfree(p->local);
-	kfree(p);
-
-	return 0;
-}
-
-/*
- * Remove a single network-interface.
- */
-int
-isdn_net_rm(char *name)
-{
-	isdn_net_dev *p;
-	isdn_net_dev *q;
-
-	/* Search name in netdev-chain */
-	p = dev->netdev;
-	q = NULL;
-	while (p) {
-		if (!strcmp(p->local->name, name))
-			return (isdn_net_realrm(p, q));
-		q = p;
-		p = (isdn_net_dev *) p->next;
-	}
-	/* If no more net-devices remain, disable auto-hangup timer */
-	if (dev->netdev == NULL)
-		isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 0);
-	return -ENODEV;
-}
-
-/*
- * Remove all network-interfaces
- */
-int
-isdn_net_rmall(void)
-{
-	unsigned long flags;
-	int ret;
-
-	/* Walk through netdev-chain */
-	save_flags(flags);
-	cli();
-	while (dev->netdev) {
-		if (!dev->netdev->local->master) {
-			/* Remove master-devices only, slaves get removed with their master */
-			if ((ret = isdn_net_realrm(dev->netdev, NULL))) {
-				restore_flags(flags);
-				return ret;
-			}
-		}
-	}
-	dev->netdev = NULL;
-	restore_flags(flags);
-	return 0;
-}
diff -Nru a/drivers/isdn/isdn_net.h b/drivers/isdn/isdn_net.h
--- a/drivers/isdn/isdn_net.h	Mon Apr 22 15:32:19 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,180 +0,0 @@
-/* $Id: isdn_net.h,v 1.19.6.4 2001/09/28 08:05:29 kai Exp $
- *
- * header for Linux ISDN subsystem, network related functions (linklevel).
- *
- * Copyright 1994-1999  by Fritz Elfert (fritz@isdn4linux.de)
- * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
- * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-			      /* Definitions for hupflags:                */
-#define ISDN_WAITCHARGE  1      /* did not get a charge info yet            */
-#define ISDN_HAVECHARGE  2      /* We know a charge info                    */
-#define ISDN_CHARGEHUP   4      /* We want to use the charge mechanism      */
-#define ISDN_INHUP       8      /* Even if incoming, close after huptimeout */
-#define ISDN_MANCHARGE  16      /* Charge Interval manually set             */
-
-/*
- * Definitions for Cisco-HDLC header.
- */
-
-#define CISCO_ADDR_UNICAST    0x0f
-#define CISCO_ADDR_BROADCAST  0x8f
-#define CISCO_CTRL            0x00
-#define CISCO_TYPE_CDP        0x2000
-#define CISCO_TYPE_INET       0x0800
-#define CISCO_TYPE_SLARP      0x8035
-#define CISCO_SLARP_REQUEST   0
-#define CISCO_SLARP_REPLY     1
-#define CISCO_SLARP_KEEPALIVE 2
-
-extern char *isdn_net_new(char *, struct net_device *);
-extern char *isdn_net_newslave(char *);
-extern int isdn_net_rm(char *);
-extern int isdn_net_rmall(void);
-extern int isdn_net_stat_callback(int, isdn_ctrl *);
-extern int isdn_net_setcfg(isdn_net_ioctl_cfg *);
-extern int isdn_net_getcfg(isdn_net_ioctl_cfg *);
-extern int isdn_net_addphone(isdn_net_ioctl_phone *);
-extern int isdn_net_getphones(isdn_net_ioctl_phone *, char *);
-extern int isdn_net_getpeer(isdn_net_ioctl_phone *, isdn_net_ioctl_phone *);
-extern int isdn_net_delphone(isdn_net_ioctl_phone *);
-extern int isdn_net_find_icall(int, int, int, setup_parm *);
-extern void isdn_net_hangup(struct net_device *);
-extern void isdn_net_dial(void);
-extern void isdn_net_autohup(void);
-extern int isdn_net_force_hangup(char *);
-extern int isdn_net_force_dial(char *);
-extern isdn_net_dev *isdn_net_findif(char *);
-extern int isdn_net_rcv_skb(int, struct sk_buff *);
-extern int isdn_net_dial_req(isdn_net_local *);
-extern void isdn_net_writebuf_skb(isdn_net_local *lp, struct sk_buff *skb);
-extern void isdn_net_write_super(isdn_net_local *lp, struct sk_buff *skb);
-
-#define ISDN_NET_MAX_QUEUE_LENGTH 2
-
-/*
- * is this particular channel busy?
- */
-static __inline__ int isdn_net_lp_busy(isdn_net_local *lp)
-{
-	if (atomic_read(&lp->frame_cnt) < ISDN_NET_MAX_QUEUE_LENGTH)
-		return 0;
-	else 
-		return 1;
-}
-
-/*
- * For the given net device, this will get a non-busy channel out of the
- * corresponding bundle. The returned channel is locked.
- */
-static __inline__ isdn_net_local * isdn_net_get_locked_lp(isdn_net_dev *nd)
-{
-	unsigned long flags;
-	isdn_net_local *lp;
-
-	spin_lock_irqsave(&nd->queue_lock, flags);
-	lp = nd->queue;         /* get lp on top of queue */
-	spin_lock_bh(&nd->queue->xmit_lock);
-	while (isdn_net_lp_busy(nd->queue)) {
-		spin_unlock_bh(&nd->queue->xmit_lock);
-		nd->queue = nd->queue->next;
-		if (nd->queue == lp) { /* not found -- should never happen */
-			lp = NULL;
-			goto errout;
-		}
-		spin_lock_bh(&nd->queue->xmit_lock);
-	}
-	lp = nd->queue;
-	nd->queue = nd->queue->next;
-errout:
-	spin_unlock_irqrestore(&nd->queue_lock, flags);
-	return lp;
-}
-
-/*
- * add a channel to a bundle
- */
-static __inline__ void isdn_net_add_to_bundle(isdn_net_dev *nd, isdn_net_local *nlp)
-{
-	isdn_net_local *lp;
-	unsigned long flags;
-
-	spin_lock_irqsave(&nd->queue_lock, flags);
-
-	lp = nd->queue;
-	nlp->last = lp->last;
-	lp->last->next = nlp;
-	lp->last = nlp;
-	nlp->next = lp;
-	nd->queue = nlp;
-
-	spin_unlock_irqrestore(&nd->queue_lock, flags);
-}
-/*
- * remove a channel from the bundle it belongs to
- */
-static __inline__ void isdn_net_rm_from_bundle(isdn_net_local *lp)
-{
-	isdn_net_local *master_lp = lp;
-	unsigned long flags;
-
-	if (lp->master)
-		master_lp = (isdn_net_local *) lp->master->priv;
-
-	spin_lock_irqsave(&master_lp->netdev->queue_lock, flags);
-	lp->last->next = lp->next;
-	lp->next->last = lp->last;
-	if (master_lp->netdev->queue == lp)
-		master_lp->netdev->queue = lp->next;
-	lp->next = lp->last = lp;	/* (re)set own pointers */
-	spin_unlock_irqrestore(&master_lp->netdev->queue_lock, flags);
-}
-
-static inline int
-put_u8(unsigned char *p, u8 x)
-{
-	*p = x;
-	return 1;
-}
-
-static inline int
-put_u16(unsigned char *p, u16 x)
-{
-	*((u16 *)p) = htons(x);
-	return 2;
-}
-
-static inline int
-put_u32(unsigned char *p, u32 x)
-{
-	*((u32 *)p) = htonl(x);
-	return 4;
-}
-
-static inline int
-get_u8(unsigned char *p, u8 *x)
-{
-	*x = *p;
-	return 1;
-}
-
-static inline int
-get_u16(unsigned char *p, u16 *x)
-{
-	*x = ntohs(*((u16 *)p));
-	return 2;
-}
-
-static inline int
-get_u32(unsigned char *p, u32 *x)
-{
-	*x = ntohl(*((u32 *)p));
-	return 4;
-}
-
-
diff -Nru a/drivers/isdn/isdn_ppp.c b/drivers/isdn/isdn_ppp.c
--- a/drivers/isdn/isdn_ppp.c	Mon Apr 22 15:32:19 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,2829 +0,0 @@
-/* $Id: isdn_ppp.c,v 1.85.6.9 2001/11/06 20:58:28 kai Exp $
- *
- * Linux ISDN subsystem, functions for synchronous PPP (linklevel).
- *
- * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#include <linux/module.h>
-#include <linux/isdn.h>
-#include <linux/smp_lock.h>
-#include <linux/poll.h>
-#include <linux/ppp-comp.h>
-
-#include "isdn_common.h"
-#include "isdn_ppp.h"
-#include "isdn_net.h"
-
-#ifndef PPP_IPX
-#define PPP_IPX 0x002b
-#endif
-
-/* Prototypes */
-static int isdn_ppp_fill_rq(unsigned char *buf, int len, int proto, int slot);
-static int isdn_ppp_closewait(int slot);
-static void isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp,
-				 struct sk_buff *skb, int proto);
-static int isdn_ppp_if_get_unit(char *namebuf);
-static int isdn_ppp_set_compressor(struct ippp_struct *is,struct isdn_ppp_comp_data *);
-static struct sk_buff *isdn_ppp_decompress(struct sk_buff *,
-				struct ippp_struct *,struct ippp_struct *,int *proto);
-static void isdn_ppp_receive_ccp(isdn_net_dev * net_dev, isdn_net_local * lp,
-				struct sk_buff *skb,int proto);
-static struct sk_buff *isdn_ppp_compress(struct sk_buff *skb_in,int *proto,
-	struct ippp_struct *is,struct ippp_struct *master,int type);
-static void isdn_ppp_send_ccp(isdn_net_dev *net_dev, isdn_net_local *lp,
-	 struct sk_buff *skb);
-
-/* New CCP stuff */
-static void isdn_ppp_ccp_kickup(struct ippp_struct *is);
-static void isdn_ppp_ccp_xmit_reset(struct ippp_struct *is, int proto,
-				    unsigned char code, unsigned char id,
-				    unsigned char *data, int len);
-static struct ippp_ccp_reset *isdn_ppp_ccp_reset_alloc(struct ippp_struct *is);
-static void isdn_ppp_ccp_reset_free(struct ippp_struct *is);
-static void isdn_ppp_ccp_reset_free_state(struct ippp_struct *is,
-					  unsigned char id);
-static void isdn_ppp_ccp_timer_callback(unsigned long closure);
-static struct ippp_ccp_reset_state *isdn_ppp_ccp_reset_alloc_state(struct ippp_struct *is,
-						      unsigned char id);
-static void isdn_ppp_ccp_reset_trans(struct ippp_struct *is,
-				     struct isdn_ppp_resetparams *rp);
-static void isdn_ppp_ccp_reset_ack_rcvd(struct ippp_struct *is,
-					unsigned char id);
-
-
-
-#ifdef CONFIG_ISDN_MPP
-static ippp_bundle * isdn_ppp_bundle_arr = NULL;
- 
-static int isdn_ppp_mp_bundle_array_init(void);
-static int isdn_ppp_mp_init( isdn_net_local * lp, ippp_bundle * add_to );
-static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, 
-							struct sk_buff *skb);
-static void isdn_ppp_mp_cleanup( isdn_net_local * lp );
-
-static int isdn_ppp_bundle(struct ippp_struct *, int unit);
-#endif	/* CONFIG_ISDN_MPP */
-  
-char *isdn_ppp_revision = "$Revision: 1.85.6.9 $";
-
-static struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS];
-
-static struct isdn_ppp_compressor *ipc_head = NULL;
-
-/*
- * frame log (debug)
- */
-static void
-isdn_ppp_frame_log(char *info, char *data, int len, int maxlen,int unit,int slot)
-{
-	int cnt,
-	 j,
-	 i;
-	char buf[80];
-
-	if (len < maxlen)
-		maxlen = len;
-
-	for (i = 0, cnt = 0; cnt < maxlen; i++) {
-		for (j = 0; j < 16 && cnt < maxlen; j++, cnt++)
-			sprintf(buf + j * 3, "%02x ", (unsigned char) data[cnt]);
-		printk(KERN_DEBUG "[%d/%d].%s[%d]: %s\n",unit,slot, info, i, buf);
-	}
-}
-
-/*
- * unbind isdn_net_local <=> ippp-device
- * note: it can happen, that we hangup/free the master before the slaves
- *       in this case we bind another lp to the master device
- */
-int
-isdn_ppp_free(isdn_net_local * lp)
-{
-	unsigned long flags;
-	struct ippp_struct *is;
-
-	if (lp->ppp_slot < 0 || lp->ppp_slot > ISDN_MAX_CHANNELS)
-		return 0;
-
-	save_flags(flags);
-	cli();
-
-#ifdef CONFIG_ISDN_MPP
-	spin_lock(&lp->netdev->pb->lock);
-#endif
-	isdn_net_rm_from_bundle(lp);
-#ifdef CONFIG_ISDN_MPP
-	if (lp->netdev->pb->ref_ct == 1)	/* last link in queue? */
-		isdn_ppp_mp_cleanup(lp);
-
-	lp->netdev->pb->ref_ct--;
-	spin_unlock(&lp->netdev->pb->lock);
-#endif /* CONFIG_ISDN_MPP */
-
-	is = ippp_table[lp->ppp_slot];
-	if ((is->state & IPPP_CONNECT))
-		isdn_ppp_closewait(lp->ppp_slot);	/* force wakeup on ippp device */
-	else if (is->state & IPPP_ASSIGNED)
-		is->state = IPPP_OPEN;	/* fallback to 'OPEN but not ASSIGNED' state */
-
-	if (is->debug & 0x1)
-		printk(KERN_DEBUG "isdn_ppp_free %d %lx %lx\n", lp->ppp_slot, (long) lp, (long) is->lp);
-
-	is->lp = NULL;          /* link is down .. set lp to NULL */
-	lp->ppp_slot = -1;      /* is this OK ?? */
-
-	restore_flags(flags);
-	return 0;
-}
-
-/*
- * bind isdn_net_local <=> ippp-device
- */
-int
-isdn_ppp_bind(isdn_net_local * lp)
-{
-	int i;
-	int unit = 0;
-	long flags;
-	struct ippp_struct *is;
-	int retval;
-
-	save_flags(flags);
-	cli();
-	if (lp->pppbind < 0) {  /* device bounded to ippp device ? */
-		isdn_net_dev *net_dev = dev->netdev;
-		char exclusive[ISDN_MAX_CHANNELS];	/* exclusive flags */
-		memset(exclusive, 0, ISDN_MAX_CHANNELS);
-		while (net_dev) {	/* step through net devices to find exclusive minors */
-			isdn_net_local *lp = net_dev->local;
-			if (lp->pppbind >= 0)
-				exclusive[lp->pppbind] = 1;
-			net_dev = net_dev->next;
-		}
-		/*
-		 * search a free device / slot
-		 */
-		for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-			if (ippp_table[i]->state == IPPP_OPEN && !exclusive[ippp_table[i]->minor]) {	/* OPEN, but not connected! */
-				break;
-			}
-		}
-	} else {
-		for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-			if (ippp_table[i]->minor == lp->pppbind &&
-			    (ippp_table[i]->state & IPPP_OPEN) == IPPP_OPEN)
-				break;
-		}
-	}
-
-	if (i >= ISDN_MAX_CHANNELS) {
-		restore_flags(flags);
-		printk(KERN_WARNING "isdn_ppp_bind: Can't find a (free) connection to the ipppd daemon.\n");
-		retval = -1;
-		goto out;
-	}
-	unit = isdn_ppp_if_get_unit(lp->name);	/* get unit number from interface name .. ugly! */
-	if (unit < 0) {
-		printk(KERN_ERR "isdn_ppp_bind: illegal interface name %s.\n", lp->name);
-		retval = -1;
-		goto out;
-	}
-	
-	lp->ppp_slot = i;
-	is = ippp_table[i];
-	is->lp = lp;
-	is->unit = unit;
-	is->state = IPPP_OPEN | IPPP_ASSIGNED;	/* assigned to a netdevice but not connected */
-#ifdef CONFIG_ISDN_MPP
-	retval = isdn_ppp_mp_init(lp, NULL);
-	if (retval < 0)
-		goto out;
-#endif /* CONFIG_ISDN_MPP */
-
-	retval = lp->ppp_slot;
-
- out:
-	restore_flags(flags);
-	return retval;
-}
-
-/*
- * kick the ipppd on the device
- * (wakes up daemon after B-channel connect)
- */
-
-void
-isdn_ppp_wakeup_daemon(isdn_net_local * lp)
-{
-	if (lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS)
-		return;
-
-	ippp_table[lp->ppp_slot]->state = IPPP_OPEN | IPPP_CONNECT | IPPP_NOBLOCK;
-
-		wake_up_interruptible(&ippp_table[lp->ppp_slot]->wq);
-}
-
-/*
- * there was a hangup on the netdevice
- * force wakeup of the ippp device
- * go into 'device waits for release' state
- */
-static int
-isdn_ppp_closewait(int slot)
-{
-	struct ippp_struct *is;
-
-	if (slot < 0 || slot >= ISDN_MAX_CHANNELS)
-		return 0;
-	is = ippp_table[slot];
-
-	if (is->state)
-		wake_up_interruptible(&is->wq);
-
-	is->state = IPPP_CLOSEWAIT;
-	return 1;
-}
-
-/*
- * isdn_ppp_find_slot / isdn_ppp_free_slot
- */
-
-static int
-isdn_ppp_get_slot(void)
-{
-	int i;
-	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-		if (!ippp_table[i]->state)
-			return i;
-	}
-	return -1;
-}
-
-/*
- * isdn_ppp_open
- */
-
-static int
-isdn_ppp_open(struct inode *ino, struct file *file)
-{
-	uint minor = minor(ino->i_rdev) - ISDN_MINOR_PPP;
-	int slot;
-	struct ippp_struct *is;
-
-	slot = isdn_ppp_get_slot();
-	if (slot < 0) {
-		return -EBUSY;
-	}
-	is = file->private_data = ippp_table[slot];
-
-		printk(KERN_DEBUG "ippp, open, slot: %d, minor: %d, state: %04x\n", slot, minor, is->state);
-
-	/* compression stuff */
-	is->link_compressor   = is->compressor = NULL;
-	is->link_decompressor = is->decompressor = NULL;
-	is->link_comp_stat    = is->comp_stat = NULL;
-	is->link_decomp_stat  = is->decomp_stat = NULL;
-	is->compflags = 0;
-
-	is->reset = isdn_ppp_ccp_reset_alloc(is);
-
-	is->lp = NULL;
-	is->mp_seqno = 0;       /* MP sequence number */
-	is->pppcfg = 0;         /* ppp configuration */
-	is->mpppcfg = 0;        /* mppp configuration */
-	is->last_link_seqno = -1;	/* MP: maybe set to Bundle-MIN, when joining a bundle ?? */
-	is->unit = -1;          /* set, when we have our interface */
-	is->mru = 1524;         /* MRU, default 1524 */
-	is->maxcid = 16;        /* VJ: maxcid */
-	is->tk = current;
-	init_waitqueue_head(&is->wq);
-	is->first = is->rq + NUM_RCV_BUFFS - 1;	/* receive queue */
-	is->last = is->rq;
-	is->minor = minor;
-#ifdef CONFIG_ISDN_PPP_VJ
-	/*
-	 * VJ header compression init
-	 */
-	is->slcomp = slhc_init(16, 16);	/* not necessary for 2. link in bundle */
-#endif
-
-	is->state = IPPP_OPEN;
-	isdn_lock_drivers();
-
-	return 0;
-}
-
-/*
- * release ippp device
- */
-static int
-isdn_ppp_release(struct inode *ino, struct file *file)
-{
-	uint minor = minor(ino->i_rdev) - ISDN_MINOR_PPP;
-	int i;
-	struct ippp_struct *is;
-
-	lock_kernel();
-
-	is = file->private_data;
-
-	if (is->debug & 0x1)
-		printk(KERN_DEBUG "ippp: release, minor: %d %lx\n", minor, (long) is->lp);
-
-	if (is->lp) {           /* a lp address says: this link is still up */
-		isdn_net_dev *p = is->lp->netdev;
-
-		is->state &= ~IPPP_CONNECT;	/* -> effect: no call of wakeup */
-		/*
-		 * isdn_net_hangup() calls isdn_ppp_free()
-		 * isdn_ppp_free() sets is->lp to NULL and lp->ppp_slot to -1
-		 * removing the IPPP_CONNECT flag omits calling of isdn_ppp_wakeup_daemon()
-		 */
-		isdn_net_hangup(&p->dev);
-	}
-	for (i = 0; i < NUM_RCV_BUFFS; i++) {
-		if (is->rq[i].buf) {
-			kfree(is->rq[i].buf);
-			is->rq[i].buf = NULL;
-		}
-	}
-	is->first = is->rq + NUM_RCV_BUFFS - 1;	/* receive queue */
-	is->last = is->rq;
-
-#ifdef CONFIG_ISDN_PPP_VJ
-/* TODO: if this was the previous master: link the slcomp to the new master */
-	slhc_free(is->slcomp);
-	is->slcomp = NULL;
-#endif
-
-/* TODO: if this was the previous master: link the the stuff to the new master */
-	if(is->comp_stat)
-		is->compressor->free(is->comp_stat);
-	if(is->link_comp_stat)
-		is->link_compressor->free(is->link_comp_stat);
-	if(is->link_decomp_stat)
-		is->link_decompressor->free(is->link_decomp_stat);
-	if(is->decomp_stat)
-		is->decompressor->free(is->decomp_stat);
-        is->compressor   = is->link_compressor   = NULL;
-        is->decompressor = is->link_decompressor = NULL;
-	is->comp_stat    = is->link_comp_stat    = NULL;
-        is->decomp_stat  = is->link_decomp_stat  = NULL;
-
-	/* Clean up if necessary */
-	if(is->reset)
-		isdn_ppp_ccp_reset_free(is);
-
-	/* this slot is ready for new connections */
-	is->state = 0;
-
-	isdn_unlock_drivers();
-	
-	unlock_kernel();
-	return 0;
-}
-
-/*
- * get_arg .. ioctl helper
- */
-static int
-get_arg(void *b, void *val, int len)
-{
-	if (len <= 0)
-		len = sizeof(void *);
-	if (copy_from_user((void *) val, b, len))
-		return -EFAULT;
-	return 0;
-}
-
-/*
- * set arg .. ioctl helper
- */
-static int
-set_arg(void *b, void *val,int len)
-{
-	if(len <= 0)
-		len = sizeof(void *);
-	if (copy_to_user(b, (void *) val, len))
-		return -EFAULT;
-	return 0;
-}
-
-/*
- * ippp device ioctl
- */
-static int
-isdn_ppp_ioctl(struct inode *ino, struct file *file, unsigned int cmd, unsigned long arg)
-{
-	unsigned long val;
-	int r,i,j;
-	struct ippp_struct *is;
-	isdn_net_local *lp;
-	struct isdn_ppp_comp_data data;
-
-	is = (struct ippp_struct *) file->private_data;
-	lp = is->lp;
-
-	if (is->debug & 0x1)
-		printk(KERN_DEBUG "isdn_ppp_ioctl: minor: %d cmd: %x state: %x\n", is->minor, cmd, is->state);
-
-	if (!(is->state & IPPP_OPEN))
-		return -EINVAL;
-
-	switch (cmd) {
-		case PPPIOCBUNDLE:
-#ifdef CONFIG_ISDN_MPP
-			if (!(is->state & IPPP_CONNECT))
-				return -EINVAL;
-			if ((r = get_arg((void *) arg, &val, sizeof(val) )))
-				return r;
-			printk(KERN_DEBUG "iPPP-bundle: minor: %d, slave unit: %d, master unit: %d\n",
-			       (int) is->minor, (int) is->unit, (int) val);
-			return isdn_ppp_bundle(is, val);
-#else
-			return -1;
-#endif
-			break;
-		case PPPIOCGUNIT:	/* get ppp/isdn unit number */
-			if ((r = set_arg((void *) arg, &is->unit, sizeof(is->unit) )))
-				return r;
-			break;
-		case PPPIOCGIFNAME:
-			if(!lp)
-				return -EINVAL;
-			if ((r = set_arg((void *) arg, lp->name, strlen(lp->name))))
-				return r;
-			break;
-		case PPPIOCGMPFLAGS:	/* get configuration flags */
-			if ((r = set_arg((void *) arg, &is->mpppcfg, sizeof(is->mpppcfg) )))
-				return r;
-			break;
-		case PPPIOCSMPFLAGS:	/* set configuration flags */
-			if ((r = get_arg((void *) arg, &val, sizeof(val) )))
-				return r;
-			is->mpppcfg = val;
-			break;
-		case PPPIOCGFLAGS:	/* get configuration flags */
-			if ((r = set_arg((void *) arg, &is->pppcfg,sizeof(is->pppcfg) )))
-				return r;
-			break;
-		case PPPIOCSFLAGS:	/* set configuration flags */
-			if ((r = get_arg((void *) arg, &val, sizeof(val) ))) {
-				return r;
-			}
-			if (val & SC_ENABLE_IP && !(is->pppcfg & SC_ENABLE_IP) && (is->state & IPPP_CONNECT)) {
-				if (lp) {
-					/* OK .. we are ready to send buffers */
-					netif_wake_queue(&lp->netdev->dev);
-				}
-			}
-			is->pppcfg = val;
-			break;
-		case PPPIOCGIDLE:	/* get idle time information */
-			if (lp) {
-				struct ppp_idle pidle;
-				pidle.xmit_idle = pidle.recv_idle = lp->huptimer;
-				if ((r = set_arg((void *) arg, &pidle,sizeof(struct ppp_idle))))
-					 return r;
-			}
-			break;
-		case PPPIOCSMRU:	/* set receive unit size for PPP */
-			if ((r = get_arg((void *) arg, &val, sizeof(val) )))
-				return r;
-			is->mru = val;
-			break;
-		case PPPIOCSMPMRU:
-			break;
-		case PPPIOCSMPMTU:
-			break;
-		case PPPIOCSMAXCID:	/* set the maximum compression slot id */
-			if ((r = get_arg((void *) arg, &val, sizeof(val) )))
-				return r;
-			val++;
-			if (is->maxcid != val) {
-#ifdef CONFIG_ISDN_PPP_VJ
-				struct slcompress *sltmp;
-#endif
-				if (is->debug & 0x1)
-					printk(KERN_DEBUG "ippp, ioctl: changed MAXCID to %ld\n", val);
-				is->maxcid = val;
-#ifdef CONFIG_ISDN_PPP_VJ
-				sltmp = slhc_init(16, val);
-				if (!sltmp) {
-					printk(KERN_ERR "ippp, can't realloc slhc struct\n");
-					return -ENOMEM;
-				}
-				if (is->slcomp)
-					slhc_free(is->slcomp);
-				is->slcomp = sltmp;
-#endif
-			}
-			break;
-		case PPPIOCGDEBUG:
-			if ((r = set_arg((void *) arg, &is->debug, sizeof(is->debug) )))
-				return r;
-			break;
-		case PPPIOCSDEBUG:
-			if ((r = get_arg((void *) arg, &val, sizeof(val) )))
-				return r;
-			is->debug = val;
-			break;
-		case PPPIOCGCOMPRESSORS:
-			{
-				unsigned long protos[8] = {0,};
-				struct isdn_ppp_compressor *ipc = ipc_head;
-				while(ipc) {
-					j = ipc->num / (sizeof(long)*8);
-					i = ipc->num % (sizeof(long)*8);
-					if(j < 8)
-						protos[j] |= (0x1<<i);
-					ipc = ipc->next;
-				}
-				if ((r = set_arg((void *) arg,protos,8*sizeof(long) )))
-					return r;
-			}
-			break;
-		case PPPIOCSCOMPRESSOR:
-			if ((r = get_arg((void *) arg, &data, sizeof(struct isdn_ppp_comp_data))))
-				return r;
-			return isdn_ppp_set_compressor(is, &data);
-		case PPPIOCGCALLINFO:
-			{
-				struct pppcallinfo pci;
-				memset((char *) &pci,0,sizeof(struct pppcallinfo));
-				if(lp)
-				{
-					strncpy(pci.local_num,lp->msn,63);
-					if(lp->dial) {
-						strncpy(pci.remote_num,lp->dial->num,63);
-					}
-					pci.charge_units = lp->charge;
-					if(lp->outgoing)
-						pci.calltype = CALLTYPE_OUTGOING;
-					else
-						pci.calltype = CALLTYPE_INCOMING;
-					if(lp->flags & ISDN_NET_CALLBACK)
-						pci.calltype |= CALLTYPE_CALLBACK;
-				}
-				return set_arg((void *)arg,&pci,sizeof(struct pppcallinfo));
-			}
-		default:
-			break;
-	}
-	return 0;
-}
-
-static unsigned int
-isdn_ppp_poll(struct file *file, poll_table * wait)
-{
-	unsigned int mask;
-	struct ippp_buf_queue *bf;
-	struct ippp_buf_queue *bl;
-	unsigned long flags;
-	struct ippp_struct *is;
-
-	lock_kernel();
-	is = file->private_data;
-
-	if (is->debug & 0x2)
-		printk(KERN_DEBUG "isdn_ppp_poll: minor: %d\n",
-				minor(file->f_dentry->d_inode->i_rdev));
-
-	/* just registers wait_queue hook. This doesn't really wait. */
-	poll_wait(file, &is->wq, wait);
-
-	if (!(is->state & IPPP_OPEN)) {
-		if(is->state == IPPP_CLOSEWAIT) {
-			mask = POLLHUP;
-			goto out;
-		}
-		printk(KERN_DEBUG "isdn_ppp: device not open\n");
-		mask = POLLERR;
-		goto out;
-	}
-	/* we're always ready to send .. */
-	mask = POLLOUT | POLLWRNORM;
-
-	save_flags(flags);
-	cli();
-	bl = is->last;
-	bf = is->first;
-	/*
-	 * if IPPP_NOBLOCK is set we return even if we have nothing to read
-	 */
-	if (bf->next != bl || (is->state & IPPP_NOBLOCK)) {
-		is->state &= ~IPPP_NOBLOCK;
-		mask |= POLLIN | POLLRDNORM;
-	}
-	restore_flags(flags);
-
- out:
-	unlock_kernel();
-	return mask;
-}
-
-/*
- *  fill up isdn_ppp_read() queue ..
- */
-
-static int
-isdn_ppp_fill_rq(unsigned char *buf, int len, int proto, int slot)
-{
-	struct ippp_buf_queue *bf,
-	*bl;
-	unsigned long flags;
-	unsigned char *nbuf;
-	struct ippp_struct *is;
-
-	if (slot < 0 || slot >= ISDN_MAX_CHANNELS) {
-		printk(KERN_WARNING "ippp: illegal slot.\n");
-		return 0;
-	}
-	is = ippp_table[slot];
-
-	if (!(is->state & IPPP_CONNECT)) {
-		printk(KERN_DEBUG "ippp: device not activated.\n");
-		return 0;
-	}
-	nbuf = (unsigned char *) kmalloc(len + 4, GFP_ATOMIC);
-	if (!nbuf) {
-		printk(KERN_WARNING "ippp: Can't alloc buf\n");
-		return 0;
-	}
-	nbuf[0] = PPP_ALLSTATIONS;
-	nbuf[1] = PPP_UI;
-	nbuf[2] = proto >> 8;
-	nbuf[3] = proto & 0xff;
-	memcpy(nbuf + 4, buf, len);
-
-	save_flags(flags);
-	cli();
-
-	bf = is->first;
-	bl = is->last;
-
-	if (bf == bl) {
-		printk(KERN_WARNING "ippp: Queue is full; discarding first buffer\n");
-		bf = bf->next;
-		kfree(bf->buf);
-		is->first = bf;
-	}
-	bl->buf = (char *) nbuf;
-	bl->len = len + 4;
-
-	is->last = bl->next;
-	restore_flags(flags);
-
-		wake_up_interruptible(&is->wq);
-
-	return len;
-}
-
-/*
- * read() .. non-blocking: ipppd calls it only after select()
- *           reports, that there is data
- */
-
-static int
-isdn_ppp_read(struct file *file, char *buf, size_t count, loff_t *off)
-{
-	struct ippp_struct *is;
-	struct ippp_buf_queue *b;
-	unsigned long flags;
-	unsigned char *save_buf;
-	int retval;
-
-	if (off != &file->f_pos)
-		return -ESPIPE;
-	
-	lock_kernel();
-
-	is = file->private_data;
-
-	if (!(is->state & IPPP_OPEN)) {
-		retval = 0;
-		goto out;
-	}
-	retval = verify_area(VERIFY_WRITE, (void *) buf, count);
-	if (retval)
-		goto out;
-
-	save_flags(flags);
-	cli();
-
-	b = is->first->next;
-	save_buf = b->buf;
-	if (!save_buf) {
-		restore_flags(flags);
-		retval = -EAGAIN;
-		goto out;
-	}
-	if (b->len < count)
-		count = b->len;
-	b->buf = NULL;
-	is->first = b;
-
-	restore_flags(flags);
-
-	copy_to_user(buf, save_buf, count);
-	kfree(save_buf);
-
-	retval = count;
-
- out:
-	unlock_kernel();
-	return retval;
-}
-
-/*
- * ipppd wanna write a packet to the card .. non-blocking
- */
-
-static int
-isdn_ppp_write(struct file *file, const char *buf, size_t count, loff_t *off)
-{
-	isdn_net_local *lp;
-	struct ippp_struct *is;
-	int proto;
-	unsigned char protobuf[4];
-	int retval;
-
-	if (off != &file->f_pos)
-		return -ESPIPE;
-
-	lock_kernel();
-
-	is = file->private_data;
-
-	if (!(is->state & IPPP_CONNECT)) {
-		retval = 0;
-		goto out;
-	}
-
-	lp = is->lp;
-
-	/* -> push it directly to the lowlevel interface */
-
-	if (!lp)
-		printk(KERN_DEBUG "isdn_ppp_write: lp == NULL\n");
-	else {
-		/*
-		 * Don't reset huptimer for
-		 * LCP packets. (Echo requests).
-		 */
-		if (copy_from_user(protobuf, buf, 4)) {
-			retval = -EFAULT;
-			goto out;
-		}
-		proto = PPP_PROTOCOL(protobuf);
-		if (proto != PPP_LCP)
-			lp->huptimer = 0;
-
-		if (lp->isdn_device < 0 || lp->isdn_channel < 0) {
-			retval = 0;
-			goto out;
-		}
-		if ((dev->drv[lp->isdn_device]->flags & DRV_FLAG_RUNNING) &&
-			lp->dialstate == 0 &&
-		    (lp->flags & ISDN_NET_CONNECTED)) {
-			unsigned short hl;
-			struct sk_buff *skb;
-			/*
-			 * we need to reserve enought space in front of
-			 * sk_buff. old call to dev_alloc_skb only reserved
-			 * 16 bytes, now we are looking what the driver want
-			 */
-			hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen;
-			skb = alloc_skb(hl+count, GFP_ATOMIC);
-			if (!skb) {
-				printk(KERN_WARNING "isdn_ppp_write: out of memory!\n");
-				retval = count;
-				goto out;
-			}
-			skb_reserve(skb, hl);
-			if (copy_from_user(skb_put(skb, count), buf, count))
-			{
-				kfree_skb(skb);
-				retval = -EFAULT;
-				goto out;
-			}
-			if (is->debug & 0x40) {
-				printk(KERN_DEBUG "ppp xmit: len %d\n", (int) skb->len);
-				isdn_ppp_frame_log("xmit", skb->data, skb->len, 32,is->unit,lp->ppp_slot);
-			}
-
-			isdn_ppp_send_ccp(lp->netdev,lp,skb); /* keeps CCP/compression states in sync */
-
-			isdn_net_write_super(lp, skb);
-		}
-	}
-	retval = count;
-	
- out:
-	unlock_kernel();
-	return retval;
-}
-
-struct file_operations isdn_ppp_fops =
-{
-	owner:		THIS_MODULE,
-	llseek:		no_llseek,
-	read:		isdn_ppp_read,
-	write:		isdn_ppp_write,
-	poll:		isdn_ppp_poll,
-	ioctl:		isdn_ppp_ioctl,
-	open:		isdn_ppp_open,
-	release:	isdn_ppp_release,
-};
-
-/*
- * init memory, structures etc.
- */
-
-int
-isdn_ppp_init(void)
-{
-	int i,
-	 j;
-	 
-#ifdef CONFIG_ISDN_MPP
-	if( isdn_ppp_mp_bundle_array_init() < 0 )
-		return -ENOMEM;
-#endif /* CONFIG_ISDN_MPP */
-
-	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-		if (!(ippp_table[i] = (struct ippp_struct *)
-		      kmalloc(sizeof(struct ippp_struct), GFP_KERNEL))) {
-			printk(KERN_WARNING "isdn_ppp_init: Could not alloc ippp_table\n");
-			for (j = 0; j < i; j++)
-				kfree(ippp_table[j]);
-			return -1;
-		}
-		memset((char *) ippp_table[i], 0, sizeof(struct ippp_struct));
-		ippp_table[i]->state = 0;
-		ippp_table[i]->first = ippp_table[i]->rq + NUM_RCV_BUFFS - 1;
-		ippp_table[i]->last = ippp_table[i]->rq;
-
-		for (j = 0; j < NUM_RCV_BUFFS; j++) {
-			ippp_table[i]->rq[j].buf = NULL;
-			ippp_table[i]->rq[j].last = ippp_table[i]->rq +
-			    (NUM_RCV_BUFFS + j - 1) % NUM_RCV_BUFFS;
-			ippp_table[i]->rq[j].next = ippp_table[i]->rq + (j + 1) % NUM_RCV_BUFFS;
-		}
-	}
-	return 0;
-}
-
-void
-isdn_ppp_cleanup(void)
-{
-	int i;
-
-	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
-		kfree(ippp_table[i]);
-
-#ifdef CONFIG_ISDN_MPP
-	if (isdn_ppp_bundle_arr)
-		kfree(isdn_ppp_bundle_arr);
-#endif /* CONFIG_ISDN_MPP */
-
-}
-
-/*
- * check for address/control field and skip if allowed
- * retval != 0 -> discard packet silently
- */
-static int isdn_ppp_skip_ac(struct ippp_struct *is, struct sk_buff *skb) 
-{
-	if (skb->len < 1)
-		return -1;
-
-	if (skb->data[0] == 0xff) {
-		if (skb->len < 2)
-			return -1;
-
-		if (skb->data[1] != 0x03)
-			return -1;
-
-		// skip address/control (AC) field
-		skb_pull(skb, 2);
-	} else { 
-		if (is->pppcfg & SC_REJ_COMP_AC)
-			// if AC compression was not negotiated, but used, discard packet
-			return -1;
-	}
-	return 0;
-}
-
-/*
- * get the PPP protocol header and pull skb
- * retval < 0 -> discard packet silently
- */
-static int isdn_ppp_strip_proto(struct sk_buff *skb) 
-{
-	int proto;
-	
-	if (skb->len < 1)
-		return -1;
-
-	if (skb->data[0] & 0x1) {
-		// protocol field is compressed
-		proto = skb->data[0];
-		skb_pull(skb, 1);
-	} else {
-		if (skb->len < 2)
-			return -1;
-		proto = ((int) skb->data[0] << 8) + skb->data[1];
-		skb_pull(skb, 2);
-	}
-	return proto;
-}
-
-
-/*
- * handler for incoming packets on a syncPPP interface
- */
-void isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb)
-{
-	struct ippp_struct *is;
-	int slot;
-	int proto;
-
-	if (net_dev->local->master)
-		BUG(); // we're called with the master device always
-
-	slot = lp->ppp_slot;
-	if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
-		printk(KERN_ERR "isdn_ppp_receive: lp->ppp_slot %d\n", lp->ppp_slot);
-		kfree_skb(skb);
-		return;
-	}
-	is = ippp_table[slot];
-
-	if (is->debug & 0x4) {
-		printk(KERN_DEBUG "ippp_receive: is:%08lx lp:%08lx slot:%d unit:%d len:%d\n",
-		       (long)is,(long)lp,lp->ppp_slot,is->unit,(int) skb->len);
-		isdn_ppp_frame_log("receive", skb->data, skb->len, 32,is->unit,lp->ppp_slot);
-	}
-
- 	if (isdn_ppp_skip_ac(is, skb) < 0) {
- 		kfree_skb(skb);
- 		return;
- 	}
-  	proto = isdn_ppp_strip_proto(skb);
- 	if (proto < 0) {
- 		kfree_skb(skb);
- 		return;
- 	}
-  
-#ifdef CONFIG_ISDN_MPP
- 	if (is->compflags & SC_LINK_DECOMP_ON) {
- 		skb = isdn_ppp_decompress(skb, is, NULL, &proto);
- 		if (!skb) // decompression error
- 			return;
- 	}
-	
- 	if (!(is->mpppcfg & SC_REJ_MP_PROT)) { // we agreed to receive MPPP
-  		if (proto == PPP_MP) {
-  			isdn_ppp_mp_receive(net_dev, lp, skb);
- 			return;
- 		}
- 	} 
-#endif
- 	isdn_ppp_push_higher(net_dev, lp, skb, proto);
-}
-
-/*
- * we receive a reassembled frame, MPPP has been taken care of before.
- * address/control and protocol have been stripped from the skb
- * note: net_dev has to be master net_dev
- */
-static void
-isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb, int proto)
-{
-	struct net_device *dev = &net_dev->dev;
- 	struct ippp_struct *is, *mis;
-	int slot;
-
-	slot = lp->ppp_slot;
-	if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
-		printk(KERN_ERR "isdn_ppp_push_higher: lp->ppp_slot %d\n", lp->ppp_slot);
-		goto drop_packet;
-	}
-	is = ippp_table[slot];
- 	
- 	if (lp->master) { // FIXME?
- 		slot = ((isdn_net_local *) (lp->master->priv))->ppp_slot;
- 		if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
- 			printk(KERN_ERR "isdn_ppp_push_higher: master->ppp_slot %d\n", lp->ppp_slot);
-			goto drop_packet;
- 		}
- 	}
- 	mis = ippp_table[slot];
-
-	if (is->debug & 0x10) {
-		printk(KERN_DEBUG "push, skb %d %04x\n", (int) skb->len, proto);
-		isdn_ppp_frame_log("rpush", skb->data, skb->len, 32,is->unit,lp->ppp_slot);
-	}
-	if (mis->compflags & SC_DECOMP_ON) {
-		skb = isdn_ppp_decompress(skb, is, mis, &proto);
-		if (!skb) // decompression error
-  			return;
-  	}
-	switch (proto) {
-		case PPP_IPX:  /* untested */
-			if (is->debug & 0x20)
-				printk(KERN_DEBUG "isdn_ppp: IPX\n");
-			skb->protocol = htons(ETH_P_IPX);
-			break;
-		case PPP_IP:
-			if (is->debug & 0x20)
-				printk(KERN_DEBUG "isdn_ppp: IP\n");
-			skb->protocol = htons(ETH_P_IP);
-			break;
-		case PPP_COMP:
-		case PPP_COMPFRAG:
-			printk(KERN_INFO "isdn_ppp: unexpected compressed frame dropped\n");
-			goto drop_packet;
-#ifdef CONFIG_ISDN_PPP_VJ
-		case PPP_VJC_UNCOMP:
-			if (is->debug & 0x20)
-				printk(KERN_DEBUG "isdn_ppp: VJC_UNCOMP\n");
-			if (slhc_remember(ippp_table[net_dev->local->ppp_slot]->slcomp, skb->data, skb->len) <= 0) {
-				printk(KERN_WARNING "isdn_ppp: received illegal VJC_UNCOMP frame!\n");
-				goto drop_packet;
-			}
-			skb->protocol = htons(ETH_P_IP);
-			break;
-		case PPP_VJC_COMP:
-			if (is->debug & 0x20)
-				printk(KERN_DEBUG "isdn_ppp: VJC_COMP\n");
-			{
-				struct sk_buff *skb_old = skb;
-				int pkt_len;
-				skb = dev_alloc_skb(skb_old->len + 128);
-
-				if (!skb) {
-					printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name);
-					skb = skb_old;
-					goto drop_packet;
-				}
-				skb_put(skb, skb_old->len + 128);
-				memcpy(skb->data, skb_old->data, skb_old->len);
-				pkt_len = slhc_uncompress(ippp_table[net_dev->local->ppp_slot]->slcomp,
-						skb->data, skb_old->len);
-				kfree_skb(skb_old);
-				if (pkt_len < 0)
-					goto drop_packet;
-
-				skb_trim(skb, pkt_len);
-				skb->protocol = htons(ETH_P_IP);
-			}
-			break;
-#endif
-		case PPP_CCP:
-		case PPP_CCPFRAG:
-			isdn_ppp_receive_ccp(net_dev,lp,skb,proto);
-			/* Dont pop up ResetReq/Ack stuff to the daemon any
-			   longer - the job is done already */
-			if(skb->data[0] == CCP_RESETREQ ||
-			   skb->data[0] == CCP_RESETACK)
-				break;
-			/* fall through */
-		default:
-			isdn_ppp_fill_rq(skb->data, skb->len, proto, lp->ppp_slot);	/* push data to pppd device */
-			kfree_skb(skb);
-			return;
-	}
-
- 	/* Reset hangup-timer */
- 	lp->huptimer = 0;
-
-	skb->dev = dev;
-	skb->mac.raw = skb->data;
-	netif_rx(skb);
-	/* net_dev->local->stats.rx_packets++; done in isdn_net.c */
-	return;
-
- drop_packet:
-	net_dev->local->stats.rx_dropped++;
-	kfree_skb(skb);
-}
-
-/*
- * isdn_ppp_skb_push ..
- * checks whether we have enough space at the beginning of the skb
- * and allocs a new SKB if necessary
- */
-static unsigned char *isdn_ppp_skb_push(struct sk_buff **skb_p,int len)
-{
-	struct sk_buff *skb = *skb_p;
-
-	if(skb_headroom(skb) < len) {
-		struct sk_buff *nskb = skb_realloc_headroom(skb, len);
-
-		if (!nskb) {
-			printk(KERN_ERR "isdn_ppp_skb_push: can't realloc headroom!\n");
-			dev_kfree_skb(skb);
-			return NULL;
-		}
-		printk(KERN_DEBUG "isdn_ppp_skb_push:under %d %d\n",skb_headroom(skb),len);
-		dev_kfree_skb(skb);
-		*skb_p = nskb;
-		return skb_push(nskb, len);
-	}
-	return skb_push(skb,len);
-}
-
-
-/*
- * send ppp frame .. we expect a PIDCOMPressable proto --
- *  (here: currently always PPP_IP,PPP_VJC_COMP,PPP_VJC_UNCOMP)
- *
- * VJ compression may change skb pointer!!! .. requeue with old
- * skb isn't allowed!!
- */
-
-int
-isdn_ppp_xmit(struct sk_buff *skb, struct net_device *netdev)
-{
-	isdn_net_local *lp,*mlp;
-	isdn_net_dev *nd;
-	unsigned int proto = PPP_IP;     /* 0x21 */
-	struct ippp_struct *ipt,*ipts;
-	int slot;
-
-	mlp = (isdn_net_local *) (netdev->priv);
-	nd = mlp->netdev;       /* get master lp */
-
-	slot = mlp->ppp_slot;
-	if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
-		printk(KERN_ERR "isdn_ppp_xmit: lp->ppp_slot %d\n", mlp->ppp_slot);
-		kfree_skb(skb);
-		return 0;
-	}
-	ipts = ippp_table[slot];
-
-	if (!(ipts->pppcfg & SC_ENABLE_IP)) {	/* PPP connected ? */
-		if (ipts->debug & 0x1)
-			printk(KERN_INFO "%s: IP frame delayed.\n", netdev->name);
-		return 1;
-	}
-
-	switch (ntohs(skb->protocol)) {
-		case ETH_P_IP:
-			proto = PPP_IP;
-			break;
-		case ETH_P_IPX:
-			proto = PPP_IPX;	/* untested */
-			break;
-		default:
-			printk(KERN_ERR "isdn_ppp: skipped unsupported protocol: %#x.\n", 
-			       skb->protocol);
-			dev_kfree_skb(skb);
-			return 0;
-	}
-
-	lp = isdn_net_get_locked_lp(nd);
-	if (!lp) {
-		printk(KERN_WARNING "%s: all channels busy - requeuing!\n", netdev->name);
-		return 1;
-	}
-	/* we have our lp locked from now on */
-
-	slot = lp->ppp_slot;
-	if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
-		printk(KERN_ERR "isdn_ppp_xmit: lp->ppp_slot %d\n", lp->ppp_slot);
-		kfree_skb(skb);
-		return 0;
-	}
-	ipt = ippp_table[slot];
-	lp->huptimer = 0;
-
-	/*
-	 * after this line .. requeueing in the device queue is no longer allowed!!!
-	 */
-
-	/* Pull off the fake header we stuck on earlier to keep
-	 * the fragmentation code happy.
-	 */
-	skb_pull(skb,IPPP_MAX_HEADER);
-
-	if (ipt->debug & 0x4)
-		printk(KERN_DEBUG "xmit skb, len %d\n", (int) skb->len);
-        if (ipts->debug & 0x40)
-                isdn_ppp_frame_log("xmit0", skb->data, skb->len, 32,ipts->unit,lp->ppp_slot);
-
-#ifdef CONFIG_ISDN_PPP_VJ
-	if (proto == PPP_IP && ipts->pppcfg & SC_COMP_TCP) {	/* ipts here? probably yes, but check this again */
-		struct sk_buff *new_skb;
-	        unsigned short hl;
-		/*
-		 * we need to reserve enought space in front of
-		 * sk_buff. old call to dev_alloc_skb only reserved
-		 * 16 bytes, now we are looking what the driver want.
-		 */
-		hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen + IPPP_MAX_HEADER;
-		/* 
-		 * Note: hl might still be insufficient because the method
-		 * above does not account for a possibible MPPP slave channel
-		 * which had larger HL header space requirements than the
-		 * master.
-		 */
-		new_skb = alloc_skb(hl+skb->len, GFP_ATOMIC);
-		if (new_skb) {
-			u_char *buf;
-			int pktlen;
-
-			skb_reserve(new_skb, hl);
-			new_skb->dev = skb->dev;
-			skb_put(new_skb, skb->len);
-			buf = skb->data;
-
-			pktlen = slhc_compress(ipts->slcomp, skb->data, skb->len, new_skb->data,
-				 &buf, !(ipts->pppcfg & SC_NO_TCP_CCID));
-
-			if (buf != skb->data) {	
-				if (new_skb->data != buf)
-					printk(KERN_ERR "isdn_ppp: FATAL error after slhc_compress!!\n");
-				dev_kfree_skb(skb);
-				skb = new_skb;
-			} else {
-				dev_kfree_skb(new_skb);
-			}
-
-			skb_trim(skb, pktlen);
-			if (skb->data[0] & SL_TYPE_COMPRESSED_TCP) {	/* cslip? style -> PPP */
-				proto = PPP_VJC_COMP;
-				skb->data[0] ^= SL_TYPE_COMPRESSED_TCP;
-			} else {
-				if (skb->data[0] >= SL_TYPE_UNCOMPRESSED_TCP)
-					proto = PPP_VJC_UNCOMP;
-				skb->data[0] = (skb->data[0] & 0x0f) | 0x40;
-			}
-		}
-	}
-#endif
-
-	/*
-	 * normal (single link) or bundle compression
-	 */
-	if(ipts->compflags & SC_COMP_ON) {
-		/* We send compressed only if both down- und upstream
-		   compression is negotiated, that means, CCP is up */
-		if(ipts->compflags & SC_DECOMP_ON) {
-			skb = isdn_ppp_compress(skb,&proto,ipt,ipts,0);
-		} else {
-			printk(KERN_DEBUG "isdn_ppp: CCP not yet up - sending as-is\n");
-		}
-	}
-
-	if (ipt->debug & 0x24)
-		printk(KERN_DEBUG "xmit2 skb, len %d, proto %04x\n", (int) skb->len, proto);
-
-#ifdef CONFIG_ISDN_MPP
-	if (ipt->mpppcfg & SC_MP_PROT) {
-		/* we get mp_seqno from static isdn_net_local */
-		long mp_seqno = ipts->mp_seqno;
-		ipts->mp_seqno++;
-		if (ipt->mpppcfg & SC_OUT_SHORT_SEQ) {
-			unsigned char *data = isdn_ppp_skb_push(&skb, 3);
-			if(!data)
-				goto unlock;
-			mp_seqno &= 0xfff;
-			data[0] = MP_BEGIN_FRAG | MP_END_FRAG | ((mp_seqno >> 8) & 0xf);	/* (B)egin & (E)ndbit .. */
-			data[1] = mp_seqno & 0xff;
-			data[2] = proto;	/* PID compression */
-		} else {
-			unsigned char *data = isdn_ppp_skb_push(&skb, 5);
-			if(!data)
-				goto unlock;
-			data[0] = MP_BEGIN_FRAG | MP_END_FRAG;	/* (B)egin & (E)ndbit .. */
-			data[1] = (mp_seqno >> 16) & 0xff;	/* sequence number: 24bit */
-			data[2] = (mp_seqno >> 8) & 0xff;
-			data[3] = (mp_seqno >> 0) & 0xff;
-			data[4] = proto;	/* PID compression */
-		}
-		proto = PPP_MP; /* MP Protocol, 0x003d */
-	}
-#endif
-
-	/*
-	 * 'link in bundle' compression  ...
-	 */
-	if(ipt->compflags & SC_LINK_COMP_ON)
-		skb = isdn_ppp_compress(skb,&proto,ipt,ipts,1);
-
-	if( (ipt->pppcfg & SC_COMP_PROT) && (proto <= 0xff) ) {
-		unsigned char *data = isdn_ppp_skb_push(&skb,1);
-		if(!data)
-			goto unlock;
-		data[0] = proto & 0xff;
-	}
-	else {
-		unsigned char *data = isdn_ppp_skb_push(&skb,2);
-		if(!data)
-			goto unlock;
-		data[0] = (proto >> 8) & 0xff;
-		data[1] = proto & 0xff;
-	}
-	if(!(ipt->pppcfg & SC_COMP_AC)) {
-		unsigned char *data = isdn_ppp_skb_push(&skb,2);
-		if(!data)
-			goto unlock;
-		data[0] = 0xff;    /* All Stations */
-		data[1] = 0x03;    /* Unnumbered information */
-	}
-
-	/* tx-stats are now updated via BSENT-callback */
-
-	if (ipts->debug & 0x40) {
-		printk(KERN_DEBUG "skb xmit: len: %d\n", (int) skb->len);
-		isdn_ppp_frame_log("xmit", skb->data, skb->len, 32,ipt->unit,lp->ppp_slot);
-	}
-	
-	isdn_net_writebuf_skb(lp, skb);
-
- unlock:
-	spin_unlock_bh(&lp->xmit_lock);
-	return 0;
-}
-
-#ifdef CONFIG_ISDN_MPP
-
-/* this is _not_ rfc1990 header, but something we convert both short and long
- * headers to for convinience's sake:
- * 	byte 0 is flags as in rfc1990
- *	bytes 1...4 is 24-bit seqence number converted to host byte order 
- */
-#define MP_HEADER_LEN	5
-
-#define MP_LONGSEQ_MASK		0x00ffffff
-#define MP_SHORTSEQ_MASK	0x00000fff
-#define MP_LONGSEQ_MAX		MP_LONGSEQ_MASK
-#define MP_SHORTSEQ_MAX		MP_SHORTSEQ_MASK
-#define MP_LONGSEQ_MAXBIT	((MP_LONGSEQ_MASK+1)>>1)
-#define MP_SHORTSEQ_MAXBIT	((MP_SHORTSEQ_MASK+1)>>1)
-
-/* sequence-wrap safe comparisions (for long sequence)*/ 
-#define MP_LT(a,b)	((a-b)&MP_LONGSEQ_MAXBIT)
-#define MP_LE(a,b) 	!((b-a)&MP_LONGSEQ_MAXBIT)
-#define MP_GT(a,b) 	((b-a)&MP_LONGSEQ_MAXBIT)
-#define MP_GE(a,b)	!((a-b)&MP_LONGSEQ_MAXBIT)
-
-#define MP_SEQ(f)	((*(u32*)(f->data+1)))
-#define MP_FLAGS(f)	(f->data[0])
-
-static int isdn_ppp_mp_bundle_array_init(void)
-{
-	int i;
-	int sz = ISDN_MAX_CHANNELS*sizeof(ippp_bundle);
-	if( (isdn_ppp_bundle_arr = (ippp_bundle*)kmalloc(sz, 
-							GFP_KERNEL)) == NULL )
-		return -ENOMEM;
-	memset(isdn_ppp_bundle_arr, 0, sz);
-	for( i = 0; i < ISDN_MAX_CHANNELS; i++ )
-		spin_lock_init(&isdn_ppp_bundle_arr[i].lock);
-	return 0;
-}
-
-static ippp_bundle * isdn_ppp_mp_bundle_alloc(void)
-{
-	int i;
-	for( i = 0; i < ISDN_MAX_CHANNELS; i++ )
-		if (isdn_ppp_bundle_arr[i].ref_ct <= 0)
-			return (isdn_ppp_bundle_arr + i);
-	return NULL;
-}
-
-static int isdn_ppp_mp_init( isdn_net_local * lp, ippp_bundle * add_to )
-{
-	struct ippp_struct * is = ippp_table[lp->ppp_slot];
-   
-	if (add_to) {
-		if( lp->netdev->pb )
-			lp->netdev->pb->ref_ct--;
-		lp->netdev->pb = add_to;
-	} else {		/* first link in a bundle */
-		is->mp_seqno = 0;
-		if ((lp->netdev->pb = isdn_ppp_mp_bundle_alloc()) == NULL)
-			return -ENOMEM;
-		lp->next = lp->last = lp;	/* nobody else in a queue */
-		lp->netdev->pb->frags = NULL;
-		lp->netdev->pb->frames = 0;
-		lp->netdev->pb->seq = LONG_MAX;
-	}
-	lp->netdev->pb->ref_ct++;
-	
-	is->last_link_seqno = 0;
-	return 0;
-}
-
-static u32 isdn_ppp_mp_get_seq( int short_seq, 
-					struct sk_buff * skb, u32 last_seq );
-static struct sk_buff * isdn_ppp_mp_discard( ippp_bundle * mp,
-			struct sk_buff * from, struct sk_buff * to );
-static void isdn_ppp_mp_reassembly( isdn_net_dev * net_dev, isdn_net_local * lp,
-				struct sk_buff * from, struct sk_buff * to );
-static void isdn_ppp_mp_free_skb( ippp_bundle * mp, struct sk_buff * skb );
-static void isdn_ppp_mp_print_recv_pkt( int slot, struct sk_buff * skb );
-
-static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, 
-							struct sk_buff *skb)
-{
-	struct ippp_struct *is;
-	isdn_net_local * lpq;
-	ippp_bundle * mp;
-	isdn_mppp_stats * stats;
-	struct sk_buff * newfrag, * frag, * start, *nextf;
-	u32 newseq, minseq, thisseq;
-	unsigned long flags;
-	int slot;
-
-	spin_lock_irqsave(&net_dev->pb->lock, flags);
-    	mp = net_dev->pb;
-        stats = &mp->stats;
-	slot = lp->ppp_slot;
-	if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
-		printk(KERN_ERR "isdn_ppp_mp_receive: lp->ppp_slot %d\n", lp->ppp_slot);
-		stats->frame_drops++;
-		dev_kfree_skb(skb);
-		spin_unlock_irqrestore(&mp->lock, flags);
-		return;
-	}
-	is = ippp_table[slot];
-    	if( ++mp->frames > stats->max_queue_len )
-		stats->max_queue_len = mp->frames;
-	
-	if (is->debug & 0x8)
-		isdn_ppp_mp_print_recv_pkt(lp->ppp_slot, skb);
-
-	newseq = isdn_ppp_mp_get_seq(is->mpppcfg & SC_IN_SHORT_SEQ, 
-						skb, is->last_link_seqno);
-
-
-	/* if this packet seq # is less than last already processed one,
-	 * toss it right away, but check for sequence start case first 
-	 */
-	if( mp->seq > MP_LONGSEQ_MAX && (newseq & MP_LONGSEQ_MAXBIT) ) {
-		mp->seq = newseq;	/* the first packet: required for
-					 * rfc1990 non-compliant clients --
-					 * prevents constant packet toss */
-	} else if( MP_LT(newseq, mp->seq) ) {
-		stats->frame_drops++;
-		isdn_ppp_mp_free_skb(mp, skb);
-		spin_unlock_irqrestore(&mp->lock, flags);
-		return;
-	}
-	
-	/* find the minimum received sequence number over all links */
-	is->last_link_seqno = minseq = newseq;
-	for (lpq = net_dev->queue;;) {
-		slot = lpq->ppp_slot;
-		if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
-			printk(KERN_ERR "isdn_ppp_mp_receive: lpq->ppp_slot %d\n", lpq->ppp_slot);
-		} else {
-			u32 lls = ippp_table[slot]->last_link_seqno;
-			if (MP_LT(lls, minseq))
-				minseq = lls;
-		}
-		if ((lpq = lpq->next) == net_dev->queue)
-			break;
-	}
-	if (MP_LT(minseq, mp->seq))
-		minseq = mp->seq;	/* can't go beyond already processed
-					 * packets */
-	newfrag = skb;
-
-  	/* if this new fragment is before the first one, then enqueue it now. */
-  	if ((frag = mp->frags) == NULL || MP_LT(newseq, MP_SEQ(frag))) {
-		newfrag->next = frag;
-    		mp->frags = frag = newfrag;
-    		newfrag = NULL;
-  	}
-
-  	start = MP_FLAGS(frag) & MP_BEGIN_FRAG &&
-				MP_SEQ(frag) == mp->seq ? frag : NULL;
-
-	/* 
-	 * main fragment traversing loop
-	 *
-	 * try to accomplish several tasks:
-	 * - insert new fragment into the proper sequence slot (once that's done
-	 *   newfrag will be set to NULL)
-	 * - reassemble any complete fragment sequence (non-null 'start'
-	 *   indicates there is a continguous sequence present)
-	 * - discard any incomplete sequences that are below minseq -- due
-	 *   to the fact that sender always increment sequence number, if there
-	 *   is an incomplete sequence below minseq, no new fragments would
-	 *   come to complete such sequence and it should be discarded
-	 *
-	 * loop completes when we accomplished the following tasks:
-	 * - new fragment is inserted in the proper sequence ('newfrag' is 
-	 *   set to NULL)
-	 * - we hit a gap in the sequence, so no reassembly/processing is 
-	 *   possible ('start' would be set to NULL)
-	 *
-	 * algorightm for this code is derived from code in the book
-	 * 'PPP Design And Debugging' by James Carlson (Addison-Wesley)
-	 */
-  	while (start != NULL || newfrag != NULL) {
-
-    		thisseq = MP_SEQ(frag);
-    		nextf = frag->next;
-
-    		/* drop any duplicate fragments */
-    		if (newfrag != NULL && thisseq == newseq) {
-      			isdn_ppp_mp_free_skb(mp, newfrag);
-      			newfrag = NULL;
-    		}
-
-    		/* insert new fragment before next element if possible. */
-    		if (newfrag != NULL && (nextf == NULL || 
-						MP_LT(newseq, MP_SEQ(nextf)))) {
-      			newfrag->next = nextf;
-      			frag->next = nextf = newfrag;
-      			newfrag = NULL;
-    		}
-
-    		if (start != NULL) {
-	    		/* check for misplaced start */
-      			if (start != frag && (MP_FLAGS(frag) & MP_BEGIN_FRAG)) {
-				printk(KERN_WARNING"isdn_mppp(seq %d): new "
-				      "BEGIN flag with no prior END", thisseq);
-				stats->seqerrs++;
-				stats->frame_drops++;
-				start = isdn_ppp_mp_discard(mp, start,frag);
-				nextf = frag->next;
-      			}
-    		} else if (MP_LE(thisseq, minseq)) {		
-      			if (MP_FLAGS(frag) & MP_BEGIN_FRAG)
-				start = frag;
-      			else {
-				if (MP_FLAGS(frag) & MP_END_FRAG)
-	  				stats->frame_drops++;
-				if( mp->frags == frag )
-					mp->frags = nextf;	
-				isdn_ppp_mp_free_skb(mp, frag);
-				frag = nextf;
-				continue;
-      			}
-		}
-		
-		/* if start is non-null and we have end fragment, then
-		 * we have full reassembly sequence -- reassemble 
-		 * and process packet now
-		 */
-    		if (start != NULL && (MP_FLAGS(frag) & MP_END_FRAG)) {
-      			minseq = mp->seq = (thisseq+1) & MP_LONGSEQ_MASK;
-      			/* Reassemble the packet then dispatch it */
-			isdn_ppp_mp_reassembly(net_dev, lp, start, nextf);
-      
-      			start = NULL;
-      			frag = NULL;
-
-      			mp->frags = nextf;
-    		}
-
-		/* check if need to update start pointer: if we just
-		 * reassembled the packet and sequence is contiguous
-		 * then next fragment should be the start of new reassembly
-		 * if sequence is contiguous, but we haven't reassembled yet,
-		 * keep going.
-		 * if sequence is not contiguous, either clear everyting
-		 * below low watermark and set start to the next frag or
-		 * clear start ptr.
-		 */ 
-    		if (nextf != NULL && 
-		    ((thisseq+1) & MP_LONGSEQ_MASK) == MP_SEQ(nextf)) {
-      			/* if we just reassembled and the next one is here, 
-			 * then start another reassembly. */
-
-      			if (frag == NULL) {
-				if (MP_FLAGS(nextf) & MP_BEGIN_FRAG)
-	  				start = nextf;
-				else
-				{
-	  				printk(KERN_WARNING"isdn_mppp(seq %d):"
-						" END flag with no following "
-						"BEGIN", thisseq);
-					stats->seqerrs++;
-				}
-			}
-
-    		} else {
-			if ( nextf != NULL && frag != NULL &&
-						MP_LT(thisseq, minseq)) {
-				/* we've got a break in the sequence
-				 * and we not at the end yet
-				 * and we did not just reassembled
-				 *(if we did, there wouldn't be anything before)
-				 * and we below the low watermark 
-			 	 * discard all the frames below low watermark 
-				 * and start over */
-				stats->frame_drops++;
-				mp->frags = isdn_ppp_mp_discard(mp,start,nextf);
-			}
-			/* break in the sequence, no reassembly */
-      			start = NULL;
-    		}
-	  			
-    		frag = nextf;
-  	}	/* while -- main loop */
-	
-  	if (mp->frags == NULL)
-    		mp->frags = frag;
-		
-	/* rather straighforward way to deal with (not very) possible 
-	 * queue overflow */
-	if (mp->frames > MP_MAX_QUEUE_LEN) {
-		stats->overflows++;
-		while (mp->frames > MP_MAX_QUEUE_LEN) {
-			frag = mp->frags->next;
-			isdn_ppp_mp_free_skb(mp, mp->frags);
-			mp->frags = frag;
-		}
-	}
-	spin_unlock_irqrestore(&mp->lock, flags);
-}
-
-static void isdn_ppp_mp_cleanup( isdn_net_local * lp )
-{
-	struct sk_buff * frag = lp->netdev->pb->frags;
-	struct sk_buff * nextfrag;
-    	while( frag ) {
-		nextfrag = frag->next;
-		isdn_ppp_mp_free_skb(lp->netdev->pb, frag);
-		frag = nextfrag;
-	}
-	lp->netdev->pb->frags = NULL;
-}
-
-static u32 isdn_ppp_mp_get_seq( int short_seq, 
-					struct sk_buff * skb, u32 last_seq )
-{
-	u32 seq;
-	int flags = skb->data[0] & (MP_BEGIN_FRAG | MP_END_FRAG);
-   
-   	if( !short_seq )
-	{
-		seq = ntohl(*(u32*)skb->data) & MP_LONGSEQ_MASK;
-		skb_push(skb,1);
-	}
-	else
-	{
-		/* convert 12-bit short seq number to 24-bit long one 
-	 	*/
-		seq = ntohs(*(u16*)skb->data) & MP_SHORTSEQ_MASK;
-	
-		/* check for seqence wrap */
-		if( !(seq &  MP_SHORTSEQ_MAXBIT) && 
-		     (last_seq &  MP_SHORTSEQ_MAXBIT) && 
-		     (unsigned long)last_seq <= MP_LONGSEQ_MAX )
-			seq |= (last_seq + MP_SHORTSEQ_MAX+1) & 
-					(~MP_SHORTSEQ_MASK & MP_LONGSEQ_MASK);
-		else
-			seq |= last_seq & (~MP_SHORTSEQ_MASK & MP_LONGSEQ_MASK);
-		
-		skb_push(skb, 3);	/* put converted seqence back in skb */
-	}
-	*(u32*)(skb->data+1) = seq; 	/* put seqence back in _host_ byte
-					 * order */
-	skb->data[0] = flags;	        /* restore flags */
-	return seq;
-}
-
-struct sk_buff * isdn_ppp_mp_discard( ippp_bundle * mp,
-			struct sk_buff * from, struct sk_buff * to )
-{
-	if( from )
-		while (from != to) {
-	  		struct sk_buff * next = from->next;
-			isdn_ppp_mp_free_skb(mp, from);
-	  		from = next;
-		}
-	return from;
-}
-
-void isdn_ppp_mp_reassembly( isdn_net_dev * net_dev, isdn_net_local * lp,
-				struct sk_buff * from, struct sk_buff * to )
-{
-	ippp_bundle * mp = net_dev->pb;
-	int proto;
-	struct sk_buff * skb;
-	unsigned int tot_len;
-
-	if( MP_FLAGS(from) == (MP_BEGIN_FRAG | MP_END_FRAG) ) {
-		if( ippp_table[lp->ppp_slot]->debug & 0x40 )
-			printk(KERN_DEBUG"isdn_mppp: reassembly: frame %d, "
-					"len %d\n", MP_SEQ(from), from->len );
-		skb = from;
-		skb_pull(skb, MP_HEADER_LEN);
-		mp->frames--;	
-	} else {
-		struct sk_buff * frag;
-		int n;
-
-		for(tot_len=n=0, frag=from; frag != to; frag=frag->next, n++)
-			tot_len += frag->len - MP_HEADER_LEN;
-
-		if( ippp_table[lp->ppp_slot]->debug & 0x40 )
-			printk(KERN_DEBUG"isdn_mppp: reassembling frames %d "
-				"to %d, len %d\n", MP_SEQ(from), 
-				(MP_SEQ(from)+n-1) & MP_LONGSEQ_MASK, tot_len );
-		if( (skb = dev_alloc_skb(tot_len)) == NULL ) {
-			printk(KERN_ERR "isdn_mppp: cannot allocate sk buff "
-					"of size %d\n", tot_len);
-			isdn_ppp_mp_discard(mp, from, to);
-			return;
-		}
-
-		while( from != to ) {
-			unsigned int len = from->len - MP_HEADER_LEN;
-
-			memcpy(skb_put(skb,len), from->data+MP_HEADER_LEN, len);
-			frag = from->next;
-			isdn_ppp_mp_free_skb(mp, from);
-			from = frag; 
-		}
-	}
-   	proto = isdn_ppp_strip_proto(skb);
-	isdn_ppp_push_higher(net_dev, lp, skb, proto);
-}
-
-static void isdn_ppp_mp_free_skb(ippp_bundle * mp, struct sk_buff * skb)
-{
-	dev_kfree_skb(skb);
-	mp->frames--;
-}
-
-static void isdn_ppp_mp_print_recv_pkt( int slot, struct sk_buff * skb )
-{
-	printk(KERN_DEBUG "mp_recv: %d/%d -> %02x %02x %02x %02x %02x %02x\n", 
-		slot, (int) skb->len, 
-		(int) skb->data[0], (int) skb->data[1], (int) skb->data[2],
-		(int) skb->data[3], (int) skb->data[4], (int) skb->data[5]);
-}
-
-static int
-isdn_ppp_bundle(struct ippp_struct *is, int unit)
-{
-	char ifn[IFNAMSIZ + 1];
-	isdn_net_dev *p;
-	isdn_net_local *lp, *nlp;
-	int rc;
-	unsigned long flags;
-
-	sprintf(ifn, "ippp%d", unit);
-	p = isdn_net_findif(ifn);
-	if (!p) {
-		printk(KERN_ERR "ippp_bundle: cannot find %s\n", ifn);
-		return -EINVAL;
-	}
-
-    	spin_lock_irqsave(&p->pb->lock, flags);
-
-	nlp = is->lp;
-	lp = p->queue;
-	if( nlp->ppp_slot < 0 || nlp->ppp_slot >= ISDN_MAX_CHANNELS ||
-		lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS ) {
-		printk(KERN_ERR "ippp_bundle: binding to invalid slot %d\n",
-			nlp->ppp_slot < 0 || nlp->ppp_slot >= ISDN_MAX_CHANNELS ? 
-			nlp->ppp_slot : lp->ppp_slot );
-		rc = -EINVAL;
-		goto out;
- 	}
-
-	isdn_net_add_to_bundle(p, nlp);
-
-	ippp_table[nlp->ppp_slot]->unit = ippp_table[lp->ppp_slot]->unit;
-
-	/* maybe also SC_CCP stuff */
-	ippp_table[nlp->ppp_slot]->pppcfg |= ippp_table[lp->ppp_slot]->pppcfg &
-		(SC_ENABLE_IP | SC_NO_TCP_CCID | SC_REJ_COMP_TCP);
-	ippp_table[nlp->ppp_slot]->mpppcfg |= ippp_table[lp->ppp_slot]->mpppcfg &
-		(SC_MP_PROT | SC_REJ_MP_PROT | SC_OUT_SHORT_SEQ | SC_IN_SHORT_SEQ);
-	rc = isdn_ppp_mp_init(nlp, p->pb);
-out:
-	spin_unlock_irqrestore(&p->pb->lock, flags);
-	return rc;
-}
-  
-#endif /* CONFIG_ISDN_MPP */
-  
-/*
- * network device ioctl handlers
- */
-
-static int
-isdn_ppp_dev_ioctl_stats(int slot, struct ifreq *ifr, struct net_device *dev)
-{
-	struct ppp_stats *res,
-	 t;
-	isdn_net_local *lp = (isdn_net_local *) dev->priv;
-	int err;
-
-	res = (struct ppp_stats *) ifr->ifr_ifru.ifru_data;
-	err = verify_area(VERIFY_WRITE, res, sizeof(struct ppp_stats));
-
-	if (err)
-		return err;
-
-	/* build a temporary stat struct and copy it to user space */
-
-	memset(&t, 0, sizeof(struct ppp_stats));
-	if (dev->flags & IFF_UP) {
-		t.p.ppp_ipackets = lp->stats.rx_packets;
-		t.p.ppp_ierrors = lp->stats.rx_errors;
-		t.p.ppp_opackets = lp->stats.tx_packets;
-		t.p.ppp_oerrors = lp->stats.tx_errors;
-#ifdef CONFIG_ISDN_PPP_VJ
-		if (slot >= 0 && ippp_table[slot]->slcomp) {
-			struct slcompress *slcomp = ippp_table[slot]->slcomp;
-			t.vj.vjs_packets = slcomp->sls_o_compressed + slcomp->sls_o_uncompressed;
-			t.vj.vjs_compressed = slcomp->sls_o_compressed;
-			t.vj.vjs_searches = slcomp->sls_o_searches;
-			t.vj.vjs_misses = slcomp->sls_o_misses;
-			t.vj.vjs_errorin = slcomp->sls_i_error;
-			t.vj.vjs_tossed = slcomp->sls_i_tossed;
-			t.vj.vjs_uncompressedin = slcomp->sls_i_uncompressed;
-			t.vj.vjs_compressedin = slcomp->sls_i_compressed;
-		}
-#endif
-	}
-	if( copy_to_user(res, &t, sizeof(struct ppp_stats))) return -EFAULT;
-	return 0;
-}
-
-int
-isdn_ppp_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
-{
-	int error=0;
-	char *r;
-	int len;
-	isdn_net_local *lp = (isdn_net_local *) dev->priv;
-
-
-	if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP)
-		return -EINVAL;
-
-	switch (cmd) {
-		case SIOCGPPPVER:
-			r = (char *) ifr->ifr_ifru.ifru_data;
-			len = strlen(PPP_VERSION) + 1;
-			if(copy_to_user(r, PPP_VERSION, len)) error = -EFAULT;
-			break;
-		case SIOCGPPPSTATS:
-			error = isdn_ppp_dev_ioctl_stats(lp->ppp_slot, ifr, dev);
-			break;
-		default:
-			error = -EINVAL;
-			break;
-	}
-	return error;
-}
-
-static int
-isdn_ppp_if_get_unit(char *name)
-{
-	int len,
-	 i,
-	 unit = 0,
-	 deci;
-
-	len = strlen(name);
-
-	if (strncmp("ippp", name, 4) || len > 8)
-		return -1;
-
-	for (i = 0, deci = 1; i < len; i++, deci *= 10) {
-		char a = name[len - i - 1];
-		if (a >= '0' && a <= '9')
-			unit += (a - '0') * deci;
-		else
-			break;
-	}
-	if (!i || len - i != 4)
-		unit = -1;
-
-	return unit;
-}
-
-
-int
-isdn_ppp_dial_slave(char *name)
-{
-#ifdef CONFIG_ISDN_MPP
-	isdn_net_dev *ndev;
-	isdn_net_local *lp;
-	struct net_device *sdev;
-
-	if (!(ndev = isdn_net_findif(name)))
-		return 1;
-	lp = ndev->local;
-	if (!(lp->flags & ISDN_NET_CONNECTED))
-		return 5;
-
-	sdev = lp->slave;
-	while (sdev) {
-		isdn_net_local *mlp = (isdn_net_local *) sdev->priv;
-		if (!(mlp->flags & ISDN_NET_CONNECTED))
-			break;
-		sdev = mlp->slave;
-	}
-	if (!sdev)
-		return 2;
-
-	isdn_net_dial_req((isdn_net_local *) sdev->priv);
-	return 0;
-#else
-	return -1;
-#endif
-}
-
-int
-isdn_ppp_hangup_slave(char *name)
-{
-#ifdef CONFIG_ISDN_MPP
-	isdn_net_dev *ndev;
-	isdn_net_local *lp;
-	struct net_device *sdev;
-
-	if (!(ndev = isdn_net_findif(name)))
-		return 1;
-	lp = ndev->local;
-	if (!(lp->flags & ISDN_NET_CONNECTED))
-		return 5;
-
-	sdev = lp->slave;
-	while (sdev) {
-		isdn_net_local *mlp = (isdn_net_local *) sdev->priv;
-
-		if (mlp->slave) { /* find last connected link in chain */
-			isdn_net_local *nlp = (isdn_net_local *) mlp->slave->priv;
-
-			if (!(nlp->flags & ISDN_NET_CONNECTED))
-				break;
-		} else if (mlp->flags & ISDN_NET_CONNECTED)
-			break;
-		
-		sdev = mlp->slave;
-	}
-	if (!sdev)
-		return 2;
-
-	isdn_net_hangup(sdev);
-	return 0;
-#else
-	return -1;
-#endif
-}
-
-/*
- * PPP compression stuff
- */
-
-
-/* Push an empty CCP Data Frame up to the daemon to wake it up and let it
-   generate a CCP Reset-Request or tear down CCP altogether */
-
-static void isdn_ppp_ccp_kickup(struct ippp_struct *is)
-{
-	isdn_ppp_fill_rq(NULL, 0, PPP_COMP, is->lp->ppp_slot);
-}
-
-/* In-kernel handling of CCP Reset-Request and Reset-Ack is necessary,
-   but absolutely nontrivial. The most abstruse problem we are facing is
-   that the generation, reception and all the handling of timeouts and
-   resends including proper request id management should be entirely left
-   to the (de)compressor, but indeed is not covered by the current API to
-   the (de)compressor. The API is a prototype version from PPP where only
-   some (de)compressors have yet been implemented and all of them are
-   rather simple in their reset handling. Especially, their is only one
-   outstanding ResetAck at a time with all of them and ResetReq/-Acks do
-   not have parameters. For this very special case it was sufficient to
-   just return an error code from the decompressor and have a single
-   reset() entry to communicate all the necessary information between
-   the framework and the (de)compressor. Bad enough, LZS is different
-   (and any other compressor may be different, too). It has multiple
-   histories (eventually) and needs to Reset each of them independently
-   and thus uses multiple outstanding Acks and history numbers as an
-   additional parameter to Reqs/Acks.
-   All that makes it harder to port the reset state engine into the
-   kernel because it is not just the same simple one as in (i)pppd but
-   it must be able to pass additional parameters and have multiple out-
-   standing Acks. We are trying to achieve the impossible by handling
-   reset transactions independent by their id. The id MUST change when
-   the data portion changes, thus any (de)compressor who uses more than
-   one resettable state must provide and recognize individual ids for
-   each individual reset transaction. The framework itself does _only_
-   differentiate them by id, because it has no other semantics like the
-   (de)compressor might.
-   This looks like a major redesign of the interface would be nice,
-   but I don't have an idea how to do it better. */
-
-/* Send a CCP Reset-Request or Reset-Ack directly from the kernel. This is
-   getting that lengthy because there is no simple "send-this-frame-out"
-   function above but every wrapper does a bit different. Hope I guess
-   correct in this hack... */
-
-static void isdn_ppp_ccp_xmit_reset(struct ippp_struct *is, int proto,
-				    unsigned char code, unsigned char id,
-				    unsigned char *data, int len)
-{
-	struct sk_buff *skb;
-	unsigned char *p;
-	int hl;
-	int cnt = 0;
-	isdn_net_local *lp = is->lp;
-
-	/* Alloc large enough skb */
-	hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen;
-	skb = alloc_skb(len + hl + 16,GFP_ATOMIC);
-	if(!skb) {
-		printk(KERN_WARNING
-		       "ippp: CCP cannot send reset - out of memory\n");
-		return;
-	}
-	skb_reserve(skb, hl);
-
-	/* We may need to stuff an address and control field first */
-	if(!(is->pppcfg & SC_COMP_AC)) {
-		p = skb_put(skb, 2);
-		*p++ = 0xff;
-		*p++ = 0x03;
-	}
-
-	/* Stuff proto, code, id and length */
-	p = skb_put(skb, 6);
-	*p++ = (proto >> 8);
-	*p++ = (proto & 0xff);
-	*p++ = code;
-	*p++ = id;
-	cnt = 4 + len;
-	*p++ = (cnt >> 8);
-	*p++ = (cnt & 0xff);
-
-	/* Now stuff remaining bytes */
-	if(len) {
-		p = skb_put(skb, len);
-		memcpy(p, data, len);
-	}
-
-	/* skb is now ready for xmit */
-	printk(KERN_DEBUG "Sending CCP Frame:\n");
-	isdn_ppp_frame_log("ccp-xmit", skb->data, skb->len, 32, is->unit,lp->ppp_slot);
-
-	isdn_net_write_super(lp, skb);
-}
-
-/* Allocate the reset state vector */
-static struct ippp_ccp_reset *isdn_ppp_ccp_reset_alloc(struct ippp_struct *is)
-{
-	struct ippp_ccp_reset *r;
-	r = kmalloc(sizeof(struct ippp_ccp_reset), GFP_KERNEL);
-	if(!r) {
-		printk(KERN_ERR "ippp_ccp: failed to allocate reset data"
-		       " structure - no mem\n");
-		return NULL;
-	}
-	memset(r, 0, sizeof(struct ippp_ccp_reset));
-	printk(KERN_DEBUG "ippp_ccp: allocated reset data structure %p\n", r);
-	is->reset = r;
-	return r;
-}
-
-/* Destroy the reset state vector. Kill all pending timers first. */
-static void isdn_ppp_ccp_reset_free(struct ippp_struct *is)
-{
-	unsigned int id;
-
-	printk(KERN_DEBUG "ippp_ccp: freeing reset data structure %p\n",
-	       is->reset);
-	for(id = 0; id < 256; id++) {
-		if(is->reset->rs[id]) {
-			isdn_ppp_ccp_reset_free_state(is, (unsigned char)id);
-		}
-	}
-	kfree(is->reset);
-	is->reset = NULL;
-}
-
-/* Free a given state and clear everything up for later reallocation */
-static void isdn_ppp_ccp_reset_free_state(struct ippp_struct *is,
-					  unsigned char id)
-{
-	struct ippp_ccp_reset_state *rs;
-
-	if(is->reset->rs[id]) {
-		printk(KERN_DEBUG "ippp_ccp: freeing state for id %d\n", id);
-		rs = is->reset->rs[id];
-		/* Make sure the kernel will not call back later */
-		if(rs->ta)
-			del_timer(&rs->timer);
-		is->reset->rs[id] = NULL;
-		kfree(rs);
-	} else {
-		printk(KERN_WARNING "ippp_ccp: id %d is not allocated\n", id);
-	}
-}
-
-/* The timer callback function which is called when a ResetReq has timed out,
-   aka has never been answered by a ResetAck */
-static void isdn_ppp_ccp_timer_callback(unsigned long closure)
-{
-	struct ippp_ccp_reset_state *rs =
-		(struct ippp_ccp_reset_state *)closure;
-
-	if(!rs) {
-		printk(KERN_ERR "ippp_ccp: timer cb with zero closure.\n");
-		return;
-	}
-	if(rs->ta && rs->state == CCPResetSentReq) {
-		/* We are correct here */
-		if(!rs->expra) {
-			/* Hmm, there is no Ack really expected. We can clean
-			   up the state now, it will be reallocated if the
-			   decompressor insists on another reset */
-			rs->ta = 0;
-			isdn_ppp_ccp_reset_free_state(rs->is, rs->id);
-			return;
-		}
-		printk(KERN_DEBUG "ippp_ccp: CCP Reset timed out for id %d\n",
-		       rs->id);
-		/* Push it again */
-		isdn_ppp_ccp_xmit_reset(rs->is, PPP_CCP, CCP_RESETREQ, rs->id,
-					rs->data, rs->dlen);
-		/* Restart timer */
-		rs->timer.expires = jiffies + HZ*5;
-		add_timer(&rs->timer);
-	} else {
-		printk(KERN_WARNING "ippp_ccp: timer cb in wrong state %d\n",
-		       rs->state);
-	}
-}
-
-/* Allocate a new reset transaction state */
-static struct ippp_ccp_reset_state *isdn_ppp_ccp_reset_alloc_state(struct ippp_struct *is,
-						      unsigned char id)
-{
-	struct ippp_ccp_reset_state *rs;
-	if(is->reset->rs[id]) {
-		printk(KERN_WARNING "ippp_ccp: old state exists for id %d\n",
-		       id);
-		return NULL;
-	} else {
-		rs = kmalloc(sizeof(struct ippp_ccp_reset_state), GFP_KERNEL);
-		if(!rs)
-			return NULL;
-		memset(rs, 0, sizeof(struct ippp_ccp_reset_state));
-		rs->state = CCPResetIdle;
-		rs->is = is;
-		rs->id = id;
-		rs->timer.data = (unsigned long)rs;
-		rs->timer.function = isdn_ppp_ccp_timer_callback;
-		is->reset->rs[id] = rs;
-	}
-	return rs;
-}
-
-
-/* A decompressor wants a reset with a set of parameters - do what is
-   necessary to fulfill it */
-static void isdn_ppp_ccp_reset_trans(struct ippp_struct *is,
-				     struct isdn_ppp_resetparams *rp)
-{
-	struct ippp_ccp_reset_state *rs;
-
-	if(rp->valid) {
-		/* The decompressor defines parameters by itself */
-		if(rp->rsend) {
-			/* And he wants us to send a request */
-			if(!(rp->idval)) {
-				printk(KERN_ERR "ippp_ccp: decompressor must"
-				       " specify reset id\n");
-				return;
-			}
-			if(is->reset->rs[rp->id]) {
-				/* There is already a transaction in existence
-				   for this id. May be still waiting for a
-				   Ack or may be wrong. */
-				rs = is->reset->rs[rp->id];
-				if(rs->state == CCPResetSentReq && rs->ta) {
-					printk(KERN_DEBUG "ippp_ccp: reset"
-					       " trans still in progress"
-					       " for id %d\n", rp->id);
-				} else {
-					printk(KERN_WARNING "ippp_ccp: reset"
-					       " trans in wrong state %d for"
-					       " id %d\n", rs->state, rp->id);
-				}
-			} else {
-				/* Ok, this is a new transaction */
-				printk(KERN_DEBUG "ippp_ccp: new trans for id"
-				       " %d to be started\n", rp->id);
-				rs = isdn_ppp_ccp_reset_alloc_state(is, rp->id);
-				if(!rs) {
-					printk(KERN_ERR "ippp_ccp: out of mem"
-					       " allocing ccp trans\n");
-					return;
-				}
-				rs->state = CCPResetSentReq;
-				rs->expra = rp->expra;
-				if(rp->dtval) {
-					rs->dlen = rp->dlen;
-					memcpy(rs->data, rp->data, rp->dlen);
-				}
-				/* HACK TODO - add link comp here */
-				isdn_ppp_ccp_xmit_reset(is, PPP_CCP,
-							CCP_RESETREQ, rs->id,
-							rs->data, rs->dlen);
-				/* Start the timer */
-				rs->timer.expires = jiffies + 5*HZ;
-				add_timer(&rs->timer);
-				rs->ta = 1;
-			}
-		} else {
-			printk(KERN_DEBUG "ippp_ccp: no reset sent\n");
-		}
-	} else {
-		/* The reset params are invalid. The decompressor does not
-		   care about them, so we just send the minimal requests
-		   and increase ids only when an Ack is received for a
-		   given id */
-		if(is->reset->rs[is->reset->lastid]) {
-			/* There is already a transaction in existence
-			   for this id. May be still waiting for a
-			   Ack or may be wrong. */
-			rs = is->reset->rs[is->reset->lastid];
-			if(rs->state == CCPResetSentReq && rs->ta) {
-				printk(KERN_DEBUG "ippp_ccp: reset"
-				       " trans still in progress"
-				       " for id %d\n", rp->id);
-			} else {
-				printk(KERN_WARNING "ippp_ccp: reset"
-				       " trans in wrong state %d for"
-				       " id %d\n", rs->state, rp->id);
-			}
-		} else {
-			printk(KERN_DEBUG "ippp_ccp: new trans for id"
-			       " %d to be started\n", is->reset->lastid);
-			rs = isdn_ppp_ccp_reset_alloc_state(is,
-							    is->reset->lastid);
-			if(!rs) {
-				printk(KERN_ERR "ippp_ccp: out of mem"
-				       " allocing ccp trans\n");
-				return;
-			}
-			rs->state = CCPResetSentReq;
-			/* We always expect an Ack if the decompressor doesnt
-			   know	better */
-			rs->expra = 1;
-			rs->dlen = 0;
-			/* HACK TODO - add link comp here */
-			isdn_ppp_ccp_xmit_reset(is, PPP_CCP, CCP_RESETREQ,
-						rs->id, NULL, 0);
-			/* Start the timer */
-			rs->timer.expires = jiffies + 5*HZ;
-			add_timer(&rs->timer);
-			rs->ta = 1;
-		}
-	}
-}
-
-/* An Ack was received for this id. This means we stop the timer and clean
-   up the state prior to calling the decompressors reset routine. */
-static void isdn_ppp_ccp_reset_ack_rcvd(struct ippp_struct *is,
-					unsigned char id)
-{
-	struct ippp_ccp_reset_state *rs = is->reset->rs[id];
-
-	if(rs) {
-		if(rs->ta && rs->state == CCPResetSentReq) {
-			/* Great, we are correct */
-			if(!rs->expra)
-				printk(KERN_DEBUG "ippp_ccp: ResetAck received"
-				       " for id %d but not expected\n", id);
-		} else {
-			printk(KERN_INFO "ippp_ccp: ResetAck received out of"
-			       "sync for id %d\n", id);
-		}
-		if(rs->ta) {
-			rs->ta = 0;
-			del_timer(&rs->timer);
-		}
-		isdn_ppp_ccp_reset_free_state(is, id);
-	} else {
-		printk(KERN_INFO "ippp_ccp: ResetAck received for unknown id"
-		       " %d\n", id);
-	}
-	/* Make sure the simple reset stuff uses a new id next time */
-	is->reset->lastid++;
-}
-
-/* 
- * decompress packet
- *
- * if master = 0, we're trying to uncompress an per-link compressed packet,
- * as opposed to an compressed reconstructed-from-MPPP packet.
- * proto is updated to protocol field of uncompressed packet.
- *
- * retval: decompressed packet,
- *         same packet if uncompressed,
- *	   NULL if decompression error
- */
-
-static struct sk_buff *isdn_ppp_decompress(struct sk_buff *skb,struct ippp_struct *is,struct ippp_struct *master,
-	int *proto)
-{
-	void *stat = NULL;
-	struct isdn_ppp_compressor *ipc = NULL;
-	struct sk_buff *skb_out;
-	int len;
-	struct ippp_struct *ri;
-	struct isdn_ppp_resetparams rsparm;
-	unsigned char rsdata[IPPP_RESET_MAXDATABYTES];
-
-	if(!master) {
-		// per-link decompression 
-		stat = is->link_decomp_stat;
-		ipc = is->link_decompressor;
-		ri = is;
-	} else {
-		stat = master->decomp_stat;
-		ipc = master->decompressor;
-		ri = master;
-	}
-
-	if (!ipc) {
-		// no decompressor -> we can't decompress.
-		printk(KERN_DEBUG "ippp: no decompressor defined!\n");
-		return skb;
-	}
-	if (!stat) // if we have a compressor, stat has been set as well
-		BUG();
-
-	if((master && *proto == PPP_COMP) || (!master && *proto == PPP_COMPFRAG) ) {
-		// compressed packets are compressed by their protocol type
-
-		// Set up reset params for the decompressor
-  		memset(&rsparm, 0, sizeof(rsparm));
-  		rsparm.data = rsdata;
-  		rsparm.maxdlen = IPPP_RESET_MAXDATABYTES;
-  
-  		skb_out = dev_alloc_skb(is->mru + PPP_HDRLEN);
-		len = ipc->decompress(stat, skb, skb_out, &rsparm);
-		kfree_skb(skb);
-		if (len <= 0) {
-			switch(len) {
-			case DECOMP_ERROR:
-				printk(KERN_INFO "ippp: decomp wants reset %s params\n",
-				       rsparm.valid ? "with" : "without");
-				
-				isdn_ppp_ccp_reset_trans(ri, &rsparm);
-				break;
-			case DECOMP_FATALERROR:
-				ri->pppcfg |= SC_DC_FERROR;
-				/* Kick ipppd to recognize the error */
-				isdn_ppp_ccp_kickup(ri);
-				break;
-			}
-			kfree_skb(skb_out);
-			return NULL;
-		}
-		*proto = isdn_ppp_strip_proto(skb_out);
-		if (*proto < 0) {
-			kfree_skb(skb_out);
-			return NULL;
-		}
-		return skb_out;
-	} else { 
-		// uncompressed packets are fed through the decompressor to
-		// update the decompressor state
-		ipc->incomp(stat, skb, *proto);
-		return skb;
-	}
-}
-
-/*
- * compress a frame 
- *   type=0: normal/bundle compression
- *       =1: link compression
- * returns original skb if we haven't compressed the frame
- * and a new skb pointer if we've done it
- */
-static struct sk_buff *isdn_ppp_compress(struct sk_buff *skb_in,int *proto,
-	struct ippp_struct *is,struct ippp_struct *master,int type)
-{
-    int ret;
-    int new_proto;
-    struct isdn_ppp_compressor *compressor;
-    void *stat;
-    struct sk_buff *skb_out;
-
-	/* we do not compress control protocols */
-    if(*proto < 0 || *proto > 0x3fff) {
-	    return skb_in;
-    }
-
-	if(type) { /* type=1 => Link compression */
-		return skb_in;
-	}
-	else {
-		if(!master) {
-			compressor = is->compressor;
-			stat = is->comp_stat;
-		}
-		else {
-			compressor = master->compressor;
-			stat = master->comp_stat;
-		}
-		new_proto = PPP_COMP;
-	}
-
-	if(!compressor) {
-		printk(KERN_ERR "isdn_ppp: No compressor set!\n");
-		return skb_in;
-	}
-	if(!stat) {
-		printk(KERN_ERR "isdn_ppp: Compressor not initialized?\n");
-		return skb_in;
-	}
-
-	/* Allow for at least 150 % expansion (for now) */
-	skb_out = alloc_skb(skb_in->len + skb_in->len/2 + 32 +
-		skb_headroom(skb_in), GFP_ATOMIC);
-	if(!skb_out)
-		return skb_in;
-	skb_reserve(skb_out, skb_headroom(skb_in));
-
-	ret = (compressor->compress)(stat,skb_in,skb_out,*proto);
-	if(!ret) {
-		dev_kfree_skb(skb_out);
-		return skb_in;
-	}
-	
-	dev_kfree_skb(skb_in);
-	*proto = new_proto;
-	return skb_out;
-}
-
-/*
- * we received a CCP frame .. 
- * not a clean solution, but we MUST handle a few cases in the kernel
- */
-static void isdn_ppp_receive_ccp(isdn_net_dev *net_dev, isdn_net_local *lp,
-	 struct sk_buff *skb,int proto)
-{
-	struct ippp_struct *is = ippp_table[lp->ppp_slot];
-	struct ippp_struct *mis;
-	int len;
-	struct isdn_ppp_resetparams rsparm;
-	unsigned char rsdata[IPPP_RESET_MAXDATABYTES];	
-
-	printk(KERN_DEBUG "Received CCP frame from peer\n");
-	isdn_ppp_frame_log("ccp-rcv", skb->data, skb->len, 32, is->unit,lp->ppp_slot);
-
-	if(lp->master)
-		mis = ippp_table[((isdn_net_local *) (lp->master->priv))->ppp_slot];
-	else
-		mis = is;
-
-	switch(skb->data[0]) {
-	case CCP_CONFREQ:
-		if(is->debug & 0x10)
-			printk(KERN_DEBUG "Disable compression here!\n");
-		if(proto == PPP_CCP)
-			mis->compflags &= ~SC_COMP_ON;		
-		else
-			is->compflags &= ~SC_LINK_COMP_ON;		
-		break;
-	case CCP_TERMREQ:
-	case CCP_TERMACK:
-		if(is->debug & 0x10)
-			printk(KERN_DEBUG "Disable (de)compression here!\n");
-		if(proto == PPP_CCP)
-			mis->compflags &= ~(SC_DECOMP_ON|SC_COMP_ON);		
-		else
-			is->compflags &= ~(SC_LINK_DECOMP_ON|SC_LINK_COMP_ON);		
-		break;
-	case CCP_CONFACK:
-		/* if we RECEIVE an ackowledge we enable the decompressor */
-		if(is->debug & 0x10)
-			printk(KERN_DEBUG "Enable decompression here!\n");
-		if(proto == PPP_CCP) {
-			if (!mis->decompressor)
-				break;
-			mis->compflags |= SC_DECOMP_ON;
-		} else {
-			if (!is->decompressor)
-				break;
-			is->compflags |= SC_LINK_DECOMP_ON;
-		}
-		break;
-
-	case CCP_RESETACK:
-		printk(KERN_DEBUG "Received ResetAck from peer\n");
-		len = (skb->data[2] << 8) | skb->data[3];
-		len -= 4;
-
-		if(proto == PPP_CCP) {
-			/* If a reset Ack was outstanding for this id, then
-			   clean up the state engine */
-			isdn_ppp_ccp_reset_ack_rcvd(mis, skb->data[1]);
-			if(mis->decompressor && mis->decomp_stat)
-				mis->decompressor->
-					reset(mis->decomp_stat,
-					      skb->data[0],
-					      skb->data[1],
-					      len ? &skb->data[4] : NULL,
-					      len, NULL);
-			/* TODO: This is not easy to decide here */
-			mis->compflags &= ~SC_DECOMP_DISCARD;
-		}
-		else {
-			isdn_ppp_ccp_reset_ack_rcvd(is, skb->data[1]);
-			if(is->link_decompressor && is->link_decomp_stat)
-				is->link_decompressor->
-					reset(is->link_decomp_stat,
-					      skb->data[0],
-					      skb->data[1],
-					      len ? &skb->data[4] : NULL,
-					      len, NULL);
-			/* TODO: neither here */
-			is->compflags &= ~SC_LINK_DECOMP_DISCARD;
-		}
-		break;
-
-	case CCP_RESETREQ:
-		printk(KERN_DEBUG "Received ResetReq from peer\n");
-		/* Receiving a ResetReq means we must reset our compressor */
-		/* Set up reset params for the reset entry */
-		memset(&rsparm, 0, sizeof(rsparm));
-		rsparm.data = rsdata;
-		rsparm.maxdlen = IPPP_RESET_MAXDATABYTES; 
-		/* Isolate data length */
-		len = (skb->data[2] << 8) | skb->data[3];
-		len -= 4;
-		if(proto == PPP_CCP) {
-			if(mis->compressor && mis->comp_stat)
-				mis->compressor->
-					reset(mis->comp_stat,
-					      skb->data[0],
-					      skb->data[1],
-					      len ? &skb->data[4] : NULL,
-					      len, &rsparm);
-		}
-		else {
-			if(is->link_compressor && is->link_comp_stat)
-				is->link_compressor->
-					reset(is->link_comp_stat,
-					      skb->data[0],
-					      skb->data[1],
-					      len ? &skb->data[4] : NULL,
-					      len, &rsparm);
-		}
-		/* Ack the Req as specified by rsparm */
-		if(rsparm.valid) {
-			/* Compressor reset handler decided how to answer */
-			if(rsparm.rsend) {
-				/* We should send a Frame */
-				isdn_ppp_ccp_xmit_reset(is, proto, CCP_RESETACK,
-							rsparm.idval ? rsparm.id
-							: skb->data[1],
-							rsparm.dtval ?
-							rsparm.data : NULL,
-							rsparm.dtval ?
-							rsparm.dlen : 0);
-			} else {
-				printk(KERN_DEBUG "ResetAck suppressed\n");
-			}
-		} else {
-			/* We answer with a straight reflected Ack */
-			isdn_ppp_ccp_xmit_reset(is, proto, CCP_RESETACK,
-						skb->data[1],
-						len ? &skb->data[4] : NULL,
-						len);
-		}
-		break;
-	}
-}
-
-
-/*
- * Daemon sends a CCP frame ...
- */
-
-/* TODO: Clean this up with new Reset semantics */
-
-/* I believe the CCP handling as-is is done wrong. Compressed frames
- * should only be sent/received after CCP reaches UP state, which means
- * both sides have sent CONF_ACK. Currently, we handle both directions
- * independently, which means we may accept compressed frames too early
- * (supposedly not a problem), but may also mean we send compressed frames
- * too early, which may turn out to be a problem.
- * This part of state machine should actually be handled by (i)pppd, but
- * that's too big of a change now. --kai
- */
-
-/* Actually, we might turn this into an advantage: deal with the RFC in
- * the old tradition of beeing generous on what we accept, but beeing
- * strict on what we send. Thus we should just
- * - accept compressed frames as soon as decompression is negotiated
- * - send compressed frames only when decomp *and* comp are negotiated
- * - drop rx compressed frames if we cannot decomp (instead of pushing them
- *   up to ipppd)
- * and I tried to modify this file according to that. --abp
- */
-
-static void isdn_ppp_send_ccp(isdn_net_dev *net_dev, isdn_net_local *lp, struct sk_buff *skb)
-{
-	struct ippp_struct *mis,*is = ippp_table[lp->ppp_slot];
-	int proto;
-	unsigned char *data;
-
-	if(!skb || skb->len < 3)
-		return;
-
-	/* Daemon may send with or without address and control field comp */
-	data = skb->data;
-	if(!(is->pppcfg & SC_COMP_AC) && data[0] == 0xff && data[1] == 0x03) {
-		data += 2;
-		if(skb->len < 5)
-			return;
-	}
-
-	proto = ((int)data[0]<<8)+data[1];
-	if(proto != PPP_CCP && proto != PPP_CCPFRAG)
-		return;
-
-	printk(KERN_DEBUG "Received CCP frame from daemon:\n");
-	isdn_ppp_frame_log("ccp-xmit", skb->data, skb->len, 32, is->unit,lp->ppp_slot);
-
-        if(lp->master)
-                mis = ippp_table[((isdn_net_local *) (lp->master->priv))->ppp_slot];
-        else
-                mis = is;
-	
-	if(mis != is)
-		printk(KERN_DEBUG "isdn_ppp: Ouch! Master CCP sends on slave slot!\n");
-	
-        switch(data[2]) {
-	case CCP_CONFREQ:
-		if(is->debug & 0x10)
-			printk(KERN_DEBUG "Disable decompression here!\n");
-		if(proto == PPP_CCP)
-			is->compflags &= ~SC_DECOMP_ON;
-		else
-			is->compflags &= ~SC_LINK_DECOMP_ON;
-		break;
-	case CCP_TERMREQ:
-	case CCP_TERMACK:
-		if(is->debug & 0x10)
-			printk(KERN_DEBUG "Disable (de)compression here!\n");
-		if(proto == PPP_CCP)
-			is->compflags &= ~(SC_DECOMP_ON|SC_COMP_ON);
-		else
-			is->compflags &= ~(SC_LINK_DECOMP_ON|SC_LINK_COMP_ON);
-		break;
-	case CCP_CONFACK:
-		/* if we SEND an ackowledge we can/must enable the compressor */
-		if(is->debug & 0x10)
-			printk(KERN_DEBUG "Enable compression here!\n");
-		if(proto == PPP_CCP) {
-			if (!is->compressor)
-				break;
-			is->compflags |= SC_COMP_ON;
-		} else {
-			if (!is->compressor)
-				break;
-			is->compflags |= SC_LINK_COMP_ON;
-		}
-		break;
-	case CCP_RESETACK:
-		/* If we send a ACK we should reset our compressor */
-		if(is->debug & 0x10)
-			printk(KERN_DEBUG "Reset decompression state here!\n");
-		printk(KERN_DEBUG "ResetAck from daemon passed by\n");
-		if(proto == PPP_CCP) {
-			/* link to master? */
-			if(is->compressor && is->comp_stat)
-				is->compressor->reset(is->comp_stat, 0, 0,
-						      NULL, 0, NULL);
-			is->compflags &= ~SC_COMP_DISCARD;	
-		}
-		else {
-			if(is->link_compressor && is->link_comp_stat)
-				is->link_compressor->reset(is->link_comp_stat,
-							   0, 0, NULL, 0, NULL);
-			is->compflags &= ~SC_LINK_COMP_DISCARD;	
-		}
-		break;
-	case CCP_RESETREQ:
-		/* Just let it pass by */
-		printk(KERN_DEBUG "ResetReq from daemon passed by\n");
-		break;
-	}
-}
-
-int isdn_ppp_register_compressor(struct isdn_ppp_compressor *ipc)
-{
-	ipc->next = ipc_head;
-	ipc->prev = NULL;
-	if(ipc_head) {
-		ipc_head->prev = ipc;
-	}
-	ipc_head = ipc;
-	return 0;
-}
-
-int isdn_ppp_unregister_compressor(struct isdn_ppp_compressor *ipc)
-{
-	if(ipc->prev)
-		ipc->prev->next = ipc->next;
-	else
-		ipc_head = ipc->next;
-	if(ipc->next)
-		ipc->next->prev = ipc->prev;
-	ipc->prev = ipc->next = NULL;
-	return 0;
-}
-
-static int isdn_ppp_set_compressor(struct ippp_struct *is, struct isdn_ppp_comp_data *data)
-{
-	struct isdn_ppp_compressor *ipc = ipc_head;
-	int ret;
-	void *stat;
-	int num = data->num;
-
-	if(is->debug & 0x10)
-		printk(KERN_DEBUG "[%d] Set %s type %d\n",is->unit,
-			(data->flags&IPPP_COMP_FLAG_XMIT)?"compressor":"decompressor",num);
-
-	/* If is has no valid reset state vector, we cannot allocate a
-	   decompressor. The decompressor would cause reset transactions
-	   sooner or later, and they need that vector. */
-
-	if(!(data->flags & IPPP_COMP_FLAG_XMIT) && !is->reset) {
-		printk(KERN_ERR "ippp_ccp: no reset data structure - can't"
-		       " allow decompression.\n");
-		return -ENOMEM;
-	}
-
-	while(ipc) {
-		if(ipc->num == num) {
-			stat = ipc->alloc(data);
-			if(stat) {
-				ret = ipc->init(stat,data,is->unit,0);
-				if(!ret) {
-					printk(KERN_ERR "Can't init (de)compression!\n");
-					ipc->free(stat);
-					stat = NULL;
-					break;
-				}
-			}
-			else {
-				printk(KERN_ERR "Can't alloc (de)compression!\n");
-				break;
-			}
-
-                        if(data->flags & IPPP_COMP_FLAG_XMIT) {
-				if(data->flags & IPPP_COMP_FLAG_LINK) {
-					if(is->link_comp_stat)
-						is->link_compressor->free(is->link_comp_stat);
-					is->link_comp_stat = stat;
-                                	is->link_compressor = ipc;
-				}
-				else {
-					if(is->comp_stat)
-						is->compressor->free(is->comp_stat);
-					is->comp_stat = stat;
-                                	is->compressor = ipc;
-				}
-			}
-                        else {
-				if(data->flags & IPPP_COMP_FLAG_LINK) {
-					if(is->link_decomp_stat)
-						is->link_decompressor->free(is->link_decomp_stat);
-					is->link_decomp_stat = stat;
-        	                        is->link_decompressor = ipc;
-				}
-				else {
-					if(is->decomp_stat)
-						is->decompressor->free(is->decomp_stat);
-					is->decomp_stat = stat;
-        	                        is->decompressor = ipc;
-				}
-			}
-			return 0;
-		}
-		ipc = ipc->next;
-	}
-	return -EINVAL;
-}
diff -Nru a/drivers/isdn/isdn_ppp.h b/drivers/isdn/isdn_ppp.h
--- a/drivers/isdn/isdn_ppp.h	Mon Apr 22 15:32:19 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,38 +0,0 @@
-/* $Id: isdn_ppp.h,v 1.17.6.1 2001/09/23 22:24:32 kai Exp $
- *
- * header for Linux ISDN subsystem, functions for synchronous PPP (linklevel).
- *
- * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#include <linux/ppp_defs.h>     /* for PPP_PROTOCOL */
-#include <linux/isdn_ppp.h>	/* for isdn_ppp info */
-
-extern struct file_operations isdn_ppp_fops;
-
-extern int isdn_ppp_init(void);
-extern void isdn_ppp_cleanup(void);
-extern int isdn_ppp_free(isdn_net_local *);
-extern int isdn_ppp_bind(isdn_net_local *);
-extern int isdn_ppp_xmit(struct sk_buff *, struct net_device *);
-extern void isdn_ppp_receive(isdn_net_dev *, isdn_net_local *, struct sk_buff *);
-extern int isdn_ppp_dev_ioctl(struct net_device *, struct ifreq *, int);
-extern int isdn_ppp_dial_slave(char *);
-extern void isdn_ppp_wakeup_daemon(isdn_net_local *);
-
-extern int isdn_ppp_register_compressor(struct isdn_ppp_compressor *ipc);
-extern int isdn_ppp_unregister_compressor(struct isdn_ppp_compressor *ipc);
-
-#define IPPP_OPEN	0x01
-#define IPPP_CONNECT	0x02
-#define IPPP_CLOSEWAIT	0x04
-#define IPPP_NOBLOCK	0x08
-#define IPPP_ASSIGNED	0x10
-
-#define IPPP_MAX_HEADER 10
-
-
diff -Nru a/drivers/isdn/isdn_tty.c b/drivers/isdn/isdn_tty.c
--- a/drivers/isdn/isdn_tty.c	Mon Apr 22 15:32:19 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,4052 +0,0 @@
-/* $Id: isdn_tty.c,v 1.94.6.9 2001/11/06 20:58:29 kai Exp $
- *
- * Linux ISDN subsystem, tty functions and AT-command emulator (linklevel).
- *
- * Copyright 1994-1999  by Fritz Elfert (fritz@isdn4linux.de)
- * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-#undef ISDN_TTY_STAT_DEBUG
-
-#include <linux/config.h>
-#include <linux/isdn.h>
-#include "isdn_common.h"
-#include "isdn_tty.h"
-#ifdef CONFIG_ISDN_AUDIO
-#include "isdn_audio.h"
-#define VBUF 0x3e0
-#define VBUFX (VBUF/16)
-#endif
-
-#define FIX_FILE_TRANSFER
-#define	DUMMY_HAYES_AT
-
-/* Prototypes */
-
-static int isdn_tty_edit_at(const char *, int, modem_info *, int);
-static void isdn_tty_check_esc(const u_char *, u_char, int, int *, int *, int);
-static void isdn_tty_modem_reset_regs(modem_info *, int);
-static void isdn_tty_cmd_ATA(modem_info *);
-static void isdn_tty_flush_buffer(struct tty_struct *);
-static void isdn_tty_modem_result(int, modem_info *);
-#ifdef CONFIG_ISDN_AUDIO
-static int isdn_tty_countDLE(unsigned char *, int);
-#endif
-
-/* Leave this unchanged unless you know what you do! */
-#define MODEM_PARANOIA_CHECK
-#define MODEM_DO_RESTART
-
-#ifdef CONFIG_DEVFS_FS
-static char *isdn_ttyname_ttyI = "isdn/ttyI%d";
-static char *isdn_ttyname_cui = "isdn/cui%d";
-#else
-static char *isdn_ttyname_ttyI = "ttyI";
-static char *isdn_ttyname_cui = "cui";
-#endif
-
-static int bit2si[8] =
-{1, 5, 7, 7, 7, 7, 7, 7};
-static int si2bit[8] =
-{4, 1, 4, 4, 4, 4, 4, 4};
-
-char *isdn_tty_revision = "$Revision: 1.94.6.9 $";
-
-
-/* isdn_tty_try_read() is called from within isdn_tty_rcv_skb()
- * to stuff incoming data directly into a tty's flip-buffer. This
- * is done to speed up tty-receiving if the receive-queue is empty.
- * This routine MUST be called with interrupts off.
- * Return:
- *  1 = Success
- *  0 = Failure, data has to be buffered and later processed by
- *      isdn_tty_readmodem().
- */
-static int
-isdn_tty_try_read(modem_info * info, struct sk_buff *skb)
-{
-	int c;
-	int len;
-	struct tty_struct *tty;
-
-	if (info->online) {
-		if ((tty = info->tty)) {
-			if (info->mcr & UART_MCR_RTS) {
-				c = TTY_FLIPBUF_SIZE - tty->flip.count;
-				len = skb->len
-#ifdef CONFIG_ISDN_AUDIO
-					+ ISDN_AUDIO_SKB_DLECOUNT(skb)
-#endif
-					;
-				if (c >= len) {
-#ifdef CONFIG_ISDN_AUDIO
-					if (ISDN_AUDIO_SKB_DLECOUNT(skb))
-						while (skb->len--) {
-							if (*skb->data == DLE)
-								tty_insert_flip_char(tty, DLE, 0);
-							tty_insert_flip_char(tty, *skb->data++, 0);
-					} else {
-#endif
-						memcpy(tty->flip.char_buf_ptr,
-						       skb->data, len);
-						tty->flip.count += len;
-						tty->flip.char_buf_ptr += len;
-						memset(tty->flip.flag_buf_ptr, 0, len);
-						tty->flip.flag_buf_ptr += len;
-#ifdef CONFIG_ISDN_AUDIO
-					}
-#endif
-					if (info->emu.mdmreg[REG_CPPP] & BIT_CPPP)
-						tty->flip.flag_buf_ptr[len - 1] = 0xff;
-					queue_task(&tty->flip.tqueue, &tq_timer);
-					kfree_skb(skb);
-					return 1;
-				}
-			}
-		}
-	}
-	return 0;
-}
-
-/* isdn_tty_readmodem() is called periodically from within timer-interrupt.
- * It tries getting received data from the receive queue an stuff it into
- * the tty's flip-buffer.
- */
-void
-isdn_tty_readmodem(void)
-{
-	int resched = 0;
-	int midx;
-	int i;
-	int c;
-	int r;
-	ulong flags;
-	struct tty_struct *tty;
-	modem_info *info;
-
-	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-		if ((midx = dev->m_idx[i]) >= 0) {
-			info = &dev->mdm.info[midx];
-			if (info->online) {
-				r = 0;
-#ifdef CONFIG_ISDN_AUDIO
-				isdn_audio_eval_dtmf(info);
-				if ((info->vonline & 1) && (info->emu.vpar[1]))
-					isdn_audio_eval_silence(info);
-#endif
-				if ((tty = info->tty)) {
-					if (info->mcr & UART_MCR_RTS) {
-						c = TTY_FLIPBUF_SIZE - tty->flip.count;
-						if (c > 0) {
-							save_flags(flags);
-							cli();
-							r = isdn_readbchan(info->isdn_driver, info->isdn_channel,
-									   tty->flip.char_buf_ptr,
-									   tty->flip.flag_buf_ptr, c, 0);
-							/* CISCO AsyncPPP Hack */
-							if (!(info->emu.mdmreg[REG_CPPP] & BIT_CPPP))
-								memset(tty->flip.flag_buf_ptr, 0, r);
-							tty->flip.count += r;
-							tty->flip.flag_buf_ptr += r;
-							tty->flip.char_buf_ptr += r;
-							if (r)
-								queue_task(&tty->flip.tqueue, &tq_timer);
-							restore_flags(flags);
-						}
-					} else
-						r = 1;
-				} else
-					r = 1;
-				if (r) {
-					info->rcvsched = 0;
-					resched = 1;
-				} else
-					info->rcvsched = 1;
-			}
-		}
-	}
-	if (!resched)
-		isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 0);
-}
-
-int
-isdn_tty_rcv_skb(int i, int di, int channel, struct sk_buff *skb)
-{
-	ulong flags;
-	int midx;
-#ifdef CONFIG_ISDN_AUDIO
-	int ifmt;
-#endif
-	modem_info *info;
-
-	if ((midx = dev->m_idx[i]) < 0) {
-		/* if midx is invalid, packet is not for tty */
-		return 0;
-	}
-	info = &dev->mdm.info[midx];
-#ifdef CONFIG_ISDN_AUDIO
-	ifmt = 1;
-	
-	if ((info->vonline) && (!info->emu.vpar[4]))
-		isdn_audio_calc_dtmf(info, skb->data, skb->len, ifmt);
-	if ((info->vonline & 1) && (info->emu.vpar[1]))
-		isdn_audio_calc_silence(info, skb->data, skb->len, ifmt);
-#endif
-	if ((info->online < 2)
-#ifdef CONFIG_ISDN_AUDIO
-	    && (!(info->vonline & 1))
-#endif
-		) {
-		/* If Modem not listening, drop data */
-		kfree_skb(skb);
-		return 1;
-	}
-	if (info->emu.mdmreg[REG_T70] & BIT_T70) {
-		if (info->emu.mdmreg[REG_T70] & BIT_T70_EXT) {
-			/* T.70 decoding: throw away the T.70 header (2 or 4 bytes)   */
-			if (skb->data[0] == 3) /* pure data packet -> 4 byte headers  */
-				skb_pull(skb, 4);
-			else
-				if (skb->data[0] == 1) /* keepalive packet -> 2 byte hdr  */
-					skb_pull(skb, 2);
-		} else
-			/* T.70 decoding: Simply throw away the T.70 header (4 bytes) */
-			if ((skb->data[0] == 1) && ((skb->data[1] == 0) || (skb->data[1] == 1)))
-				skb_pull(skb, 4);
-	}
-#ifdef CONFIG_ISDN_AUDIO
-	if (skb_headroom(skb) < sizeof(isdn_audio_skb)) {
-		printk(KERN_WARNING
-		       "isdn_audio: insufficient skb_headroom, dropping\n");
-		kfree_skb(skb);
-		return 1;
-	}
-	ISDN_AUDIO_SKB_DLECOUNT(skb) = 0;
-	ISDN_AUDIO_SKB_LOCK(skb) = 0;
-	if (info->vonline & 1) {
-		/* voice conversion/compression */
-		switch (info->emu.vpar[3]) {
-			case 2:
-			case 3:
-			case 4:
-				/* adpcm
-				 * Since compressed data takes less
-				 * space, we can overwrite the buffer.
-				 */
-				skb_trim(skb, isdn_audio_xlaw2adpcm(info->adpcmr,
-								    ifmt,
-								    skb->data,
-								    skb->data,
-								    skb->len));
-				break;
-			case 5:
-				/* a-law */
-				if (!ifmt)
-					isdn_audio_ulaw2alaw(skb->data, skb->len);
-				break;
-			case 6:
-				/* u-law */
-				if (ifmt)
-					isdn_audio_alaw2ulaw(skb->data, skb->len);
-				break;
-		}
-		ISDN_AUDIO_SKB_DLECOUNT(skb) =
-			isdn_tty_countDLE(skb->data, skb->len);
-	}
-#ifdef CONFIG_ISDN_TTY_FAX
-	else {
-		if (info->faxonline & 2) {
-			isdn_tty_fax_bitorder(info, skb);
-			ISDN_AUDIO_SKB_DLECOUNT(skb) =
-				isdn_tty_countDLE(skb->data, skb->len);
-		}
-	}
-#endif
-#endif
-	/* Try to deliver directly via tty-flip-buf if queue is empty */
-	save_flags(flags);
-	cli();
-	if (skb_queue_empty(&dev->drv[di]->rpqueue[channel]))
-		if (isdn_tty_try_read(info, skb)) {
-			restore_flags(flags);
-			return 1;
-		}
-	/* Direct deliver failed or queue wasn't empty.
-	 * Queue up for later dequeueing via timer-irq.
-	 */
-	__skb_queue_tail(&dev->drv[di]->rpqueue[channel], skb);
-	dev->drv[di]->rcvcount[channel] +=
-		(skb->len
-#ifdef CONFIG_ISDN_AUDIO
-		 + ISDN_AUDIO_SKB_DLECOUNT(skb)
-#endif
-			);
-	restore_flags(flags);
-	/* Schedule dequeuing */
-	if ((dev->modempoll) && (info->rcvsched))
-		isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
-	return 1;
-}
-
-void
-isdn_tty_cleanup_xmit(modem_info * info)
-{
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-	skb_queue_purge(&info->xmit_queue);
-#ifdef CONFIG_ISDN_AUDIO
-	skb_queue_purge(&info->dtmf_queue);
-#endif
-	restore_flags(flags);
-}
-
-static void
-isdn_tty_tint(modem_info * info)
-{
-	struct sk_buff *skb = skb_dequeue(&info->xmit_queue);
-	int len,
-	 slen;
-
-	if (!skb)
-		return;
-	len = skb->len;
-	if ((slen = isdn_writebuf_skb_stub(info->isdn_driver,
-					   info->isdn_channel, 1, skb)) == len) {
-		struct tty_struct *tty = info->tty;
-		info->send_outstanding++;
-		info->msr &= ~UART_MSR_CTS;
-		info->lsr &= ~UART_LSR_TEMT;
-		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
-		    tty->ldisc.write_wakeup)
-			(tty->ldisc.write_wakeup) (tty);
-		wake_up_interruptible(&tty->write_wait);
-		return;
-	}
-	if (slen < 0) {
-		/* Error: no channel, already shutdown, or wrong parameter */
-		dev_kfree_skb(skb);
-		return;
-	}
-	skb_queue_head(&info->xmit_queue, skb);
-}
-
-#ifdef CONFIG_ISDN_AUDIO
-static int
-isdn_tty_countDLE(unsigned char *buf, int len)
-{
-	int count = 0;
-
-	while (len--)
-		if (*buf++ == DLE)
-			count++;
-	return count;
-}
-
-/* This routine is called from within isdn_tty_write() to perform
- * DLE-decoding when sending audio-data.
- */
-static int
-isdn_tty_handleDLEdown(modem_info * info, atemu * m, int len)
-{
-	unsigned char *p = &info->xmit_buf[info->xmit_count];
-	int count = 0;
-
-	while (len > 0) {
-		if (m->lastDLE) {
-			m->lastDLE = 0;
-			switch (*p) {
-				case DLE:
-					/* Escape code */
-					if (len > 1)
-						memmove(p, p + 1, len - 1);
-					p--;
-					count++;
-					break;
-				case ETX:
-					/* End of data */
-					info->vonline |= 4;
-					return count;
-				case DC4:
-					/* Abort RX */
-					info->vonline &= ~1;
-#ifdef ISDN_DEBUG_MODEM_VOICE
-					printk(KERN_DEBUG
-					       "DLEdown: got DLE-DC4, send DLE-ETX on ttyI%d\n",
-					       info->line);
-#endif
-					isdn_tty_at_cout("\020\003", info);
-					if (!info->vonline) {
-#ifdef ISDN_DEBUG_MODEM_VOICE
-						printk(KERN_DEBUG
-						       "DLEdown: send VCON on ttyI%d\n",
-						       info->line);
-#endif
-						isdn_tty_at_cout("\r\nVCON\r\n", info);
-					}
-					/* Fall through */
-				case 'q':
-				case 's':
-					/* Silence */
-					if (len > 1)
-						memmove(p, p + 1, len - 1);
-					p--;
-					break;
-			}
-		} else {
-			if (*p == DLE)
-				m->lastDLE = 1;
-			else
-				count++;
-		}
-		p++;
-		len--;
-	}
-	if (len < 0) {
-		printk(KERN_WARNING "isdn_tty: len<0 in DLEdown\n");
-		return 0;
-	}
-	return count;
-}
-
-/* This routine is called from within isdn_tty_write() when receiving
- * audio-data. It interrupts receiving, if an character other than
- * ^S or ^Q is sent.
- */
-static int
-isdn_tty_end_vrx(const char *buf, int c, int from_user)
-{
-	char ch;
-
-	while (c--) {
-		if (from_user)
-			get_user(ch, buf);
-		else
-			ch = *buf;
-		if ((ch != 0x11) && (ch != 0x13))
-			return 1;
-		buf++;
-	}
-	return 0;
-}
-
-static int voice_cf[7] =
-{0, 0, 4, 3, 2, 0, 0};
-
-#endif                          /* CONFIG_ISDN_AUDIO */
-
-/* isdn_tty_senddown() is called either directly from within isdn_tty_write()
- * or via timer-interrupt from within isdn_tty_modem_xmit(). It pulls
- * outgoing data from the tty's xmit-buffer, handles voice-decompression or
- * T.70 if necessary, and finally queues it up for sending via isdn_tty_tint.
- */
-static void
-isdn_tty_senddown(modem_info * info)
-{
-	int buflen;
-	int skb_res;
-#ifdef CONFIG_ISDN_AUDIO
-	int audio_len;
-#endif
-	struct sk_buff *skb;
-
-#ifdef CONFIG_ISDN_AUDIO
-	if (info->vonline & 4) {
-		info->vonline &= ~6;
-		if (!info->vonline) {
-#ifdef ISDN_DEBUG_MODEM_VOICE
-			printk(KERN_DEBUG
-			       "senddown: send VCON on ttyI%d\n",
-			       info->line);
-#endif
-			isdn_tty_at_cout("\r\nVCON\r\n", info);
-		}
-	}
-#endif
-	if (!(buflen = info->xmit_count))
-		return;
- 	if ((info->emu.mdmreg[REG_CTS] & BIT_CTS) != 0)
-		info->msr &= ~UART_MSR_CTS;
-	info->lsr &= ~UART_LSR_TEMT;	
-	/* info->xmit_count is modified here and in isdn_tty_write().
-	 * So we return here if isdn_tty_write() is in the
-	 * critical section.
-	 */
-	atomic_inc(&info->xmit_lock);
-	if (!(atomic_dec_and_test(&info->xmit_lock)))
-		return;
-	if (info->isdn_driver < 0) {
-		info->xmit_count = 0;
-		return;
-	}
-	skb_res = dev->drv[info->isdn_driver]->interface->hl_hdrlen + 4;
-#ifdef CONFIG_ISDN_AUDIO
-	if (info->vonline & 2)
-		audio_len = buflen * voice_cf[info->emu.vpar[3]];
-	else
-		audio_len = 0;
-	skb = dev_alloc_skb(skb_res + buflen + audio_len);
-#else
-	skb = dev_alloc_skb(skb_res + buflen);
-#endif
-	if (!skb) {
-		printk(KERN_WARNING
-		       "isdn_tty: Out of memory in ttyI%d senddown\n",
-		       info->line);
-		return;
-	}
-	skb_reserve(skb, skb_res);
-	memcpy(skb_put(skb, buflen), info->xmit_buf, buflen);
-	info->xmit_count = 0;
-#ifdef CONFIG_ISDN_AUDIO
-	if (info->vonline & 2) {
-		/* For now, ifmt is fixed to 1 (alaw), since this
-		 * is used with ISDN everywhere in the world, except
-		 * US, Canada and Japan.
-		 * Later, when US-ISDN protocols are implemented,
-		 * this setting will depend on the D-channel protocol.
-		 */
-		int ifmt = 1;
-
-		/* voice conversion/decompression */
-		switch (info->emu.vpar[3]) {
-			case 2:
-			case 3:
-			case 4:
-				/* adpcm, compatible to ZyXel 1496 modem
-				 * with ROM revision 6.01
-				 */
-				audio_len = isdn_audio_adpcm2xlaw(info->adpcms,
-								  ifmt,
-								  skb->data,
-						    skb_put(skb, audio_len),
-								  buflen);
-				skb_pull(skb, buflen);
-				skb_trim(skb, audio_len);
-				break;
-			case 5:
-				/* a-law */
-				if (!ifmt)
-					isdn_audio_alaw2ulaw(skb->data,
-							     buflen);
-				break;
-			case 6:
-				/* u-law */
-				if (ifmt)
-					isdn_audio_ulaw2alaw(skb->data,
-							     buflen);
-				break;
-		}
-	}
-#endif                          /* CONFIG_ISDN_AUDIO */
-	if (info->emu.mdmreg[REG_T70] & BIT_T70) {
-		/* Add T.70 simplified header */
-		if (info->emu.mdmreg[REG_T70] & BIT_T70_EXT)
-			memcpy(skb_push(skb, 2), "\1\0", 2);
-		else
-			memcpy(skb_push(skb, 4), "\1\0\1\0", 4);
-	}
-	skb_queue_tail(&info->xmit_queue, skb);
-}
-
-/************************************************************
- *
- * Modem-functions
- *
- * mostly "stolen" from original Linux-serial.c and friends.
- *
- ************************************************************/
-
-/* The next routine is called once from within timer-interrupt
- * triggered within isdn_tty_modem_ncarrier(). It calls
- * isdn_tty_modem_result() to stuff a "NO CARRIER" Message
- * into the tty's flip-buffer.
- */
-static void
-isdn_tty_modem_do_ncarrier(unsigned long data)
-{
-	modem_info *info = (modem_info *) data;
-	isdn_tty_modem_result(RESULT_NO_CARRIER, info);
-}
-
-/* Next routine is called, whenever the DTR-signal is raised.
- * It checks the ncarrier-flag, and triggers the above routine
- * when necessary. The ncarrier-flag is set, whenever DTR goes
- * low.
- */
-static void
-isdn_tty_modem_ncarrier(modem_info * info)
-{
-	if (info->ncarrier) {
-		info->nc_timer.expires = jiffies + HZ;
-		info->nc_timer.function = isdn_tty_modem_do_ncarrier;
-		info->nc_timer.data = (unsigned long) info;
-		add_timer(&info->nc_timer);
-	}
-}
-
-/*
- * return the usage calculated by si and layer 2 protocol
- */
-int
-isdn_calc_usage(int si, int l2)
-{
-	int usg = ISDN_USAGE_MODEM;
-
-#ifdef CONFIG_ISDN_AUDIO
-	if (si == 1) {
-		switch(l2) {
-			case ISDN_PROTO_L2_MODEM: 
-				usg = ISDN_USAGE_MODEM;
-				break;
-#ifdef CONFIG_ISDN_TTY_FAX
-			case ISDN_PROTO_L2_FAX: 
-				usg = ISDN_USAGE_FAX;
-				break;
-#endif
-			case ISDN_PROTO_L2_TRANS: 
-			default:
-				usg = ISDN_USAGE_VOICE;
-				break;
-		}
-	}
-#endif
-	return(usg);
-}
-
-/* isdn_tty_dial() performs dialing of a tty an the necessary
- * setup of the lower levels before that.
- */
-static void
-isdn_tty_dial(char *n, modem_info * info, atemu * m)
-{
-	int usg = ISDN_USAGE_MODEM;
-	int si = 7;
-	int l2 = m->mdmreg[REG_L2PROT];
-	isdn_ctrl cmd;
-	ulong flags;
-	int i;
-	int j;
-
-	for (j = 7; j >= 0; j--)
-		if (m->mdmreg[REG_SI1] & (1 << j)) {
-			si = bit2si[j];
-			break;
-		}
-	usg = isdn_calc_usage(si, l2);
-#ifdef CONFIG_ISDN_AUDIO
-	if ((si == 1) && 
-		(l2 != ISDN_PROTO_L2_MODEM)
-#ifdef CONFIG_ISDN_TTY_FAX
-		&& (l2 != ISDN_PROTO_L2_FAX)
-#endif
-		) {
-		l2 = ISDN_PROTO_L2_TRANS;
-		usg = ISDN_USAGE_VOICE;
-	}
-#endif
-	m->mdmreg[REG_SI1I] = si2bit[si];
-	save_flags(flags);
-	cli();
-	i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1, m->msn);
-	if (i < 0) {
-		restore_flags(flags);
-		isdn_tty_modem_result(RESULT_NO_DIALTONE, info);
-	} else {
-		info->isdn_driver = dev->drvmap[i];
-		info->isdn_channel = dev->chanmap[i];
-		info->drv_index = i;
-		dev->m_idx[i] = info->line;
-		dev->usage[i] |= ISDN_USAGE_OUTGOING;
-		info->last_dir = 1;
-		strcpy(info->last_num, n);
-		isdn_info_update();
-		restore_flags(flags);
-		cmd.driver = info->isdn_driver;
-		cmd.arg = info->isdn_channel;
-		cmd.command = ISDN_CMD_CLREAZ;
-		isdn_command(&cmd);
-		strcpy(cmd.parm.num, isdn_map_eaz2msn(m->msn, info->isdn_driver));
-		cmd.driver = info->isdn_driver;
-		cmd.command = ISDN_CMD_SETEAZ;
-		isdn_command(&cmd);
-		cmd.driver = info->isdn_driver;
-		cmd.command = ISDN_CMD_SETL2;
-		info->last_l2 = l2;
-		cmd.arg = info->isdn_channel + (l2 << 8);
-		isdn_command(&cmd);
-		cmd.driver = info->isdn_driver;
-		cmd.command = ISDN_CMD_SETL3;
-		cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8);
-#ifdef CONFIG_ISDN_TTY_FAX
-		if (l2 == ISDN_PROTO_L2_FAX) {
-			cmd.parm.fax = info->fax;
-			info->fax->direction = ISDN_TTY_FAX_CONN_OUT;
-		}
-#endif
-		isdn_command(&cmd);
-		cmd.driver = info->isdn_driver;
-		cmd.arg = info->isdn_channel;
-		sprintf(cmd.parm.setup.phone, "%s", n);
-		sprintf(cmd.parm.setup.eazmsn, "%s",
-			isdn_map_eaz2msn(m->msn, info->isdn_driver));
-		cmd.parm.setup.si1 = si;
-		cmd.parm.setup.si2 = m->mdmreg[REG_SI2];
-		cmd.command = ISDN_CMD_DIAL;
-		info->dialing = 1;
-		info->emu.carrierwait = 0;
-		strcpy(dev->num[i], n);
-		isdn_info_update();
-		isdn_command(&cmd);
-		isdn_timer_ctrl(ISDN_TIMER_CARRIER, 1);
-	}
-}
-
-/* isdn_tty_hangup() disassociates a tty from the real
- * ISDN-line (hangup). The usage-status is cleared
- * and some cleanup is done also.
- */
-void
-isdn_tty_modem_hup(modem_info * info, int local)
-{
-	isdn_ctrl cmd;
-	int di, ch;
-
-	if (!info)
-		return;
-
-	di = info->isdn_driver;
-	ch = info->isdn_channel;
-	if (di < 0 || ch < 0)
-		return;
-
-	info->isdn_driver = -1;
-	info->isdn_channel = -1;
-
-#ifdef ISDN_DEBUG_MODEM_HUP
-	printk(KERN_DEBUG "Mhup ttyI%d\n", info->line);
-#endif
-	info->rcvsched = 0;
-	isdn_tty_flush_buffer(info->tty);
-	if (info->online) {
-		info->last_lhup = local;
-		info->online = 0;
-		isdn_tty_modem_result(RESULT_NO_CARRIER, info);
-	}
-#ifdef CONFIG_ISDN_AUDIO
-	info->vonline = 0;
-#ifdef CONFIG_ISDN_TTY_FAX
-	info->faxonline = 0;
-	info->fax->phase = ISDN_FAX_PHASE_IDLE;
-#endif
-	info->emu.vpar[4] = 0;
-	info->emu.vpar[5] = 8;
-	if (info->dtmf_state) {
-		kfree(info->dtmf_state);
-		info->dtmf_state = NULL;
-	}
-	if (info->silence_state) {
-		kfree(info->silence_state);
-		info->silence_state = NULL;
-	}
-	if (info->adpcms) {
-		kfree(info->adpcms);
-		info->adpcms = NULL;
-	}
-	if (info->adpcmr) {
-		kfree(info->adpcmr);
-		info->adpcmr = NULL;
-	}
-#endif
-	if ((info->msr & UART_MSR_RI) &&
-		(info->emu.mdmreg[REG_RUNG] & BIT_RUNG))
-		isdn_tty_modem_result(RESULT_RUNG, info);
-	info->msr &= ~(UART_MSR_DCD | UART_MSR_RI);
-	info->lsr |= UART_LSR_TEMT;
-
-	if (local) {
-		cmd.driver = di;
-		cmd.command = ISDN_CMD_HANGUP;
-		cmd.arg = ch;
-		isdn_command(&cmd);
-	}
-
-	isdn_all_eaz(di, ch);
-	info->emu.mdmreg[REG_RINGCNT] = 0;
-	isdn_free_channel(di, ch, 0);
-
-	if (info->drv_index >= 0) {
-		dev->m_idx[info->drv_index] = -1;
-		info->drv_index = -1;
-	}
-}
-
-/*
- * Begin of a CAPI like interface, currently used only for 
- * supplementary service (CAPI 2.0 part III)
- */
-#include "avmb1/capicmd.h"  /* this should be moved in a common place */
-
-int
-isdn_tty_capi_facility(capi_msg *cm) {
-	return(-1); /* dummy */
-}
-
-/* isdn_tty_suspend() tries to suspend the current tty connection
- */
-static void
-isdn_tty_suspend(char *id, modem_info * info, atemu * m)
-{
-	isdn_ctrl cmd;
-	
-	int l;
-
-	if (!info)
-		return;
-
-#ifdef ISDN_DEBUG_MODEM_SERVICES
-	printk(KERN_DEBUG "Msusp ttyI%d\n", info->line);
-#endif
-	l = strlen(id);
-	if ((info->isdn_driver >= 0)) {
-		cmd.parm.cmsg.Length = l+18;
-		cmd.parm.cmsg.Command = CAPI_FACILITY;
-		cmd.parm.cmsg.Subcommand = CAPI_REQ;
-		cmd.parm.cmsg.adr.Controller = info->isdn_driver + 1;
-		cmd.parm.cmsg.para[0] = 3; /* 16 bit 0x0003 suplementary service */
-		cmd.parm.cmsg.para[1] = 0;
-		cmd.parm.cmsg.para[2] = l + 3;
-		cmd.parm.cmsg.para[3] = 4; /* 16 bit 0x0004 Suspend */
-		cmd.parm.cmsg.para[4] = 0;
-		cmd.parm.cmsg.para[5] = l;
-		strncpy(&cmd.parm.cmsg.para[6], id, l);
-		cmd.command = CAPI_PUT_MESSAGE;
-		cmd.driver = info->isdn_driver;
-		cmd.arg = info->isdn_channel;
-		isdn_command(&cmd);
-	}
-}
-
-/* isdn_tty_resume() tries to resume a suspended call
- * setup of the lower levels before that. unfortunatly here is no
- * checking for compatibility of used protocols implemented by Q931
- * It does the same things like isdn_tty_dial, the last command
- * is different, may be we can merge it.
- */
-
-static void
-isdn_tty_resume(char *id, modem_info * info, atemu * m)
-{
-	int usg = ISDN_USAGE_MODEM;
-	int si = 7;
-	int l2 = m->mdmreg[REG_L2PROT];
-	isdn_ctrl cmd;
-	ulong flags;
-	int i;
-	int j;
-	int l;
-
-	l = strlen(id);
-	for (j = 7; j >= 0; j--)
-		if (m->mdmreg[REG_SI1] & (1 << j)) {
-			si = bit2si[j];
-			break;
-		}
-	usg = isdn_calc_usage(si, l2);
-#ifdef CONFIG_ISDN_AUDIO
-	if ((si == 1) && 
-		(l2 != ISDN_PROTO_L2_MODEM)
-#ifdef CONFIG_ISDN_TTY_FAX
-		&& (l2 != ISDN_PROTO_L2_FAX)
-#endif
-		) {
-		l2 = ISDN_PROTO_L2_TRANS;
-		usg = ISDN_USAGE_VOICE;
-	}
-#endif
-	m->mdmreg[REG_SI1I] = si2bit[si];
-	save_flags(flags);
-	cli();
-	i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1, m->msn);
-	if (i < 0) {
-		restore_flags(flags);
-		isdn_tty_modem_result(RESULT_NO_DIALTONE, info);
-	} else {
-		info->isdn_driver = dev->drvmap[i];
-		info->isdn_channel = dev->chanmap[i];
-		info->drv_index = i;
-		dev->m_idx[i] = info->line;
-		dev->usage[i] |= ISDN_USAGE_OUTGOING;
-		info->last_dir = 1;
-//		strcpy(info->last_num, n);
-		isdn_info_update();
-		restore_flags(flags);
-		cmd.driver = info->isdn_driver;
-		cmd.arg = info->isdn_channel;
-		cmd.command = ISDN_CMD_CLREAZ;
-		isdn_command(&cmd);
-		strcpy(cmd.parm.num, isdn_map_eaz2msn(m->msn, info->isdn_driver));
-		cmd.driver = info->isdn_driver;
-		cmd.command = ISDN_CMD_SETEAZ;
-		isdn_command(&cmd);
-		cmd.driver = info->isdn_driver;
-		cmd.command = ISDN_CMD_SETL2;
-		info->last_l2 = l2;
-		cmd.arg = info->isdn_channel + (l2 << 8);
-		isdn_command(&cmd);
-		cmd.driver = info->isdn_driver;
-		cmd.command = ISDN_CMD_SETL3;
-		cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8);
-		isdn_command(&cmd);
-		cmd.driver = info->isdn_driver;
-		cmd.arg = info->isdn_channel;
-		cmd.parm.cmsg.Length = l+18;
-		cmd.parm.cmsg.Command = CAPI_FACILITY;
-		cmd.parm.cmsg.Subcommand = CAPI_REQ;
-		cmd.parm.cmsg.adr.Controller = info->isdn_driver + 1;
-		cmd.parm.cmsg.para[0] = 3; /* 16 bit 0x0003 suplementary service */
-		cmd.parm.cmsg.para[1] = 0;
-		cmd.parm.cmsg.para[2] = l+3;
-		cmd.parm.cmsg.para[3] = 5; /* 16 bit 0x0005 Resume */
-		cmd.parm.cmsg.para[4] = 0;
-		cmd.parm.cmsg.para[5] = l;
-		strncpy(&cmd.parm.cmsg.para[6], id, l);
-		cmd.command =CAPI_PUT_MESSAGE;
-		info->dialing = 1;
-//		strcpy(dev->num[i], n);
-		isdn_info_update();
-		isdn_command(&cmd);
-		isdn_timer_ctrl(ISDN_TIMER_CARRIER, 1);
-	}
-}
-
-/* isdn_tty_send_msg() sends a message to a HL driver
- * This is used for hybrid modem cards to send AT commands to it
- */
-
-static void
-isdn_tty_send_msg(modem_info * info, atemu * m, char *msg)
-{
-	int usg = ISDN_USAGE_MODEM;
-	int si = 7;
-	int l2 = m->mdmreg[REG_L2PROT];
-	isdn_ctrl cmd;
-	ulong flags;
-	int i;
-	int j;
-	int l;
-
-	l = strlen(msg);
-	if (!l) {
-		isdn_tty_modem_result(RESULT_ERROR, info);
-		return;
-	}
-	for (j = 7; j >= 0; j--)
-		if (m->mdmreg[REG_SI1] & (1 << j)) {
-			si = bit2si[j];
-			break;
-		}
-	usg = isdn_calc_usage(si, l2);
-#ifdef CONFIG_ISDN_AUDIO
-	if ((si == 1) && 
-		(l2 != ISDN_PROTO_L2_MODEM)
-#ifdef CONFIG_ISDN_TTY_FAX
-		&& (l2 != ISDN_PROTO_L2_FAX)
-#endif
-		) {
-		l2 = ISDN_PROTO_L2_TRANS;
-		usg = ISDN_USAGE_VOICE;
-	}
-#endif
-	m->mdmreg[REG_SI1I] = si2bit[si];
-	save_flags(flags);
-	cli();
-	i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1, m->msn);
-	if (i < 0) {
-		restore_flags(flags);
-		isdn_tty_modem_result(RESULT_NO_DIALTONE, info);
-	} else {
-		info->isdn_driver = dev->drvmap[i];
-		info->isdn_channel = dev->chanmap[i];
-		info->drv_index = i;
-		dev->m_idx[i] = info->line;
-		dev->usage[i] |= ISDN_USAGE_OUTGOING;
-		info->last_dir = 1;
-		isdn_info_update();
-		restore_flags(flags);
-		cmd.driver = info->isdn_driver;
-		cmd.arg = info->isdn_channel;
-		cmd.command = ISDN_CMD_CLREAZ;
-		isdn_command(&cmd);
-		strcpy(cmd.parm.num, isdn_map_eaz2msn(m->msn, info->isdn_driver));
-		cmd.driver = info->isdn_driver;
-		cmd.command = ISDN_CMD_SETEAZ;
-		isdn_command(&cmd);
-		cmd.driver = info->isdn_driver;
-		cmd.command = ISDN_CMD_SETL2;
-		info->last_l2 = l2;
-		cmd.arg = info->isdn_channel + (l2 << 8);
-		isdn_command(&cmd);
-		cmd.driver = info->isdn_driver;
-		cmd.command = ISDN_CMD_SETL3;
-		cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8);
-		isdn_command(&cmd);
-		cmd.driver = info->isdn_driver;
-		cmd.arg = info->isdn_channel;
-		cmd.parm.cmsg.Length = l+14;
-		cmd.parm.cmsg.Command = CAPI_MANUFACTURER;
-		cmd.parm.cmsg.Subcommand = CAPI_REQ;
-		cmd.parm.cmsg.adr.Controller = info->isdn_driver + 1;
-		cmd.parm.cmsg.para[0] = l+1;
-		strncpy(&cmd.parm.cmsg.para[1], msg, l);
-		cmd.parm.cmsg.para[l+1] = 0xd;
-		cmd.command =CAPI_PUT_MESSAGE;
-/*		info->dialing = 1;
-		strcpy(dev->num[i], n);
-		isdn_info_update();
-*/
-		isdn_command(&cmd);
-	}
-}
-
-static inline int
-isdn_tty_paranoia_check(modem_info * info, kdev_t device, const char *routine)
-{
-#ifdef MODEM_PARANOIA_CHECK
-	if (!info) {
-		printk(KERN_WARNING "isdn_tty: null info_struct for (%d, %d) in %s\n",
-		       major(device), minor(device), routine);
-		return 1;
-	}
-	if (info->magic != ISDN_ASYNC_MAGIC) {
-		printk(KERN_WARNING "isdn_tty: bad magic for modem struct (%d, %d) in %s\n",
-		       major(device), minor(device), routine);
-		return 1;
-	}
-#endif
-	return 0;
-}
-
-/*
- * This routine is called to set the UART divisor registers to match
- * the specified baud rate for a serial port.
- */
-static void
-isdn_tty_change_speed(modem_info * info)
-{
-	uint cflag,
-	 cval,
-	 fcr,
-	 quot;
-	int i;
-
-	if (!info->tty || !info->tty->termios)
-		return;
-	cflag = info->tty->termios->c_cflag;
-
-	quot = i = cflag & CBAUD;
-	if (i & CBAUDEX) {
-		i &= ~CBAUDEX;
-		if (i < 1 || i > 2)
-			info->tty->termios->c_cflag &= ~CBAUDEX;
-		else
-			i += 15;
-	}
-	if (quot) {
-		info->mcr |= UART_MCR_DTR;
-		isdn_tty_modem_ncarrier(info);
-	} else {
-		info->mcr &= ~UART_MCR_DTR;
-		if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) {
-#ifdef ISDN_DEBUG_MODEM_HUP
-			printk(KERN_DEBUG "Mhup in changespeed\n");
-#endif
-			if (info->online)
-				info->ncarrier = 1;
-			isdn_tty_modem_reset_regs(info, 0);
-			isdn_tty_modem_hup(info, 1);
-		}
-		return;
-	}
-	/* byte size and parity */
-	cval = cflag & (CSIZE | CSTOPB);
-	cval >>= 4;
-	if (cflag & PARENB)
-		cval |= UART_LCR_PARITY;
-	if (!(cflag & PARODD))
-		cval |= UART_LCR_EPAR;
-	fcr = 0;
-
-	/* CTS flow control flag and modem status interrupts */
-	if (cflag & CRTSCTS) {
-		info->flags |= ISDN_ASYNC_CTS_FLOW;
-	} else
-		info->flags &= ~ISDN_ASYNC_CTS_FLOW;
-	if (cflag & CLOCAL)
-		info->flags &= ~ISDN_ASYNC_CHECK_CD;
-	else {
-		info->flags |= ISDN_ASYNC_CHECK_CD;
-	}
-}
-
-static int
-isdn_tty_startup(modem_info * info)
-{
-	ulong flags;
-
-	if (info->flags & ISDN_ASYNC_INITIALIZED)
-		return 0;
-	save_flags(flags);
-	cli();
-	isdn_MOD_INC_USE_COUNT();
-#ifdef ISDN_DEBUG_MODEM_OPEN
-	printk(KERN_DEBUG "starting up ttyi%d ...\n", info->line);
-#endif
-	/*
-	 * Now, initialize the UART
-	 */
-	info->mcr = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
-	if (info->tty)
-		clear_bit(TTY_IO_ERROR, &info->tty->flags);
-	/*
-	 * and set the speed of the serial port
-	 */
-	isdn_tty_change_speed(info);
-
-	info->flags |= ISDN_ASYNC_INITIALIZED;
-	info->msr |= (UART_MSR_DSR | UART_MSR_CTS);
-	info->send_outstanding = 0;
-	restore_flags(flags);
-	return 0;
-}
-
-/*
- * This routine will shutdown a serial port; interrupts are disabled, and
- * DTR is dropped if the hangup on close termio flag is on.
- */
-static void
-isdn_tty_shutdown(modem_info * info)
-{
-	ulong flags;
-
-	if (!(info->flags & ISDN_ASYNC_INITIALIZED))
-		return;
-#ifdef ISDN_DEBUG_MODEM_OPEN
-	printk(KERN_DEBUG "Shutting down isdnmodem port %d ....\n", info->line);
-#endif
-	save_flags(flags);
-	cli();                  /* Disable interrupts */
-	isdn_MOD_DEC_USE_COUNT();
-	info->msr &= ~UART_MSR_RI;
-	if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
-		info->mcr &= ~(UART_MCR_DTR | UART_MCR_RTS);
-		if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) {
-			isdn_tty_modem_reset_regs(info, 0);
-#ifdef ISDN_DEBUG_MODEM_HUP
-			printk(KERN_DEBUG "Mhup in isdn_tty_shutdown\n");
-#endif
-			isdn_tty_modem_hup(info, 1);
-		}
-	}
-	if (info->tty)
-		set_bit(TTY_IO_ERROR, &info->tty->flags);
-
-	info->flags &= ~ISDN_ASYNC_INITIALIZED;
-	restore_flags(flags);
-}
-
-/* isdn_tty_write() is the main send-routine. It is called from the upper
- * levels within the kernel to perform sending data. Depending on the
- * online-flag it either directs output to the at-command-interpreter or
- * to the lower level. Additional tasks done here:
- *  - If online, check for escape-sequence (+++)
- *  - If sending audio-data, call isdn_tty_DLEdown() to parse DLE-codes.
- *  - If receiving audio-data, call isdn_tty_end_vrx() to abort if needed.
- *  - If dialing, abort dial.
- */
-static int
-isdn_tty_write(struct tty_struct *tty, int from_user, const u_char * buf, int count)
-{
-	int c;
-	int total = 0;
-	modem_info *info = (modem_info *) tty->driver_data;
-	atemu *m = &info->emu;
-
-	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_write"))
-		return 0;
-	if (from_user)
-		down(&info->write_sem);
-	/* See isdn_tty_senddown() */
-	atomic_inc(&info->xmit_lock);
-	while (1) {
-		c = count;
-		if (c > info->xmit_size - info->xmit_count)
-			c = info->xmit_size - info->xmit_count;
-		if (info->isdn_driver >= 0 && c > dev->drv[info->isdn_driver]->maxbufsize)
-			c = dev->drv[info->isdn_driver]->maxbufsize;
-		if (c <= 0)
-			break;
-		if ((info->online > 1)
-#ifdef CONFIG_ISDN_AUDIO
-		    || (info->vonline & 3)
-#endif
-			) {
-#ifdef CONFIG_ISDN_AUDIO
-			if (!info->vonline)
-#endif
-				isdn_tty_check_esc(buf, m->mdmreg[REG_ESC], c,
-						   &(m->pluscount),
-						   &(m->lastplus),
-						   from_user);
-			if (from_user)
-				copy_from_user(&(info->xmit_buf[info->xmit_count]), buf, c);
-			else
-				memcpy(&(info->xmit_buf[info->xmit_count]), buf, c);
-#ifdef CONFIG_ISDN_AUDIO
-			if (info->vonline) {
-				int cc = isdn_tty_handleDLEdown(info, m, c);
-				if (info->vonline & 2) {
-					if (!cc) {
-						/* If DLE decoding results in zero-transmit, but
-						 * c originally was non-zero, do a wakeup.
-						 */
-						if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
-						 tty->ldisc.write_wakeup)
-							(tty->ldisc.write_wakeup) (tty);
-						wake_up_interruptible(&tty->write_wait);
-						info->msr |= UART_MSR_CTS;
-						info->lsr |= UART_LSR_TEMT;
-					}
-					info->xmit_count += cc;
-				}
-				if ((info->vonline & 3) == 1) {
-					/* Do NOT handle Ctrl-Q or Ctrl-S
-					 * when in full-duplex audio mode.
-					 */
-					if (isdn_tty_end_vrx(buf, c, from_user)) {
-						info->vonline &= ~1;
-#ifdef ISDN_DEBUG_MODEM_VOICE
-						printk(KERN_DEBUG
-						       "got !^Q/^S, send DLE-ETX,VCON on ttyI%d\n",
-						       info->line);
-#endif
-						isdn_tty_at_cout("\020\003\r\nVCON\r\n", info);
-					}
-				}
-			} else
-			if (TTY_IS_FCLASS1(info)) {
-				int cc = isdn_tty_handleDLEdown(info, m, c);
-				
-				if (info->vonline & 4) { /* ETX seen */
-					isdn_ctrl c;
-
-					c.command = ISDN_CMD_FAXCMD;
-					c.driver = info->isdn_driver;
-					c.arg = info->isdn_channel;
-					c.parm.aux.cmd = ISDN_FAX_CLASS1_CTRL;
-					c.parm.aux.subcmd = ETX;
-					isdn_command(&c);
-				}
-				info->vonline = 0;
-#ifdef ISDN_DEBUG_MODEM_VOICE
-				printk(KERN_DEBUG "fax dle cc/c %d/%d\n", cc, c);
-#endif
-				info->xmit_count += cc;
-			} else
-#endif
-				info->xmit_count += c;
-		} else {
-			info->msr |= UART_MSR_CTS;
-			info->lsr |= UART_LSR_TEMT;
-			if (info->dialing) {
-				info->dialing = 0;
-#ifdef ISDN_DEBUG_MODEM_HUP
-				printk(KERN_DEBUG "Mhup in isdn_tty_write\n");
-#endif
-				isdn_tty_modem_result(RESULT_NO_CARRIER, info);
-				isdn_tty_modem_hup(info, 1);
-			} else
-				c = isdn_tty_edit_at(buf, c, info, from_user);
-		}
-		buf += c;
-		count -= c;
-		total += c;
-	}
-	atomic_dec(&info->xmit_lock);
-	if ((info->xmit_count) || (skb_queue_len(&info->xmit_queue))) {
-		if (m->mdmreg[REG_DXMT] & BIT_DXMT) {
-			isdn_tty_senddown(info);
-			isdn_tty_tint(info);
-		}
-		isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, 1);
-	}
-	if (from_user)
-		up(&info->write_sem);
-	return total;
-}
-
-static int
-isdn_tty_write_room(struct tty_struct *tty)
-{
-	modem_info *info = (modem_info *) tty->driver_data;
-	int ret;
-
-	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_write_room"))
-		return 0;
-	if (!info->online)
-		return info->xmit_size;
-	ret = info->xmit_size - info->xmit_count;
-	return (ret < 0) ? 0 : ret;
-}
-
-static int
-isdn_tty_chars_in_buffer(struct tty_struct *tty)
-{
-	modem_info *info = (modem_info *) tty->driver_data;
-
-	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_chars_in_buffer"))
-		return 0;
-	if (!info->online)
-		return 0;
-	return (info->xmit_count);
-}
-
-static void
-isdn_tty_flush_buffer(struct tty_struct *tty)
-{
-	modem_info *info;
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-	if (!tty) {
-		restore_flags(flags);
-		return;
-	}
-	info = (modem_info *) tty->driver_data;
-	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_flush_buffer")) {
-		restore_flags(flags);
-		return;
-	}
-	isdn_tty_cleanup_xmit(info);
-	info->xmit_count = 0;
-	restore_flags(flags);
-	wake_up_interruptible(&tty->write_wait);
-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
-	    tty->ldisc.write_wakeup)
-		(tty->ldisc.write_wakeup) (tty);
-}
-
-static void
-isdn_tty_flush_chars(struct tty_struct *tty)
-{
-	modem_info *info = (modem_info *) tty->driver_data;
-
-	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_flush_chars"))
-		return;
-	if ((info->xmit_count) || (skb_queue_len(&info->xmit_queue)))
-		isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, 1);
-}
-
-/*
- * ------------------------------------------------------------
- * isdn_tty_throttle()
- *
- * This routine is called by the upper-layer tty layer to signal that
- * incoming characters should be throttled.
- * ------------------------------------------------------------
- */
-static void
-isdn_tty_throttle(struct tty_struct *tty)
-{
-	modem_info *info = (modem_info *) tty->driver_data;
-
-	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_throttle"))
-		return;
-	if (I_IXOFF(tty))
-		info->x_char = STOP_CHAR(tty);
-	info->mcr &= ~UART_MCR_RTS;
-}
-
-static void
-isdn_tty_unthrottle(struct tty_struct *tty)
-{
-	modem_info *info = (modem_info *) tty->driver_data;
-
-	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_unthrottle"))
-		return;
-	if (I_IXOFF(tty)) {
-		if (info->x_char)
-			info->x_char = 0;
-		else
-			info->x_char = START_CHAR(tty);
-	}
-	info->mcr |= UART_MCR_RTS;
-}
-
-/*
- * ------------------------------------------------------------
- * isdn_tty_ioctl() and friends
- * ------------------------------------------------------------
- */
-
-/*
- * isdn_tty_get_lsr_info - get line status register info
- *
- * Purpose: Let user call ioctl() to get info when the UART physically
- *          is emptied.  On bus types like RS485, the transmitter must
- *          release the bus after transmitting. This must be done when
- *          the transmit shift register is empty, not be done when the
- *          transmit holding register is empty.  This functionality
- *          allows RS485 driver to be written in user space.
- */
-static int
-isdn_tty_get_lsr_info(modem_info * info, uint * value)
-{
-	u_char status;
-	uint result;
-	ulong flags;
-
-	save_flags(flags);
-	cli();
-	status = info->lsr;
-	restore_flags(flags);
-	result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
-	return put_user(result, (uint *) value);
-}
-
-
-static int
-isdn_tty_get_modem_info(modem_info * info, uint * value)
-{
-	u_char control,
-	 status;
-	uint result;
-	ulong flags;
-
-	control = info->mcr;
-	save_flags(flags);
-	cli();
-	status = info->msr;
-	restore_flags(flags);
-	result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0)
-	    | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0)
-	    | ((status & UART_MSR_DCD) ? TIOCM_CAR : 0)
-	    | ((status & UART_MSR_RI) ? TIOCM_RNG : 0)
-	    | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0)
-	    | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0);
-	return put_user(result, (uint *) value);
-}
-
-static int
-isdn_tty_set_modem_info(modem_info * info, uint cmd, uint * value)
-{
-	uint arg;
-	int pre_dtr;
-
-	if (get_user(arg, (uint *) value))
-		return -EFAULT;
-	switch (cmd) {
-		case TIOCMBIS:
-#ifdef ISDN_DEBUG_MODEM_IOCTL
-			printk(KERN_DEBUG "ttyI%d ioctl TIOCMBIS\n", info->line);
-#endif
-			if (arg & TIOCM_RTS) {
-				info->mcr |= UART_MCR_RTS;
-			}
-			if (arg & TIOCM_DTR) {
-				info->mcr |= UART_MCR_DTR;
-				isdn_tty_modem_ncarrier(info);
-			}
-			break;
-		case TIOCMBIC:
-#ifdef ISDN_DEBUG_MODEM_IOCTL
-			printk(KERN_DEBUG "ttyI%d ioctl TIOCMBIC\n", info->line);
-#endif
-			if (arg & TIOCM_RTS) {
-				info->mcr &= ~UART_MCR_RTS;
-			}
-			if (arg & TIOCM_DTR) {
-				info->mcr &= ~UART_MCR_DTR;
-				if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) {
-					isdn_tty_modem_reset_regs(info, 0);
-#ifdef ISDN_DEBUG_MODEM_HUP
-					printk(KERN_DEBUG "Mhup in TIOCMBIC\n");
-#endif
-					if (info->online)
-						info->ncarrier = 1;
-					isdn_tty_modem_hup(info, 1);
-				}
-			}
-			break;
-		case TIOCMSET:
-#ifdef ISDN_DEBUG_MODEM_IOCTL
-			printk(KERN_DEBUG "ttyI%d ioctl TIOCMSET\n", info->line);
-#endif
-			pre_dtr = (info->mcr & UART_MCR_DTR);
-			info->mcr = ((info->mcr & ~(UART_MCR_RTS | UART_MCR_DTR))
-				 | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
-			       | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
-			if (pre_dtr |= (info->mcr & UART_MCR_DTR)) {
-				if (!(info->mcr & UART_MCR_DTR)) {
-					if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) {
-						isdn_tty_modem_reset_regs(info, 0);
-#ifdef ISDN_DEBUG_MODEM_HUP
-						printk(KERN_DEBUG "Mhup in TIOCMSET\n");
-#endif
-						if (info->online)
-							info->ncarrier = 1;
-						isdn_tty_modem_hup(info, 1);
-					}
-				} else
-					isdn_tty_modem_ncarrier(info);
-			}
-			break;
-		default:
-			return -EINVAL;
-	}
-	return 0;
-}
-
-static int
-isdn_tty_ioctl(struct tty_struct *tty, struct file *file,
-	       uint cmd, ulong arg)
-{
-	modem_info *info = (modem_info *) tty->driver_data;
-	int retval;
-
-	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_ioctl"))
-		return -ENODEV;
-	if (tty->flags & (1 << TTY_IO_ERROR))
-		return -EIO;
-	switch (cmd) {
-		case TCSBRK:   /* SVID version: non-zero arg --> no break */
-#ifdef ISDN_DEBUG_MODEM_IOCTL
-			printk(KERN_DEBUG "ttyI%d ioctl TCSBRK\n", info->line);
-#endif
-			retval = tty_check_change(tty);
-			if (retval)
-				return retval;
-			tty_wait_until_sent(tty, 0);
-			return 0;
-		case TCSBRKP:  /* support for POSIX tcsendbreak() */
-#ifdef ISDN_DEBUG_MODEM_IOCTL
-			printk(KERN_DEBUG "ttyI%d ioctl TCSBRKP\n", info->line);
-#endif
-			retval = tty_check_change(tty);
-			if (retval)
-				return retval;
-			tty_wait_until_sent(tty, 0);
-			return 0;
-		case TIOCGSOFTCAR:
-#ifdef ISDN_DEBUG_MODEM_IOCTL
-			printk(KERN_DEBUG "ttyI%d ioctl TIOCGSOFTCAR\n", info->line);
-#endif
-			return put_user(C_CLOCAL(tty) ? 1 : 0, (ulong *) arg);
-		case TIOCSSOFTCAR:
-#ifdef ISDN_DEBUG_MODEM_IOCTL
-			printk(KERN_DEBUG "ttyI%d ioctl TIOCSSOFTCAR\n", info->line);
-#endif
-			if (get_user(arg, (ulong *) arg))
-				return -EFAULT;
-			tty->termios->c_cflag =
-			    ((tty->termios->c_cflag & ~CLOCAL) |
-			     (arg ? CLOCAL : 0));
-			return 0;
-		case TIOCMGET:
-#ifdef ISDN_DEBUG_MODEM_IOCTL
-			printk(KERN_DEBUG "ttyI%d ioctl TIOCMGET\n", info->line);
-#endif
-			return isdn_tty_get_modem_info(info, (uint *) arg);
-		case TIOCMBIS:
-		case TIOCMBIC:
-		case TIOCMSET:
-			return isdn_tty_set_modem_info(info, cmd, (uint *) arg);
-		case TIOCSERGETLSR:	/* Get line status register */
-#ifdef ISDN_DEBUG_MODEM_IOCTL
-			printk(KERN_DEBUG "ttyI%d ioctl TIOCSERGETLSR\n", info->line);
-#endif
-			return isdn_tty_get_lsr_info(info, (uint *) arg);
-		default:
-#ifdef ISDN_DEBUG_MODEM_IOCTL
-			printk(KERN_DEBUG "UNKNOWN ioctl 0x%08x on ttyi%d\n", cmd, info->line);
-#endif
-			return -ENOIOCTLCMD;
-	}
-	return 0;
-}
-
-static void
-isdn_tty_set_termios(struct tty_struct *tty, struct termios *old_termios)
-{
-	modem_info *info = (modem_info *) tty->driver_data;
-
-	if (!old_termios)
-		isdn_tty_change_speed(info);
-	else {
-		if (tty->termios->c_cflag == old_termios->c_cflag)
-			return;
-		isdn_tty_change_speed(info);
-		if ((old_termios->c_cflag & CRTSCTS) &&
-		    !(tty->termios->c_cflag & CRTSCTS)) {
-			tty->hw_stopped = 0;
-		}
-	}
-}
-
-/*
- * ------------------------------------------------------------
- * isdn_tty_open() and friends
- * ------------------------------------------------------------
- */
-static int
-isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * info)
-{
-	DECLARE_WAITQUEUE(wait, NULL);
-	int do_clocal = 0;
-	unsigned long flags;
-	int retval;
-
-	/*
-	 * If the device is in the middle of being closed, then block
-	 * until it's done, and then try again.
-	 */
-	if (tty_hung_up_p(filp) ||
-	    (info->flags & ISDN_ASYNC_CLOSING)) {
-		if (info->flags & ISDN_ASYNC_CLOSING)
-			interruptible_sleep_on(&info->close_wait);
-#ifdef MODEM_DO_RESTART
-		if (info->flags & ISDN_ASYNC_HUP_NOTIFY)
-			return -EAGAIN;
-		else
-			return -ERESTARTSYS;
-#else
-		return -EAGAIN;
-#endif
-	}
-	/*
-	 * If this is a callout device, then just make sure the normal
-	 * device isn't being used.
-	 */
-	if (tty->driver.subtype == ISDN_SERIAL_TYPE_CALLOUT) {
-		if (info->flags & ISDN_ASYNC_NORMAL_ACTIVE)
-			return -EBUSY;
-		if ((info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) &&
-		    (info->flags & ISDN_ASYNC_SESSION_LOCKOUT) &&
-		    (info->session != current->session))
-			return -EBUSY;
-		if ((info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) &&
-		    (info->flags & ISDN_ASYNC_PGRP_LOCKOUT) &&
-		    (info->pgrp != current->pgrp))
-			return -EBUSY;
-		info->flags |= ISDN_ASYNC_CALLOUT_ACTIVE;
-		return 0;
-	}
-	/*
-	 * If non-blocking mode is set, then make the check up front
-	 * and then exit.
-	 */
-	if ((filp->f_flags & O_NONBLOCK) ||
-	    (tty->flags & (1 << TTY_IO_ERROR))) {
-		if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE)
-			return -EBUSY;
-		info->flags |= ISDN_ASYNC_NORMAL_ACTIVE;
-		return 0;
-	}
-	if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) {
-		if (info->normal_termios.c_cflag & CLOCAL)
-			do_clocal = 1;
-	} else {
-		if (tty->termios->c_cflag & CLOCAL)
-			do_clocal = 1;
-	}
-	/*
-	 * Block waiting for the carrier detect and the line to become
-	 * free (i.e., not in use by the callout).  While we are in
-	 * this loop, info->count is dropped by one, so that
-	 * isdn_tty_close() knows when to free things.  We restore it upon
-	 * exit, either normal or abnormal.
-	 */
-	retval = 0;
-	add_wait_queue(&info->open_wait, &wait);
-#ifdef ISDN_DEBUG_MODEM_OPEN
-	printk(KERN_DEBUG "isdn_tty_block_til_ready before block: ttyi%d, count = %d\n",
-	       info->line, info->count);
-#endif
-	save_flags(flags);
-	cli();
-	if (!(tty_hung_up_p(filp)))
-		info->count--;
-	restore_flags(flags);
-	info->blocked_open++;
-	while (1) {
-		set_current_state(TASK_INTERRUPTIBLE);
-		if (tty_hung_up_p(filp) ||
-		    !(info->flags & ISDN_ASYNC_INITIALIZED)) {
-#ifdef MODEM_DO_RESTART
-			if (info->flags & ISDN_ASYNC_HUP_NOTIFY)
-				retval = -EAGAIN;
-			else
-				retval = -ERESTARTSYS;
-#else
-			retval = -EAGAIN;
-#endif
-			break;
-		}
-		if (!(info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) &&
-		    !(info->flags & ISDN_ASYNC_CLOSING) &&
-		    (do_clocal || (info->msr & UART_MSR_DCD))) {
-			break;
-		}
-		if (signal_pending(current)) {
-			retval = -ERESTARTSYS;
-			break;
-		}
-#ifdef ISDN_DEBUG_MODEM_OPEN
-		printk(KERN_DEBUG "isdn_tty_block_til_ready blocking: ttyi%d, count = %d\n",
-		       info->line, info->count);
-#endif
-		schedule();
-	}
-	current->state = TASK_RUNNING;
-	remove_wait_queue(&info->open_wait, &wait);
-	if (!tty_hung_up_p(filp))
-		info->count++;
-	info->blocked_open--;
-#ifdef ISDN_DEBUG_MODEM_OPEN
-	printk(KERN_DEBUG "isdn_tty_block_til_ready after blocking: ttyi%d, count = %d\n",
-	       info->line, info->count);
-#endif
-	if (retval)
-		return retval;
-	info->flags |= ISDN_ASYNC_NORMAL_ACTIVE;
-	return 0;
-}
-
-/*
- * This routine is called whenever a serial port is opened.  It
- * enables interrupts for a serial port, linking in its async structure into
- * the IRQ chain.   It also performs the serial-specific
- * initialization for the tty structure.
- */
-static int
-isdn_tty_open(struct tty_struct *tty, struct file *filp)
-{
-	modem_info *info;
-	int retval,
-	 line;
-
-	line = minor(tty->device) - tty->driver.minor_start;
-	if (line < 0 || line > ISDN_MAX_CHANNELS)
-		return -ENODEV;
-	info = &dev->mdm.info[line];
-	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_open"))
-		return -ENODEV;
-#ifdef ISDN_DEBUG_MODEM_OPEN
-	printk(KERN_DEBUG "isdn_tty_open %s%d, count = %d\n", tty->driver.name,
-	       info->line, info->count);
-#endif
-	info->count++;
-	tty->driver_data = info;
-	info->tty = tty;
-	/*
-	 * Start up serial port
-	 */
-	retval = isdn_tty_startup(info);
-	if (retval) {
-#ifdef ISDN_DEBUG_MODEM_OPEN
-		printk(KERN_DEBUG "isdn_tty_open return after startup\n");
-#endif
-		return retval;
-	}
-	retval = isdn_tty_block_til_ready(tty, filp, info);
-	if (retval) {
-#ifdef ISDN_DEBUG_MODEM_OPEN
-		printk(KERN_DEBUG "isdn_tty_open return after isdn_tty_block_til_ready \n");
-#endif
-		return retval;
-	}
-	if ((info->count == 1) && (info->flags & ISDN_ASYNC_SPLIT_TERMIOS)) {
-		if (tty->driver.subtype == ISDN_SERIAL_TYPE_NORMAL)
-			*tty->termios = info->normal_termios;
-		else
-			*tty->termios = info->callout_termios;
-		isdn_tty_change_speed(info);
-	}
-	info->session = current->session;
-	info->pgrp = current->pgrp;
-#ifdef ISDN_DEBUG_MODEM_OPEN
-	printk(KERN_DEBUG "isdn_tty_open ttyi%d successful...\n", info->line);
-#endif
-	dev->modempoll++;
-#ifdef ISDN_DEBUG_MODEM_OPEN
-	printk(KERN_DEBUG "isdn_tty_open normal exit\n");
-#endif
-	return 0;
-}
-
-static void
-isdn_tty_close(struct tty_struct *tty, struct file *filp)
-{
-	modem_info *info = (modem_info *) tty->driver_data;
-	ulong flags;
-	ulong timeout;
-
-	if (!info || isdn_tty_paranoia_check(info, tty->device, "isdn_tty_close"))
-		return;
-	save_flags(flags);
-	cli();
-	if (tty_hung_up_p(filp)) {
-		restore_flags(flags);
-#ifdef ISDN_DEBUG_MODEM_OPEN
-		printk(KERN_DEBUG "isdn_tty_close return after tty_hung_up_p\n");
-#endif
-		return;
-	}
-	if ((tty->count == 1) && (info->count != 1)) {
-		/*
-		 * Uh, oh.  tty->count is 1, which means that the tty
-		 * structure will be freed.  Info->count should always
-		 * be one in these conditions.  If it's greater than
-		 * one, we've got real problems, since it means the
-		 * serial port won't be shutdown.
-		 */
-		printk(KERN_ERR "isdn_tty_close: bad port count; tty->count is 1, "
-		       "info->count is %d\n", info->count);
-		info->count = 1;
-	}
-	if (--info->count < 0) {
-		printk(KERN_ERR "isdn_tty_close: bad port count for ttyi%d: %d\n",
-		       info->line, info->count);
-		info->count = 0;
-	}
-	if (info->count) {
-		restore_flags(flags);
-#ifdef ISDN_DEBUG_MODEM_OPEN
-		printk(KERN_DEBUG "isdn_tty_close after info->count != 0\n");
-#endif
-		return;
-	}
-	info->flags |= ISDN_ASYNC_CLOSING;
-	/*
-	 * Save the termios structure, since this port may have
-	 * separate termios for callout and dialin.
-	 */
-	if (info->flags & ISDN_ASYNC_NORMAL_ACTIVE)
-		info->normal_termios = *tty->termios;
-	if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE)
-		info->callout_termios = *tty->termios;
-
-	tty->closing = 1;
-	/*
-	 * At this point we stop accepting input.  To do this, we
-	 * disable the receive line status interrupts, and tell the
-	 * interrupt driver to stop checking the data ready bit in the
-	 * line status register.
-	 */
-	if (info->flags & ISDN_ASYNC_INITIALIZED) {
-		tty_wait_until_sent(tty, 3000);	/* 30 seconds timeout */
-		/*
-		 * Before we drop DTR, make sure the UART transmitter
-		 * has completely drained; this is especially
-		 * important if there is a transmit FIFO!
-		 */
-		timeout = jiffies + HZ;
-		while (!(info->lsr & UART_LSR_TEMT)) {
-			set_current_state(TASK_INTERRUPTIBLE);
-			schedule_timeout(20);
-			if (time_after(jiffies,timeout))
-				break;
-		}
-	}
-	dev->modempoll--;
-	isdn_tty_shutdown(info);
-	if (tty->driver.flush_buffer)
-		tty->driver.flush_buffer(tty);
-	if (tty->ldisc.flush_buffer)
-		tty->ldisc.flush_buffer(tty);
-	info->tty = 0;
-	info->ncarrier = 0;
-	tty->closing = 0;
-	if (info->blocked_open) {
-		set_current_state(TASK_INTERRUPTIBLE);
-		schedule_timeout(50);
-		wake_up_interruptible(&info->open_wait);
-	}
-	info->flags &= ~(ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE |
-			 ISDN_ASYNC_CLOSING);
-	wake_up_interruptible(&info->close_wait);
-	restore_flags(flags);
-#ifdef ISDN_DEBUG_MODEM_OPEN
-	printk(KERN_DEBUG "isdn_tty_close normal exit\n");
-#endif
-}
-
-/*
- * isdn_tty_hangup() --- called by tty_hangup() when a hangup is signaled.
- */
-static void
-isdn_tty_hangup(struct tty_struct *tty)
-{
-	modem_info *info = (modem_info *) tty->driver_data;
-
-	if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_hangup"))
-		return;
-	isdn_tty_shutdown(info);
-	info->count = 0;
-	info->flags &= ~(ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE);
-	info->tty = 0;
-	wake_up_interruptible(&info->open_wait);
-}
-
-/* This routine initializes all emulator-data.
- */
-static void
-isdn_tty_reset_profile(atemu * m)
-{
-	m->profile[0] = 0;
-	m->profile[1] = 0;
-	m->profile[2] = 43;
-	m->profile[3] = 13;
-	m->profile[4] = 10;
-	m->profile[5] = 8;
-	m->profile[6] = 3;
-	m->profile[7] = 60;
-	m->profile[8] = 2;
-	m->profile[9] = 6;
-	m->profile[10] = 7;
-	m->profile[11] = 70;
-	m->profile[12] = 0x45;
-	m->profile[13] = 4;
-	m->profile[14] = ISDN_PROTO_L2_X75I;
-	m->profile[15] = ISDN_PROTO_L3_TRANS;
-	m->profile[16] = ISDN_SERIAL_XMIT_SIZE / 16;
-	m->profile[17] = ISDN_MODEM_WINSIZE;
-	m->profile[18] = 4;
-	m->profile[19] = 0;
-	m->profile[20] = 0;
-	m->profile[23] = 0;
-	m->pmsn[0] = '\0';
-	m->plmsn[0] = '\0';
-}
-
-#ifdef CONFIG_ISDN_AUDIO
-static void
-isdn_tty_modem_reset_vpar(atemu * m)
-{
-	m->vpar[0] = 2;         /* Voice-device            (2 = phone line) */
-	m->vpar[1] = 0;         /* Silence detection level (0 = none      ) */
-	m->vpar[2] = 70;        /* Silence interval        (7 sec.        ) */
-	m->vpar[3] = 2;         /* Compression type        (1 = ADPCM-2   ) */
-	m->vpar[4] = 0;         /* DTMF detection level    (0 = softcode  ) */
-	m->vpar[5] = 8;         /* DTMF interval           (8 * 5 ms.     ) */
-}
-#endif
-
-#ifdef CONFIG_ISDN_TTY_FAX
-static void
-isdn_tty_modem_reset_faxpar(modem_info * info)
-{
-	T30_s *f = info->fax;
-
-	f->code = 0;
-	f->phase = ISDN_FAX_PHASE_IDLE;
-	f->direction = 0;
-	f->resolution = 1;	/* fine */
-	f->rate = 5;		/* 14400 bit/s */
-	f->width = 0;
-	f->length = 0;
-	f->compression = 0;
-	f->ecm = 0;
-	f->binary = 0;
-	f->scantime = 0;
-	memset(&f->id[0], 32, FAXIDLEN - 1);
-	f->id[FAXIDLEN - 1] = 0;
-	f->badlin = 0;
-	f->badmul = 0;
-	f->bor = 0;
-	f->nbc = 0;
-	f->cq = 0;
-	f->cr = 0;
-	f->ctcrty = 0;
-	f->minsp = 0;
-	f->phcto = 30;
-	f->rel = 0;
-	memset(&f->pollid[0], 32, FAXIDLEN - 1);
-	f->pollid[FAXIDLEN - 1] = 0;
-}
-#endif
-
-static void
-isdn_tty_modem_reset_regs(modem_info * info, int force)
-{
-	atemu *m = &info->emu;
-	if ((m->mdmreg[REG_DTRR] & BIT_DTRR) || force) {
-		memcpy(m->mdmreg, m->profile, ISDN_MODEM_NUMREG);
-		memcpy(m->msn, m->pmsn, ISDN_MSNLEN);
-		memcpy(m->lmsn, m->plmsn, ISDN_LMSNLEN);
-		info->xmit_size = m->mdmreg[REG_PSIZE] * 16;
-	}
-#ifdef CONFIG_ISDN_AUDIO
-	isdn_tty_modem_reset_vpar(m);
-#endif
-#ifdef CONFIG_ISDN_TTY_FAX
-	isdn_tty_modem_reset_faxpar(info);
-#endif
-	m->mdmcmdl = 0;
-}
-
-static void
-modem_write_profile(atemu * m)
-{
-	memcpy(m->profile, m->mdmreg, ISDN_MODEM_NUMREG);
-	memcpy(m->pmsn, m->msn, ISDN_MSNLEN);
-	memcpy(m->plmsn, m->lmsn, ISDN_LMSNLEN);
-	if (dev->profd)
-		send_sig(SIGIO, dev->profd, 1);
-}
-
-int
-isdn_tty_modem_init(void)
-{
-	modem *m;
-	int i;
-	modem_info *info;
-
-	m = &dev->mdm;
-	memset(&m->tty_modem, 0, sizeof(struct tty_driver));
-	m->tty_modem.magic = TTY_DRIVER_MAGIC;
-	m->tty_modem.name = isdn_ttyname_ttyI;
-	m->tty_modem.major = ISDN_TTY_MAJOR;
-	m->tty_modem.minor_start = 0;
-	m->tty_modem.num = ISDN_MAX_CHANNELS;
-	m->tty_modem.type = TTY_DRIVER_TYPE_SERIAL;
-	m->tty_modem.subtype = ISDN_SERIAL_TYPE_NORMAL;
-	m->tty_modem.init_termios = tty_std_termios;
-	m->tty_modem.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
-	m->tty_modem.flags = TTY_DRIVER_REAL_RAW;
-	m->tty_modem.refcount = &m->refcount;
-	m->tty_modem.table = m->modem_table;
-	m->tty_modem.termios = m->modem_termios;
-	m->tty_modem.termios_locked = m->modem_termios_locked;
-	m->tty_modem.open = isdn_tty_open;
-	m->tty_modem.close = isdn_tty_close;
-	m->tty_modem.write = isdn_tty_write;
-	m->tty_modem.put_char = NULL;
-	m->tty_modem.flush_chars = isdn_tty_flush_chars;
-	m->tty_modem.write_room = isdn_tty_write_room;
-	m->tty_modem.chars_in_buffer = isdn_tty_chars_in_buffer;
-	m->tty_modem.flush_buffer = isdn_tty_flush_buffer;
-	m->tty_modem.ioctl = isdn_tty_ioctl;
-	m->tty_modem.throttle = isdn_tty_throttle;
-	m->tty_modem.unthrottle = isdn_tty_unthrottle;
-	m->tty_modem.set_termios = isdn_tty_set_termios;
-	m->tty_modem.stop = NULL;
-	m->tty_modem.start = NULL;
-	m->tty_modem.hangup = isdn_tty_hangup;
-	m->tty_modem.driver_name = "isdn_tty";
-	/*
-	 * The callout device is just like normal device except for
-	 * major number and the subtype code.
-	 */
-	m->cua_modem = m->tty_modem;
-	m->cua_modem.name = isdn_ttyname_cui;
-	m->cua_modem.major = ISDN_TTYAUX_MAJOR;
-	m->tty_modem.minor_start = 0;
-	m->cua_modem.subtype = ISDN_SERIAL_TYPE_CALLOUT;
-
-	if (tty_register_driver(&m->tty_modem)) {
-		printk(KERN_WARNING "isdn_tty: Couldn't register modem-device\n");
-		return -1;
-	}
-	if (tty_register_driver(&m->cua_modem)) {
-		printk(KERN_WARNING "isdn_tty: Couldn't register modem-callout-device\n");
-		return -2;
-	}
-	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-		info = &m->info[i];
-#ifdef CONFIG_ISDN_TTY_FAX
-		if (!(info->fax = kmalloc(sizeof(T30_s), GFP_KERNEL))) {
-			printk(KERN_ERR "Could not allocate fax t30-buffer\n");
-			return -3;
-		}
-#endif
-		init_MUTEX(&info->write_sem);
-		sprintf(info->last_cause, "0000");
-		sprintf(info->last_num, "none");
-		info->last_dir = 0;
-		info->last_lhup = 1;
-		info->last_l2 = -1;
-		info->last_si = 0;
-		isdn_tty_reset_profile(&info->emu);
-		isdn_tty_modem_reset_regs(info, 1);
-		info->magic = ISDN_ASYNC_MAGIC;
-		info->line = i;
-		info->tty = 0;
-		info->x_char = 0;
-		info->count = 0;
-		info->blocked_open = 0;
-		info->callout_termios = m->cua_modem.init_termios;
-		info->normal_termios = m->tty_modem.init_termios;
-		init_waitqueue_head(&info->open_wait);
-		init_waitqueue_head(&info->close_wait);
-		info->isdn_driver = -1;
-		info->isdn_channel = -1;
-		info->drv_index = -1;
-		info->xmit_size = ISDN_SERIAL_XMIT_SIZE;
-		skb_queue_head_init(&info->xmit_queue);
-#ifdef CONFIG_ISDN_AUDIO
-		skb_queue_head_init(&info->dtmf_queue);
-#endif
-		if (!(info->xmit_buf = kmalloc(ISDN_SERIAL_XMIT_MAX + 5, GFP_KERNEL))) {
-			printk(KERN_ERR "Could not allocate modem xmit-buffer\n");
-			return -3;
-		}
-		/* Make room for T.70 header */
-		info->xmit_buf += 4;
-	}
-	return 0;
-}
-
-
-/*
- * isdn_tty_match_icall(char *MSN, atemu *tty_emulator, int dev_idx)
- *      match the MSN against the MSNs (glob patterns) defined for tty_emulator,
- *      and return 0 for match, 1 for no match, 2 if MSN could match if longer.
- */
-
-static int
-isdn_tty_match_icall(char *cid, atemu *emu, int di)
-{
-#ifdef ISDN_DEBUG_MODEM_ICALL
-	printk(KERN_DEBUG "m_fi: msn=%s lmsn=%s mmsn=%s mreg[SI1]=%d mreg[SI2]=%d\n",
-	       emu->msn, emu->lmsn, isdn_map_eaz2msn(emu->msn, di),
-	       emu->mdmreg[REG_SI1], emu->mdmreg[REG_SI2]);
-#endif
-	if (strlen(emu->lmsn)) {
-		char *p = emu->lmsn;
-		char *q;
-		int  tmp;
-		int  ret = 0;
-
-		while (1) {
-			if ((q = strchr(p, ';')))
-				*q = '\0';
-			if ((tmp = isdn_msncmp(cid, isdn_map_eaz2msn(p, di))) > ret)
-				ret = tmp;
-#ifdef ISDN_DEBUG_MODEM_ICALL
-			printk(KERN_DEBUG "m_fi: lmsnX=%s mmsn=%s -> tmp=%d\n",
-			       p, isdn_map_eaz2msn(emu->msn, di), tmp);
-#endif
-			if (q) {
-				*q = ';';
-				p = q;
-				p++;
-			}
-			if (!tmp)
-				return 0;
-			if (!q)
-				break;
-		}
-		return ret;
-	} else {
-		int tmp;
-		tmp = isdn_msncmp(cid, isdn_map_eaz2msn(emu->msn, di));
-#ifdef ISDN_DEBUG_MODEM_ICALL
-			printk(KERN_DEBUG "m_fi: mmsn=%s -> tmp=%d\n",
-			       isdn_map_eaz2msn(emu->msn, di), tmp);
-#endif
-		return tmp;
-	}
-}
-
-/*
- * An incoming call-request has arrived.
- * Search the tty-devices for an appropriate device and bind
- * it to the ISDN-Channel.
- * Return:
- *
- *  0 = No matching device found.
- *  1 = A matching device found.
- *  3 = No match found, but eventually would match, if
- *      CID is longer.
- */
-int
-isdn_tty_find_icall(int di, int ch, setup_parm *setup)
-{
-	char *eaz;
-	int i;
-	int wret;
-	int idx;
-	int si1;
-	int si2;
-	char *nr;
-	ulong flags;
-
-	if (!setup->phone[0]) {
-		nr = "0";
-		printk(KERN_INFO "isdn_tty: Incoming call without OAD, assuming '0'\n");
-	} else
-		nr = setup->phone;
-	si1 = (int) setup->si1;
-	si2 = (int) setup->si2;
-	if (!setup->eazmsn[0]) {
-		printk(KERN_WARNING "isdn_tty: Incoming call without CPN, assuming '0'\n");
-		eaz = "0";
-	} else
-		eaz = setup->eazmsn;
-#ifdef ISDN_DEBUG_MODEM_ICALL
-	printk(KERN_DEBUG "m_fi: eaz=%s si1=%d si2=%d\n", eaz, si1, si2);
-#endif
-	wret = 0;
-	save_flags(flags);
-	cli();
-	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-		modem_info *info = &dev->mdm.info[i];
-
-                if (info->count == 0)
-                    continue;
-		if ((info->emu.mdmreg[REG_SI1] & si2bit[si1]) &&  /* SI1 is matching */
-		    (info->emu.mdmreg[REG_SI2] == si2))	{         /* SI2 is matching */
-			idx = isdn_dc2minor(di, ch);
-#ifdef ISDN_DEBUG_MODEM_ICALL
-			printk(KERN_DEBUG "m_fi: match1 wret=%d\n", wret);
-			printk(KERN_DEBUG "m_fi: idx=%d flags=%08lx drv=%d ch=%d usg=%d\n", idx,
-			       info->flags, info->isdn_driver, info->isdn_channel,
-			       dev->usage[idx]);
-#endif
-			if (
-#ifndef FIX_FILE_TRANSFER
-				(info->flags & ISDN_ASYNC_NORMAL_ACTIVE) &&
-#endif
-				(info->isdn_driver == -1) &&
-				(info->isdn_channel == -1) &&
-				(USG_NONE(dev->usage[idx]))) {
-				int matchret;
-
-				if ((matchret = isdn_tty_match_icall(eaz, &info->emu, di)) > wret)
-					wret = matchret;
-				if (!matchret) {                  /* EAZ is matching */
-					info->isdn_driver = di;
-					info->isdn_channel = ch;
-					info->drv_index = idx;
-					dev->m_idx[idx] = info->line;
-					dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE;
-					dev->usage[idx] |= isdn_calc_usage(si1, info->emu.mdmreg[REG_L2PROT]); 
-					strcpy(dev->num[idx], nr);
-					strcpy(info->emu.cpn, eaz);
-					info->emu.mdmreg[REG_SI1I] = si2bit[si1];
-					info->emu.mdmreg[REG_PLAN] = setup->plan;
-					info->emu.mdmreg[REG_SCREEN] = setup->screen;
-					isdn_info_update();
-					restore_flags(flags);
-					printk(KERN_INFO "isdn_tty: call from %s, -> RING on ttyI%d\n", nr,
-					       info->line);
-					info->msr |= UART_MSR_RI;
-					isdn_tty_modem_result(RESULT_RING, info);
-					isdn_timer_ctrl(ISDN_TIMER_MODEMRING, 1);
-					return 1;
-				}
-			}
-		}
-	}
-	restore_flags(flags);
-	printk(KERN_INFO "isdn_tty: call from %s -> %s %s\n", nr, eaz,
-	       ((dev->drv[di]->flags & DRV_FLAG_REJBUS) && (wret != 2))? "rejected" : "ignored");
-	return (wret == 2)?3:0;
-}
-
-#define TTY_IS_ACTIVE(info) \
-	(info->flags & (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE))
-
-int
-isdn_tty_stat_callback(int i, isdn_ctrl *c)
-{
-	int mi;
-	modem_info *info;
-	char *e;
-
-	if (i < 0)
-		return 0;
-	if ((mi = dev->m_idx[i]) >= 0) {
-		info = &dev->mdm.info[mi];
-		switch (c->command) {
-                        case ISDN_STAT_CINF:
-                                printk(KERN_DEBUG "CHARGEINFO on ttyI%d: %ld %s\n", info->line, c->arg, c->parm.num);
-                                info->emu.charge = (unsigned) simple_strtoul(c->parm.num, &e, 10);
-                                if (e == (char *)c->parm.num)
-					info->emu.charge = 0;
-				
-                                break;			
-			case ISDN_STAT_BSENT:
-#ifdef ISDN_TTY_STAT_DEBUG
-				printk(KERN_DEBUG "tty_STAT_BSENT ttyI%d\n", info->line);
-#endif
-				if ((info->isdn_driver == c->driver) &&
-				    (info->isdn_channel == c->arg)) {
-					info->msr |= UART_MSR_CTS;
-					if (info->send_outstanding)
-						if (!(--info->send_outstanding))
-							info->lsr |= UART_LSR_TEMT;
-					isdn_tty_tint(info);
-					return 1;
-				}
-				break;
-			case ISDN_STAT_CAUSE:
-#ifdef ISDN_TTY_STAT_DEBUG
-				printk(KERN_DEBUG "tty_STAT_CAUSE ttyI%d\n", info->line);
-#endif
-				/* Signal cause to tty-device */
-				strncpy(info->last_cause, c->parm.num, 5);
-				return 1;
-			case ISDN_STAT_DISPLAY:
-#ifdef ISDN_TTY_STAT_DEBUG
-				printk(KERN_DEBUG "tty_STAT_DISPLAY ttyI%d\n", info->line);
-#endif
-				/* Signal display to tty-device */
-				if ((info->emu.mdmreg[REG_DISPLAY] & BIT_DISPLAY) && 
-					!(info->emu.mdmreg[REG_RESPNUM] & BIT_RESPNUM)) {
-				  isdn_tty_at_cout("\r\n", info);
-				  isdn_tty_at_cout("DISPLAY: ", info);
-				  isdn_tty_at_cout(c->parm.display, info);
-				  isdn_tty_at_cout("\r\n", info);
-				}
-				return 1;
-			case ISDN_STAT_DCONN:
-#ifdef ISDN_TTY_STAT_DEBUG
-				printk(KERN_DEBUG "tty_STAT_DCONN ttyI%d\n", info->line);
-#endif
-				if (TTY_IS_ACTIVE(info)) {
-					if (info->dialing == 1) {
-						info->dialing = 2;
-						return 1;
-					}
-				}
-				break;
-			case ISDN_STAT_DHUP:
-#ifdef ISDN_TTY_STAT_DEBUG
-				printk(KERN_DEBUG "tty_STAT_DHUP ttyI%d\n", info->line);
-#endif
-				if (TTY_IS_ACTIVE(info)) {
-					if (info->dialing == 1) 
-						isdn_tty_modem_result(RESULT_BUSY, info);
-					if (info->dialing > 1) 
-						isdn_tty_modem_result(RESULT_NO_CARRIER, info);
-					info->dialing = 0;
-#ifdef ISDN_DEBUG_MODEM_HUP
-					printk(KERN_DEBUG "Mhup in ISDN_STAT_DHUP\n");
-#endif
-					isdn_tty_modem_hup(info, 0);
-					return 1;
-				}
-				break;
-			case ISDN_STAT_BCONN:
-#ifdef ISDN_TTY_STAT_DEBUG
-				printk(KERN_DEBUG "tty_STAT_BCONN ttyI%d\n", info->line);
-#endif
-				/* Wake up any processes waiting
-				 * for incoming call of this device when
-				 * DCD follow the state of incoming carrier
-				 */
-				if (info->blocked_open &&
-				   (info->emu.mdmreg[REG_DCD] & BIT_DCD)) {
-					wake_up_interruptible(&info->open_wait);
-				}
-
-				/* Schedule CONNECT-Message to any tty
-				 * waiting for it and
-				 * set DCD-bit of its modem-status.
-				 */
-				if (TTY_IS_ACTIVE(info) ||
-				    (info->blocked_open && (info->emu.mdmreg[REG_DCD] & BIT_DCD))) {
-					info->msr |= UART_MSR_DCD;
-					info->emu.charge = 0;
-					if (info->dialing & 0xf)
-						info->last_dir = 1;
-					else
-						info->last_dir = 0;
-					info->dialing = 0;
-					info->rcvsched = 1;
-					if (USG_MODEM(dev->usage[i])) {
-						if (info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM) {
-							strcpy(info->emu.connmsg, c->parm.num);
-							isdn_tty_modem_result(RESULT_CONNECT, info);
-						} else
-							isdn_tty_modem_result(RESULT_CONNECT64000, info);
-					}
-					if (USG_VOICE(dev->usage[i]))
-						isdn_tty_modem_result(RESULT_VCON, info);
-					return 1;
-				}
-				break;
-			case ISDN_STAT_BHUP:
-#ifdef ISDN_TTY_STAT_DEBUG
-				printk(KERN_DEBUG "tty_STAT_BHUP ttyI%d\n", info->line);
-#endif
-				if (TTY_IS_ACTIVE(info)) {
-#ifdef ISDN_DEBUG_MODEM_HUP
-					printk(KERN_DEBUG "Mhup in ISDN_STAT_BHUP\n");
-#endif
-					isdn_tty_modem_hup(info, 0);
-					return 1;
-				}
-				break;
-			case ISDN_STAT_NODCH:
-#ifdef ISDN_TTY_STAT_DEBUG
-				printk(KERN_DEBUG "tty_STAT_NODCH ttyI%d\n", info->line);
-#endif
-				if (TTY_IS_ACTIVE(info)) {
-					if (info->dialing) {
-						info->dialing = 0;
-						info->last_l2 = -1;
-						info->last_si = 0;
-						sprintf(info->last_cause, "0000");
-						isdn_tty_modem_result(RESULT_NO_DIALTONE, info);
-					}
-					isdn_tty_modem_hup(info, 0);
-					return 1;
-				}
-				break;
-			case ISDN_STAT_UNLOAD:
-#ifdef ISDN_TTY_STAT_DEBUG
-				printk(KERN_DEBUG "tty_STAT_UNLOAD ttyI%d\n", info->line);
-#endif
-				for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-					info = &dev->mdm.info[i];
-					if (info->isdn_driver == c->driver) {
-						if (info->online)
-							isdn_tty_modem_hup(info, 1);
-					}
-				}
-				return 1;
-#ifdef CONFIG_ISDN_TTY_FAX
-			case ISDN_STAT_FAXIND:
-				if (TTY_IS_ACTIVE(info)) {
-					isdn_tty_fax_command(info, c); 
-				}
-				break;
-#endif
-#ifdef CONFIG_ISDN_AUDIO
-			case ISDN_STAT_AUDIO:
-				if (TTY_IS_ACTIVE(info)) {
-					switch(c->parm.num[0]) {
-						case ISDN_AUDIO_DTMF:
-							if (info->vonline) {
-								isdn_audio_put_dle_code(info,
-									c->parm.num[1]);
-							}
-							break;
-					}
-				}
-				break;
-#endif
-		}
-	}
-	return 0;
-}
-
-/*********************************************************************
- Modem-Emulator-Routines
- *********************************************************************/
-
-#define cmdchar(c) ((c>=' ')&&(c<=0x7f))
-
-/*
- * Put a message from the AT-emulator into receive-buffer of tty,
- * convert CR, LF, and BS to values in modem-registers 3, 4 and 5.
- */
-void
-isdn_tty_at_cout(char *msg, modem_info * info)
-{
-	struct tty_struct *tty;
-	atemu *m = &info->emu;
-	char *p;
-	char c;
-	ulong flags;
-	struct sk_buff *skb = 0;
-	char *sp = 0;
-
-	if (!msg) {
-		printk(KERN_WARNING "isdn_tty: Null-Message in isdn_tty_at_cout\n");
-		return;
-	}
-	save_flags(flags);
-	cli();
-	tty = info->tty;
-	if ((info->flags & ISDN_ASYNC_CLOSING) || (!tty)) {
-		restore_flags(flags);
-		return;
-	}
-
-	/* use queue instead of direct flip, if online and */
-	/* data is in queue or flip buffer is full */
-	if ((info->online) && (((tty->flip.count + strlen(msg)) >= TTY_FLIPBUF_SIZE) ||
-	    (!skb_queue_empty(&dev->drv[info->isdn_driver]->rpqueue[info->isdn_channel])))) {
-		skb = alloc_skb(strlen(msg)
-#ifdef CONFIG_ISDN_AUDIO
-			+ sizeof(isdn_audio_skb)
-#endif
-			, GFP_ATOMIC);
-		if (!skb) {
-			restore_flags(flags);
-			return;
-		}
-#ifdef CONFIG_ISDN_AUDIO
-		skb_reserve(skb, sizeof(isdn_audio_skb));
-#endif
-		sp = skb_put(skb, strlen(msg));
-#ifdef CONFIG_ISDN_AUDIO
-		ISDN_AUDIO_SKB_DLECOUNT(skb) = 0;
-		ISDN_AUDIO_SKB_LOCK(skb) = 0;
-#endif
-	}
-
-	for (p = msg; *p; p++) {
-		switch (*p) {
-			case '\r':
-				c = m->mdmreg[REG_CR];
-				break;
-			case '\n':
-				c = m->mdmreg[REG_LF];
-				break;
-			case '\b':
-				c = m->mdmreg[REG_BS];
-				break;
-			default:
-				c = *p;
-		}
-		if (skb) {
-			*sp++ = c;
-		} else {
-			if (tty->flip.count >= TTY_FLIPBUF_SIZE)
-				break;
-			tty_insert_flip_char(tty, c, 0);
-		}
-	}
-	if (skb) {
-		__skb_queue_tail(&dev->drv[info->isdn_driver]->rpqueue[info->isdn_channel], skb);
-		dev->drv[info->isdn_driver]->rcvcount[info->isdn_channel] += skb->len;
-		restore_flags(flags);
-		/* Schedule dequeuing */
-		if ((dev->modempoll) && (info->rcvsched))
-			isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
-
-	} else {
-		restore_flags(flags);
-		queue_task(&tty->flip.tqueue, &tq_timer);
-	}
-}
-
-/*
- * Perform ATH Hangup
- */
-static void
-isdn_tty_on_hook(modem_info * info)
-{
-	if (info->isdn_channel >= 0) {
-#ifdef ISDN_DEBUG_MODEM_HUP
-		printk(KERN_DEBUG "Mhup in isdn_tty_on_hook\n");
-#endif
-		isdn_tty_modem_hup(info, 1);
-	}
-}
-
-static void
-isdn_tty_off_hook(void)
-{
-	printk(KERN_DEBUG "isdn_tty_off_hook\n");
-}
-
-#define PLUSWAIT1 (HZ/2)        /* 0.5 sec. */
-#define PLUSWAIT2 (HZ*3/2)      /* 1.5 sec */
-
-/*
- * Check Buffer for Modem-escape-sequence, activate timer-callback to
- * isdn_tty_modem_escape() if sequence found.
- *
- * Parameters:
- *   p          pointer to databuffer
- *   plus       escape-character
- *   count      length of buffer
- *   pluscount  count of valid escape-characters so far
- *   lastplus   timestamp of last character
- */
-static void
-isdn_tty_check_esc(const u_char * p, u_char plus, int count, int *pluscount,
-		   int *lastplus, int from_user)
-{
-	char cbuf[3];
-
-	if (plus > 127)
-		return;
-	if (count > 3) {
-		p += count - 3;
-		count = 3;
-		*pluscount = 0;
-	}
-	if (from_user) {
-		copy_from_user(cbuf, p, count);
-		p = cbuf;
-	}
-	while (count > 0) {
-		if (*(p++) == plus) {
-			if ((*pluscount)++) {
-				/* Time since last '+' > 0.5 sec. ? */
-				if (time_after(jiffies, *lastplus + PLUSWAIT1))
-					*pluscount = 1;
-			} else {
-				/* Time since last non-'+' < 1.5 sec. ? */
-				if (time_before(jiffies, *lastplus + PLUSWAIT2))
-					*pluscount = 0;
-			}
-			if ((*pluscount == 3) && (count == 1))
-				isdn_timer_ctrl(ISDN_TIMER_MODEMPLUS, 1);
-			if (*pluscount > 3)
-				*pluscount = 1;
-		} else
-			*pluscount = 0;
-		*lastplus = jiffies;
-		count--;
-	}
-}
-
-/*
- * Return result of AT-emulator to tty-receive-buffer, depending on
- * modem-register 12, bit 0 and 1.
- * For CONNECT-messages also switch to online-mode.
- * For RING-message handle auto-ATA if register 0 != 0
- */
-
-static void
-isdn_tty_modem_result(int code, modem_info * info)
-{
-	atemu *m = &info->emu;
-	static char *msg[] =
-	{"OK", "CONNECT", "RING", "NO CARRIER", "ERROR",
-	 "CONNECT 64000", "NO DIALTONE", "BUSY", "NO ANSWER",
-	 "RINGING", "NO MSN/EAZ", "VCON", "RUNG"};
-	ulong flags;
-	char s[ISDN_MSNLEN+10];
-
-	switch (code) {
-		case RESULT_RING:
-			m->mdmreg[REG_RINGCNT]++;
-			if (m->mdmreg[REG_RINGCNT] == m->mdmreg[REG_RINGATA])
-				/* Automatically accept incoming call */
-				isdn_tty_cmd_ATA(info);
-			break;
-		case RESULT_NO_CARRIER:
-#ifdef ISDN_DEBUG_MODEM_HUP
-			printk(KERN_DEBUG "modem_result: NO CARRIER %d %d\n",
-			       (info->flags & ISDN_ASYNC_CLOSING),
-			       (!info->tty));
-#endif
-			save_flags(flags);
-			cli();
-			m->mdmreg[REG_RINGCNT] = 0;
-			del_timer(&info->nc_timer);
-			info->ncarrier = 0;
-			if ((info->flags & ISDN_ASYNC_CLOSING) || (!info->tty)) {
-				restore_flags(flags);
-				return;
-			}
-			restore_flags(flags);
-#ifdef CONFIG_ISDN_AUDIO
-			if (info->vonline & 1) {
-#ifdef ISDN_DEBUG_MODEM_VOICE
-				printk(KERN_DEBUG "res3: send DLE-ETX on ttyI%d\n",
-				       info->line);
-#endif
-				/* voice-recording, add DLE-ETX */
-				isdn_tty_at_cout("\020\003", info);
-			}
-			if (info->vonline & 2) {
-#ifdef ISDN_DEBUG_MODEM_VOICE
-				printk(KERN_DEBUG "res3: send DLE-DC4 on ttyI%d\n",
-				       info->line);
-#endif
-				/* voice-playing, add DLE-DC4 */
-				isdn_tty_at_cout("\020\024", info);
-			}
-#endif
-			break;
-		case RESULT_CONNECT:
-		case RESULT_CONNECT64000:
-			sprintf(info->last_cause, "0000");
-			if (!info->online)
-				info->online = 2;
-			break;
-		case RESULT_VCON:
-#ifdef ISDN_DEBUG_MODEM_VOICE
-			printk(KERN_DEBUG "res3: send VCON on ttyI%d\n",
-			       info->line);
-#endif
-			sprintf(info->last_cause, "0000");
-			if (!info->online)
-				info->online = 1;
-			break;
-	} /* switch(code) */
-
-	if (m->mdmreg[REG_RESP] & BIT_RESP) {
-		/* Show results */
-		if (m->mdmreg[REG_RESPNUM] & BIT_RESPNUM) {
-			/* Show numeric results only */
-			sprintf(s, "\r\n%d\r\n", code);
-			isdn_tty_at_cout(s, info);
-		} else {
-			if (code == RESULT_RING) {
-			    /* return if "show RUNG" and ringcounter>1 */
-			    if ((m->mdmreg[REG_RUNG] & BIT_RUNG) &&
-				    (m->mdmreg[REG_RINGCNT] > 1))
-						return;
-			    /* print CID, _before_ _every_ ring */
-			    if (!(m->mdmreg[REG_CIDONCE] & BIT_CIDONCE)) {
-				    isdn_tty_at_cout("\r\nCALLER NUMBER: ", info);
-				    isdn_tty_at_cout(dev->num[info->drv_index], info);
-				    if (m->mdmreg[REG_CDN] & BIT_CDN) {
-					    isdn_tty_at_cout("\r\nCALLED NUMBER: ", info);
-					    isdn_tty_at_cout(info->emu.cpn, info);
-				    }
-			    }
-			}
-			isdn_tty_at_cout("\r\n", info);
-			isdn_tty_at_cout(msg[code], info);
-			switch (code) {
-				case RESULT_CONNECT:
-					switch (m->mdmreg[REG_L2PROT]) {
-						case ISDN_PROTO_L2_MODEM:
-							isdn_tty_at_cout(" ", info);
-							isdn_tty_at_cout(m->connmsg, info);
-							break;
-					}
-					break;
-				case RESULT_RING:
-					/* Append CPN, if enabled */
-					if ((m->mdmreg[REG_CPN] & BIT_CPN)) {
-						sprintf(s, "/%s", m->cpn);
-						isdn_tty_at_cout(s, info);
-					}
-					/* Print CID only once, _after_ 1st RING */
-					if ((m->mdmreg[REG_CIDONCE] & BIT_CIDONCE) &&
-					    (m->mdmreg[REG_RINGCNT] == 1)) {
-						isdn_tty_at_cout("\r\n", info);
-						isdn_tty_at_cout("CALLER NUMBER: ", info);
-						isdn_tty_at_cout(dev->num[info->drv_index], info);
-						if (m->mdmreg[REG_CDN] & BIT_CDN) {
-							isdn_tty_at_cout("\r\nCALLED NUMBER: ", info);
-							isdn_tty_at_cout(info->emu.cpn, info);
-						}
-					}
-					break;
-				case RESULT_NO_CARRIER:
-				case RESULT_NO_DIALTONE:
-				case RESULT_BUSY:
-				case RESULT_NO_ANSWER:
-					m->mdmreg[REG_RINGCNT] = 0;
-					/* Append Cause-Message if enabled */
-					if (m->mdmreg[REG_RESPXT] & BIT_RESPXT) {
-						sprintf(s, "/%s", info->last_cause);
-						isdn_tty_at_cout(s, info);
-					}
-					break;
-				case RESULT_CONNECT64000:
-					/* Append Protocol to CONNECT message */
-					switch (m->mdmreg[REG_L2PROT]) {
-						case ISDN_PROTO_L2_X75I:
-						case ISDN_PROTO_L2_X75UI:
-						case ISDN_PROTO_L2_X75BUI:
-							isdn_tty_at_cout("/X.75", info);
-							break;
-						case ISDN_PROTO_L2_HDLC:
-							isdn_tty_at_cout("/HDLC", info);
-							break;
-						case ISDN_PROTO_L2_V11096:
-							isdn_tty_at_cout("/V110/9600", info);
-							break;
-						case ISDN_PROTO_L2_V11019:
-							isdn_tty_at_cout("/V110/19200", info);
-							break;
-						case ISDN_PROTO_L2_V11038:
-							isdn_tty_at_cout("/V110/38400", info);
-							break;
-					}
-					if (m->mdmreg[REG_T70] & BIT_T70) {
-						isdn_tty_at_cout("/T.70", info);
-						if (m->mdmreg[REG_T70] & BIT_T70_EXT)
-							isdn_tty_at_cout("+", info);
-					}
-					break;
-			}
-			isdn_tty_at_cout("\r\n", info);
-		}
-	}
-	if (code == RESULT_NO_CARRIER) {
-		save_flags(flags);
-		cli();
-		if ((info->flags & ISDN_ASYNC_CLOSING) || (!info->tty)) {
-			restore_flags(flags);
-			return;
-		}
-		if (info->tty->ldisc.flush_buffer)
-			info->tty->ldisc.flush_buffer(info->tty);
-		if ((info->flags & ISDN_ASYNC_CHECK_CD) &&
-		    (!((info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) &&
-		       (info->flags & ISDN_ASYNC_CALLOUT_NOHUP)))) {
-			tty_hangup(info->tty);
-		}
-		restore_flags(flags);
-	}
-}
-
-
-/*
- * Display a modem-register-value.
- */
-static void
-isdn_tty_show_profile(int ridx, modem_info * info)
-{
-	char v[6];
-
-	sprintf(v, "\r\n%d", info->emu.mdmreg[ridx]);
-	isdn_tty_at_cout(v, info);
-}
-
-/*
- * Get MSN-string from char-pointer, set pointer to end of number
- */
-static void
-isdn_tty_get_msnstr(char *n, char **p)
-{
-	int limit = ISDN_MSNLEN - 1;
-
-	while (((*p[0] >= '0' && *p[0] <= '9') ||
-		/* Why a comma ??? */
-		(*p[0] == ',') || (*p[0] == ':')) &&
-		(limit--))
-		*n++ = *p[0]++;
-	*n = '\0';
-}
-
-/*
- * Get phone-number from modem-commandbuffer
- */
-static void
-isdn_tty_getdial(char *p, char *q,int cnt)
-{
-	int first = 1;
-	int limit = ISDN_MSNLEN - 1;	/* MUST match the size of interface var to avoid
-					buffer overflow */
-
-	while (strchr(" 0123456789,#.*WPTS-", *p) && *p && --cnt>0) {
-		if ((*p >= '0' && *p <= '9') || ((*p == 'S') && first) ||
-		    (*p == '*') || (*p == '#')) {
-			*q++ = *p;
-			limit--;
-		}
-		if(!limit)
-			break;
-		p++;
-		first = 0;
-	}
-	*q = 0;
-}
-
-#define PARSE_ERROR { isdn_tty_modem_result(RESULT_ERROR, info); return; }
-#define PARSE_ERROR1 { isdn_tty_modem_result(RESULT_ERROR, info); return 1; }
-
-static void
-isdn_tty_report(modem_info * info)
-{
-	atemu *m = &info->emu;
-	char s[80];
-
-	isdn_tty_at_cout("\r\nStatistics of last connection:\r\n\r\n", info);
-	sprintf(s, "    Remote Number:    %s\r\n", info->last_num);
-	isdn_tty_at_cout(s, info);
-	sprintf(s, "    Direction:        %s\r\n", info->last_dir ? "outgoing" : "incoming");
-	isdn_tty_at_cout(s, info);
-	isdn_tty_at_cout("    Layer-2 Protocol: ", info);
-	switch (info->last_l2) {
-		case ISDN_PROTO_L2_X75I:
-			isdn_tty_at_cout("X.75i", info);
-			break;
-		case ISDN_PROTO_L2_X75UI:
-			isdn_tty_at_cout("X.75ui", info);
-			break;
-		case ISDN_PROTO_L2_X75BUI:
-			isdn_tty_at_cout("X.75bui", info);
-			break;
-		case ISDN_PROTO_L2_HDLC:
-			isdn_tty_at_cout("HDLC", info);
-			break;
-		case ISDN_PROTO_L2_V11096:
-			isdn_tty_at_cout("V.110 9600 Baud", info);
-			break;
-		case ISDN_PROTO_L2_V11019:
-			isdn_tty_at_cout("V.110 19200 Baud", info);
-			break;
-		case ISDN_PROTO_L2_V11038:
-			isdn_tty_at_cout("V.110 38400 Baud", info);
-			break;
-		case ISDN_PROTO_L2_TRANS:
-			isdn_tty_at_cout("transparent", info);
-			break;
-		case ISDN_PROTO_L2_MODEM:
-			isdn_tty_at_cout("modem", info);
-			break;
-		case ISDN_PROTO_L2_FAX:
-			isdn_tty_at_cout("fax", info);
-			break;
-		default:
-			isdn_tty_at_cout("unknown", info);
-			break;
-	}
-	if (m->mdmreg[REG_T70] & BIT_T70) {
-		isdn_tty_at_cout("/T.70", info);
-		if (m->mdmreg[REG_T70] & BIT_T70_EXT)
-			isdn_tty_at_cout("+", info);
-	}
-	isdn_tty_at_cout("\r\n", info);
-	isdn_tty_at_cout("    Service:          ", info);
-	switch (info->last_si) {
-		case 1:
-			isdn_tty_at_cout("audio\r\n", info);
-			break;
-		case 5:
-			isdn_tty_at_cout("btx\r\n", info);
-			break;
-		case 7:
-			isdn_tty_at_cout("data\r\n", info);
-			break;
-		default:
-			sprintf(s, "%d\r\n", info->last_si);
-			isdn_tty_at_cout(s, info);
-			break;
-	}
-	sprintf(s, "    Hangup location:  %s\r\n", info->last_lhup ? "local" : "remote");
-	isdn_tty_at_cout(s, info);
-	sprintf(s, "    Last cause:       %s\r\n", info->last_cause);
-	isdn_tty_at_cout(s, info);
-}
-
-/*
- * Parse AT&.. commands.
- */
-static int
-isdn_tty_cmd_ATand(char **p, modem_info * info)
-{
-	atemu *m = &info->emu;
-	int i;
-	char rb[100];
-
-#define MAXRB (sizeof(rb) - 1)
-
-	switch (*p[0]) {
-		case 'B':
-			/* &B - Set Buffersize */
-			p[0]++;
-			i = isdn_getnum(p);
-			if ((i < 0) || (i > ISDN_SERIAL_XMIT_MAX))
-				PARSE_ERROR1;
-#ifdef CONFIG_ISDN_AUDIO
-			if ((m->mdmreg[REG_SI1] & 1) && (i > VBUF))
-				PARSE_ERROR1;
-#endif
-			m->mdmreg[REG_PSIZE] = i / 16;
-			info->xmit_size = m->mdmreg[REG_PSIZE] * 16;
-			switch (m->mdmreg[REG_L2PROT]) {
-				case ISDN_PROTO_L2_V11096:
-				case ISDN_PROTO_L2_V11019:
-				case ISDN_PROTO_L2_V11038:
-					info->xmit_size /= 10;		
-			}
-			break;
-		case 'C':
-			/* &C - DCD Status */
-			p[0]++;
-			switch (isdn_getnum(p)) {
-				case 0:
-					m->mdmreg[REG_DCD] &= ~BIT_DCD;
-					break;
-				case 1:
-					m->mdmreg[REG_DCD] |= BIT_DCD;
-					break;
-				default:
-					PARSE_ERROR1
-			}
-			break;
-		case 'D':
-			/* &D - Set DTR-Low-behavior */
-			p[0]++;
-			switch (isdn_getnum(p)) {
-				case 0:
-					m->mdmreg[REG_DTRHUP] &= ~BIT_DTRHUP;
-					m->mdmreg[REG_DTRR] &= ~BIT_DTRR;
-					break;
-				case 2:
-					m->mdmreg[REG_DTRHUP] |= BIT_DTRHUP;
-					m->mdmreg[REG_DTRR] &= ~BIT_DTRR;
-					break;
-				case 3:
-					m->mdmreg[REG_DTRHUP] |= BIT_DTRHUP;
-					m->mdmreg[REG_DTRR] |= BIT_DTRR;
-					break;
-				default:
-					PARSE_ERROR1
-			}
-			break;
-		case 'E':
-			/* &E -Set EAZ/MSN */
-			p[0]++;
-			isdn_tty_get_msnstr(m->msn, p);
-			break;
-		case 'F':
-			/* &F -Set Factory-Defaults */
-			p[0]++;
-			if (info->msr & UART_MSR_DCD)
-				PARSE_ERROR1;
-			isdn_tty_reset_profile(m);
-			isdn_tty_modem_reset_regs(info, 1);
-			break;
-#ifdef DUMMY_HAYES_AT
-		case 'K':
-			/* only for be compilant with common scripts */
-			/* &K Flowcontrol - no function */
-			p[0]++;
-			isdn_getnum(p);
-			break;
-#endif
-		case 'L':
-			/* &L -Set Numbers to listen on */
-			p[0]++;
-			i = 0;
-			while (*p[0] && (strchr("0123456789,-*[]?;", *p[0])) &&
-			       (i < ISDN_LMSNLEN))
-				m->lmsn[i++] = *p[0]++;
-			m->lmsn[i] = '\0';
-			break;
-		case 'R':
-			/* &R - Set V.110 bitrate adaption */
-			p[0]++;
-			i = isdn_getnum(p);
-			switch (i) {
-				case 0:
-					/* Switch off V.110, back to X.75 */
-					m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I;
-					m->mdmreg[REG_SI2] = 0;
-					info->xmit_size = m->mdmreg[REG_PSIZE] * 16;
-					break;
-				case 9600:
-					m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_V11096;
-					m->mdmreg[REG_SI2] = 197;
-					info->xmit_size = m->mdmreg[REG_PSIZE] * 16 / 10;
-					break;
-				case 19200:
-					m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_V11019;
-					m->mdmreg[REG_SI2] = 199;
-					info->xmit_size = m->mdmreg[REG_PSIZE] * 16 / 10;
-					break;
-				case 38400:
-					m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_V11038;
-					m->mdmreg[REG_SI2] = 198; /* no existing standard for this */
-					info->xmit_size = m->mdmreg[REG_PSIZE] * 16 / 10;
-					break;
-				default:
-					PARSE_ERROR1;
-			}
-			/* Switch off T.70 */
-			m->mdmreg[REG_T70] &= ~(BIT_T70 | BIT_T70_EXT);
-			/* Set Service 7 */
-			m->mdmreg[REG_SI1] |= 4;
-			break;
-		case 'S':
-			/* &S - Set Windowsize */
-			p[0]++;
-			i = isdn_getnum(p);
-			if ((i > 0) && (i < 9))
-				m->mdmreg[REG_WSIZE] = i;
-			else
-				PARSE_ERROR1;
-			break;
-		case 'V':
-			/* &V - Show registers */
-			p[0]++;
-			isdn_tty_at_cout("\r\n", info);
-			for (i = 0; i < ISDN_MODEM_NUMREG; i++) {
-				sprintf(rb, "S%02d=%03d%s", i,
-					m->mdmreg[i], ((i + 1) % 10) ? " " : "\r\n");
-				isdn_tty_at_cout(rb, info);
-			}
-			sprintf(rb, "\r\nEAZ/MSN: %.50s\r\n",
-				strlen(m->msn) ? m->msn : "None");
-			isdn_tty_at_cout(rb, info);
-			if (strlen(m->lmsn)) {
-				isdn_tty_at_cout("\r\nListen: ", info);
-				isdn_tty_at_cout(m->lmsn, info);
-				isdn_tty_at_cout("\r\n", info);
-			}
-			break;
-		case 'W':
-			/* &W - Write Profile */
-			p[0]++;
-			switch (*p[0]) {
-				case '0':
-					p[0]++;
-					modem_write_profile(m);
-					break;
-				default:
-					PARSE_ERROR1;
-			}
-			break;
-		case 'X':
-			/* &X - Switch to BTX-Mode and T.70 */
-			p[0]++;
-			switch (isdn_getnum(p)) {
-				case 0:
-					m->mdmreg[REG_T70] &= ~(BIT_T70 | BIT_T70_EXT);
-					info->xmit_size = m->mdmreg[REG_PSIZE] * 16;
-					break;
-				case 1:
-					m->mdmreg[REG_T70] |= BIT_T70;
-					m->mdmreg[REG_T70] &= ~BIT_T70_EXT;
-					m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I;
-					info->xmit_size = 112;
-					m->mdmreg[REG_SI1] = 4;
-					m->mdmreg[REG_SI2] = 0;
-					break;
-				case 2:
-					m->mdmreg[REG_T70] |= (BIT_T70 | BIT_T70_EXT);
-					m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I;
-					info->xmit_size = 112;
-					m->mdmreg[REG_SI1] = 4;
-					m->mdmreg[REG_SI2] = 0;
-					break;
-				default:
-					PARSE_ERROR1;
-			}
-			break;
-		default:
-			PARSE_ERROR1;
-	}
-	return 0;
-}
-
-static int
-isdn_tty_check_ats(int mreg, int mval, modem_info * info, atemu * m)
-{
-	/* Some plausibility checks */
-	switch (mreg) {
-		case REG_L2PROT:
-			if (mval > ISDN_PROTO_L2_MAX)
-				return 1;
-			break;
-		case REG_PSIZE:
-			if ((mval * 16) > ISDN_SERIAL_XMIT_MAX)
-				return 1;
-#ifdef CONFIG_ISDN_AUDIO
-			if ((m->mdmreg[REG_SI1] & 1) && (mval > VBUFX))
-				return 1;
-#endif
-			info->xmit_size = mval * 16;
-			switch (m->mdmreg[REG_L2PROT]) {
-				case ISDN_PROTO_L2_V11096:
-				case ISDN_PROTO_L2_V11019:
-				case ISDN_PROTO_L2_V11038:
-					info->xmit_size /= 10;		
-			}
-			break;
-		case REG_SI1I:
-		case REG_PLAN:
-		case REG_SCREEN:
-			/* readonly registers */
-			return 1;
-	}
-	return 0;
-}
-
-/*
- * Perform ATS command
- */
-static int
-isdn_tty_cmd_ATS(char **p, modem_info * info)
-{
-	atemu *m = &info->emu;
-	int bitpos;
-	int mreg;
-	int mval;
-	int bval;
-
-	mreg = isdn_getnum(p);
-	if (mreg < 0 || mreg >= ISDN_MODEM_NUMREG)
-		PARSE_ERROR1;
-	switch (*p[0]) {
-		case '=':
-			p[0]++;
-			mval = isdn_getnum(p);
-			if (mval < 0 || mval > 255)
-				PARSE_ERROR1;
-			if (isdn_tty_check_ats(mreg, mval, info, m))
-				PARSE_ERROR1;
-			m->mdmreg[mreg] = mval;
-			break;
-		case '.':
-			/* Set/Clear a single bit */
-			p[0]++;
-			bitpos = isdn_getnum(p);
-			if ((bitpos < 0) || (bitpos > 7))
-				PARSE_ERROR1;
-			switch (*p[0]) {
-				case '=':
-					p[0]++;
-					bval = isdn_getnum(p);
-					if (bval < 0 || bval > 1)
-						PARSE_ERROR1;
-					if (bval)
-						mval = m->mdmreg[mreg] | (1 << bitpos);
-					else
-						mval = m->mdmreg[mreg] & ~(1 << bitpos);
-					if (isdn_tty_check_ats(mreg, mval, info, m))
-						PARSE_ERROR1;
-					m->mdmreg[mreg] = mval;
-					break;
-				case '?':
-					p[0]++;
-					isdn_tty_at_cout("\r\n", info);
-					isdn_tty_at_cout((m->mdmreg[mreg] & (1 << bitpos)) ? "1" : "0",
-							 info);
-					break;
-				default:
-					PARSE_ERROR1;
-			}
-			break;
-		case '?':
-			p[0]++;
-			isdn_tty_show_profile(mreg, info);
-			break;
-		default:
-			PARSE_ERROR1;
-			break;
-	}
-	return 0;
-}
-
-/*
- * Perform ATA command
- */
-static void
-isdn_tty_cmd_ATA(modem_info * info)
-{
-	atemu *m = &info->emu;
-	isdn_ctrl cmd;
-	int l2;
-
-	if (info->msr & UART_MSR_RI) {
-		/* Accept incoming call */
-		info->last_dir = 0;
-		strcpy(info->last_num, dev->num[info->drv_index]);
-		m->mdmreg[REG_RINGCNT] = 0;
-		info->msr &= ~UART_MSR_RI;
-		l2 = m->mdmreg[REG_L2PROT];
-#ifdef CONFIG_ISDN_AUDIO
-		/* If more than one bit set in reg18, autoselect Layer2 */
-		if ((m->mdmreg[REG_SI1] & m->mdmreg[REG_SI1I]) != m->mdmreg[REG_SI1]) {
-			if (m->mdmreg[REG_SI1I] == 1) {
-				if ((l2 != ISDN_PROTO_L2_MODEM) && (l2 != ISDN_PROTO_L2_FAX))
-					l2 = ISDN_PROTO_L2_TRANS;
-			} else
-				l2 = ISDN_PROTO_L2_X75I;
-		}
-#endif
-		cmd.driver = info->isdn_driver;
-		cmd.command = ISDN_CMD_SETL2;
-		cmd.arg = info->isdn_channel + (l2 << 8);
-		info->last_l2 = l2;
-		isdn_command(&cmd);
-		cmd.driver = info->isdn_driver;
-		cmd.command = ISDN_CMD_SETL3;
-		cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8);
-#ifdef CONFIG_ISDN_TTY_FAX
-		if (l2 == ISDN_PROTO_L2_FAX) {
-			cmd.parm.fax = info->fax;
-			info->fax->direction = ISDN_TTY_FAX_CONN_IN;
-		}
-#endif
-		isdn_command(&cmd);
-		cmd.driver = info->isdn_driver;
-		cmd.arg = info->isdn_channel;
-		cmd.command = ISDN_CMD_ACCEPTD;
-		info->dialing = 16;
-		info->emu.carrierwait = 0;
-		isdn_command(&cmd);
-		isdn_timer_ctrl(ISDN_TIMER_CARRIER, 1);
-	} else
-		isdn_tty_modem_result(RESULT_NO_ANSWER, info);
-}
-
-#ifdef CONFIG_ISDN_AUDIO
-/*
- * Parse AT+F.. commands
- */
-static int
-isdn_tty_cmd_PLUSF(char **p, modem_info * info)
-{
-	atemu *m = &info->emu;
-	char rs[20];
-
-	if (!strncmp(p[0], "CLASS", 5)) {
-		p[0] += 5;
-		switch (*p[0]) {
-			case '?':
-				p[0]++;
-				sprintf(rs, "\r\n%d",
-					(m->mdmreg[REG_SI1] & 1) ? 8 : 0);
-#ifdef CONFIG_ISDN_TTY_FAX
-				if (TTY_IS_FCLASS2(info))
-						sprintf(rs, "\r\n2");
-				else if (TTY_IS_FCLASS1(info))
-						sprintf(rs, "\r\n1");
-#endif
-				isdn_tty_at_cout(rs, info);
-				break;
-			case '=':
-				p[0]++;
-				switch (*p[0]) {
-					case '0':
-						p[0]++;
-						m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I;
-						m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_TRANS;
-						m->mdmreg[REG_SI1] = 4;
-						info->xmit_size =
-						    m->mdmreg[REG_PSIZE] * 16;
-						break;
-#ifdef CONFIG_ISDN_TTY_FAX
-					case '1':
-						p[0]++;
-						if (!(dev->global_features &
-							ISDN_FEATURE_L3_FCLASS1))
-							PARSE_ERROR1;
-						m->mdmreg[REG_SI1] = 1;
-						m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_FAX;
-						m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_FCLASS1;
-						info->xmit_size =
-						    m->mdmreg[REG_PSIZE] * 16;
-						break;
-					case '2':
-						p[0]++;
-						if (!(dev->global_features &
-							ISDN_FEATURE_L3_FCLASS2))
-							PARSE_ERROR1;
-						m->mdmreg[REG_SI1] = 1;
-						m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_FAX;
-						m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_FCLASS2;
-						info->xmit_size =
-						    m->mdmreg[REG_PSIZE] * 16;
-						break;
-#endif
-					case '8':
-						p[0]++;
-						/* L2 will change on dialout with si=1 */
-						m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I;
-						m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_TRANS;
-						m->mdmreg[REG_SI1] = 5;
-						info->xmit_size = VBUF;
-						break;
-					case '?':
-						p[0]++;
-						strcpy(rs, "\r\n0,");
-#ifdef CONFIG_ISDN_TTY_FAX
-						if (dev->global_features &
-							ISDN_FEATURE_L3_FCLASS1)
-							strcat(rs, "1,");
-						if (dev->global_features &
-							ISDN_FEATURE_L3_FCLASS2)
-							strcat(rs, "2,");
-#endif
-						strcat(rs, "8");
-						isdn_tty_at_cout(rs, info);
-						break;
-					default:
-						PARSE_ERROR1;
-				}
-				break;
-			default:
-				PARSE_ERROR1;
-		}
-		return 0;
-	}
-#ifdef CONFIG_ISDN_TTY_FAX
-	return (isdn_tty_cmd_PLUSF_FAX(p, info));
-#else
-	PARSE_ERROR1;
-#endif
-}
-
-/*
- * Parse AT+V.. commands
- */
-static int
-isdn_tty_cmd_PLUSV(char **p, modem_info * info)
-{
-	atemu *m = &info->emu;
-	isdn_ctrl cmd;
-	static char *vcmd[] =
-	{"NH", "IP", "LS", "RX", "SD", "SM", "TX", "DD", NULL};
-	int i;
-	int par1;
-	int par2;
-	char rs[20];
-
-	i = 0;
-	while (vcmd[i]) {
-		if (!strncmp(vcmd[i], p[0], 2)) {
-			p[0] += 2;
-			break;
-		}
-		i++;
-	}
-	switch (i) {
-		case 0:
-			/* AT+VNH - Auto hangup feature */
-			switch (*p[0]) {
-				case '?':
-					p[0]++;
-					isdn_tty_at_cout("\r\n1", info);
-					break;
-				case '=':
-					p[0]++;
-					switch (*p[0]) {
-						case '1':
-							p[0]++;
-							break;
-						case '?':
-							p[0]++;
-							isdn_tty_at_cout("\r\n1", info);
-							break;
-						default:
-							PARSE_ERROR1;
-					}
-					break;
-				default:
-					PARSE_ERROR1;
-			}
-			break;
-		case 1:
-			/* AT+VIP - Reset all voice parameters */
-			isdn_tty_modem_reset_vpar(m);
-			break;
-		case 2:
-			/* AT+VLS - Select device, accept incoming call */
-			switch (*p[0]) {
-				case '?':
-					p[0]++;
-					sprintf(rs, "\r\n%d", m->vpar[0]);
-					isdn_tty_at_cout(rs, info);
-					break;
-				case '=':
-					p[0]++;
-					switch (*p[0]) {
-						case '0':
-							p[0]++;
-							m->vpar[0] = 0;
-							break;
-						case '2':
-							p[0]++;
-							m->vpar[0] = 2;
-							break;
-						case '?':
-							p[0]++;
-							isdn_tty_at_cout("\r\n0,2", info);
-							break;
-						default:
-							PARSE_ERROR1;
-					}
-					break;
-				default:
-					PARSE_ERROR1;
-			}
-			break;
-		case 3:
-			/* AT+VRX - Start recording */
-			if (!m->vpar[0])
-				PARSE_ERROR1;
-			if (info->online != 1) {
-				isdn_tty_modem_result(RESULT_NO_ANSWER, info);
-				return 1;
-			}
-			info->dtmf_state = isdn_audio_dtmf_init(info->dtmf_state);
-			if (!info->dtmf_state) {
-				printk(KERN_WARNING "isdn_tty: Couldn't malloc dtmf state\n");
-				PARSE_ERROR1;
-			}
-			info->silence_state = isdn_audio_silence_init(info->silence_state);
-			if (!info->silence_state) {
-				printk(KERN_WARNING "isdn_tty: Couldn't malloc silence state\n");
-				PARSE_ERROR1;
-			}
-			if (m->vpar[3] < 5) {
-				info->adpcmr = isdn_audio_adpcm_init(info->adpcmr, m->vpar[3]);
-				if (!info->adpcmr) {
-					printk(KERN_WARNING "isdn_tty: Couldn't malloc adpcm state\n");
-					PARSE_ERROR1;
-				}
-			}
-#ifdef ISDN_DEBUG_AT
-			printk(KERN_DEBUG "AT: +VRX\n");
-#endif
-			info->vonline |= 1;
-			isdn_tty_modem_result(RESULT_CONNECT, info);
-			return 0;
-			break;
-		case 4:
-			/* AT+VSD - Silence detection */
-			switch (*p[0]) {
-				case '?':
-					p[0]++;
-					sprintf(rs, "\r\n<%d>,<%d>",
-						m->vpar[1],
-						m->vpar[2]);
-					isdn_tty_at_cout(rs, info);
-					break;
-				case '=':
-					p[0]++;
-					if ((*p[0]>='0') && (*p[0]<='9')) {
-						par1 = isdn_getnum(p);
-						if ((par1 < 0) || (par1 > 31))
-							PARSE_ERROR1;
-						if (*p[0] != ',')
-							PARSE_ERROR1;
-						p[0]++;
-						par2 = isdn_getnum(p);
-						if ((par2 < 0) || (par2 > 255))
-							PARSE_ERROR1;
-						m->vpar[1] = par1;
-						m->vpar[2] = par2;
-						break;
-					} else 
-					if (*p[0] == '?') {
-						p[0]++;
-						isdn_tty_at_cout("\r\n<0-31>,<0-255>",
-							   info);
-						break;
-					} else
-					PARSE_ERROR1;
-					break;
-				default:
-					PARSE_ERROR1;
-			}
-			break;
-		case 5:
-			/* AT+VSM - Select compression */
-			switch (*p[0]) {
-				case '?':
-					p[0]++;
-					sprintf(rs, "\r\n<%d>,<%d><8000>",
-						m->vpar[3],
-						m->vpar[1]);
-					isdn_tty_at_cout(rs, info);
-					break;
-				case '=':
-					p[0]++;
-					switch (*p[0]) {
-						case '2':
-						case '3':
-						case '4':
-						case '5':
-						case '6':
-							par1 = isdn_getnum(p);
-							if ((par1 < 2) || (par1 > 6))
-								PARSE_ERROR1;
-							m->vpar[3] = par1;
-							break;
-						case '?':
-							p[0]++;
-							isdn_tty_at_cout("\r\n2;ADPCM;2;0;(8000)\r\n",
-								   info);
-							isdn_tty_at_cout("3;ADPCM;3;0;(8000)\r\n",
-								   info);
-							isdn_tty_at_cout("4;ADPCM;4;0;(8000)\r\n",
-								   info);
-							isdn_tty_at_cout("5;ALAW;8;0;(8000)\r\n",
-								   info);
-							isdn_tty_at_cout("6;ULAW;8;0;(8000)\r\n",
-								   info);
-							break;
-						default:
-							PARSE_ERROR1;
-					}
-					break;
-				default:
-					PARSE_ERROR1;
-			}
-			break;
-		case 6:
-			/* AT+VTX - Start sending */
-			if (!m->vpar[0])
-				PARSE_ERROR1;
-			if (info->online != 1) {
-				isdn_tty_modem_result(RESULT_NO_ANSWER, info);
-				return 1;
-			}
-			info->dtmf_state = isdn_audio_dtmf_init(info->dtmf_state);
-			if (!info->dtmf_state) {
-				printk(KERN_WARNING "isdn_tty: Couldn't malloc dtmf state\n");
-				PARSE_ERROR1;
-			}
-			if (m->vpar[3] < 5) {
-				info->adpcms = isdn_audio_adpcm_init(info->adpcms, m->vpar[3]);
-				if (!info->adpcms) {
-					printk(KERN_WARNING "isdn_tty: Couldn't malloc adpcm state\n");
-					PARSE_ERROR1;
-				}
-			}
-#ifdef ISDN_DEBUG_AT
-			printk(KERN_DEBUG "AT: +VTX\n");
-#endif
-			m->lastDLE = 0;
-			info->vonline |= 2;
-			isdn_tty_modem_result(RESULT_CONNECT, info);
-			return 0;
-			break;
-		case 7:
-			/* AT+VDD - DTMF detection */
-			switch (*p[0]) {
-				case '?':
-					p[0]++;
-					sprintf(rs, "\r\n<%d>,<%d>",
-						m->vpar[4],
-						m->vpar[5]);
-					isdn_tty_at_cout(rs, info);
-					break;
-				case '=':
-					p[0]++;
-					if ((*p[0]>='0') && (*p[0]<='9')) {
-						if (info->online != 1)
-							PARSE_ERROR1;
-						par1 = isdn_getnum(p);
-						if ((par1 < 0) || (par1 > 15))
-							PARSE_ERROR1;
-						if (*p[0] != ',')
-							PARSE_ERROR1;
-						p[0]++;
-						par2 = isdn_getnum(p);
-						if ((par2 < 0) || (par2 > 255))
-							PARSE_ERROR1;
-						m->vpar[4] = par1;
-						m->vpar[5] = par2;
-						cmd.driver = info->isdn_driver;
-						cmd.command = ISDN_CMD_AUDIO;
-						cmd.arg = info->isdn_channel + (ISDN_AUDIO_SETDD << 8);
-						cmd.parm.num[0] = par1;
-						cmd.parm.num[1] = par2;
-						isdn_command(&cmd);
-						break;
-					} else
-					if (*p[0] == '?') {
-						p[0]++;
-						isdn_tty_at_cout("\r\n<0-15>,<0-255>",
-							info);
-						break;
-					} else
-					PARSE_ERROR1;
-					break;
-				default:
-					PARSE_ERROR1;
-			}
-			break;
-		default:
-			PARSE_ERROR1;
-	}
-	return 0;
-}
-#endif                          /* CONFIG_ISDN_AUDIO */
-
-/*
- * Parse and perform an AT-command-line.
- */
-static void
-isdn_tty_parse_at(modem_info * info)
-{
-	atemu *m = &info->emu;
-	char *p;
-	char ds[40];
-
-#ifdef ISDN_DEBUG_AT
-	printk(KERN_DEBUG "AT: '%s'\n", m->mdmcmd);
-#endif
-	for (p = &m->mdmcmd[2]; *p;) {
-		switch (*p) {
-			case ' ':
-				p++;
-				break;
-			case 'A':
-				/* A - Accept incoming call */
-				p++;
-				isdn_tty_cmd_ATA(info);
-				return;
-				break;
-			case 'D':
-				/* D - Dial */
-				if (info->msr & UART_MSR_DCD)
-					PARSE_ERROR;
-				if (info->msr & UART_MSR_RI) {
-					isdn_tty_modem_result(RESULT_NO_CARRIER, info);
-					return;
-				}
-				isdn_tty_getdial(++p, ds, sizeof ds);
-				p += strlen(p);
-				if (!strlen(m->msn))
-					isdn_tty_modem_result(RESULT_NO_MSN_EAZ, info);
-				else if (strlen(ds))
-					isdn_tty_dial(ds, info, m);
-				else
-					PARSE_ERROR;
-				return;
-			case 'E':
-				/* E - Turn Echo on/off */
-				p++;
-				switch (isdn_getnum(&p)) {
-					case 0:
-						m->mdmreg[REG_ECHO] &= ~BIT_ECHO;
-						break;
-					case 1:
-						m->mdmreg[REG_ECHO] |= BIT_ECHO;
-						break;
-					default:
-						PARSE_ERROR;
-				}
-				break;
-			case 'H':
-				/* H - On/Off-hook */
-				p++;
-				switch (*p) {
-					case '0':
-						p++;
-						isdn_tty_on_hook(info);
-						break;
-					case '1':
-						p++;
-						isdn_tty_off_hook();
-						break;
-					default:
-						isdn_tty_on_hook(info);
-						break;
-				}
-				break;
-			case 'I':
-				/* I - Information */
-				p++;
-				isdn_tty_at_cout("\r\nLinux ISDN", info);
-				switch (*p) {
-					case '0':
-					case '1':
-						p++;
-						break;
-					case '2':
-						p++;
-						isdn_tty_report(info);
-						break;
-					case '3':
-                                                p++;
-                                                sprintf(ds, "\r\n%d", info->emu.charge);
-                                                isdn_tty_at_cout(ds, info);
-                                                break;
-					default:;
-				}
-				break;
-#ifdef DUMMY_HAYES_AT
-			case 'L':
-			case 'M':
-				/* only for be compilant with common scripts */
-				/* no function */
-				p++;
-				isdn_getnum(&p);
-				break;
-#endif
-			case 'O':
-				/* O - Go online */
-				p++;
-				if (info->msr & UART_MSR_DCD)
-					/* if B-Channel is up */
-					isdn_tty_modem_result((m->mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM) ? RESULT_CONNECT:RESULT_CONNECT64000, info);
-				else
-					isdn_tty_modem_result(RESULT_NO_CARRIER, info);
-				return;
-			case 'Q':
-				/* Q - Turn Emulator messages on/off */
-				p++;
-				switch (isdn_getnum(&p)) {
-					case 0:
-						m->mdmreg[REG_RESP] |= BIT_RESP;
-						break;
-					case 1:
-						m->mdmreg[REG_RESP] &= ~BIT_RESP;
-						break;
-					default:
-						PARSE_ERROR;
-				}
-				break;
-			case 'S':
-				/* S - Set/Get Register */
-				p++;
-				if (isdn_tty_cmd_ATS(&p, info))
-					return;
-				break;
-			case 'V':
-				/* V - Numeric or ASCII Emulator-messages */
-				p++;
-				switch (isdn_getnum(&p)) {
-					case 0:
-						m->mdmreg[REG_RESP] |= BIT_RESPNUM;
-						break;
-					case 1:
-						m->mdmreg[REG_RESP] &= ~BIT_RESPNUM;
-						break;
-					default:
-						PARSE_ERROR;
-				}
-				break;
-			case 'Z':
-				/* Z - Load Registers from Profile */
-				p++;
-				if (info->msr & UART_MSR_DCD) {
-					info->online = 0;
-					isdn_tty_on_hook(info);
-				}
-				isdn_tty_modem_reset_regs(info, 1);
-				break;
-			case '+':
-				p++;
-				switch (*p) {
-#ifdef CONFIG_ISDN_AUDIO
-					case 'F':
-						p++;
-						if (isdn_tty_cmd_PLUSF(&p, info))
-							return;
-						break;
-					case 'V':
-						if ((!(m->mdmreg[REG_SI1] & 1)) ||
-							(m->mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM))
-							PARSE_ERROR;
-						p++;
-						if (isdn_tty_cmd_PLUSV(&p, info))
-							return;
-						break;
-#endif                          /* CONFIG_ISDN_AUDIO */
-					case 'S':	/* SUSPEND */
-						p++;
-						isdn_tty_get_msnstr(ds, &p);
-						isdn_tty_suspend(ds, info, m);
-						break;
-					case 'R':	/* RESUME */
-						p++;
-						isdn_tty_get_msnstr(ds, &p);
-						isdn_tty_resume(ds, info, m);
-						break;
-					case 'M':	/* MESSAGE */
-						p++;
-						isdn_tty_send_msg(info, m, p);
-						break;
-					default:
-						PARSE_ERROR;
-				}
-				break;
-			case '&':
-				p++;
-				if (isdn_tty_cmd_ATand(&p, info))
-					return;
-				break;
-			default:
-				PARSE_ERROR;
-		}
-	}
-#ifdef CONFIG_ISDN_AUDIO
-	if (!info->vonline)
-#endif
-		isdn_tty_modem_result(RESULT_OK, info);
-}
-
-/* Need own toupper() because standard-toupper is not available
- * within modules.
- */
-#define my_toupper(c) (((c>='a')&&(c<='z'))?(c&0xdf):c)
-
-/*
- * Perform line-editing of AT-commands
- *
- * Parameters:
- *   p        inputbuffer
- *   count    length of buffer
- *   channel  index to line (minor-device)
- *   user     flag: buffer is in userspace
- */
-static int
-isdn_tty_edit_at(const char *p, int count, modem_info * info, int user)
-{
-	atemu *m = &info->emu;
-	int total = 0;
-	u_char c;
-	char eb[2];
-	int cnt;
-
-	for (cnt = count; cnt > 0; p++, cnt--) {
-		if (user)
-			get_user(c, p);
-		else
-			c = *p;
-		total++;
-		if (c == m->mdmreg[REG_CR] || c == m->mdmreg[REG_LF]) {
-			/* Separator (CR or LF) */
-			m->mdmcmd[m->mdmcmdl] = 0;
-			if (m->mdmreg[REG_ECHO] & BIT_ECHO) {
-				eb[0] = c;
-				eb[1] = 0;
-				isdn_tty_at_cout(eb, info);
-			}
-			if ((m->mdmcmdl >= 2) && (!(strncmp(m->mdmcmd, "AT", 2))))
-				isdn_tty_parse_at(info);
-			m->mdmcmdl = 0;
-			continue;
-		}
-		if (c == m->mdmreg[REG_BS] && m->mdmreg[REG_BS] < 128) {
-			/* Backspace-Function */
-			if ((m->mdmcmdl > 2) || (!m->mdmcmdl)) {
-				if (m->mdmcmdl)
-					m->mdmcmdl--;
-				if (m->mdmreg[REG_ECHO] & BIT_ECHO)
-					isdn_tty_at_cout("\b", info);
-			}
-			continue;
-		}
-		if (cmdchar(c)) {
-			if (m->mdmreg[REG_ECHO] & BIT_ECHO) {
-				eb[0] = c;
-				eb[1] = 0;
-				isdn_tty_at_cout(eb, info);
-			}
-			if (m->mdmcmdl < 255) {
-				c = my_toupper(c);
-				switch (m->mdmcmdl) {
-					case 1:
-						if (c == 'T') {
-							m->mdmcmd[m->mdmcmdl] = c;
-							m->mdmcmd[++m->mdmcmdl] = 0;
-							break;
-						} else
-							m->mdmcmdl = 0;
-						/* Fall through, check for 'A' */
-					case 0:
-						if (c == 'A') {
-							m->mdmcmd[m->mdmcmdl] = c;
-							m->mdmcmd[++m->mdmcmdl] = 0;
-						}
-						break;
-					default:
-						m->mdmcmd[m->mdmcmdl] = c;
-						m->mdmcmd[++m->mdmcmdl] = 0;
-				}
-			}
-		}
-	}
-	return total;
-}
-
-/*
- * Switch all modem-channels who are online and got a valid
- * escape-sequence 1.5 seconds ago, to command-mode.
- * This function is called every second via timer-interrupt from within
- * timer-dispatcher isdn_timer_function()
- */
-void
-isdn_tty_modem_escape(void)
-{
-	int ton = 0;
-	int i;
-	int midx;
-
-	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
-		if (USG_MODEM(dev->usage[i]))
-			if ((midx = dev->m_idx[i]) >= 0) {
-				modem_info *info = &dev->mdm.info[midx];
-				if (info->online) {
-					ton = 1;
-					if ((info->emu.pluscount == 3) &&
-					    time_after(jiffies , info->emu.lastplus + PLUSWAIT2)) {
-						info->emu.pluscount = 0;
-						info->online = 0;
-						isdn_tty_modem_result(RESULT_OK, info);
-					}
-				}
-			}
-	isdn_timer_ctrl(ISDN_TIMER_MODEMPLUS, ton);
-}
-
-/*
- * Put a RING-message to all modem-channels who have the RI-bit set.
- * This function is called every second via timer-interrupt from within
- * timer-dispatcher isdn_timer_function()
- */
-void
-isdn_tty_modem_ring(void)
-{
-	int ton = 0;
-	int i;
-
-	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-		modem_info *info = &dev->mdm.info[i];
-		if (info->msr & UART_MSR_RI) {
-			ton = 1;
-			isdn_tty_modem_result(RESULT_RING, info);
-		}
-	}
-	isdn_timer_ctrl(ISDN_TIMER_MODEMRING, ton);
-}
-
-/*
- * For all online tty's, try sending data to
- * the lower levels.
- */
-void
-isdn_tty_modem_xmit(void)
-{
-	int ton = 1;
-	int i;
-
-	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-		modem_info *info = &dev->mdm.info[i];
-		if (info->online) {
-			ton = 1;
-			isdn_tty_senddown(info);
-			isdn_tty_tint(info);
-		}
-	}
-	isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, ton);
-}
-
-/*
- * Check all channels if we have a 'no carrier' timeout.
- * Timeout value is set by Register S7.
- */
-void
-isdn_tty_carrier_timeout(void)
-{
-	int ton = 0;
-	int i;
-
-	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
-		modem_info *info = &dev->mdm.info[i];
-		if (info->dialing) {
-			if (info->emu.carrierwait++ > info->emu.mdmreg[REG_WAITC]) {
-				info->dialing = 0;
-				isdn_tty_modem_result(RESULT_NO_CARRIER, info);
-				isdn_tty_modem_hup(info, 1);
-			}
-			else
-				ton = 1;
-		}
-	}
-	isdn_timer_ctrl(ISDN_TIMER_CARRIER, ton);
-}
diff -Nru a/drivers/isdn/isdn_tty.h b/drivers/isdn/isdn_tty.h
--- a/drivers/isdn/isdn_tty.h	Mon Apr 22 15:32:19 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,121 +0,0 @@
-/* $Id: isdn_tty.h,v 1.22.6.2 2001/09/23 22:24:32 kai Exp $
- *
- * header for Linux ISDN subsystem, tty related functions (linklevel).
- *
- * Copyright 1994-1999  by Fritz Elfert (fritz@isdn4linux.de)
- * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#include <linux/config.h>
-
-#define DLE 0x10
-#define ETX 0x03
-#define DC4 0x14
-
-
-/*
- * Definition of some special Registers of AT-Emulator
- */
-#define REG_RINGATA   0
-#define REG_RINGCNT   1  /* ring counter register */
-#define REG_ESC       2
-#define REG_CR        3
-#define REG_LF        4
-#define REG_BS        5
-
-#define REG_WAITC     7
-
-#define REG_RESP     12  /* show response messages register */
-#define BIT_RESP      1  /* show response messages bit      */
-#define REG_RESPNUM  12  /* show numeric responses register */
-#define BIT_RESPNUM   2  /* show numeric responses bit      */
-#define REG_ECHO     12
-#define BIT_ECHO      4
-#define REG_DCD      12
-#define BIT_DCD       8
-#define REG_CTS      12
-#define BIT_CTS      16
-#define REG_DTRR     12
-#define BIT_DTRR     32
-#define REG_DSR      12
-#define BIT_DSR      64
-#define REG_CPPP     12
-#define BIT_CPPP    128
-
-#define REG_DXMT     13
-#define BIT_DXMT      1
-#define REG_T70      13
-#define BIT_T70       2
-#define BIT_T70_EXT  32
-#define REG_DTRHUP   13
-#define BIT_DTRHUP    4
-#define REG_RESPXT   13
-#define BIT_RESPXT    8
-#define REG_CIDONCE  13
-#define BIT_CIDONCE  16
-#define REG_RUNG     13  /* show RUNG message register      */
-#define BIT_RUNG     64  /* show RUNG message bit           */
-#define REG_DISPLAY  13
-#define BIT_DISPLAY 128
-
-#define REG_L2PROT   14
-#define REG_L3PROT   15
-#define REG_PSIZE    16
-#define REG_WSIZE    17
-#define REG_SI1      18
-#define REG_SI2      19
-#define REG_SI1I     20
-#define REG_PLAN     21
-#define REG_SCREEN   22
-
-#define REG_CPN      23
-#define BIT_CPN       1
-#define REG_CPNFCON  23
-#define BIT_CPNFCON   2
-#define REG_CDN      23
-#define BIT_CDN       4
-
-/* defines for result codes */
-#define RESULT_OK		0
-#define RESULT_CONNECT		1
-#define RESULT_RING		2
-#define RESULT_NO_CARRIER	3
-#define RESULT_ERROR		4
-#define RESULT_CONNECT64000	5
-#define RESULT_NO_DIALTONE	6
-#define RESULT_BUSY		7
-#define RESULT_NO_ANSWER	8
-#define RESULT_RINGING		9
-#define RESULT_NO_MSN_EAZ	10
-#define RESULT_VCON		11
-#define RESULT_RUNG		12
-
-#define TTY_IS_FCLASS1(info) \
-	((info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_FAX) && \
-	 (info->emu.mdmreg[REG_L3PROT] == ISDN_PROTO_L3_FCLASS1))
-#define TTY_IS_FCLASS2(info) \
-	((info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_FAX) && \
-	 (info->emu.mdmreg[REG_L3PROT] == ISDN_PROTO_L3_FCLASS2))
-
-extern void isdn_tty_modem_escape(void);
-extern void isdn_tty_modem_ring(void);
-extern void isdn_tty_carrier_timeout(void);
-extern void isdn_tty_modem_xmit(void);
-extern int isdn_tty_modem_init(void);
-extern void isdn_tty_readmodem(void);
-extern int isdn_tty_find_icall(int, int, setup_parm *);
-extern void isdn_tty_cleanup_xmit(modem_info *);
-extern int isdn_tty_stat_callback(int, isdn_ctrl *);
-extern int isdn_tty_rcv_skb(int, int, int, struct sk_buff *);
-extern int isdn_tty_capi_facility(capi_msg *cm); 
-extern void isdn_tty_at_cout(char *, modem_info *);
-extern void isdn_tty_modem_hup(modem_info *, int);
-#ifdef CONFIG_ISDN_TTY_FAX
-extern int isdn_tty_cmd_PLUSF_FAX(char **, modem_info *);
-extern int isdn_tty_fax_command(modem_info *, isdn_ctrl *);
-extern void isdn_tty_fax_bitorder(modem_info *, struct sk_buff *);
-#endif
diff -Nru a/drivers/isdn/isdn_ttyfax.c b/drivers/isdn/isdn_ttyfax.c
--- a/drivers/isdn/isdn_ttyfax.c	Mon Apr 22 15:32:18 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,1122 +0,0 @@
-/* $Id: isdn_ttyfax.c,v 1.7.6.2 2001/09/23 22:24:32 kai Exp $
- *
- * Linux ISDN subsystem, tty_fax AT-command emulator (linklevel).
- *
- * Copyright 1999    by Armin Schindler (mac@melware.de)
- * Copyright 1999    by Ralf Spachmann (mel@melware.de)
- * Copyright 1999    by Cytronics & Melware
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#undef ISDN_TTY_FAX_STAT_DEBUG
-#undef ISDN_TTY_FAX_CMD_DEBUG
-
-#include <linux/isdn.h>
-#include "isdn_common.h"
-#include "isdn_tty.h"
-#include "isdn_ttyfax.h"
-
-
-static char *isdn_tty_fax_revision = "$Revision: 1.7.6.2 $";
-
-#define PARSE_ERROR1 { isdn_tty_fax_modem_result(1, info); return 1; }
-
-static char *
-isdn_getrev(const char *revision)
-{
-	char *rev;
-	char *p;
-
-	if ((p = strchr(revision, ':'))) {
-		rev = p + 2;
-		p = strchr(rev, '$');
-		*--p = 0;
-	} else
-		rev = "???";
-	return rev;
-}
-
-/*
- * Fax Class 2 Modem results
- *
- */
-
-static void
-isdn_tty_fax_modem_result(int code, modem_info * info)
-{
-	atemu *m = &info->emu;
-	T30_s *f = info->fax;
-	char rs[50];
-	char rss[50];
-	char *rp;
-	int i;
-	static char *msg[] =
-	{"OK", "ERROR", "+FCON", "+FCSI:", "+FDIS:",
-	 "+FHNG:", "+FDCS:", "CONNECT", "+FTSI:",
-	 "+FCFR", "+FPTS:", "+FET:"};
-
-
-	isdn_tty_at_cout("\r\n", info);
-	isdn_tty_at_cout(msg[code], info);
-
-#ifdef ISDN_TTY_FAX_CMD_DEBUG
-	printk(KERN_DEBUG "isdn_tty: Fax send %s on ttyI%d\n",
-		msg[code], info->line);
-#endif
-	switch (code) {
-		case 0: /* OK */
-			break;
-		case 1: /* ERROR */
-			break;
-		case 2:	/* +FCON */
-			/* Append CPN, if enabled */
-			if ((m->mdmreg[REG_CPNFCON] & BIT_CPNFCON) &&
-				(!(dev->usage[info->isdn_channel] & ISDN_USAGE_OUTGOING))) {
-				sprintf(rs, "/%s", m->cpn);
-				isdn_tty_at_cout(rs, info);
-			}
-			info->online = 1;
-			f->fet = 0;
-			if (f->phase == ISDN_FAX_PHASE_A)
-				f->phase = ISDN_FAX_PHASE_B;
-			break;
-		case 3:	/* +FCSI */
-		case 8:	/* +FTSI */
-			sprintf(rs, "\"%s\"", f->r_id);
-			isdn_tty_at_cout(rs, info);
-			break;
-		case 4:	/* +FDIS */
-			rs[0] = 0;
-			rp = &f->r_resolution;
-			for (i = 0; i < 8; i++) {
-				sprintf(rss, "%c%s", rp[i] + 48,
-					(i < 7) ? "," : "");
-				strcat(rs, rss);
-			}
-			isdn_tty_at_cout(rs, info);
-#ifdef ISDN_TTY_FAX_CMD_DEBUG
-			printk(KERN_DEBUG "isdn_tty: Fax DIS=%s on ttyI%d\n",
-			       rs, info->line);
-#endif
-			break;
-		case 5:	/* +FHNG */
-			sprintf(rs, "%d", f->code);
-			isdn_tty_at_cout(rs, info);
-			info->faxonline = 0;
-			break;
-		case 6:	/* +FDCS */
-			rs[0] = 0;
-			rp = &f->r_resolution;
-			for (i = 0; i < 8; i++) {
-				sprintf(rss, "%c%s", rp[i] + 48,
-					(i < 7) ? "," : "");
-				strcat(rs, rss);
-			}
-			isdn_tty_at_cout(rs, info);
-#ifdef ISDN_TTY_FAX_CMD_DEBUG
-			printk(KERN_DEBUG "isdn_tty: Fax DCS=%s on ttyI%d\n",
-			       rs, info->line);
-#endif
-			break;
-		case 7:	/* CONNECT */
-			info->faxonline |= 2;
-			break;
-		case 9:	/* FCFR */
-			break;
-		case 10:	/* FPTS */
-			isdn_tty_at_cout("1", info);
-			break;
-		case 11:	/* FET */
-			sprintf(rs, "%d", f->fet);
-			isdn_tty_at_cout(rs, info);
-			break;
-	}
-
-	isdn_tty_at_cout("\r\n", info);
-
-	switch (code) {
-		case 7:	/* CONNECT */
-			info->online = 2;
-			if (info->faxonline & 1) {
-				sprintf(rs, "%c", XON);
-				isdn_tty_at_cout(rs, info);
-			}
-			break;
-	}
-}
-
-int
-isdn_tty_fax_command1(modem_info * info, isdn_ctrl * c)
-{
-	static char *msg[] =
-	{"OK", "CONNECT", "NO CARRIER", "ERROR", "FCERROR"};
-
-#ifdef ISDN_TTY_FAX_CMD_DEBUG
-	printk(KERN_DEBUG "isdn_tty: FCLASS1 cmd(%d)\n", c->parm.aux.cmd);
-#endif
-	if (c->parm.aux.cmd < ISDN_FAX_CLASS1_QUERY) {
-		if (info->online)
-			info->online = 1;
-		isdn_tty_at_cout("\r\n", info);
-		isdn_tty_at_cout(msg[c->parm.aux.cmd], info);
-		isdn_tty_at_cout("\r\n", info);
-	}
-	switch (c->parm.aux.cmd) {
-		case ISDN_FAX_CLASS1_CONNECT:
-			info->online = 2;
-			break;
-		case ISDN_FAX_CLASS1_OK:
-		case ISDN_FAX_CLASS1_FCERROR:
-		case ISDN_FAX_CLASS1_ERROR:
-		case ISDN_FAX_CLASS1_NOCARR:
-			break;
-		case ISDN_FAX_CLASS1_QUERY:
-			isdn_tty_at_cout("\r\n", info);
-			if (!c->parm.aux.para[0]) {
-				isdn_tty_at_cout(msg[ISDN_FAX_CLASS1_ERROR], info);
-				isdn_tty_at_cout("\r\n", info);
-			} else {
-				isdn_tty_at_cout(c->parm.aux.para, info);
-				isdn_tty_at_cout("\r\nOK\r\n", info);
-			}
-			break;
-	}
-	return (0);
-}
-
-int
-isdn_tty_fax_command(modem_info * info, isdn_ctrl * c)
-{
-	T30_s *f = info->fax;
-	char rs[10];
-
-	if (TTY_IS_FCLASS1(info))
-		return (isdn_tty_fax_command1(info, c));
-
-#ifdef ISDN_TTY_FAX_CMD_DEBUG
-	printk(KERN_DEBUG "isdn_tty: Fax cmd %d on ttyI%d\n",
-	       f->r_code, info->line);
-#endif
-	switch (f->r_code) {
-		case ISDN_TTY_FAX_FCON:
-			info->faxonline = 1;
-			isdn_tty_fax_modem_result(2, info);	/* +FCON */
-			return (0);
-		case ISDN_TTY_FAX_FCON_I:
-			info->faxonline = 16;
-			isdn_tty_fax_modem_result(2, info);	/* +FCON */
-			return (0);
-		case ISDN_TTY_FAX_RID:
-			if (info->faxonline & 1)
-				isdn_tty_fax_modem_result(3, info);	/* +FCSI */
-			if (info->faxonline & 16)
-				isdn_tty_fax_modem_result(8, info);	/* +FTSI */
-			return (0);
-		case ISDN_TTY_FAX_DIS:
-			isdn_tty_fax_modem_result(4, info);	/* +FDIS */
-			return (0);
-		case ISDN_TTY_FAX_HNG:
-			if (f->phase == ISDN_FAX_PHASE_C) {
-				if (f->direction == ISDN_TTY_FAX_CONN_IN) {
-					sprintf(rs, "%c%c", DLE, ETX);
-					isdn_tty_at_cout(rs, info);
-				} else {
-					sprintf(rs, "%c", 0x18);
-					isdn_tty_at_cout(rs, info);
-				}
-				info->faxonline &= ~2;	/* leave data mode */
-				info->online = 1;
-			}
-			f->phase = ISDN_FAX_PHASE_E;
-			isdn_tty_fax_modem_result(5, info);	/* +FHNG */
-			isdn_tty_fax_modem_result(0, info);	/* OK */
-			return (0);
-		case ISDN_TTY_FAX_DCS:
-			isdn_tty_fax_modem_result(6, info);	/* +FDCS */
-			isdn_tty_fax_modem_result(7, info);	/* CONNECT */
-			f->phase = ISDN_FAX_PHASE_C;
-			return (0);
-		case ISDN_TTY_FAX_TRAIN_OK:
-			isdn_tty_fax_modem_result(6, info);	/* +FDCS */
-			isdn_tty_fax_modem_result(0, info);	/* OK */
-			return (0);
-		case ISDN_TTY_FAX_SENT:
-			isdn_tty_fax_modem_result(0, info);	/* OK */
-			return (0);
-		case ISDN_TTY_FAX_CFR:
-			isdn_tty_fax_modem_result(9, info);	/* +FCFR */
-			return (0);
-		case ISDN_TTY_FAX_ET:
-			sprintf(rs, "%c%c", DLE, ETX);
-			isdn_tty_at_cout(rs, info);
-			isdn_tty_fax_modem_result(10, info);	/* +FPTS */
-			isdn_tty_fax_modem_result(11, info);	/* +FET */
-			isdn_tty_fax_modem_result(0, info);	/* OK */
-			info->faxonline &= ~2;	/* leave data mode */
-			info->online = 1;
-			f->phase = ISDN_FAX_PHASE_D;
-			return (0);
-		case ISDN_TTY_FAX_PTS:
-			isdn_tty_fax_modem_result(10, info);	/* +FPTS */
-			if (f->direction == ISDN_TTY_FAX_CONN_OUT) {
-				if (f->fet == 1)
-					f->phase = ISDN_FAX_PHASE_B;
-				if (f->fet == 0)
-					isdn_tty_fax_modem_result(0, info);	/* OK */
-			}
-			return (0);
-		case ISDN_TTY_FAX_EOP:
-			info->faxonline &= ~2;	/* leave data mode */
-			info->online = 1;
-			f->phase = ISDN_FAX_PHASE_D;
-			return (0);
-
-	}
-	return (-1);
-}
-
-
-void
-isdn_tty_fax_bitorder(modem_info * info, struct sk_buff *skb)
-{
-	__u8 LeftMask;
-	__u8 RightMask;
-	__u8 fBit;
-	__u8 Data;
-	int i;
-
-	if (!info->fax->bor) {
-		for (i = 0; i < skb->len; i++) {
-			Data = skb->data[i];
-			for (
-				    LeftMask = 0x80, RightMask = 0x01;
-				    LeftMask > RightMask;
-				    LeftMask >>= 1, RightMask <<= 1
-			    ) {
-				fBit = (Data & LeftMask);
-				if (Data & RightMask)
-					Data |= LeftMask;
-				else
-					Data &= ~LeftMask;
-				if (fBit)
-					Data |= RightMask;
-				else
-					Data &= ~RightMask;
-
-			}
-			skb->data[i] = Data;
-		}
-	}
-}
-
-/*
- * Parse AT+F.. FAX class 1 commands
- */
-
-int
-isdn_tty_cmd_FCLASS1(char **p, modem_info * info)
-{
-	static char *cmd[] =
-	{"AE", "TS", "RS", "TM", "RM", "TH", "RH"};
-	isdn_ctrl c;
-	int par, i;
-	long flags;
-
-	for (c.parm.aux.cmd = 0; c.parm.aux.cmd < 7; c.parm.aux.cmd++)
-		if (!strncmp(p[0], cmd[c.parm.aux.cmd], 2))
-			break;
-
-#ifdef ISDN_TTY_FAX_CMD_DEBUG
-	printk(KERN_DEBUG "isdn_tty_cmd_FCLASS1 (%s,%d)\n", p[0], c.parm.aux.cmd);
-#endif
-	if (c.parm.aux.cmd == 7)
-		PARSE_ERROR1;
-
-	p[0] += 2;
-	switch (*p[0]) {
-		case '?':
-			p[0]++;
-			c.parm.aux.subcmd = AT_QUERY;
-			break;
-		case '=':
-			p[0]++;
-			if (*p[0] == '?') {
-				p[0]++;
-				c.parm.aux.subcmd = AT_EQ_QUERY;
-			} else {
-				par = isdn_getnum(p);
-				if ((par < 0) || (par > 255))
-					PARSE_ERROR1;
-				c.parm.aux.subcmd = AT_EQ_VALUE;
-				c.parm.aux.para[0] = par;
-			}
-			break;
-		case 0:
-			c.parm.aux.subcmd = AT_COMMAND;
-			break;
-		default:
-			PARSE_ERROR1;
-	}
-	c.command = ISDN_CMD_FAXCMD;
-#ifdef ISDN_TTY_FAX_CMD_DEBUG
-	printk(KERN_DEBUG "isdn_tty_cmd_FCLASS1 %d/%d/%d)\n",
-	       c.parm.aux.cmd, c.parm.aux.subcmd, c.parm.aux.para[0]);
-#endif
-	if (info->isdn_driver < 0) {
-		save_flags(flags);
-		cli();
-		if ((c.parm.aux.subcmd == AT_EQ_VALUE) ||
-		    (c.parm.aux.subcmd == AT_COMMAND)) {
-			restore_flags(flags);
-			PARSE_ERROR1;
-		}
-		/* get a temporary connection to the first free fax driver */
-		i = isdn_get_free_channel(ISDN_USAGE_FAX, ISDN_PROTO_L2_FAX,
-					  ISDN_PROTO_L3_FCLASS1, -1, -1, "00");
-		if (i < 0) {
-			restore_flags(flags);
-			PARSE_ERROR1;
-		}
-		info->isdn_driver = dev->drvmap[i];
-		info->isdn_channel = dev->chanmap[i];
-		info->drv_index = i;
-		dev->m_idx[i] = info->line;
-		c.driver = info->isdn_driver;
-		c.arg = info->isdn_channel;
-		isdn_command(&c);
-		isdn_free_channel(info->isdn_driver, info->isdn_channel,
-				  ISDN_USAGE_FAX);
-		info->isdn_driver = -1;
-		info->isdn_channel = -1;
-		if (info->drv_index >= 0) {
-			dev->m_idx[info->drv_index] = -1;
-			info->drv_index = -1;
-		}
-		restore_flags(flags);
-	} else {
-		c.driver = info->isdn_driver;
-		c.arg = info->isdn_channel;
-		isdn_command(&c);
-	}
-	return 1;
-}
-
-/*
- * Parse AT+F.. FAX class 2 commands
- */
-
-int
-isdn_tty_cmd_FCLASS2(char **p, modem_info * info)
-{
-	atemu *m = &info->emu;
-	T30_s *f = info->fax;
-	isdn_ctrl cmd;
-	int par;
-	char rs[50];
-	char rss[50];
-	int maxdccval[] =
-	{1, 5, 2, 2, 3, 2, 0, 7};
-
-	/* FAA still unchanged */
-	if (!strncmp(p[0], "AA", 2)) {	/* TODO */
-		p[0] += 2;
-		switch (*p[0]) {
-			case '?':
-				p[0]++;
-				sprintf(rs, "\r\n%d", 0);
-				isdn_tty_at_cout(rs, info);
-				break;
-			case '=':
-				p[0]++;
-				par = isdn_getnum(p);
-				if ((par < 0) || (par > 255))
-					PARSE_ERROR1;
-				break;
-			default:
-				PARSE_ERROR1;
-		}
-		return 0;
-	}
-	/* BADLIN=value - dummy 0=disable errorchk disabled, 1-255 nr. of lines for making page bad */
-	if (!strncmp(p[0], "BADLIN", 6)) {
-		p[0] += 6;
-		switch (*p[0]) {
-			case '?':
-				p[0]++;
-				sprintf(rs, "\r\n%d", f->badlin);
-				isdn_tty_at_cout(rs, info);
-				break;
-			case '=':
-				p[0]++;
-				if (*p[0] == '?') {
-					p[0]++;
-					sprintf(rs, "\r\n0-255");
-					isdn_tty_at_cout(rs, info);
-				} else {
-					par = isdn_getnum(p);
-					if ((par < 0) || (par > 255))
-						PARSE_ERROR1;
-					f->badlin = par;
-#ifdef ISDN_TTY_FAX_STAT_DEBUG
-					printk(KERN_DEBUG "isdn_tty: Fax FBADLIN=%d\n", par);
-#endif
-				}
-				break;
-			default:
-				PARSE_ERROR1;
-		}
-		return 0;
-	}
-	/* BADMUL=value - dummy 0=disable errorchk disabled (treshold multiplier) */
-	if (!strncmp(p[0], "BADMUL", 6)) {
-		p[0] += 6;
-		switch (*p[0]) {
-			case '?':
-				p[0]++;
-				sprintf(rs, "\r\n%d", f->badmul);
-				isdn_tty_at_cout(rs, info);
-				break;
-			case '=':
-				p[0]++;
-				if (*p[0] == '?') {
-					p[0]++;
-					sprintf(rs, "\r\n0-255");
-					isdn_tty_at_cout(rs, info);
-				} else {
-					par = isdn_getnum(p);
-					if ((par < 0) || (par > 255))
-						PARSE_ERROR1;
-					f->badmul = par;
-#ifdef ISDN_TTY_FAX_STAT_DEBUG
-					printk(KERN_DEBUG "isdn_tty: Fax FBADMUL=%d\n", par);
-#endif
-				}
-				break;
-			default:
-				PARSE_ERROR1;
-		}
-		return 0;
-	}
-	/* BOR=n - Phase C bit order, 0=direct, 1=reverse */
-	if (!strncmp(p[0], "BOR", 3)) {
-		p[0] += 3;
-		switch (*p[0]) {
-			case '?':
-				p[0]++;
-				sprintf(rs, "\r\n%d", f->bor);
-				isdn_tty_at_cout(rs, info);
-				break;
-			case '=':
-				p[0]++;
-				if (*p[0] == '?') {
-					p[0]++;
-					sprintf(rs, "\r\n0,1");
-					isdn_tty_at_cout(rs, info);
-				} else {
-					par = isdn_getnum(p);
-					if ((par < 0) || (par > 1))
-						PARSE_ERROR1;
-					f->bor = par;
-#ifdef ISDN_TTY_FAX_STAT_DEBUG
-					printk(KERN_DEBUG "isdn_tty: Fax FBOR=%d\n", par);
-#endif
-				}
-				break;
-			default:
-				PARSE_ERROR1;
-		}
-		return 0;
-	}
-	/* NBC=n - No Best Capabilities */
-	if (!strncmp(p[0], "NBC", 3)) {
-		p[0] += 3;
-		switch (*p[0]) {
-			case '?':
-				p[0]++;
-				sprintf(rs, "\r\n%d", f->nbc);
-				isdn_tty_at_cout(rs, info);
-				break;
-			case '=':
-				p[0]++;
-				if (*p[0] == '?') {
-					p[0]++;
-					sprintf(rs, "\r\n0,1");
-					isdn_tty_at_cout(rs, info);
-				} else {
-					par = isdn_getnum(p);
-					if ((par < 0) || (par > 1))
-						PARSE_ERROR1;
-					f->nbc = par;
-#ifdef ISDN_TTY_FAX_STAT_DEBUG
-					printk(KERN_DEBUG "isdn_tty: Fax FNBC=%d\n", par);
-#endif
-				}
-				break;
-			default:
-				PARSE_ERROR1;
-		}
-		return 0;
-	}
-	/* BUF? - Readonly buffersize readout  */
-	if (!strncmp(p[0], "BUF?", 4)) {
-		p[0] += 4;
-#ifdef ISDN_TTY_FAX_STAT_DEBUG
-		printk(KERN_DEBUG "isdn_tty: Fax FBUF? (%d) \n", (16 * m->mdmreg[REG_PSIZE]));
-#endif
-		p[0]++;
-		sprintf(rs, "\r\n %d ", (16 * m->mdmreg[REG_PSIZE]));
-		isdn_tty_at_cout(rs, info);
-		return 0;
-	}
-	/* CIG=string - local fax station id string for polling rx */
-	if (!strncmp(p[0], "CIG", 3)) {
-		int i, r;
-		p[0] += 3;
-		switch (*p[0]) {
-			case '?':
-				p[0]++;
-				sprintf(rs, "\r\n\"%s\"", f->pollid);
-				isdn_tty_at_cout(rs, info);
-				break;
-			case '=':
-				p[0]++;
-				if (*p[0] == '?') {
-					p[0]++;
-					sprintf(rs, "\r\n\"STRING\"");
-					isdn_tty_at_cout(rs, info);
-				} else {
-					if (*p[0] == '"')
-						p[0]++;
-					for (i = 0; (*p[0]) && i < (FAXIDLEN - 1) && (*p[0] != '"'); i++) {
-						f->pollid[i] = *p[0]++;
-					}
-					if (*p[0] == '"')
-						p[0]++;
-					for (r = i; r < FAXIDLEN; r++) {
-						f->pollid[r] = 32;
-					}
-					f->pollid[FAXIDLEN - 1] = 0;
-#ifdef ISDN_TTY_FAX_STAT_DEBUG
-					printk(KERN_DEBUG "isdn_tty: Fax local poll ID rx \"%s\"\n", f->pollid);
-#endif
-				}
-				break;
-			default:
-				PARSE_ERROR1;
-		}
-		return 0;
-	}
-	/* CQ=n - copy qlty chk, 0= no chk, 1=only 1D chk, 2=1D+2D chk */
-	if (!strncmp(p[0], "CQ", 2)) {
-		p[0] += 2;
-		switch (*p[0]) {
-			case '?':
-				p[0]++;
-				sprintf(rs, "\r\n%d", f->cq);
-				isdn_tty_at_cout(rs, info);
-				break;
-			case '=':
-				p[0]++;
-				if (*p[0] == '?') {
-					p[0]++;
-					sprintf(rs, "\r\n0,1,2");
-					isdn_tty_at_cout(rs, info);
-				} else {
-					par = isdn_getnum(p);
-					if ((par < 0) || (par > 2))
-						PARSE_ERROR1;
-					f->cq = par;
-#ifdef ISDN_TTY_FAX_STAT_DEBUG
-					printk(KERN_DEBUG "isdn_tty: Fax FCQ=%d\n", par);
-#endif
-				}
-				break;
-			default:
-				PARSE_ERROR1;
-		}
-		return 0;
-	}
-	/* CR=n - can receive? 0= no data rx or poll remote dev, 1=do receive data or poll remote dev */
-	if (!strncmp(p[0], "CR", 2)) {
-		p[0] += 2;
-		switch (*p[0]) {
-			case '?':
-				p[0]++;
-				sprintf(rs, "\r\n%d", f->cr);	/* read actual value from struct and print */
-				isdn_tty_at_cout(rs, info);
-				break;
-			case '=':
-				p[0]++;
-				if (*p[0] == '?') {
-					p[0]++;
-					sprintf(rs, "\r\n0,1");		/* display online help */
-					isdn_tty_at_cout(rs, info);
-				} else {
-					par = isdn_getnum(p);
-					if ((par < 0) || (par > 1))
-						PARSE_ERROR1;
-					f->cr = par;
-#ifdef ISDN_TTY_FAX_STAT_DEBUG
-					printk(KERN_DEBUG "isdn_tty: Fax FCR=%d\n", par);
-#endif
-				}
-				break;
-			default:
-				PARSE_ERROR1;
-		}
-		return 0;
-	}
-	/* CTCRTY=value - ECM retry count */
-	if (!strncmp(p[0], "CTCRTY", 6)) {
-		p[0] += 6;
-		switch (*p[0]) {
-			case '?':
-				p[0]++;
-				sprintf(rs, "\r\n%d", f->ctcrty);
-				isdn_tty_at_cout(rs, info);
-				break;
-			case '=':
-				p[0]++;
-				if (*p[0] == '?') {
-					p[0]++;
-					sprintf(rs, "\r\n0-255");
-					isdn_tty_at_cout(rs, info);
-				} else {
-					par = isdn_getnum(p);
-					if ((par < 0) || (par > 255))
-						PARSE_ERROR1;
-					f->ctcrty = par;
-#ifdef ISDN_TTY_FAX_STAT_DEBUG
-					printk(KERN_DEBUG "isdn_tty: Fax FCTCRTY=%d\n", par);
-#endif
-				}
-				break;
-			default:
-				PARSE_ERROR1;
-		}
-		return 0;
-	}
-	/* DCC=vr,br,wd,ln,df,ec,bf,st - DCE capabilities parms */
-	if (!strncmp(p[0], "DCC", 3)) {
-		char *rp = &f->resolution;
-		int i;
-
-		p[0] += 3;
-		switch (*p[0]) {
-			case '?':
-				p[0]++;
-				strcpy(rs, "\r\n");
-				for (i = 0; i < 8; i++) {
-					sprintf(rss, "%c%s", rp[i] + 48,
-						(i < 7) ? "," : "");
-					strcat(rs, rss);
-				}
-				isdn_tty_at_cout(rs, info);
-				break;
-			case '=':
-				p[0]++;
-				if (*p[0] == '?') {
-					isdn_tty_at_cout("\r\n(0,1),(0-5),(0-2),(0-2),(0-3),(0-2),(0),(0-7)", info);
-					p[0]++;
-				} else {
-					for (i = 0; (((*p[0] >= '0') && (*p[0] <= '9')) || (*p[0] == ',')) && (i < 8); i++) {
-						if (*p[0] != ',') {
-							if ((*p[0] - 48) > maxdccval[i]) {
-								PARSE_ERROR1;
-							}
-							rp[i] = *p[0] - 48;
-							p[0]++;
-							if (*p[0] == ',')
-								p[0]++;
-						} else
-							p[0]++;
-					}
-#ifdef ISDN_TTY_FAX_STAT_DEBUG
-					printk(KERN_DEBUG "isdn_tty: Fax FDCC capabilities DCE=%d,%d,%d,%d,%d,%d,%d,%d\n",
-					       rp[0], rp[1], rp[2], rp[3], rp[4], rp[5], rp[6], rp[7]);
-#endif
-				}
-				break;
-			default:
-				PARSE_ERROR1;
-		}
-		return 0;
-	}
-	/* DIS=vr,br,wd,ln,df,ec,bf,st - current session parms */
-	if (!strncmp(p[0], "DIS", 3)) {
-		char *rp = &f->resolution;
-		int i;
-
-		p[0] += 3;
-		switch (*p[0]) {
-			case '?':
-				p[0]++;
-				strcpy(rs, "\r\n");
-				for (i = 0; i < 8; i++) {
-					sprintf(rss, "%c%s", rp[i] + 48,
-						(i < 7) ? "," : "");
-					strcat(rs, rss);
-				}
-				isdn_tty_at_cout(rs, info);
-				break;
-			case '=':
-				p[0]++;
-				if (*p[0] == '?') {
-					isdn_tty_at_cout("\r\n(0,1),(0-5),(0-2),(0-2),(0-3),(0-2),(0),(0-7)", info);
-					p[0]++;
-				} else {
-					for (i = 0; (((*p[0] >= '0') && (*p[0] <= '9')) || (*p[0] == ',')) && (i < 8); i++) {
-						if (*p[0] != ',') {
-							if ((*p[0] - 48) > maxdccval[i]) {
-								PARSE_ERROR1;
-							}
-							rp[i] = *p[0] - 48;
-							p[0]++;
-							if (*p[0] == ',')
-								p[0]++;
-						} else
-							p[0]++;
-					}
-#ifdef ISDN_TTY_FAX_STAT_DEBUG
-					printk(KERN_DEBUG "isdn_tty: Fax FDIS session parms=%d,%d,%d,%d,%d,%d,%d,%d\n",
-					       rp[0], rp[1], rp[2], rp[3], rp[4], rp[5], rp[6], rp[7]);
-#endif
-				}
-				break;
-			default:
-				PARSE_ERROR1;
-		}
-		return 0;
-	}
-	/* DR - Receive Phase C data command, initiates document reception */
-	if (!strncmp(p[0], "DR", 2)) {
-		p[0] += 2;
-		if ((info->faxonline & 16) &&	/* incoming connection */
-		    ((f->phase == ISDN_FAX_PHASE_B) || (f->phase == ISDN_FAX_PHASE_D))) {
-#ifdef ISDN_TTY_FAX_STAT_DEBUG
-			printk(KERN_DEBUG "isdn_tty: Fax FDR\n");
-#endif
-			f->code = ISDN_TTY_FAX_DR;
-			cmd.driver = info->isdn_driver;
-			cmd.arg = info->isdn_channel;
-			cmd.command = ISDN_CMD_FAXCMD;
-			isdn_command(&cmd);
-			if (f->phase == ISDN_FAX_PHASE_B) {
-				f->phase = ISDN_FAX_PHASE_C;
-			} else if (f->phase == ISDN_FAX_PHASE_D) {
-				switch (f->fet) {
-					case 0:	/* next page will be received */
-						f->phase = ISDN_FAX_PHASE_C;
-						isdn_tty_fax_modem_result(7, info);	/* CONNECT */
-						break;
-					case 1:	/* next doc will be received */
-						f->phase = ISDN_FAX_PHASE_B;
-						break;
-					case 2:	/* fax session is terminating */
-						f->phase = ISDN_FAX_PHASE_E;
-						break;
-					default:
-						PARSE_ERROR1;
-				}
-			}
-		} else {
-			PARSE_ERROR1;
-		}
-		return 1;
-	}
-	/* DT=df,vr,wd,ln - TX phase C data command (release DCE to proceed with negotiation) */
-	if (!strncmp(p[0], "DT", 2)) {
-		int i, val[] =
-		{4, 0, 2, 3};
-		char *rp = &f->resolution;
-
-		p[0] += 2;
-		if (!info->faxonline & 1)	/* not outgoing connection */
-			PARSE_ERROR1;
-
-		for (i = 0; (((*p[0] >= '0') && (*p[0] <= '9')) || (*p[0] == ',')) && (i < 4); i++) {
-			if (*p[0] != ',') {
-				if ((*p[0] - 48) > maxdccval[val[i]]) {
-					PARSE_ERROR1;
-				}
-				rp[val[i]] = *p[0] - 48;
-				p[0]++;
-				if (*p[0] == ',')
-					p[0]++;
-			} else
-				p[0]++;
-		}
-#ifdef ISDN_TTY_FAX_STAT_DEBUG
-		printk(KERN_DEBUG "isdn_tty: Fax FDT tx data command parms=%d,%d,%d,%d\n",
-		       rp[4], rp[0], rp[2], rp[3]);
-#endif
-		if ((f->phase == ISDN_FAX_PHASE_B) || (f->phase == ISDN_FAX_PHASE_D)) {
-			f->code = ISDN_TTY_FAX_DT;
-			cmd.driver = info->isdn_driver;
-			cmd.arg = info->isdn_channel;
-			cmd.command = ISDN_CMD_FAXCMD;
-			isdn_command(&cmd);
-			if (f->phase == ISDN_FAX_PHASE_D) {
-				f->phase = ISDN_FAX_PHASE_C;
-				isdn_tty_fax_modem_result(7, info);	/* CONNECT */
-			}
-		} else {
-			PARSE_ERROR1;
-		}
-		return 1;
-	}
-	/* ECM=n - Error mode control 0=disabled, 2=enabled, handled by DCE alone incl. buff of partial pages */
-	if (!strncmp(p[0], "ECM", 3)) {
-		p[0] += 3;
-		switch (*p[0]) {
-			case '?':
-				p[0]++;
-				sprintf(rs, "\r\n%d", f->ecm);
-				isdn_tty_at_cout(rs, info);
-				break;
-			case '=':
-				p[0]++;
-				if (*p[0] == '?') {
-					p[0]++;
-					sprintf(rs, "\r\n0,2");
-					isdn_tty_at_cout(rs, info);
-				} else {
-					par = isdn_getnum(p);
-					if ((par != 0) && (par != 2))
-						PARSE_ERROR1;
-					f->ecm = par;
-#ifdef ISDN_TTY_FAX_STAT_DEBUG
-					printk(KERN_DEBUG "isdn_tty: Fax FECM=%d\n", par);
-#endif
-				}
-				break;
-			default:
-				PARSE_ERROR1;
-		}
-		return 0;
-	}
-	/* ET=n - End of page or document */
-	if (!strncmp(p[0], "ET=", 3)) {
-		p[0] += 3;
-		if (*p[0] == '?') {
-			p[0]++;
-			sprintf(rs, "\r\n0-2");
-			isdn_tty_at_cout(rs, info);
-		} else {
-			if ((f->phase != ISDN_FAX_PHASE_D) || (!info->faxonline & 1))
-				PARSE_ERROR1;
-			par = isdn_getnum(p);
-			if ((par < 0) || (par > 2))
-				PARSE_ERROR1;
-			f->fet = par;
-			f->code = ISDN_TTY_FAX_ET;
-			cmd.driver = info->isdn_driver;
-			cmd.arg = info->isdn_channel;
-			cmd.command = ISDN_CMD_FAXCMD;
-			isdn_command(&cmd);
-#ifdef ISDN_TTY_FAX_STAT_DEBUG
-			printk(KERN_DEBUG "isdn_tty: Fax FET=%d\n", par);
-#endif
-			return 1;
-		}
-		return 0;
-	}
-	/* K - terminate */
-	if (!strncmp(p[0], "K", 1)) {
-		p[0] += 1;
-		if ((f->phase == ISDN_FAX_PHASE_IDLE) || (f->phase == ISDN_FAX_PHASE_E))
-			PARSE_ERROR1;
-		isdn_tty_modem_hup(info, 1);
-		return 1;
-	}
-	/* LID=string - local fax ID */
-	if (!strncmp(p[0], "LID", 3)) {
-		int i, r;
-		p[0] += 3;
-		switch (*p[0]) {
-			case '?':
-				p[0]++;
-				sprintf(rs, "\r\n\"%s\"", f->id);
-				isdn_tty_at_cout(rs, info);
-				break;
-			case '=':
-				p[0]++;
-				if (*p[0] == '?') {
-					p[0]++;
-					sprintf(rs, "\r\n\"STRING\"");
-					isdn_tty_at_cout(rs, info);
-				} else {
-					if (*p[0] == '"')
-						p[0]++;
-					for (i = 0; (*p[0]) && i < (FAXIDLEN - 1) && (*p[0] != '"'); i++) {
-						f->id[i] = *p[0]++;
-					}
-					if (*p[0] == '"')
-						p[0]++;
-					for (r = i; r < FAXIDLEN; r++) {
-						f->id[r] = 32;
-					}
-					f->id[FAXIDLEN - 1] = 0;
-#ifdef ISDN_TTY_FAX_STAT_DEBUG
-					printk(KERN_DEBUG "isdn_tty: Fax local ID \"%s\"\n", f->id);
-#endif
-				}
-				break;
-			default:
-				PARSE_ERROR1;
-		}
-		return 0;
-	}
-
-	/* MDL? - DCE Model       */
-	if (!strncmp(p[0], "MDL?", 4)) {
-		p[0] += 4;
-#ifdef ISDN_TTY_FAX_STAT_DEBUG
-		printk(KERN_DEBUG "isdn_tty: FMDL?\n");
-#endif
-		isdn_tty_at_cout("\r\nisdn4linux", info);
-		return 0;
-	}
-	/* MFR? - DCE Manufacturer */
-	if (!strncmp(p[0], "MFR?", 4)) {
-		p[0] += 4;
-#ifdef ISDN_TTY_FAX_STAT_DEBUG
-		printk(KERN_DEBUG "isdn_tty: FMFR?\n");
-#endif
-		isdn_tty_at_cout("\r\nisdn4linux", info);
-		return 0;
-	}
-	/* MINSP=n - Minimum Speed for Phase C */
-	if (!strncmp(p[0], "MINSP", 5)) {
-		p[0] += 5;
-		switch (*p[0]) {
-			case '?':
-				p[0]++;
-				sprintf(rs, "\r\n%d", f->minsp);
-				isdn_tty_at_cout(rs, info);
-				break;
-			case '=':
-				p[0]++;
-				if (*p[0] == '?') {
-					p[0]++;
-					sprintf(rs, "\r\n0-5");
-					isdn_tty_at_cout(rs, info);
-				} else {
-					par = isdn_getnum(p);
-					if ((par < 0) || (par > 5))
-						PARSE_ERROR1;
-					f->minsp = par;
-#ifdef ISDN_TTY_FAX_STAT_DEBUG
-					printk(KERN_DEBUG "isdn_tty: Fax FMINSP=%d\n", par);
-#endif
-				}
-				break;
-			default:
-				PARSE_ERROR1;
-		}
-		return 0;
-	}
-	/* PHCTO=value - DTE phase C timeout */
-	if (!strncmp(p[0], "PHCTO", 5)) {
-		p[0] += 5;
-		switch (*p[0]) {
-			case '?':
-				p[0]++;
-				sprintf(rs, "\r\n%d", f->phcto);
-				isdn_tty_at_cout(rs, info);
-				break;
-			case '=':
-				p[0]++;
-				if (*p[0] == '?') {
-					p[0]++;
-					sprintf(rs, "\r\n0-255");
-					isdn_tty_at_cout(rs, info);
-				} else {
-					par = isdn_getnum(p);
-					if ((par < 0) || (par > 255))
-						PARSE_ERROR1;
-					f->phcto = par;
-#ifdef ISDN_TTY_FAX_STAT_DEBUG
-					printk(KERN_DEBUG "isdn_tty: Fax FPHCTO=%d\n", par);
-#endif
-				}
-				break;
-			default:
-				PARSE_ERROR1;
-		}
-		return 0;
-	}
-
-	/* REL=n - Phase C received EOL alignment */
-	if (!strncmp(p[0], "REL", 3)) {
-		p[0] += 3;
-		switch (*p[0]) {
-			case '?':
-				p[0]++;
-				sprintf(rs, "\r\n%d", f->rel);
-				isdn_tty_at_cout(rs, info);
-				break;
-			case '=':
-				p[0]++;
-				if (*p[0] == '?') {
-					p[0]++;
-					sprintf(rs, "\r\n0,1");
-					isdn_tty_at_cout(rs, info);
-				} else {
-					par = isdn_getnum(p);
-					if ((par < 0) || (par > 1))
-						PARSE_ERROR1;
-					f->rel = par;
-#ifdef ISDN_TTY_FAX_STAT_DEBUG
-					printk(KERN_DEBUG "isdn_tty: Fax FREL=%d\n", par);
-#endif
-				}
-				break;
-			default:
-				PARSE_ERROR1;
-		}
-		return 0;
-	}
-	/* REV? - DCE Revision */
-	if (!strncmp(p[0], "REV?", 4)) {
-		p[0] += 4;
-#ifdef ISDN_TTY_FAX_STAT_DEBUG
-		printk(KERN_DEBUG "isdn_tty: FREV?\n");
-#endif
-		strcpy(rss, isdn_tty_fax_revision);
-		sprintf(rs, "\r\nRev: %s", isdn_getrev(rss));
-		isdn_tty_at_cout(rs, info);
-		return 0;
-	}
-
-	/* Phase C Transmit Data Block Size */
-	if (!strncmp(p[0], "TBC=", 4)) {	/* dummy, not used */
-		p[0] += 4;
-#ifdef ISDN_TTY_FAX_STAT_DEBUG
-		printk(KERN_DEBUG "isdn_tty: Fax FTBC=%c\n", *p[0]);
-#endif
-		switch (*p[0]) {
-			case '0':
-				p[0]++;
-				break;
-			default:
-				PARSE_ERROR1;
-		}
-		return 0;
-	}
-	printk(KERN_DEBUG "isdn_tty: unknown token=>AT+F%s<\n", p[0]);
-	PARSE_ERROR1;
-}
-
-int
-isdn_tty_cmd_PLUSF_FAX(char **p, modem_info * info)
-{
-	if (TTY_IS_FCLASS2(info))
-		return (isdn_tty_cmd_FCLASS2(p, info));
-	else if (TTY_IS_FCLASS1(info))
-		return (isdn_tty_cmd_FCLASS1(p, info));
-	PARSE_ERROR1;
-}
diff -Nru a/drivers/isdn/isdn_ttyfax.h b/drivers/isdn/isdn_ttyfax.h
--- a/drivers/isdn/isdn_ttyfax.h	Mon Apr 22 15:32:19 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,18 +0,0 @@
-/* $Id: isdn_ttyfax.h,v 1.2.6.1 2001/09/23 22:24:32 kai Exp $
- *
- * header for Linux ISDN subsystem, tty_fax related functions (linklevel).
- *
- * Copyright 1999   by Armin Schindler (mac@melware.de)
- * Copyright 1999   by Ralf Spachmann (mel@melware.de)
- * Copyright 1999   by Cytronics & Melware
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-
-#define XON	0x11
-#define XOFF	0x13
-#define DC2	0x12
-
diff -Nru a/drivers/isdn/isdn_v110.c b/drivers/isdn/isdn_v110.c
--- a/drivers/isdn/isdn_v110.c	Mon Apr 22 15:32:19 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,614 +0,0 @@
-/* $Id: isdn_v110.c,v 1.5.6.4 2001/09/23 22:24:32 kai Exp $
- *
- * Linux ISDN subsystem, V.110 related functions (linklevel).
- *
- * Copyright by Thomas Pfeiffer (pfeiffer@pds.de)
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#include <linux/string.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-
-#include <linux/isdn.h>
-#include "isdn_v110.h"
-
-#undef ISDN_V110_DEBUG
-
-char *isdn_v110_revision = "$Revision: 1.5.6.4 $";
-
-#define V110_38400 255
-#define V110_19200  15
-#define V110_9600    3
-
-/* 
- * The following data are precoded matrices, online and offline matrix 
- * for 9600, 19200 und 38400, respectively
- */
-static unsigned char V110_OnMatrix_9600[] =
-{0xfc, 0xfc, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff,
- 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd,
- 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff,
- 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd};
-
-static unsigned char V110_OffMatrix_9600[] =
-{0xfc, 0xfc, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
-
-static unsigned char V110_OnMatrix_19200[] =
-{0xf0, 0xf0, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0xf7,
- 0xfd, 0xff, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0xf7};
-
-static unsigned char V110_OffMatrix_19200[] =
-{0xf0, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
-
-static unsigned char V110_OnMatrix_38400[] =
-{0x00, 0x7f, 0x7f, 0x7f, 0x7f, 0xfd, 0x7f, 0x7f, 0x7f, 0x7f};
-
-static unsigned char V110_OffMatrix_38400[] =
-{0x00, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff};
-
-/* 
- * FlipBits reorders sequences of keylen bits in one byte.
- * E.g. source order 7654321 will be converted to 45670123 when keylen = 4,
- * and to 67452301 when keylen = 2. This is necessary because ordering on
- * the isdn line is the other way.
- */
-static __inline unsigned char
-FlipBits(unsigned char c, int keylen)
-{
-	unsigned char b = c;
-	unsigned char bit = 128;
-	int i;
-	int j;
-	int hunks = (8 / keylen);
-
-	c = 0;
-	for (i = 0; i < hunks; i++) {
-		for (j = 0; j < keylen; j++) {
-			if (b & (bit >> j))
-				c |= bit >> (keylen - j - 1);
-		}
-		bit >>= keylen;
-	}
-	return c;
-}
-
-
-/* isdn_v110_open allocates and initializes private V.110 data
- * structures and returns a pointer to these.
- */
-static isdn_v110_stream *
-isdn_v110_open(unsigned char key, int hdrlen, int maxsize)
-{
-	int i;
-	isdn_v110_stream *v;
-
-	if ((v = kmalloc(sizeof(isdn_v110_stream), GFP_ATOMIC)) == NULL)
-		return NULL;
-	memset(v, 0, sizeof(isdn_v110_stream));
-	v->key = key;
-	v->nbits = 0;
-	for (i = 0; key & (1 << i); i++)
-		v->nbits++;
-
-	v->nbytes = 8 / v->nbits;
-	v->decodelen = 0;
-
-	switch (key) {
-		case V110_38400:
-			v->OnlineFrame = V110_OnMatrix_38400;
-			v->OfflineFrame = V110_OffMatrix_38400;
-			break;
-		case V110_19200:
-			v->OnlineFrame = V110_OnMatrix_19200;
-			v->OfflineFrame = V110_OffMatrix_19200;
-			break;
-		default:
-			v->OnlineFrame = V110_OnMatrix_9600;
-			v->OfflineFrame = V110_OffMatrix_9600;
-			break;
-	}
-	v->framelen = v->nbytes * 10;
-	v->SyncInit = 5;
-	v->introducer = 0;
-	v->dbit = 1;
-	v->b = 0;
-	v->skbres = hdrlen;
-	v->maxsize = maxsize - hdrlen;
-	if ((v->encodebuf = kmalloc(maxsize, GFP_ATOMIC)) == NULL) {
-		kfree(v);
-		return NULL;
-	}
-	return v;
-}
-
-/* isdn_v110_close frees private V.110 data structures */
-void
-isdn_v110_close(isdn_v110_stream * v)
-{
-	if (v == NULL)
-		return;
-#ifdef ISDN_V110_DEBUG
-	printk(KERN_DEBUG "v110 close\n");
-#endif
-	kfree(v->encodebuf);
-	kfree(v);
-}
-
-
-/* 
- * ValidHeaderBytes return the number of valid bytes in v->decodebuf 
- */
-static int
-ValidHeaderBytes(isdn_v110_stream * v)
-{
-	int i;
-	for (i = 0; (i < v->decodelen) && (i < v->nbytes); i++)
-		if ((v->decodebuf[i] & v->key) != 0)
-			break;
-	return i;
-}
-
-/* 
- * SyncHeader moves the decodebuf ptr to the next valid header 
- */
-static void
-SyncHeader(isdn_v110_stream * v)
-{
-	unsigned char *rbuf = v->decodebuf;
-	int len = v->decodelen;
-
-	if (len == 0)
-		return;
-	for (rbuf++, len--; len > 0; len--, rbuf++)	/* such den SyncHeader in buf ! */
-		if ((*rbuf & v->key) == 0)	/* erstes byte gefunden ?       */
-			break;  /* jupp!                        */
-	if (len)
-		memcpy(v->decodebuf, rbuf, len);
-
-	v->decodelen = len;
-#ifdef ISDN_V110_DEBUG
-	printk(KERN_DEBUG "isdn_v110: Header resync\n");
-#endif
-}
-
-/* DecodeMatrix takes n (n>=1) matrices (v110 frames, 10 bytes) where
-   len is the number of matrix-lines. len must be a multiple of 10, i.e.
-   only complete matices must be given.
-   From these, netto data is extracted and returned in buf. The return-value
-   is the bytecount of the decoded data.
- */
-static int
-DecodeMatrix(isdn_v110_stream * v, unsigned char *m, int len, unsigned char *buf)
-{
-	int line = 0;
-	int buflen = 0;
-	int mbit = 64;
-	int introducer = v->introducer;
-	int dbit = v->dbit;
-	unsigned char b = v->b;
-
-	while (line < len) {    /* Are we done with all lines of the matrix? */
-		if ((line % 10) == 0) {	/* the 0. line of the matrix is always 0 ! */
-			if (m[line] != 0x00) {	/* not 0 ? -> error! */
-#ifdef ISDN_V110_DEBUG
-				printk(KERN_DEBUG "isdn_v110: DecodeMatrix, V110 Bad Header\n");
-				/* returning now is not the right thing, though :-( */
-#endif
-			} 
-			line++; /* next line of matrix */
-			continue;
-		} else if ((line % 10) == 5) {	/* in line 5 there's only e-bits ! */
-			if ((m[line] & 0x70) != 0x30) {	/* 011 has to be at the beginning! */
-#ifdef ISDN_V110_DEBUG
-				printk(KERN_DEBUG "isdn_v110: DecodeMatrix, V110 Bad 5th line\n");
-				/* returning now is not the right thing, though :-( */
-#endif
-			}
-			line++; /* next line */
-			continue;
-		} else if (!introducer) {	/* every byte starts with 10 (stopbit, startbit) */
-			introducer = (m[line] & mbit) ? 0 : 1;	/* current bit of the matrix */
-		      next_byte:
-			if (mbit > 2) {	/* was it the last bit in this line ? */
-				mbit >>= 1;	/* no -> take next */
-				continue;
-			}       /* otherwise start with leftmost bit in the next line */
-			mbit = 64;
-			line++;
-			continue;
-		} else {        /* otherwise we need to set a data bit */
-			if (m[line] & mbit)	/* was that bit set in the matrix ? */
-				b |= dbit;	/* yes -> set it in the data byte */
-			else
-				b &= dbit - 1;	/* no -> clear it in the data byte */
-			if (dbit < 128)	/* is that data byte done ? */
-				dbit <<= 1;	/* no, got the next bit */
-			else {  /* data byte is done */
-				buf[buflen++] = b;	/* copy byte into the output buffer */
-				introducer = b = 0;	/* init of the intro sequence and of the data byte */
-				dbit = 1;	/* next we look for the 0th bit */
-			}
-			goto next_byte;	/* look for next bit in the matrix */
-		}
-	}
-	v->introducer = introducer;
-	v->dbit = dbit;
-	v->b = b;
-	return buflen;          /* return number of bytes in the output buffer */
-}
-
-/* 
- * DecodeStream receives V.110 coded data from the input stream. It recovers the 
- * original frames.
- * The input stream doesn't need to be framed
- */
-struct sk_buff *
-isdn_v110_decode(isdn_v110_stream * v, struct sk_buff *skb)
-{
-	int i;
-	int j;
-	int len;
-	unsigned char *v110_buf;
-	unsigned char *rbuf;
-
-	if (!skb) {
-		printk(KERN_WARNING "isdn_v110_decode called with NULL skb!\n");
-		return NULL;
-	}
-	rbuf = skb->data;
-	len = skb->len;
-	if (v == NULL) {
-		/* invalid handle, no chance to proceed */
-		printk(KERN_WARNING "isdn_v110_decode called with NULL stream!\n");
-		dev_kfree_skb(skb);
-		return NULL;
-	}
-	if (v->decodelen == 0)  /* cache empty?               */
-		for (; len > 0; len--, rbuf++)	/* scan for SyncHeader in buf */
-			if ((*rbuf & v->key) == 0)
-				break;	/* found first byte           */
-	if (len == 0) {
-		dev_kfree_skb(skb);
-		return NULL;
-	}
-	/* copy new data to decode-buffer */
-	memcpy(&(v->decodebuf[v->decodelen]), rbuf, len);
-	v->decodelen += len;
-      ReSync:
-	if (v->decodelen < v->nbytes) {	/* got a new header ? */
-		dev_kfree_skb(skb);
-		return NULL;    /* no, try later      */
-	}
-	if (ValidHeaderBytes(v) != v->nbytes) {	/* is that a valid header? */
-		SyncHeader(v);  /* no -> look for header */
-		goto ReSync;
-	}
-	len = (v->decodelen - (v->decodelen % (10 * v->nbytes))) / v->nbytes;
-	if ((v110_buf = kmalloc(len, GFP_ATOMIC)) == NULL) {
-		printk(KERN_WARNING "isdn_v110_decode: Couldn't allocate v110_buf\n");
-		dev_kfree_skb(skb);
-		return NULL;
-	}
-	for (i = 0; i < len; i++) {
-		v110_buf[i] = 0;
-		for (j = 0; j < v->nbytes; j++)
-			v110_buf[i] |= (v->decodebuf[(i * v->nbytes) + j] & v->key) << (8 - ((j + 1) * v->nbits));
-		v110_buf[i] = FlipBits(v110_buf[i], v->nbits);
-	}
-	v->decodelen = (v->decodelen % (10 * v->nbytes));
-	memcpy(v->decodebuf, &(v->decodebuf[len * v->nbytes]), v->decodelen);
-
-	skb_trim(skb, DecodeMatrix(v, v110_buf, len, skb->data));
-	kfree(v110_buf);
-	if (skb->len)
-		return skb;
-	else {
-		kfree_skb(skb);
-		return NULL;
-	}
-}
-
-/* EncodeMatrix takes input data in buf, len is the bytecount.
-   Data is encoded into v110 frames in m. Return value is the number of
-   matrix-lines generated.
- */
-static int
-EncodeMatrix(unsigned char *buf, int len, unsigned char *m, int mlen)
-{
-	int line = 0;
-	int i = 0;
-	int mbit = 128;
-	int dbit = 1;
-	int introducer = 3;
-	int ibit[] = {0, 1, 1};
-
-	while ((i < len) && (line < mlen)) {	/* while we still have input data */
-		switch (line % 10) {	/* in which line of the matrix are we? */
-			case 0:
-				m[line++] = 0x00;	/* line 0 is always 0 */
-				mbit = 128;	/* go on with the 7th bit */
-				break;
-			case 5:
-				m[line++] = 0xbf;	/* line 5 is always 10111111 */
-				mbit = 128;	/* go on with the 7th bit */
-				break;
-		}
-		if (line >= mlen) {
-			printk(KERN_WARNING "isdn_v110 (EncodeMatrix): buffer full!\n");
-			return line;
-		}
-	next_bit:
-		switch (mbit) { /* leftmost or rightmost bit ? */
-			case 1:
-				line++;	/* rightmost -> go to next line */
-				if (line >= mlen) {
-					printk(KERN_WARNING "isdn_v110 (EncodeMatrix): buffer full!\n");
-					return line;
-				}
-			case 128:
-				m[line] = 128;	/* leftmost -> set byte to 1000000 */
-				mbit = 64;	/* current bit in the matrix line */
-				continue;
-		}
-		if (introducer) {	/* set 110 sequence ? */
-			introducer--;	/* set on digit less */
-			m[line] |= ibit[introducer] ? mbit : 0;	/* set corresponding bit */
-			mbit >>= 1;	/* bit of matrix line  >> 1 */
-			goto next_bit;	/* and go on there */
-		}               /* else push data bits into the matrix! */
-		m[line] |= (buf[i] & dbit) ? mbit : 0;	/* set data bit in matrix */
-		if (dbit == 128) {	/* was it the last one? */
-			dbit = 1;	/* then go on with first bit of  */
-			i++;            /* next byte in input buffer */
-			if (i < len)	/* input buffer done ? */
-				introducer = 3;	/* no, write introducer 110 */
-			else {  /* input buffer done ! */
-				m[line] |= (mbit - 1) & 0xfe;	/* set remaining bits in line to 1 */
-				break;
-			}
-		} else          /* not the last data bit */
-			dbit <<= 1;	/* then go to next data bit */
-		mbit >>= 1;     /* go to next bit of matrix */
-		goto next_bit;
-
-	}
-	/* if necessary, generate remaining lines of the matrix... */
-	if ((line) && ((line + 10) < mlen))
-		switch (++line % 10) {
-			case 1:
-				m[line++] = 0xfe;
-			case 2:
-				m[line++] = 0xfe;
-			case 3:
-				m[line++] = 0xfe;
-			case 4:
-				m[line++] = 0xfe;
-			case 5:
-				m[line++] = 0xbf;
-			case 6:
-				m[line++] = 0xfe;
-			case 7:
-				m[line++] = 0xfe;
-			case 8:
-				m[line++] = 0xfe;
-			case 9:
-				m[line++] = 0xfe;
-		}
-	return line;            /* that's how many lines we have */
-}
-
-/*
- * Build a sync frame.
- */
-static struct sk_buff *
-isdn_v110_sync(isdn_v110_stream *v)
-{
-	struct sk_buff *skb;
-
-	if (v == NULL) {
-		/* invalid handle, no chance to proceed */
-		printk(KERN_WARNING "isdn_v110_sync called with NULL stream!\n");
-		return NULL;
-	}
-	if ((skb = dev_alloc_skb(v->framelen + v->skbres))) {
-		skb_reserve(skb, v->skbres);
-		memcpy(skb_put(skb, v->framelen), v->OfflineFrame, v->framelen);
-	}
-	return skb;
-}
-
-/*
- * Build an idle frame.
- */
-static struct sk_buff *
-isdn_v110_idle(isdn_v110_stream *v)
-{
-	struct sk_buff *skb;
-
-	if (v == NULL) {
-		/* invalid handle, no chance to proceed */
-		printk(KERN_WARNING "isdn_v110_sync called with NULL stream!\n");
-		return NULL;
-	}
-	if ((skb = dev_alloc_skb(v->framelen + v->skbres))) {
-		skb_reserve(skb, v->skbres);
-		memcpy(skb_put(skb, v->framelen), v->OnlineFrame, v->framelen);
-	}
-	return skb;
-}
-
-struct sk_buff *
-isdn_v110_encode(isdn_v110_stream * v, struct sk_buff *skb)
-{
-	int i;
-	int j;
-	int rlen;
-	int mlen;
-	int olen;
-	int size;
-	int sval1;
-	int sval2;
-	int nframes;
-	unsigned char *v110buf;
-	unsigned char *rbuf;
-	struct sk_buff *nskb;
-
-	if (v == NULL) {
-		/* invalid handle, no chance to proceed */
-		printk(KERN_WARNING "isdn_v110_encode called with NULL stream!\n");
-		return NULL;
-	}
-	if (!skb) {
-		/* invalid skb, no chance to proceed */
-		printk(KERN_WARNING "isdn_v110_encode called with NULL skb!\n");
-		return NULL;
-	}
-	rlen = skb->len;
-	nframes = (rlen + 3) / 4;
-	v110buf = v->encodebuf;
-	if ((nframes * 40) > v->maxsize) {
-		size = v->maxsize;
-		rlen = v->maxsize / 40;
-	} else
-		size = nframes * 40;
-	if (!(nskb = dev_alloc_skb(size + v->skbres + sizeof(int)))) {
-		printk(KERN_WARNING "isdn_v110_encode: Couldn't alloc skb\n");
-		return NULL;
-	}
-	skb_reserve(nskb, v->skbres + sizeof(int));
-	if (skb->len == 0) {
-		memcpy(skb_put(nskb, v->framelen), v->OnlineFrame, v->framelen);
-		*((int *)skb_push(nskb, sizeof(int))) = 0;
-		return nskb;
-	}
-	mlen = EncodeMatrix(skb->data, rlen, v110buf, size);
-	/* now distribute 2 or 4 bits each to the output stream! */
-	rbuf = skb_put(nskb, size);
-	olen = 0;
-	sval1 = 8 - v->nbits;
-	sval2 = v->key << sval1;
-	for (i = 0; i < mlen; i++) {
-		v110buf[i] = FlipBits(v110buf[i], v->nbits);
-		for (j = 0; j < v->nbytes; j++) {
-			if (size--)
-				*rbuf++ = ~v->key | (((v110buf[i] << (j * v->nbits)) & sval2) >> sval1);
-			else {
-				printk(KERN_WARNING "isdn_v110_encode: buffers full!\n");
-				goto buffer_full;
-			}
-			olen++;
-		}
-	}
-buffer_full:
-	skb_trim(nskb, olen);
-	*((int *)skb_push(nskb, sizeof(int))) = rlen;
-	return nskb;
-}
-
-int
-isdn_v110_stat_callback(int idx, isdn_ctrl * c)
-{
-	isdn_v110_stream *v = NULL;
-	int i;
-	int ret;
-
-	if (idx < 0)
-		return 0;
-	switch (c->command) {
-		case ISDN_STAT_BSENT:
-                        /* Keep the send-queue of the driver filled
-			 * with frames:
-			 * If number of outstanding frames < 3,
-			 * send down an Idle-Frame (or an Sync-Frame, if
-			 * v->SyncInit != 0). 
-			 */
-			if (!(v = dev->v110[idx]))
-				return 0;
-			atomic_inc(&dev->v110use[idx]);
-			if (v->skbidle > 0) {
-				v->skbidle--;
-				ret = 1;
-			} else {
-				if (v->skbuser > 0)
-					v->skbuser--;
-				ret = 0;
-			}
-			for (i = v->skbuser + v->skbidle; i < 2; i++) {
-				struct sk_buff *skb;
-				if (v->SyncInit > 0)
-					skb = isdn_v110_sync(v);
-				else
-					skb = isdn_v110_idle(v);
-				if (skb) {
-					if (dev->drv[c->driver]->interface->writebuf_skb(c->driver, c->arg, 1, skb) <= 0) {
-						dev_kfree_skb(skb);
-						break;
-					} else {
-						if (v->SyncInit)
-							v->SyncInit--;
-						v->skbidle++;
-					}
-				} else
-					break;
-			}
-			atomic_dec(&dev->v110use[idx]);
-			return ret;
-		case ISDN_STAT_DHUP:
-		case ISDN_STAT_BHUP:
-			while (1) {
-				atomic_inc(&dev->v110use[idx]);
-				if (atomic_dec_and_test(&dev->v110use[idx])) {
-					isdn_v110_close(dev->v110[idx]);
-					dev->v110[idx] = NULL;
-					break;
-				}
-				sti();
-			}
-			break;
-		case ISDN_STAT_BCONN:
-			if (dev->v110emu[idx] && (dev->v110[idx] == NULL)) {
-				int hdrlen = dev->drv[c->driver]->interface->hl_hdrlen;
-				int maxsize = dev->drv[c->driver]->interface->maxbufsize;
-				atomic_inc(&dev->v110use[idx]);
-				switch (dev->v110emu[idx]) {
-					case ISDN_PROTO_L2_V11096:
-						dev->v110[idx] = isdn_v110_open(V110_9600, hdrlen, maxsize);
-						break;
-					case ISDN_PROTO_L2_V11019:
-						dev->v110[idx] = isdn_v110_open(V110_19200, hdrlen, maxsize);
-						break;
-					case ISDN_PROTO_L2_V11038:
-						dev->v110[idx] = isdn_v110_open(V110_38400, hdrlen, maxsize);
-						break;
-					default:;
-				}
-				if ((v = dev->v110[idx])) {
-					while (v->SyncInit) {
-						struct sk_buff *skb = isdn_v110_sync(v);
-						if (dev->drv[c->driver]->interface->writebuf_skb(c->driver, c->arg, 1, skb) <= 0) {
-							dev_kfree_skb(skb);
-							/* Unable to send, try later */
-							break;
-						}
-						v->SyncInit--;
-						v->skbidle++;
-					}
-				} else
-					printk(KERN_WARNING "isdn_v110: Couldn't open stream for chan %d\n", idx);
-				atomic_dec(&dev->v110use[idx]);
-			}
-			break;
-		default:
-			return 0;
-	}
-	return 0;
-}
diff -Nru a/drivers/isdn/isdn_v110.h b/drivers/isdn/isdn_v110.h
--- a/drivers/isdn/isdn_v110.h	Mon Apr 22 15:32:18 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,29 +0,0 @@
-/* $Id: isdn_v110.h,v 1.4.6.1 2001/09/23 22:24:32 kai Exp $
- *
- * Linux ISDN subsystem, V.110 related functions (linklevel).
- *
- * Copyright by Thomas Pfeiffer (pfeiffer@pds.de)
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#ifndef _isdn_v110_h_
-#define _isdn_v110_h_
-
-/* 
- * isdn_v110_encode will take raw data and encode it using V.110 
- */
-extern struct sk_buff *isdn_v110_encode(isdn_v110_stream *, struct sk_buff *);
-
-/* 
- * isdn_v110_decode receives V.110 coded data from the stream and rebuilds
- * frames from them. The source stream doesn't need to be framed.
- */
-extern struct sk_buff *isdn_v110_decode(isdn_v110_stream *, struct sk_buff *);
-
-extern int isdn_v110_stat_callback(int, isdn_ctrl *);
-extern void isdn_v110_close(isdn_v110_stream * v);
-
-#endif
diff -Nru a/drivers/isdn/isdn_x25iface.c b/drivers/isdn/isdn_x25iface.c
--- a/drivers/isdn/isdn_x25iface.c	Mon Apr 22 15:32:19 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,341 +0,0 @@
-/* $Id: isdn_x25iface.c,v 1.9.6.1 2001/09/23 22:24:32 kai Exp $
- *
- * Linux ISDN subsystem, X.25 related functions
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- * stuff needed to support the Linux X.25 PLP code on top of devices that
- * can provide a lab_b service using the concap_proto mechanism.
- * This module supports a network interface wich provides lapb_sematics
- * -- as defined in ../../Documentation/networking/x25-iface.txt -- to
- * the upper layer and assumes that the lower layer provides a reliable
- * data link service by means of the concap_device_ops callbacks.
- *
- * Only protocol specific stuff goes here. Device specific stuff
- * goes to another -- device related -- concap_proto support source file.
- *
- */
-
-/* #include <linux/isdn.h> */
-#include <linux/netdevice.h>
-#include <linux/concap.h>
-#include <linux/wanrouter.h>
-#include "isdn_x25iface.h"
-
-/* for debugging messages not to cause an oops when device pointer is NULL*/
-#define MY_DEVNAME(dev)  ( (dev) ? (dev)->name : "DEVICE UNSPECIFIED" )
-
-
-typedef struct isdn_x25iface_proto_data {
-	int magic;
-	enum wan_states state;
-	/* Private stuff, not to be accessed via proto_data. We provide the
-	   other storage for the concap_proto instance here as well,
-	   enabling us to allocate both with just one kmalloc(): */ 
-	struct concap_proto priv;
-} ix25_pdata_t;
-
-
-
-/* is now in header file (extern): struct concap_proto * isdn_x25iface_proto_new(void); */
-void isdn_x25iface_proto_del( struct concap_proto * );
-int isdn_x25iface_proto_close( struct concap_proto * );
-int isdn_x25iface_proto_restart( struct concap_proto *,
-				 struct net_device *,
-				 struct concap_device_ops *);
-int isdn_x25iface_xmit( struct concap_proto *, struct sk_buff * );
-int isdn_x25iface_receive( struct concap_proto *, struct sk_buff * );
-int isdn_x25iface_connect_ind( struct concap_proto * );
-int isdn_x25iface_disconn_ind( struct concap_proto * );
-
-
-static struct concap_proto_ops ix25_pops = {
-	&isdn_x25iface_proto_new,
-	&isdn_x25iface_proto_del,
-	&isdn_x25iface_proto_restart,
-	&isdn_x25iface_proto_close,
-	&isdn_x25iface_xmit,
-	&isdn_x25iface_receive,
-	&isdn_x25iface_connect_ind,
-	&isdn_x25iface_disconn_ind
-};
-
-/* error message helper function */
-static void illegal_state_warn( unsigned state, unsigned char firstbyte) 
-{
-	printk( KERN_WARNING "isdn_x25iface: firstbyte %x illegal in"
-		"current state %d\n",firstbyte, state );
-}
-
-/* check protocol data field for consistency */
-static int pdata_is_bad( ix25_pdata_t * pda ){
-
-	if( pda  &&  pda -> magic == ISDN_X25IFACE_MAGIC ) return 0;
-	printk( KERN_WARNING
-		"isdn_x25iface_xxx: illegal pointer to proto data\n" );
-	return 1;
-}
-
-/* create a new x25 interface protocol instance
- */
-struct concap_proto * isdn_x25iface_proto_new()
-{
-	ix25_pdata_t * tmp = kmalloc(sizeof(ix25_pdata_t),GFP_KERNEL);
-	IX25DEBUG("isdn_x25iface_proto_new\n");
-	if( tmp ){
-		tmp -> magic = ISDN_X25IFACE_MAGIC;
-		tmp -> state = WAN_UNCONFIGURED;
-		/* private data space used to hold the concap_proto data.
-		   Only to be accessed via the returned pointer */
-		tmp -> priv.dops       = NULL;
-		tmp -> priv.net_dev    = NULL;
-		tmp -> priv.pops       = &ix25_pops;
-		tmp -> priv.flags      = 0;
-		tmp -> priv.proto_data = tmp;
-		return( &(tmp -> priv) );
-	}
-	return NULL;
-};
-
-/* close the x25iface encapsulation protocol 
- */
-int isdn_x25iface_proto_close(struct concap_proto *cprot){
-
-	ix25_pdata_t *tmp;
-        int ret = 0;
-	ulong flags;
-
-	if( ! cprot ){
-		printk( KERN_ERR "isdn_x25iface_proto_close: "
-			"invalid concap_proto pointer\n" );
-		return -1;
-	}
-	IX25DEBUG( "isdn_x25iface_proto_close %s \n", MY_DEVNAME(cprot -> net_dev) );
-	save_flags(flags);
-	cli();  /* avoid races with incoming events calling pops methods while
-		 cprot members are inconsistent */  
-	cprot -> dops    = NULL;
-	cprot -> net_dev = NULL;
-	tmp = cprot -> proto_data;
-	if( pdata_is_bad( tmp ) ){
-		ret = -1;
-	} else {
-		tmp -> state = WAN_UNCONFIGURED;
-	}
-	restore_flags(flags);
-
-	return ret;
-}
-
-/* Delete the x25iface encapsulation protocol instance
- */
-void isdn_x25iface_proto_del(struct concap_proto *cprot){
-
-	ix25_pdata_t * tmp;
- 
-	IX25DEBUG( "isdn_x25iface_proto_del \n" );
-	if( ! cprot ){
-		printk( KERN_ERR "isdn_x25iface_proto_del: "
-			"concap_proto pointer is NULL\n" );
-		return;
-	}
-	tmp = cprot -> proto_data;
-	if( tmp == NULL ){ 
-		printk( KERN_ERR "isdn_x25iface_proto_del: inconsistent "
-			"proto_data pointer (maybe already deleted?)\n"); 
-		return;
-	}
-	/* close if the protocol is still open */
-	if( cprot -> dops ) isdn_x25iface_proto_close(cprot);
-	/* freeing the storage should be sufficient now. But some additional
-	   settings might help to catch wild pointer bugs */
-	tmp -> magic = 0;
-	cprot -> proto_data = NULL;
-
-	kfree( tmp );
-	return;
-}
-
-/* (re-)initialize the data structures for x25iface encapsulation
- */
-int isdn_x25iface_proto_restart(struct concap_proto *cprot,
-				struct net_device *ndev, 
-				struct concap_device_ops *dops)
-{
-	ix25_pdata_t * pda = cprot -> proto_data ;
-	ulong flags;
-
-	IX25DEBUG( "isdn_x25iface_proto_restart %s \n", MY_DEVNAME(ndev) );
-
-	if ( pdata_is_bad( pda ) ) return -1;
-
-	if( !( dops  && dops -> data_req && dops -> connect_req 
-	       && dops -> disconn_req )  ){
-		printk( KERN_WARNING "isdn_x25iface_restart: required dops"
-			" missing\n" );
-		isdn_x25iface_proto_close(cprot);
-		return -1;
-	}
-	save_flags(flags);
-	cli();  /* avoid races with incoming events calling pops methods while
-		 cprot members are inconsistent */  
-	cprot -> net_dev = ndev;
-	cprot -> pops = &ix25_pops;
-	cprot -> dops = dops;
-	pda -> state = WAN_DISCONNECTED;
-	restore_flags(flags);
-	return 0;
-}
-
-/* deliver a dl_data frame received from i4l HL driver to the network layer 
- */
-int isdn_x25iface_receive(struct concap_proto *cprot, struct sk_buff *skb)
-{
-  	IX25DEBUG( "isdn_x25iface_receive %s \n", MY_DEVNAME(cprot->net_dev) );
-	if ( ( (ix25_pdata_t*) (cprot->proto_data) ) 
-	     -> state == WAN_CONNECTED ){
-		skb -> dev = cprot -> net_dev;
-		skb -> protocol = htons(ETH_P_X25);
-		skb -> pkt_type = PACKET_HOST;
-		if( skb_push(skb, 1)){
-			skb -> data[0]=0x00;
-			skb -> mac.raw = skb -> data;
-			netif_rx(skb);
-			return 0;
-		}
-	}
-	printk(KERN_WARNING "isdn_x25iface_receive %s: not connected, skb dropped\n", MY_DEVNAME(cprot->net_dev) );
-	dev_kfree_skb(skb);
-	return -1;
-}
-
-/* a connection set up is indicated by lower layer 
- */
-int isdn_x25iface_connect_ind(struct concap_proto *cprot)
-{
-	struct sk_buff * skb = dev_alloc_skb(1);
-	enum wan_states *state_p 
-	  = &( ( (ix25_pdata_t*) (cprot->proto_data) ) -> state);
-	IX25DEBUG( "isdn_x25iface_connect_ind %s \n"
-		   , MY_DEVNAME(cprot->net_dev) );
-	if( *state_p == WAN_UNCONFIGURED ){ 
-		printk(KERN_WARNING 
-		       "isdn_x25iface_connect_ind while unconfigured %s\n"
-		       , MY_DEVNAME(cprot->net_dev) );
-		return -1;
-	}
-	*state_p = WAN_CONNECTED;
-	if( skb ){
-		*( skb_put(skb, 1) ) = 0x01;
-		skb -> mac.raw = skb -> data;
-		skb -> dev  = cprot -> net_dev;
-		skb -> protocol = htons(ETH_P_X25);
-		skb -> pkt_type = PACKET_HOST;
-		netif_rx(skb);
-		return 0;
-	} else {
-		printk(KERN_WARNING "isdn_x25iface_connect_ind: "
-		       " out of memory -- disconnecting\n");
-		cprot -> dops -> disconn_req(cprot);
-		return -1;
-	}
-}
-	
-/* a disconnect is indicated by lower layer 
- */
-int isdn_x25iface_disconn_ind(struct concap_proto *cprot)
-{
-	struct sk_buff *skb;
-	enum wan_states *state_p 
-	  = &( ( (ix25_pdata_t*) (cprot->proto_data) ) -> state);
-	IX25DEBUG( "isdn_x25iface_disconn_ind %s \n", MY_DEVNAME(cprot -> net_dev) );
-	if( *state_p == WAN_UNCONFIGURED ){ 
-		printk(KERN_WARNING 
-		       "isdn_x25iface_disconn_ind while unconfigured\n");
-		return -1;
-	}
-	if(! cprot -> net_dev) return -1;
-	*state_p = WAN_DISCONNECTED;
-	skb = dev_alloc_skb(1);
-	if( skb ){
-		*( skb_put(skb, 1) ) = 0x02;
-		skb -> mac.raw = skb -> data;
-		skb -> dev  = cprot -> net_dev;
-		skb -> protocol = htons(ETH_P_X25);
-		skb -> pkt_type = PACKET_HOST;
-		netif_rx(skb);
-		return 0;
-	} else {
-		printk(KERN_WARNING "isdn_x25iface_disconn_ind:"
-		       " out of memory\n");
-		return -1;
-	}
-}
-
-/* process a frame handed over to us from linux network layer. First byte
-   semantics as defined in ../../Documentation/networking/x25-iface.txt 
-   */
-int isdn_x25iface_xmit(struct concap_proto *cprot, struct sk_buff *skb)
-{
-	unsigned char firstbyte = skb->data[0];
-	unsigned *state = 
-		&( ( (ix25_pdata_t*) (cprot -> proto_data) ) -> state  );
-	int ret = 0;
-	IX25DEBUG( "isdn_x25iface_xmit: %s first=%x state=%d \n", MY_DEVNAME(cprot -> net_dev), firstbyte, *state );
-	switch ( firstbyte ){
-	case 0x00: /* dl_data request */
-		if( *state == WAN_CONNECTED ){
-			skb_pull(skb, 1);
-			cprot -> net_dev -> trans_start = jiffies;
-			ret = ( cprot -> dops -> data_req(cprot, skb) );
-			/* prepare for future retransmissions */
-			if( ret ) skb_push(skb,1);
-			return ret;
-		}
-		illegal_state_warn( *state, firstbyte ); 
-		break;
-	case 0x01: /* dl_connect request */
-		if( *state == WAN_DISCONNECTED ){
-			*state = WAN_CONNECTING;
-		        ret = cprot -> dops -> connect_req(cprot);
-			if(ret){
-				/* reset state and notify upper layer about
-				 * immidiatly failed attempts */
-				isdn_x25iface_disconn_ind(cprot);
-			}
-		} else {
-			illegal_state_warn( *state, firstbyte );
-		}
-		break;
-	case 0x02: /* dl_disconnect request */
-		switch ( *state ){
-		case WAN_DISCONNECTED: 
-			/* Should not happen. However, give upper layer a
-			   chance to recover from inconstistency  but don't
-			   trust the lower layer sending the disconn_confirm
-			   when already disconnected */
-			printk(KERN_WARNING "isdn_x25iface_xmit: disconnect "
-			       " requested while disconnected\n" );
-			isdn_x25iface_disconn_ind(cprot);
-			break; /* prevent infinite loops */
-		case WAN_CONNECTING:
-		case WAN_CONNECTED:
-			*state = WAN_DISCONNECTED;
-			cprot -> dops -> disconn_req(cprot);
-			break;
-		default:
-			illegal_state_warn( *state, firstbyte );
-		}
-		break;
-	case 0x03: /* changing lapb parameters requested */
-		printk(KERN_WARNING "isdn_x25iface_xmit: setting of lapb"
-		       " options not yet supported\n");
-		break;
-	default:
-		printk(KERN_WARNING "isdn_x25iface_xmit: frame with illegal"
-		       " first byte %x ignored:\n", firstbyte);
-	}
-	dev_kfree_skb(skb);
-	return 0;
-}
diff -Nru a/drivers/isdn/isdn_x25iface.h b/drivers/isdn/isdn_x25iface.h
--- a/drivers/isdn/isdn_x25iface.h	Mon Apr 22 15:32:19 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,39 +0,0 @@
-/* $Id: isdn_x25iface.h,v 1.3.6.1 2001/09/23 22:24:32 kai Exp $
- *
- * header for Linux ISDN subsystem, x.25 related functions
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#ifndef _LINUX_ISDN_X25IFACE_H
-#define _LINUX_ISDN_X25IFACE_H
-
-#define ISDN_X25IFACE_MAGIC 0x1e75a2b9
-/* #define DEBUG_ISDN_X25 if you want isdn_x25 debugging messages */
-#ifdef DEBUG_ISDN_X25
-#   define IX25DEBUG(fmt,args...) printk(KERN_DEBUG fmt , ## args)
-#else
-#   define IX25DEBUG(fmt,args...)
-#endif
-
-#include <linux/skbuff.h>
-#include <linux/wanrouter.h>
-#include <linux/isdn.h>
-#include <linux/concap.h>
-
-extern struct concap_proto_ops * isdn_x25iface_concap_proto_ops_pt;
-extern struct concap_proto     * isdn_x25iface_proto_new(void);
-
-
-
-#endif
-
-
-
-
-
-
-
-
diff -Nru a/drivers/isdn/pcbit/Config.help b/drivers/isdn/pcbit/Config.help
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/pcbit/Config.help	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,8 @@
+CONFIG_ISDN_DRV_PCBIT
+  This enables support for the PCBIT ISDN-card.  This card is
+  manufactured in Portugal by Octal.  For running this card,
+  additional firmware is necessary, which has to be downloaded into
+  the card using a utility which is distributed separately.  See
+  <file:Documentation/isdn/README> and
+  <file:Documentation/isdn/README.pcbit> for more information.
+
diff -Nru a/drivers/isdn/pcbit/Config.in b/drivers/isdn/pcbit/Config.in
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/pcbit/Config.in	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,5 @@
+#
+# Config.in for PCBIT ISDN driver
+#
+
+dep_tristate       'PCBIT-D support' CONFIG_ISDN_DRV_PCBIT $CONFIG_ISDN
diff -Nru a/drivers/isdn/sc/Config.help b/drivers/isdn/sc/Config.help
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/sc/Config.help	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,8 @@
+CONFIG_ISDN_DRV_SC
+  This enables support for the Spellcaster BRI ISDN boards.  This
+  driver currently builds only in a modularized version ( = code which
+  can be inserted in and removed from the running kernel whenever you
+  want, details in <file:Documentation/modules.txt>); the module will
+  be called sc.o.  See <file:Documentation/isdn/README.sc> and
+  <http://www.spellcast.com/> for more information.
+
diff -Nru a/drivers/isdn/sc/Config.in b/drivers/isdn/sc/Config.in
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/sc/Config.in	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,5 @@
+#
+# Config.in for Spellcaster ISDN driver
+#
+
+dep_tristate       'Spellcaster support' CONFIG_ISDN_DRV_SC $CONFIG_ISDN
diff -Nru a/drivers/isdn/tpam/Config.help b/drivers/isdn/tpam/Config.help
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/tpam/Config.help	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,8 @@
+CONFIG_ISDN_DRV_TPAM
+  This enables support for the Auvertech TurboPAM ISDN-card.
+  For running this card, additional firmware is necessary, which has
+  to be downloaded into the card using a utility which is distributed
+  separately from the Auvertech's web site: <http://www.auvertech.fr/>.
+
+  Please redirect all support questions to support@auvertech.fr.
+
diff -Nru a/drivers/isdn/tpam/Config.in b/drivers/isdn/tpam/Config.in
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/tpam/Config.in	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,7 @@
+#
+# Config.in for Auvertech TurboPAM ISDN driver
+#
+
+if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then
+   dep_tristate    'Auvertech TurboPAM support' CONFIG_ISDN_DRV_TPAM $CONFIG_ISDN $CONFIG_PCI
+fi
diff -Nru a/drivers/md/Makefile b/drivers/md/Makefile
--- a/drivers/md/Makefile	Mon Apr 22 15:32:18 2002
+++ b/drivers/md/Makefile	Mon Apr 22 15:32:18 2002
@@ -5,7 +5,6 @@
 O_TARGET	:= mddev.o
 
 export-objs	:= md.o xor.o
-list-multi	:= lvm-mod.o
 lvm-mod-objs	:= lvm.o lvm-snap.o lvm-fs.o
 
 # Note: link order is important.  All raid personalities
@@ -22,6 +21,3 @@
 obj-$(CONFIG_BLK_DEV_LVM)	+= lvm-mod.o
 
 include $(TOPDIR)/Rules.make
-
-lvm-mod.o: $(lvm-mod-objs)
-	$(LD) -r -o $@ $(lvm-mod-objs)
diff -Nru a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile
--- a/drivers/media/radio/Makefile	Mon Apr 22 15:32:18 2002
+++ b/drivers/media/radio/Makefile	Mon Apr 22 15:32:18 2002
@@ -1,13 +1,6 @@
 #
 # Makefile for the kernel character device drivers.
 #
-# Note! Dependencies are done automagically by 'make dep', which also
-# removes any old dependencies. DON'T put your own dependencies here
-# unless it's something special (ie not a .c file).
-#
-# Note 2! The CFLAGS definitions are now inherited from the
-# parent makes..
-#
 
 # Object file lists.
 
@@ -23,8 +16,6 @@
 
 export-objs     := miropcm20-rds-core.o
 
-list-multi	:= miropcm20.o
-
 miropcm20-objs	:= miropcm20-rds-core.o miropcm20-radio.o
 
 obj-$(CONFIG_RADIO_AZTECH) += radio-aztech.o
@@ -44,6 +35,3 @@
 obj-$(CONFIG_RADIO_MAESTRO) += radio-maestro.o
 
 include $(TOPDIR)/Rules.make
-
-miropcm20.o: $(miropcm20-objs)
-	$(LD) -r -o $@ $(miropcm20-objs)
diff -Nru a/drivers/media/video/Makefile b/drivers/media/video/Makefile
--- a/drivers/media/video/Makefile	Mon Apr 22 15:32:19 2002
+++ b/drivers/media/video/Makefile	Mon Apr 22 15:32:19 2002
@@ -1,13 +1,6 @@
 #
 # Makefile for the kernel character device drivers.
 #
-# Note! Dependencies are done automagically by 'make dep', which also
-# removes any old dependencies. DON'T put your own dependencies here
-# unless it's something special (ie not a .c file).
-#
-# Note 2! The CFLAGS definitions are now inherited from the
-# parent makes..
-#
 
 # Object file lists.
 
@@ -16,10 +9,6 @@
 obj-n		:=
 obj-		:=
 
-SUB_DIRS     := 
-MOD_SUB_DIRS := $(SUB_DIRS)
-ALL_SUB_DIRS := $(SUB_DIRS)
-
 O_TARGET := video.o
 
 # All of the (potential) objects that export symbols.
@@ -27,7 +16,6 @@
 
 export-objs     :=	i2c-old.o videodev.o bttv-if.o cpia.o video-buf.o
 
-list-multi	:=	bttv.o zoran.o
 bttv-objs	:=	bttv-driver.o bttv-cards.o bttv-if.o \
 			bttv-risc.o bttv-vbi.o
 zoran-objs      :=	zr36120.o zr36120_i2c.o zr36120_mem.o
@@ -59,29 +47,4 @@
 obj-$(CONFIG_VIDEO_MEYE) += meye.o
 obj-$(CONFIG_TUNER_3036) += tuner-3036.o
 
-# Extract lists of the multi-part drivers.
-# The 'int-*' lists are the intermediate files used to build the multi's.
-
-multi-y         := $(filter $(list-multi), $(obj-y))
-multi-m         := $(filter $(list-multi), $(obj-m))
-int-y           := $(sort $(foreach m, $(multi-y), $($(basename $(m))-objs)))
-int-m           := $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs)))
-
-# Files that are both resident and modular: remove from modular.
-
-obj-m           := $(filter-out $(obj-y), $(obj-m))
-int-m           := $(filter-out $(int-y), $(int-m))
-
-# Take multi-part drivers out of obj-y and put components in.
-
-obj-y           := $(filter-out $(list-multi), $(obj-y)) $(int-y)
-
 include $(TOPDIR)/Rules.make
-
-fastdep:
-
-zoran.o: zr36120.o zr36120_i2c.o zr36120_mem.o
-	$(LD) $(LD_RFLAG) -r -o $@ zr36120.o zr36120_i2c.o zr36120_mem.o
-
-bttv.o: $(bttv-objs)
-	$(LD) $(LD_RFLAG) -r -o $@ $(bttv-objs)
diff -Nru a/drivers/message/fusion/Makefile b/drivers/message/fusion/Makefile
--- a/drivers/message/fusion/Makefile	Mon Apr 22 15:32:18 2002
+++ b/drivers/message/fusion/Makefile	Mon Apr 22 15:32:18 2002
@@ -1,14 +1,7 @@
 #
 # Makefile for the LSI Logic Fusion MPT (Message Passing Technology) drivers.
 #
-# Note! Dependencies are done automagically by 'make dep', which also
-# removes any old dependencies. DON'T put your own dependencies here
-# unless it's something special (ie not a .c file).
-#
-# Note 2! The CFLAGS definition is now inherited from the
-# parent makefile.
-#
-# Note 3! If you want to turn on various debug defines for an extended period of
+# Note! If you want to turn on various debug defines for an extended period of
 # time but don't want them lingering around in the Makefile when you pass it on 
 # to someone else, use the MPT_CFLAGS env variable (thanks Steve). -nromer
 
@@ -54,18 +47,10 @@
 
 export-objs	:= mptbase.o mptscsih.o mptlan.o mptctl.o isense.o
 
-# ? what's list-multi for?
-#list-multi	:= fusion.o mptscsih.o
-
 obj-$(CONFIG_FUSION)		+= mptbase.o mptscsih.o
 obj-$(CONFIG_FUSION_ISENSE)	+= isense.o
 obj-$(CONFIG_FUSION_CTL)	+= mptctl.o
 obj-$(CONFIG_FUSION_LAN)	+= mptlan.o
-
-O_OBJS		:= $(filter-out		$(export-objs), $(obj-y))
-OX_OBJS		:= $(filter		$(export-objs), $(obj-y))
-M_OBJS		:= $(sort $(filter-out	$(export-objs), $(obj-m)))
-MX_OBJS		:= $(sort $(filter	$(export-objs), $(obj-m)))
 
 include $(TOPDIR)/Rules.make
 
diff -Nru a/drivers/mtd/Makefile b/drivers/mtd/Makefile
--- a/drivers/mtd/Makefile	Mon Apr 22 15:32:19 2002
+++ b/drivers/mtd/Makefile	Mon Apr 22 15:32:19 2002
@@ -1,12 +1,6 @@
 #
 # Makefile for the memory technology device drivers.
 #
-# Note! Dependencies are done automagically by 'make dep', which also
-# removes any old dependencies. DON'T put your own dependencies here
-# unless it's something special (ie not a .c file).
-#
-# Note 2! The CFLAGS definitions are now inherited from the
-# parent makes..
 #
 # $Id: Makefile,v 1.63 2001/06/13 09:43:07 dwmw2 Exp $
 
@@ -20,7 +14,6 @@
 O_TARGET	:= mtdlink.o
 
 export-objs	:= mtdcore.o mtdpart.o redboot.o bootldr.o afs.o
-list-multi	:= nftl.o
 
 mod-subdirs	:= 
 subdir-y	:= chips maps devices nand
@@ -59,7 +52,3 @@
 nftl-objs	:= nftlcore.o nftlmount.o
 
 include $(TOPDIR)/Rules.make
-
-nftl.o: $(nftl-objs)
-	$(LD) -r -o $@ $(nftl-objs)
-
diff -Nru a/drivers/net/dl2k.c b/drivers/net/dl2k.c
--- a/drivers/net/dl2k.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/net/dl2k.c	Mon Apr 22 15:32:19 2002
@@ -327,7 +327,7 @@
 	return 0;
 }
 
-int
+static int __devinit
 parse_eeprom (struct net_device *dev)
 {
 	int i, j;
@@ -1275,7 +1275,7 @@
 #define EEP_READ 0x0200
 #define EEP_BUSY 0x8000
 /* Read the EEPROM word */
-int
+static int __devinit
 read_eeprom (long ioaddr, int eep_addr)
 {
 	int i = 1000;
diff -Nru a/drivers/net/eepro100.c b/drivers/net/eepro100.c
--- a/drivers/net/eepro100.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/net/eepro100.c	Mon Apr 22 15:32:19 2002
@@ -632,7 +632,7 @@
 	return -ENODEV;
 }
 
-static int speedo_found1(struct pci_dev *pdev,
+static int __devinit speedo_found1(struct pci_dev *pdev,
 		long ioaddr, int card_idx, int acpi_idle_state)
 {
 	struct net_device *dev;
@@ -866,7 +866,7 @@
    interval for serial EEPROM.  However, it looks like that there is an
    additional requirement dictating larger udelay's in the code below.
    2000/05/24  SAW */
-static int do_eeprom_cmd(long ioaddr, int cmd, int cmd_len)
+static int __devinit do_eeprom_cmd(long ioaddr, int cmd, int cmd_len)
 {
 	unsigned retval = 0;
 	long ee_addr = ioaddr + SCBeeprom;
diff -Nru a/drivers/net/sis900.c b/drivers/net/sis900.c
--- a/drivers/net/sis900.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/net/sis900.c	Mon Apr 22 15:32:19 2002
@@ -656,7 +656,7 @@
  *	Note that location is in word (16 bits) unit
  */
 
-static u16 read_eeprom(long ioaddr, int location)
+static u16 __devinit read_eeprom(long ioaddr, int location)
 {
 	int i;
 	u16 retval = 0;
diff -Nru a/drivers/net/tokenring/olympic.c b/drivers/net/tokenring/olympic.c
--- a/drivers/net/tokenring/olympic.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/net/tokenring/olympic.c	Mon Apr 22 15:32:19 2002
@@ -629,8 +629,7 @@
 	printk(" stat_ring[7]: %p\n", &(olympic_priv->olympic_rx_status_ring[7])  );
 
 	printk("RXCDA: %x, rx_ring[0]: %p\n",readl(olympic_mmio+RXCDA),&olympic_priv->olympic_rx_ring[0]);
-	printk("Rx_ring_dma_addr = %08x, rx_status_dma_addr =
-%08x\n",olympic_priv->rx_ring_dma_addr,olympic_priv->rx_status_ring_dma_addr) ; 
+	printk("Rx_ring_dma_addr = %08x, rx_status_dma_addr = %08x\n",olympic_priv->rx_ring_dma_addr,olympic_priv->rx_status_ring_dma_addr) ; 
 #endif
 
 	writew((((readw(olympic_mmio+RXENQ)) & 0x8000) ^ 0x8000) | i,olympic_mmio+RXENQ);
diff -Nru a/drivers/net/wan/dscc4.c b/drivers/net/wan/dscc4.c
--- a/drivers/net/wan/dscc4.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/net/wan/dscc4.c	Mon Apr 22 15:32:19 2002
@@ -1,8 +1,8 @@
 /*
- * drivers/net/wan/dscc4/dscc4_main.c: a DSCC4 HDLC driver for Linux
+ * drivers/net/wan/dscc4/dscc4.c: a DSCC4 HDLC driver for Linux
  *
- * This software may be used and distributed according to the terms of the 
- * GNU General Public License. 
+ * This software may be used and distributed according to the terms of the
+ * GNU General Public License.
  *
  * The author may be reached as romieu@cogenit.fr.
  * Specific bug reports/asian food will be welcome.
@@ -16,11 +16,12 @@
  * I. Board Compatibility
  *
  * This device driver is designed for the Siemens PEB20534 4 ports serial
- * controller as found on Etinc PCISYNC cards. The documentation for the 
+ * controller as found on Etinc PCISYNC cards. The documentation for the
  * chipset is available at http://www.infineon.com:
  * - Data Sheet "DSCC4, DMA Supported Serial Communication Controller with
  * 4 Channels, PEB 20534 Version 2.1, PEF 20534 Version 2.1";
  * - Application Hint "Management of DSCC4 on-chip FIFO resources".
+ * - Errata sheet DS5 (courtesy of Michael Skerritt).
  * Jens David has built an adapter based on the same chipset. Take a look
  * at http://www.afthd.tu-darmstadt.de/~dg1kjd/pciscc4 for a specific
  * driver.
@@ -37,15 +38,13 @@
  *
  * III. Driver operation
  *
- * The rx/tx operations are based on a linked list of descriptor. I haven't
- * tried the start/stop descriptor method as this one looks like the cheapest
- * in terms of PCI manipulation.
+ * The rx/tx operations are based on a linked list of descriptors. The driver
+ * doesn't use HOLD mode any more. HOLD mode is definitely buggy and the more
+ * I tried to fix it, the more it started to look like (convoluted) software
+ * mutation of LxDA method. Errata sheet DS5 suggests to use LxDA: consider
+ * this a rfc2119 MUST.
  *
  * Tx direction
- * Once the data section of the current descriptor processed, the next linked
- * descriptor is loaded if the HOLD bit isn't set in the current descriptor.
- * If HOLD is met, the transmission is stopped until the host unsets it and
- * signals the change via TxPOLL.
  * When the tx ring is full, the xmit routine issues a call to netdev_stop.
  * The device is supposed to be enabled again during an ALLS irq (we could
  * use HI but as it's easy to loose events, it's fscked).
@@ -55,12 +54,6 @@
  * I may implement it some day but it isn't the highest ranked item.
  *
  * IV. Notes
- * The chipset is buggy. Typically, under some specific load patterns (I
- * wouldn't call them "high"), the irq queues and the descriptors look like
- * some event has been lost. Even assuming some fancy PCI feature, it won't 
- * explain the reproductible missing "C" bit in the descriptors. Faking an 
- * irq in the periodic timer isn't really elegant but at least it seems 
- * reliable.
  * The current error (XDU, RFO) recovery code is untested.
  * So far, RDO takes his RX channel down and the right sequence to enable it
  * again is still a mistery. If RDO happens, plan a reboot. More details
@@ -69,7 +62,7 @@
  * suggest it for DCE either but at least one can get some messages instead
  * of a complete instant freeze.
  * Tests are done on Rev. 20 of the silicium. The RDO handling changes with
- * the documentation/chipset releases. An on-line errata would be welcome.
+ * the documentation/chipset releases.
  *
  * TODO:
  * - test X25.
@@ -115,14 +108,13 @@
 #include <linux/hdlc.h>
 
 /* Version */
-static const char version[] = "$Id: dscc4.c,v 1.158 2002/01/30 00:40:37 romieu Exp $\n";
+static const char version[] = "$Id: dscc4.c,v 1.159 2002/04/10 22:05:17 romieu Exp $ for Linux\n";
 static int debug;
 static int quartz;
 
 #define	DRV_NAME	"dscc4"
 
 #undef DSCC4_POLLING
-#define DEBUG
 
 /* Module parameters */
 
@@ -148,7 +140,7 @@
 	u32 next;
 	u32 data;
 	u32 complete;
-	u32 jiffies; /* more hack to come :o) */
+	u32 jiffies; /* Allows sizeof(TxFD) == sizeof(RxFD) + extra hack */
 };
 
 struct RxFD {
@@ -159,23 +151,24 @@
 	u32 end;
 };
 
-#define DEBUG
-#define DEBUG_PARANOIA
-#define TX_RING_SIZE    32
-#define RX_RING_SIZE    32
-#define IRQ_RING_SIZE   64 /* Keep it A multiple of 32 */
-#define TX_TIMEOUT      (HZ/10)
-#define DSCC4_HZ_MAX	33000000
-#define BRR_DIVIDER_MAX 64*0x00008000
-#define dev_per_card	4
-
-#define SOURCE_ID(flags) (((flags) >> 28 ) & 0x03)
-#define TO_SIZE(state) (((state) >> 16) & 0x1fff)
-#define TO_STATE(len) cpu_to_le32(((len) & TxSizeMax) << 16)
-#define RX_MAX(len) ((((len) >> 5) + 1)<< 5)
-#define SCC_REG_START(id) SCC_START+(id)*SCC_OFFSET
-
-#undef DEBUG
+#define DUMMY_SKB_SIZE		64
+#define TX_LOW			8
+#define TX_RING_SIZE		32
+#define RX_RING_SIZE		32
+#define TX_TOTAL_SIZE		TX_RING_SIZE*sizeof(struct TxFD)
+#define RX_TOTAL_SIZE		RX_RING_SIZE*sizeof(struct RxFD)
+#define IRQ_RING_SIZE		64		/* Keep it a multiple of 32 */
+#define TX_TIMEOUT		(HZ/10)
+#define DSCC4_HZ_MAX		33000000
+#define BRR_DIVIDER_MAX		64*0x00004000	/* Cf errata DS5 p.10 */
+#define dev_per_card		4
+#define SCC_REGISTERS_MAX	23		/* Cf errata DS5 p.4 */
+
+#define SOURCE_ID(flags)	(((flags) >> 28) & 0x03)
+#define TO_SIZE(state)		(((state) >> 16) & 0x1fff)
+#define TO_STATE(len)		cpu_to_le32(((len) & TxSizeMax) << 16)
+#define RX_MAX(len)		((((len) >> 5) + 1) << 5)
+#define SCC_REG_START(dpriv)	(SCC_START+(dpriv->dev_id)*SCC_OFFSET)
 
 struct dscc4_pci_priv {
         u32 *iqcfg;
@@ -197,21 +190,24 @@
         u32 *iqrx;
         u32 *iqtx;
 
+	/* FIXME: check all the volatile are required */
+        volatile u32 tx_current;
         u32 rx_current;
-        u32 tx_current;
-        u32 iqrx_current;
         u32 iqtx_current;
+        u32 iqrx_current;
 
-        u32 tx_dirty;
-	int bad_tx_frame;
-	int bad_rx_frame;
-	int rx_needs_refill;
+        volatile u32 tx_dirty;
+        volatile u32 ltda;
+        u32 rx_dirty;
+        u32 lrda;
 
         dma_addr_t tx_fd_dma;
         dma_addr_t rx_fd_dma;
         dma_addr_t iqtx_dma;
         dma_addr_t iqrx_dma;
 
+	volatile u32 scc_regs[SCC_REGISTERS_MAX]; /* Cf errata DS5 p.4 */
+
 	struct timer_list timer;
 
         struct dscc4_pci_priv *pci_priv;
@@ -220,13 +216,12 @@
         int dev_id;
 	volatile u32 flags;
 	u32 timer_help;
-	u32 hi_expected;
 
-	hdlc_device hdlc;
-	sync_serial_settings settings;
 	unsigned short encoding;
 	unsigned short parity;
-	u32 pad __attribute__ ((aligned (4)));
+	hdlc_device hdlc;
+	sync_serial_settings settings;
+	u32 __pad __attribute__ ((aligned (4)));
 };
 
 /* GLOBAL registers definitions */
@@ -245,6 +240,10 @@
 #define CH0CFG  0x50
 #define CH0BRDA 0x54
 #define CH0BTDA 0x58
+#define CH0FRDA 0x98
+#define CH0FTDA 0xb0
+#define CH0LRDA 0xc8
+#define CH0LTDA 0xe0
 
 /* SCC registers definitions */
 #define SCC_START	0x0100
@@ -263,8 +262,8 @@
 #define EncodingMask	0x00700000
 #define CrcMask		0x00000003
 
-#define IntRxScc0       0x10000000
-#define IntTxScc0       0x01000000
+#define IntRxScc0	0x10000000
+#define IntTxScc0	0x01000000
 
 #define TxPollCmd	0x00000400
 #define RxActivate	0x08000000
@@ -279,11 +278,13 @@
 
 #define Ccr0ClockMask	0x0000003f
 #define Ccr1LoopMask	0x00000200
+#define IsrMask		0x000fffff
 #define BrrExpMask	0x00000f00
 #define BrrMultMask	0x0000003f
 #define EncodingMask	0x00700000
 #define Hold		0x40000000
 #define SccBusy		0x10000000
+#define PowerUp		0x80000000
 #define FrameOk		(FrameVfr | FrameCrc)
 #define FrameVfr	0x80
 #define FrameRdo	0x40
@@ -303,10 +304,12 @@
 #define TxEvt		0x0f000000
 #define Alls		0x00040000
 #define Xdu		0x00010000
+#define Cts		0x00004000
 #define Xmr		0x00002000
 #define Xpr		0x00001000
 #define Rdo		0x00000080
 #define Rfs		0x00000040
+#define Cd		0x00000004
 #define Rfo		0x00000002
 #define Flex		0x00000001
 
@@ -338,7 +341,7 @@
 static void dscc4_tx_timeout(struct net_device *);
 static void dscc4_irq(int irq, void *dev_id, struct pt_regs *ptregs);
 static int dscc4_hdlc_attach(hdlc_device *, unsigned short, unsigned short);
-static int dscc4_set_iface(struct net_device *);
+static int dscc4_set_iface(struct dscc4_dev_priv *, struct net_device *);
 static inline int dscc4_set_quartz(struct dscc4_dev_priv *, int);
 #ifdef DSCC4_POLLING
 static int dscc4_tx_poll(struct dscc4_dev_priv *, struct net_device *);
@@ -349,40 +352,96 @@
 	return list_entry(dev, struct dscc4_dev_priv, hdlc.netdev);
 }
 
-static inline void dscc4_patch_register(u32 ioaddr, u32 mask, u32 value)
+static void scc_patchl(u32 mask, u32 value, struct dscc4_dev_priv *dpriv,
+			struct net_device *dev, int offset)
 {
 	u32 state;
 
-	state = readl(ioaddr);
+	/* Cf scc_writel for concern regarding thread-safety */
+	state = dpriv->scc_regs[offset];
 	state &= ~mask;
 	state |= value;
-	writel(state, ioaddr); 
+	dpriv->scc_regs[offset] = state;
+	writel(state, dev->base_addr + SCC_REG_START(dpriv) + offset);
+}
+
+static void scc_writel(u32 bits, struct dscc4_dev_priv *dpriv,
+		       struct net_device *dev, int offset)
+{
+	/*
+	 * Thread-UNsafe.
+	 * As of 2002/02/16, there are no thread racing for access.
+	 */
+	dpriv->scc_regs[offset] = bits;
+	writel(bits, dev->base_addr + SCC_REG_START(dpriv) + offset);
+}
+
+static inline u32 scc_readl(struct dscc4_dev_priv *dpriv, int offset)
+{
+	return dpriv->scc_regs[offset];
+}
+
+static u32 scc_readl_star(struct dscc4_dev_priv *dpriv, struct net_device *dev)
+{
+	/* Cf errata DS5 p.4 */
+	readl(dev->base_addr + SCC_REG_START(dpriv) + STAR);
+	return readl(dev->base_addr + SCC_REG_START(dpriv) + STAR);
+}
+
+static inline void dscc4_do_tx(struct dscc4_dev_priv *dpriv,
+			       struct net_device *dev)
+{
+	dpriv->ltda = dpriv->tx_fd_dma +
+                      ((dpriv->tx_current-1)%TX_RING_SIZE)*sizeof(struct TxFD);
+	writel(dpriv->ltda, dev->base_addr + CH0LTDA + dpriv->dev_id*4);
+	/* Flush posted writes *NOW* */
+	readl(dev->base_addr + CH0LTDA + dpriv->dev_id*4);
+}
+
+static inline void dscc4_rx_update(struct dscc4_dev_priv *dpriv,
+				   struct net_device *dev)
+{
+	dpriv->lrda = dpriv->rx_fd_dma +
+		      ((dpriv->rx_dirty - 1)%RX_RING_SIZE)*sizeof(struct RxFD);
+	writel(dpriv->lrda, dev->base_addr + CH0LRDA + dpriv->dev_id*4);
+}
+
+static inline unsigned int dscc4_tx_done(struct dscc4_dev_priv *dpriv)
+{
+	return dpriv->tx_current == dpriv->tx_dirty;
+}
+
+static inline unsigned int dscc4_tx_quiescent(struct dscc4_dev_priv *dpriv,
+					      struct net_device *dev)
+{
+	return readl(dev->base_addr + CH0FTDA + dpriv->dev_id*4) == dpriv->ltda;
 }
 
-int state_check(u32 state, struct dscc4_dev_priv *dpriv, 
-	struct net_device *dev, const char *msg)
+int state_check(u32 state, struct dscc4_dev_priv *dpriv, struct net_device *dev,
+		const char *msg)
 {
-#ifdef DEBUG_PARANOIA
+	int ret = 0;
+
+	if (debug > 1) {
 	if (SOURCE_ID(state) != dpriv->dev_id) {
 		printk(KERN_DEBUG "%s (%s): Source Id=%d, state=%08x\n",
 		       dev->name, msg, SOURCE_ID(state), state );
-		return -1;
+			ret = -1;
 	}
 	if (state & 0x0df80c00) {
 		printk(KERN_DEBUG "%s (%s): state=%08x (UFO alert)\n",
 		       dev->name, msg, state);
-		return -1;
+			ret = -1;
 	}
-	return 0;
-#else
-	return 1;
-#endif
+	}
+	return ret;
 }
 
-void inline reset_TxFD(struct TxFD *tx_fd) {
-	/* FIXME: test with the last arg (size specification) = 0 */
-	tx_fd->state = FrameEnd | Hold | 0x00100000;
-	tx_fd->complete = 0x00000000;
+void dscc4_tx_print(struct net_device *dev, struct dscc4_dev_priv *dpriv,
+		    char *msg)
+{
+	printk(KERN_DEBUG "%s: tx_current=%02d tx_dirty=%02d (%s)\n",
+	       dev->name, dpriv->tx_current, dpriv->tx_dirty, msg);
 }
 
 static void dscc4_release_ring(struct dscc4_dev_priv *dpriv)
@@ -393,15 +452,13 @@
 	struct sk_buff **skbuff;
 	int i;
 
-	pci_free_consistent(pdev, TX_RING_SIZE*sizeof(struct TxFD), tx_fd, 
-			    dpriv->tx_fd_dma);
-	pci_free_consistent(pdev, RX_RING_SIZE*sizeof(struct RxFD), rx_fd, 
-			    dpriv->rx_fd_dma);
+	pci_free_consistent(pdev, TX_TOTAL_SIZE, tx_fd, dpriv->tx_fd_dma);
+	pci_free_consistent(pdev, RX_TOTAL_SIZE, rx_fd, dpriv->rx_fd_dma);
 
 	skbuff = dpriv->tx_skbuff;
 	for (i = 0; i < TX_RING_SIZE; i++) {
 		if (*skbuff) {
-			pci_unmap_single(pdev, tx_fd->data, (*skbuff)->len, 
+			pci_unmap_single(pdev, tx_fd->data, (*skbuff)->len,
 				PCI_DMA_TODEVICE);
 			dev_kfree_skb(*skbuff);
 		}
@@ -412,7 +469,7 @@
 	skbuff = dpriv->rx_skbuff;
 	for (i = 0; i < RX_RING_SIZE; i++) {
 		if (*skbuff) {
-			pci_unmap_single(pdev, rx_fd->data, (*skbuff)->len, 
+			pci_unmap_single(pdev, rx_fd->data, (*skbuff)->len,
 				PCI_DMA_FROMDEVICE);
 			dev_kfree_skb(*skbuff);
 		}
@@ -421,96 +478,145 @@
 	}
 }
 
-void inline try_get_rx_skb(struct dscc4_dev_priv *priv, int cur, struct net_device *dev)
+inline int try_get_rx_skb(struct dscc4_dev_priv *dpriv, struct net_device *dev)
 {
+	unsigned int dirty = dpriv->rx_dirty%RX_RING_SIZE;
+	struct RxFD *rx_fd = dpriv->rx_fd + dirty;
 	struct sk_buff *skb;
+	int ret = 0;
 
 	skb = dev_alloc_skb(RX_MAX(HDLC_MAX_MRU));
-	priv->rx_skbuff[cur] = skb;
-	if (!skb) {
-		priv->rx_fd[cur--].data = (u32) NULL;
-		priv->rx_fd[cur%RX_RING_SIZE].state1 |= Hold;
-		priv->rx_needs_refill++;
-		return;
-	}
+	dpriv->rx_skbuff[dirty] = skb;
+	if (skb) {
 	skb->dev = dev;
 	skb->protocol = htons(ETH_P_IP);
 	skb->mac.raw = skb->data;
-	priv->rx_fd[cur].data = pci_map_single(priv->pci_priv->pdev, skb->data,
+		rx_fd->data = pci_map_single(dpriv->pci_priv->pdev, skb->data,
 					       skb->len, PCI_DMA_FROMDEVICE);
+	} else {
+		rx_fd->data = (u32) NULL;
+		ret = -1;
+	}
+	return ret;
 }
 
 /*
  * IRQ/thread/whatever safe
  */
-static int dscc4_wait_ack_cec(u32 ioaddr, struct net_device *dev, char *msg)
+static int dscc4_wait_ack_cec(struct dscc4_dev_priv *dpriv,
+			      struct net_device *dev, char *msg)
 {
-	s16 i = 0;
+	s8 i = 0;
 
-	while (readl(ioaddr + STAR) & SccBusy) {
-		if (i++ < 0)  {
-			printk(KERN_ERR "%s: %s timeout\n", dev->name, msg);
-			return -1;
+	do {
+		if (!(scc_readl_star(dpriv, dev) & SccBusy)) {
+			printk(KERN_DEBUG "%s: %s ack (%d try)\n", dev->name,
+			       msg, i);
+			goto done;
 		}
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(10);
 		rmb();
-	}
-	printk(KERN_DEBUG "%s: %s ack (%d try)\n", dev->name, msg, i);
-	return 0;
+	} while (++i > 0);
+	printk(KERN_ERR "%s: %s timeout\n", dev->name, msg);
+done:
+	return (i >= 0) ? i : -EAGAIN;
 }
 
 static int dscc4_do_action(struct net_device *dev, char *msg)
 {
 	unsigned long ioaddr = dev->base_addr;
-	u32 state;
-	s16 i;
+	s16 i = 0;
 
 	writel(Action, ioaddr + GCMDR);
 	ioaddr += GSTAR;
-	for (i = 0; i >= 0; i++) {
-		state = readl(ioaddr);
-		if (state & Arf) {
+	do {
+		u32 state = readl(ioaddr);
+
+		if (state & ArAck) {
+			printk(KERN_DEBUG "%s: %s ack\n", dev->name, msg);
+			writel(ArAck, ioaddr);
+			goto done;
+		} else if (state & Arf) {
 			printk(KERN_ERR "%s: %s failed\n", dev->name, msg);
 			writel(Arf, ioaddr);
-			return -1;
-		} else if (state & ArAck) {
-			printk(KERN_DEBUG "%s: %s ack (%d try)\n",
-			       dev->name, msg, i);
-			writel(ArAck, ioaddr);
-			return 0;
-		}
+			i = -1;
+			goto done;
 	}
+		rmb();
+	} while (++i > 0);
 	printk(KERN_ERR "%s: %s timeout\n", dev->name, msg);
-	return -1;
+done:
+	return i;
 }
 
 static inline int dscc4_xpr_ack(struct dscc4_dev_priv *dpriv)
 {
 	int cur = dpriv->iqtx_current%IRQ_RING_SIZE;
-	s16 i = 0;
+	s8 i = 0;
 
 	do {
 		if (!(dpriv->flags & (NeedIDR | NeedIDT)) ||
 		    (dpriv->iqtx[cur] & Xpr))
 			break;
 		smp_rmb();
-	} while (i++ >= 0);
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(10);
+	} while (++i > 0);
 
-	return i;
+	return (i >= 0 ) ? i : -EAGAIN;
 }
 
-static inline void dscc4_rx_skb(struct dscc4_dev_priv *dpriv, int cur,
-	struct RxFD *rx_fd, struct net_device *dev)
+/* Requires protection against interrupt */
+static void dscc4_rx_reset(struct dscc4_dev_priv *dpriv, struct net_device *dev)
 {
-	struct net_device_stats *stats = &dev_to_hdlc(dev)->stats;
+	/* Cf errata DS5 p.6 */
+	writel(0x00000000, dev->base_addr + CH0LRDA + dpriv->dev_id*4);
+	scc_writel(~PowerUp & scc_readl(dpriv, CCR0), dpriv, dev, CCR0);
+	readl(dev->base_addr + CH0LRDA + dpriv->dev_id*4);
+	writel(MTFi|Rdr, dev->base_addr + dpriv->dev_id*0x0c + CH0CFG);
+	writel(Action, dev->base_addr + GCMDR);
+}
+
+static void dscc4_tx_reset(struct dscc4_dev_priv *dpriv, struct net_device *dev)
+{
+	u16 i = 0;
+
+	/* Cf errata DS5 p.7 */
+	scc_writel(~PowerUp & scc_readl(dpriv, CCR0), dpriv, dev, CCR0);
+	scc_writel(0x00050000, dpriv, dev, CCR2);
+	/*
+	 * Must be longer than the time required to fill the fifo.
+	 */
+	while (!dscc4_tx_quiescent(dpriv, dev) && ++i) {
+		udelay(1);
+		wmb();
+	}
+
+	writel(MTFi|Rdt, dev->base_addr + dpriv->dev_id*0x0c + CH0CFG);
+	if (dscc4_do_action(dev, "Rdt") < 0)
+		printk(KERN_ERR "%s: Tx reset failed\n", dev->name);
+}
+
+/* TODO: (ab)use this function to refill a completely depleted RX ring. */
+static inline void dscc4_rx_skb(struct dscc4_dev_priv *dpriv,
+				struct net_device *dev)
+{
+	struct RxFD *rx_fd = dpriv->rx_fd + dpriv->rx_current%RX_RING_SIZE;
+	struct net_device_stats *stats = &dpriv->hdlc.stats;
 	struct pci_dev *pdev = dpriv->pci_priv->pdev;
 	struct sk_buff *skb;
 	int pkt_len;
 
-	skb = dpriv->rx_skbuff[cur];
+	skb = dpriv->rx_skbuff[dpriv->rx_current++%RX_RING_SIZE];
+	if (!skb) {
+		printk(KERN_DEBUG "%s: skb=0 (%s)\n", dev->name, __FUNCTION__);
+		goto refill;
+	}
 	pkt_len = TO_SIZE(rx_fd->state2);
 	pci_dma_sync_single(pdev, rx_fd->data, pkt_len, PCI_DMA_FROMDEVICE);
-	if((skb->data[--pkt_len] & FrameOk) == FrameOk) {
-		pci_unmap_single(pdev, rx_fd->data, skb->len, PCI_DMA_FROMDEVICE);
+	pci_unmap_single(pdev, rx_fd->data, pkt_len, PCI_DMA_FROMDEVICE);
+	if ((skb->data[--pkt_len] & FrameOk) == FrameOk) {
 		stats->rx_packets++;
 		stats->rx_bytes += pkt_len;
 		skb->tail += pkt_len;
@@ -519,29 +625,29 @@
 			skb->protocol = htons(ETH_P_HDLC);
 		skb->dev->last_rx = jiffies;
 		netif_rx(skb);
-		try_get_rx_skb(dpriv, cur, dev);
 	} else {
-		if(skb->data[pkt_len] & FrameRdo)
+		if (skb->data[pkt_len] & FrameRdo)
 			stats->rx_fifo_errors++;
-		else if(!(skb->data[pkt_len] | ~FrameCrc))
+		else if (!(skb->data[pkt_len] | ~FrameCrc))
 			stats->rx_crc_errors++;
-		else if(!(skb->data[pkt_len] | ~(FrameVfr | FrameRab)))
+		else if (!(skb->data[pkt_len] | ~(FrameVfr | FrameRab)))
 			stats->rx_length_errors++;
 		else
 			stats->rx_errors++;
+		dev_kfree_skb_irq(skb);
 	}
-	rx_fd->state1 |= Hold;
+refill:
+	while ((dpriv->rx_dirty - dpriv->rx_current) % RX_RING_SIZE) {
+		if (try_get_rx_skb(dpriv, dev) < 0)
+			break;
+		dpriv->rx_dirty++;
+	}
+	dscc4_rx_update(dpriv, dev);
 	rx_fd->state2 = 0x00000000;
 	rx_fd->end = 0xbabeface;
-	if (!rx_fd->data)
-		return;
-	rx_fd--;
-	if (!cur)
-		rx_fd += RX_RING_SIZE;
-	rx_fd->state1 &= ~Hold;
 }
 
-static int __init dscc4_init_one (struct pci_dev *pdev,
+static int __init dscc4_init_one(struct pci_dev *pdev,
 				  const struct pci_device_id *ent)
 {
 	struct dscc4_pci_priv *priv;
@@ -569,7 +675,7 @@
 	ioaddr = (unsigned long)ioremap(pci_resource_start(pdev, 0),
 					pci_resource_len(pdev, 0));
 	if (!ioaddr) {
-		printk(KERN_ERR "%s: cannot remap MMIO region %lx @ %lx\n", 
+		printk(KERN_ERR "%s: cannot remap MMIO region %lx @ %lx\n",
 			DRV_NAME, pci_resource_len(pdev, 0),
 			pci_resource_start(pdev, 0));
 		goto err_out_free_mmio_region;
@@ -578,8 +684,8 @@
 	        pci_resource_start(pdev, 0),
 	        pci_resource_start(pdev, 1), pdev->irq);
 
-	/* No need for High PCI latency. Cf app. note. */
-	pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0x10);
+	/* Cf errata DS5 p.2 */
+	pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xf8);
 	pci_set_master(pdev);
 
 	if (dscc4_found1(pdev, ioaddr))
@@ -592,8 +698,8 @@
 		goto err_out_iounmap;
 	}
 
-	/* power up/little endian/dma core controlled via hold bit */
-	writel(0x00000000, ioaddr + GMODE);
+	/* power up/little endian/dma core controlled via lrda/ltda */
+	writel(0x00000001, ioaddr + GMODE);
 	/* Shared interrupt queue */
 	{
 		u32 bits;
@@ -612,8 +718,8 @@
 		goto err_out_free_irq;
 	writel(priv->iqcfg_dma, ioaddr + IQCFG);
 
-	/* 
-	 * SCC 0-3 private rx/tx irq structures 
+	/*
+	 * SCC 0-3 private rx/tx irq structures
 	 * IQRX/TXi needs to be set soon. Learned it the hard way...
 	 */
 	for (i = 0; i < dev_per_card; i++) {
@@ -650,17 +756,17 @@
 err_out_free_iqrx:
 	while (--i >= 0) {
 		dpriv = priv->root + i;
-		pci_free_consistent(pdev, IRQ_RING_SIZE*sizeof(u32), 
+		pci_free_consistent(pdev, IRQ_RING_SIZE*sizeof(u32),
 				    dpriv->iqrx, dpriv->iqrx_dma);
 	}
 	i = dev_per_card;
 err_out_free_iqtx:
 	while (--i >= 0) {
 		dpriv = priv->root + i;
-		pci_free_consistent(pdev, IRQ_RING_SIZE*sizeof(u32), 
+		pci_free_consistent(pdev, IRQ_RING_SIZE*sizeof(u32),
 				    dpriv->iqtx, dpriv->iqtx_dma);
 	}
-	pci_free_consistent(pdev, IRQ_RING_SIZE*sizeof(u32), priv->iqcfg, 
+	pci_free_consistent(pdev, IRQ_RING_SIZE*sizeof(u32), priv->iqcfg,
 			    priv->iqcfg_dma);
 err_out_free_irq:
 	free_irq(pdev->irq, priv->root);
@@ -676,32 +782,36 @@
 	return -ENODEV;
 };
 
-/* 
+/*
  * Let's hope the default values are decent enough to protect my
  * feet from the user's gun - Ueimor
  */
-static void dscc4_init_registers(u32 base_addr, int dev_id)
+static void dscc4_init_registers(struct dscc4_dev_priv *dpriv,
+				 struct net_device *dev)
 {
-	u32 ioaddr = base_addr + SCC_REG_START(dev_id);
-
-	writel(0x80001000, ioaddr + CCR0);
+	scc_writel(0x80001000, dpriv, dev, CCR0);
 
-	writel(LengthCheck | (HDLC_MAX_MRU >> 5), ioaddr + RLCR);
+	scc_writel(LengthCheck | (HDLC_MAX_MRU >> 5), dpriv, dev, RLCR);
 
-	/* no address recognition/crc-CCITT/cts enabled */
-	writel(0x021c8000, ioaddr + CCR1);
+	/*
+	 * No address recognition/crc-CCITT/cts enabled
+	 * Shared flags transmission disabled - cf errata DS5 p.11
+	 * Carrier detect disabled - cf errata p.14
+	 */
+	scc_writel(0x021c8000, dpriv, dev, CCR1);
 
-	/* crc not forwarded */
-	writel(0x00050008 & ~RxActivate, ioaddr + CCR2);
+	/* crc not forwarded - Cf errata DS5 p.11 */
+	scc_writel(0x00050008 & ~RxActivate, dpriv, dev, CCR2);
 	// crc forwarded
-	//writel(0x00250008 & ~RxActivate, ioaddr + CCR2);
+	//scc_writel(0x00250008 & ~RxActivate, dpriv, dev, CCR2);
 
 	/* Don't mask RDO. Ever. */
 #ifdef DSCC4_POLLING
-	writel(0xfffeef7f, ioaddr + IMR); /* Interrupt mask */
+	scc_writel(0xfffeef7f, dpriv, dev, IMR); /* Interrupt mask */
 #else
-	//writel(0xfffaef7f, ioaddr + IMR); /* Interrupt mask */
-	writel(0xfffaef7e, ioaddr + IMR); /* Interrupt mask */
+	//scc_writel(0xfffaef7f, dpriv, dev, IMR); /* Interrupt mask */
+	//scc_writel(0xfffaef7e, dpriv, dev, IMR); /* Interrupt mask */
+	scc_writel(0xfffa8f7a, dpriv, dev, IMR); /* Interrupt mask */
 #endif
 }
 
@@ -754,7 +864,7 @@
 	        }
 		hdlc->proto = IF_PROTO_HDLC;
 		SET_MODULE_OWNER(d);
-		dscc4_init_registers(ioaddr, i);
+		dscc4_init_registers(dpriv, d);
 		dpriv->parity = PARITY_CRC16_PR0_CCITT;
 		dpriv->encoding = ENCODING_NRZ;
 	}
@@ -775,54 +885,15 @@
 	return -1;
 };
 
+/* FIXME: get rid of the unneeded code */
 static void dscc4_timer(unsigned long data)
 {
 	struct net_device *dev = (struct net_device *)data;
-	struct dscc4_dev_priv *dpriv;
-	struct dscc4_pci_priv *ppriv;
-
-	dpriv = dscc4_priv(dev);
-	if (netif_queue_stopped(dev) && 
-	   ((jiffies - dev->trans_start) > TX_TIMEOUT)) {
-		ppriv = dpriv->pci_priv;
-		if (dpriv->iqtx[dpriv->iqtx_current%IRQ_RING_SIZE]) {
-			u32 flags;
-
-			printk(KERN_DEBUG "%s: pending events\n", dev->name);
-			dev->trans_start = jiffies;
-			spin_lock_irqsave(&ppriv->lock, flags);
-			dscc4_tx_irq(ppriv, dpriv);
-			spin_unlock_irqrestore(&ppriv->lock, flags);
-		} else {
-			struct TxFD *tx_fd;
-			struct sk_buff *skb;
-			int i,j;
+	struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
+//	struct dscc4_pci_priv *ppriv;
 
-			printk(KERN_DEBUG "%s: missing events\n", dev->name);
-			i = dpriv->tx_dirty%TX_RING_SIZE; 
-			j = dpriv->tx_current - dpriv->tx_dirty;
-			dev_to_hdlc(dev)->stats.tx_dropped += j;
-			while(j--) {
-				skb = dpriv->tx_skbuff[i];
-				tx_fd = dpriv->tx_fd + i;
-				if (skb) {
-					dpriv->tx_skbuff[i] = NULL;
-					pci_unmap_single(ppriv->pdev, tx_fd->data, skb->len,
-						 	 PCI_DMA_TODEVICE);
-					dev_kfree_skb_irq(skb);
-				} else 
-					printk(KERN_INFO "%s: hardware on drugs!\n", dev->name);
-				tx_fd->data = 0; /* DEBUG */
-				tx_fd->complete &= ~DataComplete;
-				i++;	
-				i %= TX_RING_SIZE; 
-			}
-			dpriv->tx_dirty = dpriv->tx_current;
-			dev->trans_start = jiffies;
-			netif_wake_queue(dev);
-			printk(KERN_DEBUG "%s: re-enabled\n", dev->name);	
-		}
-	}
+	goto done;
+done:
         dpriv->timer.expires = jiffies + TX_TIMEOUT;
         add_timer(&dpriv->timer);
 }
@@ -850,7 +921,6 @@
 	struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
 	hdlc_device *hdlc = &dpriv->hdlc;
 	struct dscc4_pci_priv *ppriv;
-	u32 ioaddr;
 	int ret = -EAGAIN;
 
 	if ((dscc4_loopback_check(dpriv) < 0) || !dev->hard_start_xmit)
@@ -863,11 +933,9 @@
 
 	ppriv = dpriv->pci_priv;
 
-	if (dscc4_init_ring(dev))
+	if ((ret = dscc4_init_ring(dev)))
 		goto err_out;
 
-	ioaddr = dev->base_addr + SCC_REG_START(dpriv->dev_id);
-
 	/* IDT+IDR during XPR */
 	dpriv->flags = NeedIDR | NeedIDT;
 
@@ -878,29 +946,34 @@
 	 * power-down mode or..." and CCR2.RAC = 1 are two different
 	 * situations.
 	 */
-	if (readl(ioaddr + STAR) & SccBusy) {
+	if (scc_readl_star(dpriv, dev) & SccBusy) {
 		printk(KERN_ERR "%s busy. Try later\n", dev->name);
+		ret = -EAGAIN;
 		goto err_free_ring;
-	}
+	} else
+		printk(KERN_INFO "%s: available. Good\n", dev->name);
 
-	/* Posted write is flushed in the busy-waiting loop */
-	writel(TxSccRes | RxSccRes, ioaddr + CMDR);
+	/* Posted write is flushed in the wait_ack loop */
+	scc_writel(TxSccRes | RxSccRes, dpriv, dev, CMDR);
 
-	if (dscc4_wait_ack_cec(ioaddr, dev, "Cec"))
+	if ((ret = dscc4_wait_ack_cec(dpriv, dev, "Cec")) < 0)
 		goto err_free_ring;
 
-	/* 
+	/*
 	 * I would expect XPR near CE completion (before ? after ?).
 	 * At worst, this code won't see a late XPR and people
-	 * will have to re-issue an ifconfig (this is harmless). 
-	 * WARNING, a really missing XPR usually means a hardware 
+	 * will have to re-issue an ifconfig (this is harmless).
+	 * WARNING, a really missing XPR usually means a hardware
 	 * reset is needed. Suggestions anyone ?
 	 */
-	if (dscc4_xpr_ack(dpriv) < 0) {
+	if ((ret = dscc4_xpr_ack(dpriv)) < 0) {
 		printk(KERN_ERR "%s: %s timeout\n", DRV_NAME, "XPR");
 		goto err_free_ring;
 	}
 	
+	if (debug > 2)
+		dscc4_tx_print(dev, dpriv, "Open");
+
 	netif_start_queue(dev);
 
         init_timer(&dpriv->timer);
@@ -931,70 +1004,55 @@
 static int dscc4_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
 	struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
-	struct dscc4_pci_priv *ppriv;
+	struct dscc4_pci_priv *ppriv = dpriv->pci_priv;
 	struct TxFD *tx_fd;
-	int cur, next;
+	int next;
 
-	ppriv = dpriv->pci_priv;
-	cur = dpriv->tx_current++%TX_RING_SIZE;
 	next = dpriv->tx_current%TX_RING_SIZE;
 	dpriv->tx_skbuff[next] = skb;
 	tx_fd = dpriv->tx_fd + next;
-	tx_fd->state = FrameEnd | Hold | TO_STATE(skb->len);
+	tx_fd->state = FrameEnd | TO_STATE(skb->len);
 	tx_fd->data = pci_map_single(ppriv->pdev, skb->data, skb->len,
 				     PCI_DMA_TODEVICE);
 	tx_fd->complete = 0x00000000;
-	mb(); // FIXME: suppress ?
+	tx_fd->jiffies = jiffies;
+	mb();
 
 #ifdef DSCC4_POLLING
 	spin_lock(&dpriv->lock);
-	while(dscc4_tx_poll(dpriv, dev));
+	while (dscc4_tx_poll(dpriv, dev));
 	spin_unlock(&dpriv->lock);
 #endif
-	/*
-	 * I know there's a window for a race in the following lines but
-	 * dscc4_timer will take good care of it. The chipset eats events
-	 * (especially the net_dev re-enabling ones) thus there is no
-	 * reason to try and be smart.
-	 */
-	if ((dpriv->tx_dirty + 16) < dpriv->tx_current) {
-			netif_stop_queue(dev);
-			dpriv->hi_expected = 2;
-	}
-	tx_fd = dpriv->tx_fd + cur;
-	tx_fd->state &= ~Hold;
-	mb(); // FIXME: suppress ?
-
-	/* 
-	 * One may avoid some pci transactions during intense TX periods.
-	 * Not sure it's worth the pain...
-	 */
-	writel((TxPollCmd << dpriv->dev_id) | NoAck, dev->base_addr + GCMDR);
+
 	dev->trans_start = jiffies;
+
+	if (debug > 2)
+		dscc4_tx_print(dev, dpriv, "Xmit");
+	/* To be cleaned(unsigned int)/optimized. Later, ok ? */
+	if (!((++dpriv->tx_current - dpriv->tx_dirty)%TX_RING_SIZE))
+		netif_stop_queue(dev);
+
+	if (dscc4_tx_quiescent(dpriv, dev))
+		dscc4_do_tx(dpriv, dev);
+
 	return 0;
 }
 
 static int dscc4_close(struct net_device *dev)
 {
 	struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
-	u32 ioaddr = dev->base_addr;
-	int dev_id;
 	hdlc_device *hdlc = dev_to_hdlc(dev);
+	unsigned long flags;
 
 	del_timer_sync(&dpriv->timer);
 	netif_stop_queue(dev);
 
-	dev_id = dpriv->dev_id;
+	spin_lock_irqsave(&dpriv->pci_priv->lock, flags);
+	dscc4_rx_reset(dpriv, dev);
+	spin_unlock_irqrestore(&dpriv->pci_priv->lock, flags);
+
+	dscc4_tx_reset(dpriv, dev);
 
-	writel(0x00050000, ioaddr + SCC_REG_START(dev_id) + CCR2);
-	writel(MTFi|Rdr|Rdt, ioaddr + CH0CFG + dev_id*0x0c); /* Reset Rx/Tx */
-	writel(0x00000001, ioaddr + GCMDR);
-	readl(ioaddr + GCMDR);
-
-	/* 
-	 * FIXME: wait for the command ack before returning the memory
-	 * structures to the kernel.
-	 */
 	hdlc_close(hdlc);
 	dscc4_release_ring(dpriv);
 
@@ -1016,6 +1074,7 @@
 static int dscc4_set_clock(struct net_device *dev, u32 *bps, u32 *state)
 {
 	struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
+	int ret = -1;
 	u32 brr;
 
 	*state &= ~Ccr0ClockMask;
@@ -1025,9 +1084,9 @@
 
 		xtal = dpriv->pci_priv->xtal_hz;
 		if (!xtal)
-			return -1;
+			goto done;
 		if (dscc4_check_clock_ability(dpriv->dev_id) < 0)
-			return -1;
+			goto done;
 		divider = xtal / *bps;
 		if (divider > BRR_DIVIDER_MAX) {
 			divider >>= 4;
@@ -1052,16 +1111,17 @@
 			divider <<= 4;
 		*bps = xtal / divider;
 	} else {
-		/* 
-		 * External clock - DTE 
-		 * "state" already reflects Clock mode 0a. 
-		 * Nothing more to be done 
+		/*
+		 * External clock - DTE
+		 * "state" already reflects Clock mode 0a.
+		 * Nothing more to be done
 		 */
 		brr = 0;
 	}
-	writel(brr, dev->base_addr + BRR + SCC_REG_START(dpriv->dev_id));
-
-	return 0;
+	scc_writel(brr, dpriv, dev, BRR);
+	ret = 0;
+done:
+	return ret;
 }
 
 static int dscc4_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
@@ -1085,12 +1145,12 @@
 		break;
 
 	case IF_IFACE_SYNC_SERIAL:
-		if(!capable(CAP_NET_ADMIN))
+		if (!capable(CAP_NET_ADMIN))
 			return -EPERM;
 
 		if (copy_from_user(&dpriv->settings, &line->sync, size))
 			return -EFAULT;
-		ret = dscc4_set_iface(dev);
+		ret = dscc4_set_iface(dpriv, dev);
 		break;
 
 	default:
@@ -1127,36 +1187,37 @@
 		return i;
 }
 
-static int dscc4_clock_setting(struct net_device *dev)
+static int dscc4_clock_setting(struct dscc4_dev_priv *dpriv,
+			       struct net_device *dev)
 {
-	struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
 	sync_serial_settings *settings = &dpriv->settings;
+	int ret = -EOPNOTSUPP;
 	u32 bps, state;
-	u32 ioaddr;
 
 	bps = settings->clock_rate;
-	ioaddr = dev->base_addr + CCR0 + SCC_REG_START(dpriv->dev_id);
-	state = readl(ioaddr);
-	if(dscc4_set_clock(dev, &bps, &state) < 0)
-		return -EOPNOTSUPP;
+	state = scc_readl(dpriv, CCR0);
+	if (dscc4_set_clock(dev, &bps, &state) < 0)
+		goto done;
 	if (bps) { /* DCE */
 		printk(KERN_DEBUG "%s: generated RxClk (DCE)\n", dev->name);
 		if (settings->clock_rate != bps) {
 			settings->clock_rate = bps;
-			printk(KERN_DEBUG "%s: clock adjusted from %08d to %08d \n",
+			printk(KERN_DEBUG "%s: clock adjusted (%08d -> %08d)\n",
 				dev->name, dpriv->settings.clock_rate, bps);
 		}
 	} else { /* DTE */
 		state = 0x80001000;
 		printk(KERN_DEBUG "%s: external RxClk (DTE)\n", dev->name);
 	}
-	writel(state, ioaddr); 
-	return 0;
+	scc_writel(state, dpriv, dev, CCR0);
+	ret = 0;
+done:
+	return ret;
 }
 
-static int dscc4_encoding_setting(struct net_device *dev)
+static int dscc4_encoding_setting(struct dscc4_dev_priv *dpriv,
+				  struct net_device *dev)
 {
-	struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
 	struct thingie encoding[] = {
 		{ ENCODING_NRZ,		0x00000000 },
 		{ ENCODING_NRZI,	0x00200000 },
@@ -1168,24 +1229,20 @@
 	int i, ret = 0;
 
 	i = dscc4_match(encoding, dpriv->encoding);
-	if (i >= 0) {
-		u32 ioaddr;
-
-		ioaddr = dev->base_addr + CCR0 + SCC_REG_START(dpriv->dev_id);
-		dscc4_patch_register(ioaddr, EncodingMask, encoding[i].bits);
-	} else
+	if (i >= 0)
+		scc_patchl(EncodingMask, encoding[i].bits, dpriv, dev, CCR0);
+	else
 		ret = -EOPNOTSUPP;
 	return ret;
 }
 
-static int dscc4_loopback_setting(struct net_device *dev)
+static int dscc4_loopback_setting(struct dscc4_dev_priv *dpriv,
+				  struct net_device *dev)
 {
-	struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
 	sync_serial_settings *settings = &dpriv->settings;
-	u32 ioaddr, state;
+	u32 state;
 
-	ioaddr = dev->base_addr + CCR1 + SCC_REG_START(dpriv->dev_id);
-	state = readl(ioaddr);
+	state = scc_readl(dpriv, CCR1);
 	if (settings->loopback) {
 		printk(KERN_DEBUG "%s: loopback\n", dev->name);
 		state |= 0x00000100;
@@ -1193,13 +1250,13 @@
 		printk(KERN_DEBUG "%s: normal\n", dev->name);
 		state &= ~0x00000100;
 	}
-	writel(state, ioaddr);
+	scc_writel(state, dpriv, dev, CCR1);
 	return 0;
 }
 
-static int dscc4_crc_setting(struct net_device *dev)
+static int dscc4_crc_setting(struct dscc4_dev_priv *dpriv,
+			     struct net_device *dev)
 {
-	struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
 	struct thingie crc[] = {
 		{ PARITY_CRC16_PR0_CCITT,	0x00000010 },
 		{ PARITY_CRC16_PR1_CCITT,	0x00000000 },
@@ -1209,20 +1266,17 @@
 	int i, ret = 0;
 
 	i = dscc4_match(crc, dpriv->parity);
-	if (i >= 0) {
-		u32 ioaddr;
-
-		ioaddr = dev->base_addr + CCR1 + SCC_REG_START(dpriv->dev_id);
-		dscc4_patch_register(ioaddr, CrcMask, crc[i].bits);
-	} else
+	if (i >= 0)
+		scc_patchl(CrcMask, crc[i].bits, dpriv, dev, CCR1);
+	else
 		ret = -EOPNOTSUPP;
 	return ret;
 }
 
-static int dscc4_set_iface(struct net_device *dev)
+static int dscc4_set_iface(struct dscc4_dev_priv *dpriv, struct net_device *dev)
 {
 	struct {
-		int (*action)(struct net_device *);
+		int (*action)(struct dscc4_dev_priv *, struct net_device *);
 	} *p, do_setting[] = {
 		{ dscc4_encoding_setting },
 		{ dscc4_clock_setting },
@@ -1233,7 +1287,7 @@
 	int ret = 0;
 
 	for (p = do_setting; p->action; p++) {
-		if ((ret = p->action(dev)) < 0)
+		if ((ret = p->action(dpriv, dev)) < 0)
 			break;
 	}
 	return ret;
@@ -1260,15 +1314,15 @@
 		goto out;
 	writel(state, ioaddr + GSTAR);
 
-	if (state & Arf) { 
+	if (state & Arf) {
 		printk(KERN_ERR "%s: failure (Arf). Harass the maintener\n",
 		       dev->name);
 		goto out;
 	}
 	state &= ~ArAck;
 	if (state & Cfg) {
-		if (debug)
-			printk(KERN_DEBUG "CfgIV\n");
+		if (debug > 0)
+			printk(KERN_DEBUG "%s: CfgIV\n", DRV_NAME);
 		if (priv->iqcfg[priv->cfg_cur++%IRQ_RING_SIZE] & Arf)
 			printk(KERN_ERR "%s: %s failed\n", dev->name, "CFG");
 		if (!(state &= ~Cfg))
@@ -1292,7 +1346,8 @@
 	spin_unlock_irqrestore(&priv->lock, flags);
 }
 
-static inline void dscc4_tx_irq(struct dscc4_pci_priv *ppriv, struct dscc4_dev_priv *dpriv)
+static inline void dscc4_tx_irq(struct dscc4_pci_priv *ppriv,
+				struct dscc4_dev_priv *dpriv)
 {
 	struct net_device *dev = hdlc_to_dev(&dpriv->hdlc);
 	u32 state;
@@ -1302,142 +1357,143 @@
 	cur = dpriv->iqtx_current%IRQ_RING_SIZE;
 	state = dpriv->iqtx[cur];
 	if (!state) {
-#ifdef DEBUG
-		if (loop > 1)
+		if ((debug > 1) && (loop > 1))
 			printk(KERN_DEBUG "%s: Tx irq loop=%d\n", dev->name, loop);
-#endif
 		if (loop && netif_queue_stopped(dev))
-			if ((dpriv->tx_dirty + 8) >= dpriv->tx_current)
+			if ((dpriv->tx_current - dpriv->tx_dirty)%TX_RING_SIZE)
 				netif_wake_queue(dev);
+
+		if (netif_running(dev) && dscc4_tx_quiescent(dpriv, dev) &&
+		    !dscc4_tx_done(dpriv))
+				dscc4_do_tx(dpriv, dev);
 		return;
 	}
 	loop++;
 	dpriv->iqtx[cur] = 0;
 	dpriv->iqtx_current++;
 
-	if (state_check(state, dpriv, dev, "Tx"))
+	if (state_check(state, dpriv, dev, "Tx") < 0)
 		return;
 
-	// state &= 0x0fffffff; /* Tracking the analyzed bits */
 	if (state & SccEvt) {
 		if (state & Alls) {
-			struct TxFD *tx_fd;
+			struct net_device_stats *stats = &dpriv->hdlc.stats;
 			struct sk_buff *skb;
+			struct TxFD *tx_fd;
 
+			if (debug > 2)
+				dscc4_tx_print(dev, dpriv, "Alls");
+			/*
+			 * DataComplete can't be trusted for Tx completion.
+			 * Cf errata DS5 p.8
+			 */
 			cur = dpriv->tx_dirty%TX_RING_SIZE;
 			tx_fd = dpriv->tx_fd + cur;
-
 			skb = dpriv->tx_skbuff[cur];
-
-			/* XXX: hideous kludge - to be removed "later" */
-			if (!skb) {
-				printk(KERN_ERR "%s: NULL skb in tx_irq at index %d\n", dev->name, cur);
-				goto try;
-			}
-			dpriv->tx_dirty++; // MUST be after skb test
-
-			/* Happens sometime. Don't know what triggers it */
-			if (!(tx_fd->complete & DataComplete)) {
-				u32 ioaddr, isr;
-
-				ioaddr = dev->base_addr + 
-					 SCC_REG_START(dpriv->dev_id) + ISR;
-				isr = readl(ioaddr);
-				printk(KERN_DEBUG 
-				       "%s: DataComplete=0 cur=%d isr=%08x state=%08x\n",
-				       dev->name, cur, isr, state);
-				writel(isr, ioaddr);
-				dev_to_hdlc(dev)->stats.tx_dropped++;
-			} else {
-				tx_fd->complete &= ~DataComplete;
+			if (skb) {
+				pci_unmap_single(ppriv->pdev, tx_fd->data,
+						 skb->len, PCI_DMA_TODEVICE);
 				if (tx_fd->state & FrameEnd) {
-					dev_to_hdlc(dev)->stats.tx_packets++;
-					dev_to_hdlc(dev)->stats.tx_bytes += skb->len;
+					stats->tx_packets++;
+					stats->tx_bytes += skb->len;
 				}
+				dev_kfree_skb_irq(skb);
+				dpriv->tx_skbuff[cur] = NULL;
+				++dpriv->tx_dirty;
+			} else {
+				if (debug > 1)
+					printk(KERN_ERR "%s Tx: NULL skb %d\n",
+						dev->name, cur);
 			}
+			/*
+			 * If the driver ends sending crap on the wire, it
+			 * will be way easier to diagnose than the (not so)
+			 * random freeze induced by null sized tx frames.
+			 */
+			tx_fd->data = tx_fd->next;
+			tx_fd->state = FrameEnd | TO_STATE(2*DUMMY_SKB_SIZE);
+			tx_fd->complete = 0x00000000;
+			tx_fd->jiffies = 0;
 
-			dpriv->tx_skbuff[cur] = NULL;
-			pci_unmap_single(ppriv->pdev, tx_fd->data, skb->len,
-					 PCI_DMA_TODEVICE);
-			tx_fd->data = 0; /* DEBUG */
-			dev_kfree_skb_irq(skb);
-{ // DEBUG
-			cur = (dpriv->tx_dirty-1)%TX_RING_SIZE;
-			tx_fd = dpriv->tx_fd + cur;
-			tx_fd->state |= Hold;
-}
 			if (!(state &= ~Alls))
 				goto try;
 		}
-		/* 
+		/*
 		 * Transmit Data Underrun
 		 */
 		if (state & Xdu) {
 			printk(KERN_ERR "%s: XDU. Ask maintainer\n", DRV_NAME);
-			dpriv->flags = NeedIDT; 
+			dpriv->flags = NeedIDT;
 			/* Tx reset */
-			writel(MTFi | Rdt, 
+			writel(MTFi | Rdt,
 			       dev->base_addr + 0x0c*dpriv->dev_id + CH0CFG);
-			writel(0x00000001, dev->base_addr + GCMDR);
+			writel(Action, dev->base_addr + GCMDR);
 			return;
 		}
+		if (state & Cts) {
+			printk(KERN_INFO "%s: CTS transition\n", dev->name);
+			if (!(state &= ~Cts)) /* DEBUG */
+				goto try;
+		}
 		if (state & Xmr) {
 			/* Frame needs to be sent again - FIXME */
-			//dscc4_start_xmit(dpriv->tx_skbuff[dpriv->tx_dirty], dev);
-			if (!(state &= ~0x00002000)) /* DEBUG */
+			printk(KERN_ERR "%s: Xmr. Ask maintainer\n", DRV_NAME);
+			if (!(state &= ~Xmr)) /* DEBUG */
 				goto try;
 		}
 		if (state & Xpr) {
-			unsigned long ioaddr = dev->base_addr;
-			unsigned long scc_offset;
-			u32 scc_addr;
+			u32 scc_addr, ring;
 
-			scc_offset = ioaddr + SCC_REG_START(dpriv->dev_id);
-			scc_addr = ioaddr + 0x0c*dpriv->dev_id;
-			if (readl(scc_offset + STAR) & SccBusy)
-				printk(KERN_DEBUG "%s busy. Fatal\n", 
-				       dev->name);
-			/*
-			 * Keep this order: IDT before IDR
-			 */
+			if (scc_readl_star(dpriv, dev) & SccBusy)
+				printk(KERN_ERR "%s busy. Fatal\n", dev->name);
+			scc_addr = dev->base_addr + 0x0c*dpriv->dev_id;
+			/* Keep this order: IDT before IDR */
 			if (dpriv->flags & NeedIDT) {
-				writel(MTFi | Idt, scc_addr + CH0CFG);
-				writel(dpriv->tx_fd_dma + 
+				if (debug > 2)
+					dscc4_tx_print(dev, dpriv, "Xpr");
+				ring = dpriv->tx_fd_dma +
 				       (dpriv->tx_dirty%TX_RING_SIZE)*
-				       sizeof(struct TxFD), scc_addr + CH0BTDA);
-				if(dscc4_do_action(dev, "IDT"))
+				       sizeof(struct TxFD);
+				writel(ring, scc_addr + CH0BTDA);
+				dscc4_do_tx(dpriv, dev);
+				writel(MTFi | Idt, scc_addr + CH0CFG);
+				if (dscc4_do_action(dev, "IDT") < 0)
 					goto err_xpr;
 				dpriv->flags &= ~NeedIDT;
-				mb();
 			}
 			if (dpriv->flags & NeedIDR) {
-				writel(MTFi | Idr, scc_addr + CH0CFG);
-				writel(dpriv->rx_fd_dma + 
+				ring = dpriv->rx_fd_dma +
 				       (dpriv->rx_current%RX_RING_SIZE)*
-				       sizeof(struct RxFD), scc_addr + CH0BRDA);
-				if(dscc4_do_action(dev, "IDR"))
+				       sizeof(struct RxFD);
+				writel(ring, scc_addr + CH0BRDA);
+				dscc4_rx_update(dpriv, dev);
+				writel(MTFi | Idr, scc_addr + CH0CFG);
+				if (dscc4_do_action(dev, "IDR") < 0)
 					goto err_xpr;
 				dpriv->flags &= ~NeedIDR;
-				mb();
+				smp_wmb();
 				/* Activate receiver and misc */
-				writel(0x08050008, scc_offset + CCR2);
+				scc_writel(0x08050008, dpriv, dev, CCR2);
 			}
 		err_xpr:
 			if (!(state &= ~Xpr))
 				goto try;
 		}
+		if (state & Cd) {
+			printk(KERN_INFO "%s: CD transition\n", dev->name);
+			if (!(state &= ~Cd)) /* DEBUG */
+				goto try;
+		}
 	} else { /* ! SccEvt */
 		if (state & Hi) {
 #ifdef DSCC4_POLLING
-			while(!dscc4_tx_poll(dpriv, dev));
+			while (!dscc4_tx_poll(dpriv, dev));
 #endif
+			printk(KERN_INFO "%s: Tx Hi\n", dev->name);
 			state &= ~Hi;
 		}
-		/*
-		 * FIXME: it may be avoided. Re-re-re-read the manual.
-		 */
 		if (state & Err) {
-			printk(KERN_ERR "%s (Tx): ERR\n", dev->name);
+			printk(KERN_INFO "%s: Tx ERR\n", dev->name);
 			dev_to_hdlc(dev)->stats.tx_errors++;
 			state &= ~Err;
 		}
@@ -1460,7 +1516,7 @@
 	dpriv->iqrx[cur] = 0;
 	dpriv->iqrx_current++;
 
-	if (state_check(state, dpriv, dev, "Tx"))
+	if (state_check(state, dpriv, dev, "Rx") < 0)
 		return;
 
 	if (!(state & SccEvt)){
@@ -1468,29 +1524,29 @@
 
 		state &= 0x00ffffff;
 		if (state & Err) { /* Hold or reset */
-			printk(KERN_DEBUG "%s (Rx): ERR\n", dev->name);
-			cur = dpriv->rx_current;
+			printk(KERN_DEBUG "%s: Rx ERR\n", dev->name);
+			cur = dpriv->rx_current%RX_RING_SIZE;
 			rx_fd = dpriv->rx_fd + cur;
 			/*
-			 * Presume we're not facing a DMAC receiver reset. 
-			 * As We use the rx size-filtering feature of the 
-			 * DSCC4, the beginning of a new frame is waiting in 
-			 * the rx fifo. I bet a Receive Data Overflow will 
+			 * Presume we're not facing a DMAC receiver reset.
+			 * As We use the rx size-filtering feature of the
+			 * DSCC4, the beginning of a new frame is waiting in
+			 * the rx fifo. I bet a Receive Data Overflow will
 			 * happen most of time but let's try and avoid it.
 			 * Btw (as for RDO) if one experiences ERR whereas
-			 * the system looks rather idle, there may be a 
+			 * the system looks rather idle, there may be a
 			 * problem with latency. In this case, increasing
 			 * RX_RING_SIZE may help.
 			 */
-			while (dpriv->rx_needs_refill) {
-				while(!(rx_fd->state1 & Hold)) {
+			//while (dpriv->rx_needs_refill) {
+				while (!(rx_fd->state1 & Hold)) {
 					rx_fd++;
 					cur++;
 					if (!(cur = cur%RX_RING_SIZE))
 						rx_fd = dpriv->rx_fd;
 				}
-				dpriv->rx_needs_refill--;
-				try_get_rx_skb(dpriv, cur, dev);
+				//dpriv->rx_needs_refill--;
+				try_get_rx_skb(dpriv, dev);
 				if (!rx_fd->data)
 					goto try;
 				rx_fd->state1 &= ~Hold;
@@ -1500,67 +1556,67 @@
 			goto try;
 		}
 		if (state & Fi) {
-			cur = dpriv->rx_current%RX_RING_SIZE;
-			rx_fd = dpriv->rx_fd + cur;
-			dscc4_rx_skb(dpriv, cur, rx_fd, dev);
-			dpriv->rx_current++;
+			dscc4_rx_skb(dpriv, dev);
 			goto try;
 		}
 		if (state & Hi ) { /* HI bit */
+			printk(KERN_INFO "%s: Rx Hi\n", dev->name);
 			state &= ~Hi;
 			goto try;
 		}
-	} else { /* ! SccEvt */
-#ifdef DEBUG_PARANOIA
+	} else { /* SccEvt */
+		if (debug > 1) {
+			//FIXME: verifier la presence de tous les evenements
 		static struct {
 			u32 mask;
 			const char *irq_name;
 		} evts[] = {
 			{ 0x00008000, "TIN"},
-			{ 0x00004000, "CSC"},
 			{ 0x00000020, "RSC"},
 			{ 0x00000010, "PCE"},
 			{ 0x00000008, "PLLA"},
-			{ 0x00000004, "CDSC"},
 			{ 0, NULL}
 		}, *evt;
-#endif /* DEBUG_PARANOIA */
-		state &= 0x00ffffff;
-#ifdef DEBUG_PARANOIA
+
 		for (evt = evts; evt->irq_name; evt++) {
 			if (state & evt->mask) {
-				printk(KERN_DEBUG "%s: %s\n", dev->name, 
-					evt->irq_name);
+					printk(KERN_DEBUG "%s: %s\n",
+						dev->name, evt->irq_name);
 				if (!(state &= ~evt->mask))
 					goto try;
 			}
 		}
-#endif /* DEBUG_PARANOIA */
+		} else {
+			if (!(state &= ~0x0000c03c))
+				goto try;
+		}
+		if (state & Cts) {
+			printk(KERN_INFO "%s: CTS transition\n", dev->name);
+			if (!(state &= ~Cts)) /* DEBUG */
+				goto try;
+		}
 		/*
 		 * Receive Data Overflow (FIXME: fscked)
 		 */
 		if (state & Rdo) {
-			u32 ioaddr, scc_offset, scc_addr;
 			struct RxFD *rx_fd;
+			u32 scc_addr;
 			int cur;
 
 			//if (debug)
 			//	dscc4_rx_dump(dpriv);
-			ioaddr = dev->base_addr;
-			scc_addr = ioaddr + 0x0c*dpriv->dev_id;
-			scc_offset = ioaddr + SCC_REG_START(dpriv->dev_id);
+			scc_addr = dev->base_addr + 0x0c*dpriv->dev_id;
 
-			writel(readl(scc_offset + CCR2) & ~RxActivate, 
-			       scc_offset + CCR2);
+			scc_patchl(RxActivate, 0, dpriv, dev, CCR2);
 			/*
 			 * This has no effect. Why ?
 			 * ORed with TxSccRes, one sees the CFG ack (for
 			 * the TX part only).
-			 */	
-			writel(RxSccRes, scc_offset + CMDR);
+			 */
+			scc_writel(RxSccRes, dpriv, dev, CMDR);
 			dpriv->flags |= RdoSet;
 
-			/* 
+			/*
 			 * Let's try and save something in the received data.
 			 * rx_current must be incremented at least once to
 			 * avoid HOLD in the BRDA-to-be-pointed desc.
@@ -1575,47 +1631,43 @@
 					rx_fd->state1 |= Hold;
 					rx_fd->state2 = 0x00000000;
 					rx_fd->end = 0xbabeface;
-				} else 
-					dscc4_rx_skb(dpriv, cur, rx_fd, dev);
+				} else
+					dscc4_rx_skb(dpriv, dev);
 			} while (1);
 
-			if (debug) {
+			if (debug > 0) {
 				if (dpriv->flags & RdoSet)
-					printk(KERN_DEBUG 
+					printk(KERN_DEBUG
 					       "%s: no RDO in Rx data\n", DRV_NAME);
 			}
 #ifdef DSCC4_RDO_EXPERIMENTAL_RECOVERY
 			/*
 			 * FIXME: must the reset be this violent ?
 			 */
-			writel(dpriv->rx_fd_dma + 
+#warning "FIXME: CH0BRDA"
+			writel(dpriv->rx_fd_dma +
 			       (dpriv->rx_current%RX_RING_SIZE)*
 			       sizeof(struct RxFD), scc_addr + CH0BRDA);
 			writel(MTFi|Rdr|Idr, scc_addr + CH0CFG);
-			if(dscc4_do_action(dev, "RDR")) {
+			if (dscc4_do_action(dev, "RDR") < 0) {
 				printk(KERN_ERR "%s: RDO recovery failed(%s)\n",
 				       dev->name, "RDR");
 				goto rdo_end;
 			}
 			writel(MTFi|Idr, scc_addr + CH0CFG);
-			if(dscc4_do_action(dev, "IDR")) {
+			if (dscc4_do_action(dev, "IDR") < 0) {
 				printk(KERN_ERR "%s: RDO recovery failed(%s)\n",
 				       dev->name, "IDR");
 				goto rdo_end;
 			}
 		rdo_end:
 #endif
-			writel(readl(scc_offset + CCR2) | RxActivate, 
-			       scc_offset + CCR2);
+			scc_patchl(0, RxActivate, dpriv, dev, CCR2);
 			goto try;
 		}
-		/* These will be used later */
-		if (state & Rfs) {
-			if (!(state &= ~Rfs))
-				goto try;
-		}
-		if (state & Rfo) {
-			if (!(state &= ~Rfo))
+		if (state & Cd) {
+			printk(KERN_INFO "%s: CD transition\n", dev->name);
+			if (!(state &= ~Cd)) /* DEBUG */
 				goto try;
 		}
 		if (state & Flex) {
@@ -1626,85 +1678,91 @@
 	}
 }
 
+/*
+ * I had expected the following to work for the first descriptor
+ * (tx_fd->state = 0xc0000000)
+ * - Hold=1 (don't try and branch to the next descripto);
+ * - No=0 (I want an empty data section, i.e. size=0);
+ * - Fe=1 (required by No=0 or we got an Err irq and must reset).
+ * It failed and locked solid. Thus the introduction of a dummy skb.
+ * Problem is acknowledged in errata sheet DS5. Joy :o/
+ */
+struct sk_buff *dscc4_init_dummy_skb(struct dscc4_dev_priv *dpriv)
+{
+	struct sk_buff *skb;
+
+	skb = dev_alloc_skb(DUMMY_SKB_SIZE);
+	if (skb) {
+		int last = dpriv->tx_dirty%TX_RING_SIZE;
+		struct TxFD *tx_fd = dpriv->tx_fd + last;
+
+		skb->len = DUMMY_SKB_SIZE;
+		memcpy(skb->data, version, strlen(version)%DUMMY_SKB_SIZE);
+		tx_fd->state = FrameEnd | TO_STATE(DUMMY_SKB_SIZE);
+		tx_fd->data = pci_map_single(dpriv->pci_priv->pdev, skb->data,
+					     DUMMY_SKB_SIZE, PCI_DMA_TODEVICE);
+		dpriv->tx_skbuff[last] = skb;
+	}
+	return skb;
+}
+
 static int dscc4_init_ring(struct net_device *dev)
 {
 	struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
+	struct pci_dev *pdev = dpriv->pci_priv->pdev;
 	struct TxFD *tx_fd;
 	struct RxFD *rx_fd;
+	void *ring;
 	int i;
 
-	tx_fd = (struct TxFD *) pci_alloc_consistent(dpriv->pci_priv->pdev,
-		TX_RING_SIZE*sizeof(struct TxFD), &dpriv->tx_fd_dma);
-	if (!tx_fd)
+	ring = pci_alloc_consistent(pdev, RX_TOTAL_SIZE, &dpriv->rx_fd_dma);
+	if (!ring)
 		goto err_out;
-	rx_fd = (struct RxFD *) pci_alloc_consistent(dpriv->pci_priv->pdev,
-		RX_RING_SIZE*sizeof(struct RxFD), &dpriv->rx_fd_dma);
-	if (!rx_fd)
-		goto err_free_dma_tx;
+	dpriv->rx_fd = rx_fd = (struct RxFD *) ring;
 
-	dpriv->tx_fd = tx_fd;
-	dpriv->rx_fd = rx_fd;
-	dpriv->rx_current = 0;
-	dpriv->tx_current = 0;
-	dpriv->tx_dirty = 0;
-
-	/* the dma core of the dscc4 will be locked on the first desc */
-	for (i = 0; i < TX_RING_SIZE; ) {
-		reset_TxFD(tx_fd);
+	ring = pci_alloc_consistent(pdev, TX_TOTAL_SIZE, &dpriv->tx_fd_dma);
+	if (!ring)
+		goto err_free_dma_rx;
+	dpriv->tx_fd = tx_fd = (struct TxFD *) ring;
+
+	memset(dpriv->tx_skbuff, 0, sizeof(struct sk_buff *)*TX_RING_SIZE);
+	dpriv->tx_dirty = 0xffffffff;
+	i = dpriv->tx_current = 0;
+	do {
+		tx_fd->state = FrameEnd | TO_STATE(2*DUMMY_SKB_SIZE);
+		tx_fd->complete = 0x00000000;
 	        /* FIXME: NULL should be ok - to be tried */
 	        tx_fd->data = dpriv->tx_fd_dma;
-	        dpriv->tx_skbuff[i] = NULL;
-		i++;
-		tx_fd->next = (u32)(dpriv->tx_fd_dma + i*sizeof(struct TxFD));
-		tx_fd++;
-	}
-	(--tx_fd)->next = (u32)dpriv->tx_fd_dma;
-{
-	/*
-	 * XXX: I would expect the following to work for the first descriptor
-	 * (tx_fd->state = 0xc0000000)
-	 * - Hold=1 (don't try and branch to the next descripto);
-	 * - No=0 (I want an empty data section, i.e. size=0);
-	 * - Fe=1 (required by No=0 or we got an Err irq and must reset).
-	 * Alas, it fails (and locks solid). Thus the introduction of a dummy
-	 * skb to avoid No=0 (choose one: Ugly [ ] Tasteless [ ] VMS [ ]).
-	 * 2002/01: errata sheet acknowledges the problem [X].
-	 */
-	struct sk_buff *skb;
+		(tx_fd++)->next = (u32)(dpriv->tx_fd_dma +
+					(++i%TX_RING_SIZE)*sizeof(*tx_fd));
+	} while (i < TX_RING_SIZE);
 
-	skb = dev_alloc_skb(32);
-	if (!skb)
+	if (dscc4_init_dummy_skb(dpriv) < 0)
 		goto err_free_dma_tx;
-	skb->len = 32;
-	memset(skb->data, 0xaa, 16);
-	tx_fd -= (TX_RING_SIZE - 1);
-	tx_fd->state = 0xc0000000;
-	tx_fd->state |= ((u32)(skb->len & TxSizeMax)) << 16;
-	tx_fd->data = pci_map_single(dpriv->pci_priv->pdev, skb->data, 
-				     skb->len, PCI_DMA_TODEVICE);
-	dpriv->tx_skbuff[0] = skb;
-}
-	for (i = 0; i < RX_RING_SIZE; ) {
+
+	memset(dpriv->rx_skbuff, 0, sizeof(struct sk_buff *)*RX_RING_SIZE);
+	i = dpriv->rx_dirty = dpriv->rx_current = 0;
+	do {
 		/* size set by the host. Multiple of 4 bytes please */
-	        rx_fd->state1 = HiDesc; /* Hi, no Hold */
+	        rx_fd->state1 = HiDesc;
 	        rx_fd->state2 = 0x00000000;
 	        rx_fd->end = 0xbabeface;
 	        rx_fd->state1 |= (RX_MAX(HDLC_MAX_MRU) << 16);
-		try_get_rx_skb(dpriv, i, dev);
-		i++;
-		rx_fd->next = (u32)(dpriv->rx_fd_dma + i*sizeof(struct RxFD));
-		rx_fd++;
-	}
-	(--rx_fd)->next = (u32)dpriv->rx_fd_dma;
-	rx_fd->state1 |= 0x40000000; /* Hold */
+		// FIXME: return value verifiee mais traitement suspect
+		if (try_get_rx_skb(dpriv, dev) >= 0)
+			dpriv->rx_dirty++;
+		(rx_fd++)->next = (u32)(dpriv->rx_fd_dma +
+					(++i%RX_RING_SIZE)*sizeof(*rx_fd));
+	} while (i < RX_RING_SIZE);
 
 	return 0;
 
 err_free_dma_tx:
-	pci_free_consistent(dpriv->pci_priv->pdev, TX_RING_SIZE*sizeof(*tx_fd),
-			    tx_fd, dpriv->tx_fd_dma);
+	pci_free_consistent(pdev, TX_TOTAL_SIZE, ring, dpriv->tx_fd_dma);
+err_free_dma_rx:
+	pci_free_consistent(pdev, RX_TOTAL_SIZE, rx_fd, dpriv->rx_fd_dma);
 err_out:
-	return -1;
+	return -ENOMEM;
 }
 
 static void __exit dscc4_remove_one(struct pci_dev *pdev)
@@ -1719,7 +1777,7 @@
 
 	ioaddr = hdlc_to_dev(&root->hdlc)->base_addr;
 	free_irq(pdev->irq, root);
-	pci_free_consistent(pdev, IRQ_RING_SIZE*sizeof(u32), ppriv->iqcfg, 
+	pci_free_consistent(pdev, IRQ_RING_SIZE*sizeof(u32), ppriv->iqcfg,
 			    ppriv->iqcfg_dma);
 	for (i = 0; i < dev_per_card; i++) {
 		struct dscc4_dev_priv *dpriv = root + i;
@@ -1727,9 +1785,9 @@
 
 		unregister_hdlc_device(hdlc);
 
-		pci_free_consistent(pdev, IRQ_RING_SIZE*sizeof(u32), 
+		pci_free_consistent(pdev, IRQ_RING_SIZE*sizeof(u32),
 				    dpriv->iqrx, dpriv->iqrx_dma);
-		pci_free_consistent(pdev, IRQ_RING_SIZE*sizeof(u32), 
+		pci_free_consistent(pdev, IRQ_RING_SIZE*sizeof(u32),
 				    dpriv->iqtx, dpriv->iqtx_dma);
 	}
 
@@ -1745,7 +1803,7 @@
 			   pci_resource_len(pdev, 0));
 }
 
-static int dscc4_hdlc_attach(hdlc_device *hdlc, unsigned short encoding, 
+static int dscc4_hdlc_attach(hdlc_device *hdlc, unsigned short encoding,
 	unsigned short parity)
 {
 	struct net_device *dev = hdlc_to_dev(hdlc);
@@ -1769,6 +1827,17 @@
         dpriv->parity = parity;
 	return 0;
 }
+
+static int __init dscc4_setup(char *str)
+{
+	int *args[] = { &debug, &quartz, NULL }, **p = args;
+
+	while (*p && (get_option(&str, *p) == 2))
+		p++;
+	return 1;
+}
+
+__setup("dscc4.setup=", dscc4_setup);
 
 static struct pci_device_id dscc4_pci_tbl[] __devinitdata = {
 	{ PCI_VENDOR_ID_SIEMENS, PCI_DEVICE_ID_SIEMENS_DSCC4,
diff -Nru a/drivers/parport/Makefile b/drivers/parport/Makefile
--- a/drivers/parport/Makefile	Mon Apr 22 15:32:18 2002
+++ b/drivers/parport/Makefile	Mon Apr 22 15:32:18 2002
@@ -13,7 +13,6 @@
 
 export-objs	:= init.o parport_pc.o
 
-list-multi	:= parport.o
 parport-objs	:= share.o ieee1284.o ieee1284_ops.o init.o procfs.o
 
 ifeq ($(CONFIG_PARPORT_1284),y)
@@ -23,7 +22,7 @@
 obj-$(CONFIG_PARPORT)		+= parport.o
 obj-$(CONFIG_PARPORT_PC)	+= parport_pc.o
 obj-$(CONFIG_PARPORT_SERIAL)	+= parport_serial.o
-obj-$(CONFIG_PARPORT_PC_PCMCIA)+= parport_cs.o
+obj-$(CONFIG_PARPORT_PC_PCMCIA) += parport_cs.o
 obj-$(CONFIG_PARPORT_AMIGA)	+= parport_amiga.o
 obj-$(CONFIG_PARPORT_MFC3)	+= parport_mfc3.o
 obj-$(CONFIG_PARPORT_ATARI)	+= parport_atari.o
@@ -31,6 +30,3 @@
 obj-$(CONFIG_PARPORT_GSC)	+= parport_gsc.o
 
 include $(TOPDIR)/Rules.make
-
-parport.o: $(parport-objs)
-	$(LD) -r -o $@ $(parport-objs)
diff -Nru a/drivers/pci/pci.ids b/drivers/pci/pci.ids
--- a/drivers/pci/pci.ids	Mon Apr 22 15:32:18 2002
+++ b/drivers/pci/pci.ids	Mon Apr 22 15:32:18 2002
@@ -931,6 +931,9 @@
 	121a  NetServer SMIC Controller
 	121b  NetServer Legacy COM Port Decoder
 	121c  NetServer PCI COM Port Decoder
+	1229  zx1 System Bus Adapter
+	122a  zx1 I/O Controller
+	122e  zx1 Local Bus Adapter
 	2910  E2910A
 	2925  E2925A
 103e  Solliday Engineering
diff -Nru a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile
--- a/drivers/pcmcia/Makefile	Mon Apr 22 15:32:19 2002
+++ b/drivers/pcmcia/Makefile	Mon Apr 22 15:32:19 2002
@@ -1,19 +1,11 @@
 #
 # Makefile for the kernel pcmcia subsystem (c/o David Hinds)
 #
-# Note! Dependencies are done automagically by 'make dep', which also
-# removes any old dependencies. DON'T put your own dependencies here
-# unless it's something special (ie not a .c file).
-#
-# Note 2! The CFLAGS definitions are now inherited from the
-# parent makes..
 
 O_TARGET := pcmcia.o
 
 export-objs := ds.o cs.o yenta.o pci_socket.o
 
-list-multi := pcmcia_core.o yenta_socket.o
-
 yenta_socket-objs := pci_socket.o yenta.o
 pcmcia_core-objs := cistpl.o rsrc_mgr.o bulkmem.o cs.o
 
@@ -79,16 +71,8 @@
 sa1100_cs-objs-$(CONFIG_SA1100_SHANNON)		+= sa1100_shannon.o
 sa1100_cs-objs-$(CONFIG_SA1100_SIMPAD)		+= sa1100_simpad.o
 sa1100_cs-objs-$(CONFIG_SA1100_STORK)		+= sa1100_stork.o
+sa1100_cs-objs-$(CONFIG_SA1100_TRIZEPS) 	+= sa1100_trizeps.o
 sa1100_cs-objs-$(CONFIG_SA1100_XP860)		+= sa1100_xp860.o sa1111_generic.o
 sa1100_cs-objs-$(CONFIG_SA1100_YOPY)		+= sa1100_yopy.o
 
 include $(TOPDIR)/Rules.make
-
-pcmcia_core.o:  $(pcmcia_core-objs)
-	$(LD) $(LD_RFLAG) -r -o $@ $(pcmcia_core-objs)
-
-sa1100_cs.o: $(sa1100_cs-objs-y)
-	$(LD) -r -o $@ $(sort $(sa1100_cs-objs-y))
-
-yenta_socket.o: $(yenta_socket-objs)
-	$(LD) $(LD_RFLAG) -r -o $@ $(yenta_socket-objs)
diff -Nru a/drivers/pcmcia/sa1100.h b/drivers/pcmcia/sa1100.h
--- a/drivers/pcmcia/sa1100.h	Mon Apr 22 15:32:18 2002
+++ b/drivers/pcmcia/sa1100.h	Mon Apr 22 15:32:18 2002
@@ -237,6 +237,9 @@
 extern int pcmcia_system3_init(void);
 extern void pcmcia_system3_exit(void);
 
+extern int pcmcia_trizeps_init(void);
+extern void pcmcia_trizeps_exit(void);
+
 extern int pcmcia_xp860_init(void);
 extern void pcmcia_xp860_exit(void);
 
diff -Nru a/drivers/pcmcia/sa1100_generic.c b/drivers/pcmcia/sa1100_generic.c
--- a/drivers/pcmcia/sa1100_generic.c	Mon Apr 22 15:32:18 2002
+++ b/drivers/pcmcia/sa1100_generic.c	Mon Apr 22 15:32:18 2002
@@ -1180,6 +1180,9 @@
 #ifdef CONFIG_SA1100_STORK
 	pcmcia_stork_init();
 #endif
+#ifdef CONFIG_SA1100_TRIZEPS
+	pcmcia_trizeps_init();
+#endif
 #ifdef CONFIG_SA1100_XP860
 	pcmcia_xp860_init();
 #endif
diff -Nru a/drivers/pcmcia/sa1100_trizeps.c b/drivers/pcmcia/sa1100_trizeps.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/pcmcia/sa1100_trizeps.c	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,214 @@
+/*
+ * drivers/pcmcia/sa1100_trizeps.c
+ *
+ * PCMCIA implementation routines for Trizeps
+ *
+ * Authors:
+ * Andreas Hofer <ho@dsa-ac.de>,
+ * Peter Lueg <pl@dsa-ac.de>,
+ * Guennadi Liakhovetski <gl@dsa-ac.de>
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+
+#include <asm/hardware.h>   // included trizeps.h
+#include <asm/system.h>
+#include <asm/irq.h>
+#include "sa1100_generic.h"
+
+#define NUMBER_OF_TRIZEPS_PCMCIA_SLOTS 1
+/**
+ *
+ *
+ ******************************************************/
+static int trizeps_pcmcia_init(struct pcmcia_init *init)
+{
+	int res;
+
+	/* Enable CF bus: */
+	TRIZEPS_BCR_clear(TRIZEPS_BCR1, TRIZEPS_nPCM_ENA_REG);
+
+	/* All those are inputs */
+	GPDR &= ~((GPIO_GPIO(TRIZEPS_GPIO_PCMCIA_CD0))
+		    | (GPIO_GPIO(TRIZEPS_GPIO_PCMCIA_IRQ0)));
+
+	/* Set transition detect */
+//	set_irq_type(SA1100_GPIO_TO_IRQ(GPIO_TRIZEPS_PCMCIA_CD0), IRQT_BOTHEDGE);
+	set_irq_type(TRIZEPS_IRQ_PCMCIA_IRQ0, IRQT_FALLING);
+
+	/* Register SOCKET interrupts */
+	/* WHY? */
+	set_irq_type(TRIZEPS_IRQ_PCMCIA_CD0, IRQT_NOEDGE);
+	res = request_irq( TRIZEPS_IRQ_PCMCIA_CD0, init->handler, SA_INTERRUPT, "PCMCIA_CD0", NULL );
+	if( res < 0 ) goto irq_err;
+
+	//MECR = 0x00060006; // Initialised on trizeps init
+
+	// return=sa1100_pcmcia_socket_count (sa1100_generic.c)
+	//        -> number of PCMCIA Slots
+	// Slot 0 -> Trizeps PCMCIA
+	// Slot 1 -> Trizeps ISA-Bus
+	return NUMBER_OF_TRIZEPS_PCMCIA_SLOTS;
+
+ irq_err:
+	printk( KERN_ERR __FUNCTION__ ": PCMCIA Request for IRQ %u failed\n", TRIZEPS_IRQ_PCMCIA_CD0 );
+	return -1;
+}
+
+/**
+ *
+ *
+ ******************************************************/
+static int trizeps_pcmcia_shutdown(void)
+{
+	printk(">>>>>PCMCIA TRIZEPS shutdown\n");
+	/* disable IRQs */
+	free_irq(TRIZEPS_IRQ_PCMCIA_CD0, NULL );
+
+	/* Disable CF bus: */
+	TRIZEPS_BCR_set(TRIZEPS_BCR1, TRIZEPS_nPCM_ENA_REG);
+
+	return 0;
+}
+
+/**
+ *
+
+ ******************************************************/
+static int trizeps_pcmcia_socket_state(struct pcmcia_state_array
+				       *state_array){
+	unsigned long levels;
+
+	if (state_array->size < NUMBER_OF_TRIZEPS_PCMCIA_SLOTS) return -1;
+
+	memset(state_array->state, 0,
+	       (state_array->size)*sizeof(struct pcmcia_state));
+
+	levels = GPLR;
+
+	state_array->state[0].detect = ((levels & GPIO_GPIO(TRIZEPS_GPIO_PCMCIA_CD0)) == 0) ? 1 : 0;
+	state_array->state[0].ready  = ((levels & GPIO_GPIO(TRIZEPS_GPIO_PCMCIA_IRQ0)) != 0) ? 1 : 0;
+	state_array->state[0].bvd1   = ((TRIZEPS_BCR1 & TRIZEPS_PCM_BVD1) !=0 ) ? 1 : 0;
+	state_array->state[0].bvd2   = ((TRIZEPS_BCR1 & TRIZEPS_PCM_BVD2) != 0) ? 1 : 0;
+	state_array->state[0].wrprot = 0; // not write protected
+	state_array->state[0].vs_3v  = ((TRIZEPS_BCR1 & TRIZEPS_nPCM_VS1) == 0) ? 1 : 0; //VS1=0 -> vs_3v=1
+	state_array->state[0].vs_Xv  = ((TRIZEPS_BCR1 & TRIZEPS_nPCM_VS2) == 0) ? 1 : 0; //VS2=0 -> vs_Xv=1
+
+	return 1;  // success
+}
+
+/**
+ *
+ *
+ ******************************************************/
+static int trizeps_pcmcia_get_irq_info(struct pcmcia_irq_info *info){
+
+	switch (info->sock) {
+	case 0:
+		info->irq=TRIZEPS_IRQ_PCMCIA_IRQ0;
+		break;
+	case 1:
+		// MFTB2 use only one Slot
+	default:
+		return -1;
+	}
+	return 0;
+}
+
+/**
+ *
+ *
+ ******************************************************/
+static int trizeps_pcmcia_configure_socket(const struct pcmcia_configure *configure)
+{
+	unsigned long flags;
+
+	if(configure->sock>1) return -1;
+
+	local_irq_save(flags);
+
+	switch (configure->vcc) {
+	case 0:
+		printk(">>> PCMCIA Power off\n");
+		TRIZEPS_BCR_clear(TRIZEPS_BCR1, TRIZEPS_PCM_V3_EN_REG);
+		TRIZEPS_BCR_clear(TRIZEPS_BCR1, TRIZEPS_PCM_V5_EN_REG);
+		break;
+
+	case 33:
+		// 3.3V Power on
+		TRIZEPS_BCR_clear(TRIZEPS_BCR1, TRIZEPS_PCM_V3_EN_REG);
+		TRIZEPS_BCR_set(TRIZEPS_BCR1, TRIZEPS_PCM_V5_EN_REG);
+		break;
+	case 50:
+		// 5.0V Power on
+		TRIZEPS_BCR_set(TRIZEPS_BCR1, TRIZEPS_PCM_V3_EN_REG);
+		TRIZEPS_BCR_clear(TRIZEPS_BCR1, TRIZEPS_PCM_V5_EN_REG);
+		break;
+	default:
+		printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__,
+		       configure->vcc);
+		local_irq_restore(flags);
+		return -1;
+	}
+
+	if (configure->reset)
+		TRIZEPS_BCR_set(TRIZEPS_BCR1, TRIZEPS_nPCM_RESET_DISABLE);   // Reset
+	else
+		TRIZEPS_BCR_clear(TRIZEPS_BCR1, TRIZEPS_nPCM_RESET_DISABLE); // no Reset
+	/*
+	  printk(" vcc=%u vpp=%u -->reset=%i\n",
+	  configure->vcc,
+	  configure->vpp,
+	  ((BCR_read(1) & nPCM_RESET_DISABLE)? 1:0));
+	*/
+	local_irq_restore(flags);
+
+	if (configure->irq) {
+		enable_irq(TRIZEPS_IRQ_PCMCIA_CD0);
+		enable_irq(TRIZEPS_IRQ_PCMCIA_IRQ0);
+	} else {
+		disable_irq(TRIZEPS_IRQ_PCMCIA_IRQ0);
+		disable_irq(TRIZEPS_IRQ_PCMCIA_CD0);
+	}
+
+	return 0;
+}
+
+static int trizeps_pcmcia_socket_init(int sock)
+{
+	return 0;
+}
+
+static int trizeps_pcmcia_socket_suspend(int sock)
+{
+	return 0;
+}
+
+/**
+ * low-level PCMCIA interface
+ *
+ ******************************************************/
+struct pcmcia_low_level trizeps_pcmcia_ops = {
+	init:			trizeps_pcmcia_init,
+	shutdown:		trizeps_pcmcia_shutdown,
+	socket_state:		trizeps_pcmcia_socket_state,
+	get_irq_info:		trizeps_pcmcia_get_irq_info,
+	configure_socket:	trizeps_pcmcia_configure_socket,
+	socket_init:		trizeps_pcmcia_socket_init,
+	socket_suspend:		trizeps_pcmcia_socket_suspend,
+};
+
+int __init pcmcia_trizeps_init(void)
+{
+	if (machine_is_trizeps()) {
+		return sa1100_register_pcmcia(&trizeps_pcmcia_ops);
+	}
+	return -ENODEV;
+}
+
+void __exit pcmcia_trizeps_exit(void)
+{
+	sa1100_unregister_pcmcia(&trizeps_pcmcia_ops);
+}
diff -Nru a/drivers/pnp/Makefile b/drivers/pnp/Makefile
--- a/drivers/pnp/Makefile	Mon Apr 22 15:32:18 2002
+++ b/drivers/pnp/Makefile	Mon Apr 22 15:32:18 2002
@@ -1,17 +1,10 @@
 #
 # Makefile for the kernel Plug-and-Play device drivers.
 #
-# Note! Dependencies are done automagically by 'make dep', which also
-# removes any old dependencies. DON'T put your own dependencies here
-# unless it's something special (ie not a .c file).
-#
-# Note 2! The CFLAGS definitions are now inherited from the
-# parent makes..
 
 O_TARGET := pnp.o
 
 export-objs := isapnp.o pnpbios_core.o
-list-multi := isa-pnp.o pnpbios.o
 
 isa-pnp-proc-$(CONFIG_PROC_FS) = isapnp_proc.o
 pnpbios-proc-$(CONFIG_PROC_FS) = pnpbios_proc.o
@@ -23,9 +16,3 @@
 obj-$(CONFIG_PNPBIOS) += pnpbios.o
 
 include $(TOPDIR)/Rules.make
-
-isa-pnp.o: $(isa-pnp-objs)
-	$(LD) $(LD_RFLAG) -r -o $@ $(isa-pnp-objs)
-
-pnpbios.o: $(pnpbios-objs)
-	$(LD) $(LD_RFLAG) -r -o $@ $(pnpbios-objs)
diff -Nru a/drivers/pnp/pnpbios_core.c b/drivers/pnp/pnpbios_core.c
--- a/drivers/pnp/pnpbios_core.c	Mon Apr 22 15:32:18 2002
+++ b/drivers/pnp/pnpbios_core.c	Mon Apr 22 15:32:18 2002
@@ -1,10 +1,19 @@
 /*
- * PnP BIOS services
+ * pnpbios -- PnP BIOS driver
+ *
+ * This driver provides access to Plug-'n'-Play services provided by
+ * the PnP BIOS firmware, described in the following documents:
+ *   Plug and Play BIOS Specification, Version 1.0A, 5 May 1994
+ *   Plug and Play BIOS Clarification Paper, 6 October 1994
+ *     Compaq Computer Corporation, Phoenix Technologies Ltd., Intel Corp.
  * 
  * Originally (C) 1998 Christian Schmidt <schmidt@digadd.de>
- * Modifications (c) 1998 Tom Lees <tom@lpsg.demon.co.uk>
+ * Modifications (C) 1998 Tom Lees <tom@lpsg.demon.co.uk>
  * Minor reorganizations by David Hinds <dahinds@users.sourceforge.net>
- * Modifications (c) 2001,2002 by Thomas Hood <jdthood@mail.com>
+ * Further modifications (C) 2001, 2002 by:
+ *   Alan Cox <alan@redhat.com>
+ *   Thomas Hood <jdthood@mail.com>
+ *   Brian Gerst <bgerst@didntduck.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
@@ -19,12 +28,6 @@
  * 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
- *
- * References:
- *   Compaq Computer Corporation, Phoenix Technologies Ltd., Intel Corporation
- *   Plug and Play BIOS Specification, Version 1.0A, May 5, 1994
- *   Plug and Play BIOS Clarification Paper, October 6, 1994
- *
  */
 
 #include <linux/types.h>
@@ -141,7 +144,9 @@
 static spinlock_t pnp_bios_lock;
 
 static inline u16 call_pnp_bios(u16 func, u16 arg1, u16 arg2, u16 arg3,
-                                u16 arg4, u16 arg5, u16 arg6, u16 arg7)
+				u16 arg4, u16 arg5, u16 arg6, u16 arg7,
+				void *ts1_base, u32 ts1_size,
+				void *ts2_base, u32 ts2_size)
 {
 	unsigned long flags;
 	u16 status;
@@ -155,7 +160,12 @@
 
 	/* On some boxes IRQ's during PnP BIOS calls are deadly.  */
 	spin_lock_irqsave(&pnp_bios_lock, flags);
-	__cli();
+
+	if (ts1_size)
+		Q2_SET_SEL(PNP_TS1, ts1_base, ts1_size);
+	if (ts2_size)
+		Q2_SET_SEL(PNP_TS2, ts2_base, ts2_size);
+
 	__asm__ __volatile__(
 	        "pushl %%ebp\n\t"
 		"pushl %%edi\n\t"
@@ -258,8 +268,8 @@
 	u16 status;
 	if (!pnp_bios_present())
 		return PNP_FUNCTION_NOT_SUPPORTED;
-	Q2_SET_SEL(PNP_TS1, data, sizeof(struct pnp_dev_node_info));
-	status = call_pnp_bios(PNP_GET_NUM_SYS_DEV_NODES, 0, PNP_TS1, 2, PNP_TS1, PNP_DS, 0, 0);
+	status = call_pnp_bios(PNP_GET_NUM_SYS_DEV_NODES, 0, PNP_TS1, 2, PNP_TS1, PNP_DS, 0, 0,
+			       data, sizeof(struct pnp_dev_node_info), 0, 0);
 	data->no_nodes &= 0xff;
 	return status;
 }
@@ -293,9 +303,8 @@
 		return PNP_FUNCTION_NOT_SUPPORTED;
 	if ( !boot & pnpbios_dont_use_current_config )
 		return PNP_FUNCTION_NOT_SUPPORTED;
-	Q2_SET_SEL(PNP_TS1, nodenum, sizeof(char));
-	Q2_SET_SEL(PNP_TS2, data, 64 * 1024);
-	status = call_pnp_bios(PNP_GET_SYS_DEV_NODE, 0, PNP_TS1, 0, PNP_TS2, boot ? 2 : 1, PNP_DS, 0);
+	status = call_pnp_bios(PNP_GET_SYS_DEV_NODE, 0, PNP_TS1, 0, PNP_TS2, boot ? 2 : 1, PNP_DS, 0,
+			       nodenum, sizeof(char), data, 65536);
 	return status;
 }
 
@@ -322,8 +331,8 @@
 		return PNP_FUNCTION_NOT_SUPPORTED;
 	if ( !boot & pnpbios_dont_use_current_config )
 		return PNP_FUNCTION_NOT_SUPPORTED;
-	Q2_SET_SEL(PNP_TS1, data, /* *((u16 *) data)*/ 65536);
-	status = call_pnp_bios(PNP_SET_SYS_DEV_NODE, nodenum, 0, PNP_TS1, boot ? 2 : 1, PNP_DS, 0, 0);
+	status = call_pnp_bios(PNP_SET_SYS_DEV_NODE, nodenum, 0, PNP_TS1, boot ? 2 : 1, PNP_DS, 0, 0,
+			       data, 65536, 0, 0);
 	return status;
 }
 
@@ -354,8 +363,8 @@
 	u16 status;
 	if (!pnp_bios_present())
 		return PNP_FUNCTION_NOT_SUPPORTED;
-	Q2_SET_SEL(PNP_TS1, event, sizeof(u16));
-	status = call_pnp_bios(PNP_GET_EVENT, 0, PNP_TS1, PNP_DS, 0, 0 ,0 ,0);
+	status = call_pnp_bios(PNP_GET_EVENT, 0, PNP_TS1, PNP_DS, 0, 0 ,0 ,0,
+			       event, sizeof(u16), 0, 0);
 	return status;
 }
 #endif
@@ -369,7 +378,7 @@
 	u16 status;
 	if (!pnp_bios_present())
 		return PNP_FUNCTION_NOT_SUPPORTED;
-	status = call_pnp_bios(PNP_SEND_MESSAGE, message, PNP_DS, 0, 0, 0, 0, 0);
+	status = call_pnp_bios(PNP_SEND_MESSAGE, message, PNP_DS, 0, 0, 0, 0, 0, 0, 0, 0, 0);
 	return status;
 }
 #endif
@@ -383,8 +392,8 @@
 	u16 status;
 	if (!pnp_bios_present())
 		return PNP_FUNCTION_NOT_SUPPORTED;
-	Q2_SET_SEL(PNP_TS1, data, sizeof(struct pnp_docking_station_info));
-	status = call_pnp_bios(PNP_GET_DOCKING_STATION_INFORMATION, 0, PNP_TS1, PNP_DS, 0, 0, 0, 0);
+	status = call_pnp_bios(PNP_GET_DOCKING_STATION_INFORMATION, 0, PNP_TS1, PNP_DS, 0, 0, 0, 0,
+			       data, sizeof(struct pnp_docking_station_info), 0, 0);
 	return status;
 }
 #endif
@@ -399,8 +408,8 @@
 	u16 status;
 	if (!pnp_bios_present())
 		return PNP_FUNCTION_NOT_SUPPORTED;
-	Q2_SET_SEL(PNP_TS1, info, *((u16 *) info));
-	status = call_pnp_bios(PNP_SET_STATIC_ALLOCED_RES_INFO, 0, PNP_TS1, PNP_DS, 0, 0, 0, 0);
+	status = call_pnp_bios(PNP_SET_STATIC_ALLOCED_RES_INFO, 0, PNP_TS1, PNP_DS, 0, 0, 0, 0,
+			       info, *((u16 *) info), 0, 0);
 	return status;
 }
 #endif
@@ -414,8 +423,8 @@
 	u16 status;
 	if (!pnp_bios_present())
 		return PNP_FUNCTION_NOT_SUPPORTED;
-	Q2_SET_SEL(PNP_TS1, info, 64 * 1024);
-	status = call_pnp_bios(PNP_GET_STATIC_ALLOCED_RES_INFO, 0, PNP_TS1, PNP_DS, 0, 0, 0, 0);
+	status = call_pnp_bios(PNP_GET_STATIC_ALLOCED_RES_INFO, 0, PNP_TS1, PNP_DS, 0, 0, 0, 0,
+			       info, 65536, 0, 0);
 	return status;
 }
 
@@ -437,9 +446,8 @@
 	u16 status;
 	if (!pnp_bios_present())
 		return PNP_FUNCTION_NOT_SUPPORTED;
-	Q2_SET_SEL(PNP_TS1, table, *size);
-	Q2_SET_SEL(PNP_TS2, size, sizeof(u16));
-	status = call_pnp_bios(PNP_GET_APM_ID_TABLE, 0, PNP_TS2, 0, PNP_TS1, PNP_DS, 0, 0);
+	status = call_pnp_bios(PNP_GET_APM_ID_TABLE, 0, PNP_TS2, 0, PNP_TS1, PNP_DS, 0, 0,
+			       table, *size, size, sizeof(u16));
 	return status;
 }
 #endif
@@ -452,8 +460,8 @@
 	u16 status;
 	if (!pnp_bios_present())
 		return PNP_FUNCTION_NOT_SUPPORTED;
-	Q2_SET_SEL(PNP_TS1, data, sizeof(struct pnp_isa_config_struc));
-	status = call_pnp_bios(PNP_GET_PNP_ISA_CONFIG_STRUC, 0, PNP_TS1, PNP_DS, 0, 0, 0, 0);
+	status = call_pnp_bios(PNP_GET_PNP_ISA_CONFIG_STRUC, 0, PNP_TS1, PNP_DS, 0, 0, 0, 0,
+			       data, sizeof(struct pnp_isa_config_struc), 0, 0);
 	return status;
 }
 
@@ -474,8 +482,8 @@
 	u16 status;
 	if (!pnp_bios_present())
 		return ESCD_FUNCTION_NOT_SUPPORTED;
-	Q2_SET_SEL(PNP_TS1, data, sizeof(struct escd_info_struc));
-	status = call_pnp_bios(PNP_GET_ESCD_INFO, 0, PNP_TS1, 2, PNP_TS1, 4, PNP_TS1, PNP_DS);
+	status = call_pnp_bios(PNP_GET_ESCD_INFO, 0, PNP_TS1, 2, PNP_TS1, 4, PNP_TS1, PNP_DS,
+			       data, sizeof(struct escd_info_struc), 0, 0);
 	return status;
 }
 
@@ -497,10 +505,8 @@
 	u16 status;
 	if (!pnp_bios_present())
 		return ESCD_FUNCTION_NOT_SUPPORTED;
-	Q2_SET_SEL(PNP_TS1, data, 64 * 1024);
-	set_base(gdt[PNP_TS2 >> 3], nvram_base);
-	set_limit(gdt[PNP_TS2 >> 3], 64 * 1024);
-	status = call_pnp_bios(PNP_READ_ESCD, 0, PNP_TS1, PNP_TS2, PNP_DS, 0, 0, 0);
+	status = call_pnp_bios(PNP_READ_ESCD, 0, PNP_TS1, PNP_TS2, PNP_DS, 0, 0, 0,
+			       data, 65536, (void *)nvram_base, 65536);
 	return status;
 }
 
@@ -522,10 +528,8 @@
 	u16 status;
 	if (!pnp_bios_present())
 		return ESCD_FUNCTION_NOT_SUPPORTED;
-	Q2_SET_SEL(PNP_TS1, data, 64 * 1024);
-	set_base(gdt[PNP_TS2 >> 3], nvram_base);
-	set_limit(gdt[PNP_TS2 >> 3], 64 * 1024);
-	status = call_pnp_bios(PNP_WRITE_ESCD, 0, PNP_TS1, PNP_TS2, PNP_DS, 0, 0, 0);
+	status = call_pnp_bios(PNP_WRITE_ESCD, 0, PNP_TS1, PNP_TS2, PNP_DS, 0, 0, 0,
+			       data, 65536, nvram_base, 65536);
 	return status;
 }
 #endif
diff -Nru a/drivers/s390/block/Makefile b/drivers/s390/block/Makefile
--- a/drivers/s390/block/Makefile	Mon Apr 22 15:32:19 2002
+++ b/drivers/s390/block/Makefile	Mon Apr 22 15:32:19 2002
@@ -4,7 +4,6 @@
 
 O_TARGET := s390-block.o
 
-list-multi := dasd_mod.o dasd_eckd_mod.o dasd_fba_mod.o dasd_diag_mod.o
 export-objs := dasd.o
 
 dasd_eckd_mod-objs := dasd_eckd.o dasd_3990_erp.o dasd_9343_erp.o
@@ -19,16 +18,3 @@
 obj-$(CONFIG_BLK_DEV_XPRAM) += xpram.o
 
 include $(TOPDIR)/Rules.make
-
-dasd_mod.o: $(dasd_mod-objs)
-	$(LD) -r -o $@ $(dasd_mod-objs)
-
-dasd_eckd_mod.o: $(dasd_eckd_mod-objs)
-	$(LD) -r -o $@ $(dasd_eckd_mod-objs)
-
-dasd_fba_mod.o: $(dasd_fba_mod-objs)
-	$(LD) -r -o $@ $(dasd_fba_mod-objs)
-
-dasd_diag_mod.o: $(dasd_diag_mod-objs)
-	$(LD) -r -o $@ $(dasd_diag_mod-objs)
-
diff -Nru a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile
--- a/drivers/s390/char/Makefile	Mon Apr 22 15:32:19 2002
+++ b/drivers/s390/char/Makefile	Mon Apr 22 15:32:19 2002
@@ -4,7 +4,6 @@
 
 O_TARGET := s390-char.o
 
-list-multi	:= tub3270.o tape390.o
 export-objs	:= hwc_rw.o
 
 tub3270-objs := tuball.o tubfs.o tubtty.o \
@@ -25,10 +24,3 @@
 obj-$(CONFIG_S390_TAPE) += tape390.o
 
 include $(TOPDIR)/Rules.make
-
-tub3270.o: $(tub3270-objs)
-	$(LD) -r -o $@ $(tub3270-objs)
-
-tape390.o: $(tape390-objs)
-	$(LD) -r -o $@ $(tape390-objs)
-
diff -Nru a/drivers/s390/net/Makefile b/drivers/s390/net/Makefile
--- a/drivers/s390/net/Makefile	Mon Apr 22 15:32:19 2002
+++ b/drivers/s390/net/Makefile	Mon Apr 22 15:32:19 2002
@@ -4,7 +4,6 @@
 
 O_TARGET := s390-net.o
 
-list-multi := ctc.o
 export-objs := iucv.o fsm.o
 
 ctc-objs := ctcmain.o ctctty.o
@@ -14,7 +13,3 @@
 obj-$(CONFIG_IUCV) += netiucv.o
 
 include $(TOPDIR)/Rules.make
-
-ctc.o: $(ctc-objs)
-	$(LD) -r -o $@ $(ctc-objs)
-
diff -Nru a/drivers/sbus/char/Makefile b/drivers/sbus/char/Makefile
--- a/drivers/sbus/char/Makefile	Mon Apr 22 15:32:19 2002
+++ b/drivers/sbus/char/Makefile	Mon Apr 22 15:32:19 2002
@@ -12,7 +12,6 @@
 export-objs := su.o bbc_i2c.o
 obj-y := sunkbd.o sunkbdmap.o sunmouse.o sunserial.o zs.o
 
-list-multi := vfc.o bbc.o
 vfc-objs := vfc_dev.o vfc_i2c.o
 bbc-objs := bbc_i2c.o bbc_envctrl.o
 
@@ -36,9 +35,3 @@
 include $(TOPDIR)/Rules.make
 
 sunkbdmap.o: sunkeymap.c
-
-vfc.o: $(vfc-objs)
-	$(LD) -r -o $@ $(vfc-objs)
-
-bbc.o: $(bbc-objs)
-	$(LD) -r -o $@ $(bbc-objs)
diff -Nru a/drivers/scsi/Makefile b/drivers/scsi/Makefile
--- a/drivers/scsi/Makefile	Mon Apr 22 15:32:19 2002
+++ b/drivers/scsi/Makefile	Mon Apr 22 15:32:19 2002
@@ -89,7 +89,7 @@
 obj-$(CONFIG_SCSI_NCR53C7xx)	+= 53c7,8xx.o 
 subdir-$(CONFIG_SCSI_SYM53C8XX_2)	+= sym53c8xx_2
 ifeq ($(CONFIG_SCSI_SYM53C8XX_2),y)
-  obj-$(CONFIG_SCSI_SYM53C8XX_2)	+= sym53c8xx_2/sym53c8xx.o
+  obj-$(CONFIG_SCSI_SYM53C8XX_2)	+= sym53c8xx_2/sym53c8xx_2.o
 endif
 obj-$(CONFIG_SCSI_SYM53C8XX)	+= sym53c8xx.o 
 obj-$(CONFIG_SCSI_NCR53C8XX)	+= ncr53c8xx.o 
@@ -132,7 +132,6 @@
 obj-$(CONFIG_BLK_DEV_SR)	+= sr_mod.o
 obj-$(CONFIG_CHR_DEV_SG)	+= sg.o
 
-list-multi	:= scsi_mod.o sd_mod.o sr_mod.o initio.o a100u2w.o cpqfc.o
 scsi_mod-objs	:= scsi.o hosts.o scsi_ioctl.o constants.o scsicam.o \
 			scsi_proc.o scsi_error.o scsi_queue.o scsi_lib.o \
 			scsi_merge.o scsi_scan.o scsi_syms.o
@@ -144,25 +143,6 @@
 		   cpqfcTSworker.o cpqfcTStrigger.o
 
 include $(TOPDIR)/Rules.make
-
-
-scsi_mod.o: $(scsi_mod-objs)
-	$(LD) -r -o $@ $(scsi_mod-objs)
-
-sd_mod.o: $(sd_mod-objs)
-	$(LD) -r -o $@ $(sd_mod-objs)
-
-sr_mod.o: $(sr_mod-objs)
-	$(LD) -r -o $@ $(sr_mod-objs)
-
-initio.o: $(initio-objs)
-	$(LD) -r -o $@ $(initio-objs)
-
-a100u2w.o: $(a100u2w-objs)
-	$(LD) -r -o $@ $(a100u2w-objs)
-
-cpqfc.o: $(cpqfc-objs)
-	$(LD) -r -o $@ $(cpqfc-objs)
 
 53c8xx_d.h: 53c7,8xx.scr script_asm.pl
 	ln -sf 53c7,8xx.scr fake8.c
diff -Nru a/drivers/scsi/advansys.c b/drivers/scsi/advansys.c
--- a/drivers/scsi/advansys.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/scsi/advansys.c	Mon Apr 22 15:32:19 2002
@@ -1,4 +1,4 @@
-#define ASC_VERSION "3.3GG"    /* AdvanSys Driver Version */
+#define ASC_VERSION "3.3GJ"    /* AdvanSys Driver Version */
 
 /*
  * advansys.c - Linux Host Driver for AdvanSys SCSI Adapters
@@ -670,7 +670,7 @@
          1. Return an error from narrow boards if passed a 16 byte
             CDB. The wide board can already handle 16 byte CDBs.
 
-     3.3GG (01/02/02):
+     3.3GJ (4/15/02):
 	 1. hacks for lk 2.5 series (D. Gilbert)
 
   I. Known Problems/Fix List (XXX)
@@ -752,7 +752,6 @@
 
 */
 
-#error Please convert me to Documentation/DMA-mapping.txt
 
 /*
  * --- Linux Version
@@ -3614,23 +3613,6 @@
 #define ASC_MIN(a, b) (((a) < (b)) ? (a) : (b))
 #endif /* CONFIG_PROC_FS */
 
-/*
- * XXX - Release and acquire the io_request_lock. These macros are needed
- * because the 2.4 kernel SCSI mid-level driver holds the 'io_request_lock'
- * on entry to SCSI low-level drivers.
- *
- * These definitions and all code that uses code should be removed when the
- * SCSI mid-level driver no longer holds the 'io_request_lock' on entry to
- * SCSI low-level driver detect, queuecommand, and reset entrypoints.
- *
- * The interrupt flags values doesn't matter in the macros because the
- * SCSI mid-level will save and restore the flags values before and after
- * calling advansys_detect, advansys_queuecommand, and advansys_reset where
- * these macros are used. We do want interrupts enabled after the lock is
- * released so an explicit sti() is done. The driver only needs interrupts
- * disabled when it acquires the per board lock.
- */
-
 /* Asc Library return codes */
 #define ASC_TRUE        1
 #define ASC_FALSE       0
@@ -4822,7 +4804,7 @@
             boardp->id = asc_board_count - 1;
 
             /* Initialize spinlock. */
-            boardp->lock = SPIN_LOCK_UNLOCKED; /* replaced by host_lock dpg */
+            boardp->lock = SPIN_LOCK_UNLOCKED;
 
             /*
              * Handle both narrow and wide boards.
@@ -5872,7 +5854,7 @@
 
     /* host_lock taken by mid-level prior to call but need to protect */
     /* against own ISR */
-    spin_lock_irqsave(boardp->lock, flags);
+    spin_lock_irqsave(&boardp->lock, flags);
 
     /*
      * Block new commands while handling a reset or abort request.
@@ -6751,8 +6733,7 @@
         slp = (struct scatterlist *) scp->request_buffer;
         for (sgcnt = 0; sgcnt < scp->use_sg; sgcnt++, slp++) {
             asc_sg_head.sg_list[sgcnt].addr =
-                cpu_to_le32(virt_to_bus(slp->address ? 
-		(unsigned char *)slp->address :
+                cpu_to_le32(virt_to_bus(
 		(unsigned char *)page_address(slp->page) + slp->offset));
             asc_sg_head.sg_list[sgcnt].bytes = cpu_to_le32(slp->length);
             ASC_STATS_ADD(scp->host, sg_xfer, ASC_CEILING(slp->length, 512));
@@ -7011,9 +6992,8 @@
         for (i = 0; i < NO_OF_SG_PER_BLOCK; i++)
         {
             sg_block->sg_list[i].sg_addr =
-                cpu_to_le32(virt_to_bus(slp->address ? 
-		(unsigned char *)slp->address :
-                (unsigned char *)page_address(slp->page) + slp->offset));
+                cpu_to_le32(virt_to_bus(
+                   (unsigned char *)page_address(slp->page) + slp->offset));
             sg_block->sg_list[i].sg_count = cpu_to_le32(slp->length);
             ASC_STATS_ADD(scp->host, sg_xfer, ASC_CEILING(slp->length, 512));
 
@@ -9415,8 +9395,8 @@
 
     printk("Scsi_Host at addr 0x%lx\n", (ulong) s);
     printk(
-" next 0x%lx, extra_bytes %u, host_busy %u, host_no %d, last_reset %d,\n",
-        (ulong) s->next, s->extra_bytes, s->host_busy, s->host_no,
+" next 0x%lx, host_busy %u, host_no %d, last_reset %d,\n",
+        (ulong) s->next, s->host_busy, s->host_no,
         (unsigned) s->last_reset);
 
 #if ASC_LINUX_KERNEL24
diff -Nru a/drivers/scsi/aic7xxx/Makefile b/drivers/scsi/aic7xxx/Makefile
--- a/drivers/scsi/aic7xxx/Makefile	Mon Apr 22 15:32:19 2002
+++ b/drivers/scsi/aic7xxx/Makefile	Mon Apr 22 15:32:19 2002
@@ -1,39 +1,30 @@
 #
-# drivers/scsi/aic7xxx/Makefile
-#
 # Makefile for the Linux aic7xxx SCSI driver.
 #
 
 O_TARGET := aic7xxx_drv.o
-
-list-multi	:= aic7xxx_mod.o
+MOD_TARGET = aic7xxx.o
 
 obj-$(CONFIG_SCSI_AIC7XXX)	+= aic7xxx_mod.o
 
-#EXTRA_CFLAGS += -g
+# Core files
+aix7xxx_mod-objs	+= aic7xxx.o aic7xxx_93cx6.o aic7770.o
 
 # Platform Specific Files
-AIC7XXX_OBJS = aic7xxx_linux.o 
-AIC7XXX_OBJS += aic7xxx_proc.o aic7770_linux.o
-#PCI Specific Platform Files
-ifeq ($(CONFIG_PCI),y)
-AIC7XXX_OBJS += aic7xxx_linux_pci.o
-endif
-# Core Files
-AIC7XXX_OBJS += aic7xxx.o aic7xxx_93cx6.o aic7770.o
-#PCI Specific Core Files
+aic7xxx_mod-objs	+= aic7xxx_linux.o aic7xxx_proc.o aic7770_linux.o
+
+# PCI Specific Files
 ifeq ($(CONFIG_PCI),y)
-AIC7XXX_OBJS += aic7xxx_pci.o
+  # Core PCI files
+  aic7xxx_mod-objs	+= aic7xxx_pci.o
+  # Platform Specific PCI Files
+  aic7xxx_mod-objs	+= aic7xxx_linux_pci.o
 endif
 
-# Override our module desitnation
-MOD_TARGET = aic7xxx.o
+#EXTRA_CFLAGS += -g
 
 include $(TOPDIR)/Rules.make
 
-aic7xxx_mod.o: aic7xxx_seq.h aic7xxx_reg.h $(AIC7XXX_OBJS)
-	$(LD) $(LD_RFLAG) -r -o $@ $(AIC7XXX_OBJS)
-
 ifeq ($(CONFIG_AIC7XXX_BUILD_FIRMWARE),y)
 aic7xxx_seq.h aic7xxx_reg.h: aic7xxx.seq aic7xxx.reg aicasm/aicasm
 	aicasm/aicasm -I. -r aic7xxx_reg.h -o aic7xxx_seq.h aic7xxx.seq
@@ -41,3 +32,5 @@
 
 aicasm/aicasm: aicasm/*.[chyl]
 	$(MAKE) -C aicasm
+
+aix7xxx_mod.o: aic7xxx_seq.h aic7xxx_reg.h
diff -Nru a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c
--- a/drivers/scsi/ide-scsi.c	Mon Apr 22 15:32:18 2002
+++ b/drivers/scsi/ide-scsi.c	Mon Apr 22 15:32:18 2002
@@ -140,7 +140,7 @@
 		}
 		count = min(pc->sg->length - pc->b_count, bcount);
 		buf = page_address(pc->sg->page) + pc->sg->offset;
-		atapi_input_bytes (drive, buf + pc->b_count, count);
+		atapi_read(drive, buf + pc->b_count, count);
 		bcount -= count; pc->b_count += count;
 		if (pc->b_count == pc->sg->length) {
 			pc->sg++;
@@ -162,7 +162,7 @@
 		}
 		count = min(pc->sg->length - pc->b_count, bcount);
 		buf = page_address(pc->sg->page) + pc->sg->offset;
-		atapi_output_bytes (drive, buf + pc->b_count, count);
+		atapi_write(drive, buf + pc->b_count, count);
 		bcount -= count; pc->b_count += count;
 		if (pc->b_count == pc->sg->length) {
 			pc->sg++;
@@ -363,7 +363,7 @@
 					if (pc->sg)
 						idescsi_input_buffers(drive, pc, temp);
 					else
-						atapi_input_bytes(drive, pc->current_position, temp);
+						atapi_read(drive, pc->current_position, temp);
 					printk(KERN_ERR "ide-scsi: transferred %d of %d bytes\n", temp, bcount);
 				}
 				pc->actually_transferred += temp;
@@ -382,13 +382,13 @@
 		if (pc->sg)
 			idescsi_input_buffers (drive, pc, bcount);
 		else
-			atapi_input_bytes (drive,pc->current_position,bcount);
+			atapi_read(drive,pc->current_position,bcount);
 	} else {
 		set_bit(PC_WRITING, &pc->flags);
 		if (pc->sg)
 			idescsi_output_buffers (drive, pc, bcount);
 		else
-			atapi_output_bytes (drive,pc->current_position,bcount);
+			atapi_write(drive,pc->current_position,bcount);
 	}
 	pc->actually_transferred+=bcount;				/* Update the current position */
 	pc->current_position+=bcount;
@@ -414,7 +414,7 @@
 		return ide_stopped;
 	}
 	ide_set_handler(drive, &idescsi_pc_intr, get_timeout(pc), NULL);	/* Set the interrupt routine */
-	atapi_output_bytes (drive, scsi->pc->c, 12);			/* Send the actual packet */
+	atapi_write(drive, scsi->pc->c, 12);			/* Send the actual packet */
 	return ide_started;
 }
 
diff -Nru a/drivers/scsi/ips.c b/drivers/scsi/ips.c
--- a/drivers/scsi/ips.c	Mon Apr 22 15:32:18 2002
+++ b/drivers/scsi/ips.c	Mon Apr 22 15:32:18 2002
@@ -140,7 +140,6 @@
  * ioctlsize            - Initial size of the IOCTL buffer
  */
  
-#error Please convert me to Documentation/DMA-mapping.txt
 
 #include <asm/io.h>
 #include <asm/byteorder.h>
@@ -848,6 +847,13 @@
          /* found a controller */
          sh = scsi_register(SHT, sizeof(ips_ha_t));
 
+         /*
+          * Set pci_dev and dma_mask
+          */
+         pci_set_dma_mask(dev[i], (u64) 0xffffffff);
+   
+         scsi_set_pci_device(sh, dev[i]);
+
          if (sh == NULL) {
             printk(KERN_WARNING "(%s%d) Unable to register controller with SCSI subsystem - skipping controller\n",
                    ips_name, ips_next_controller);
@@ -1178,6 +1184,8 @@
       sh = ips_sh[i];
 
       if (!ha->active) {
+         printk(KERN_WARNING "(%s%d) controller not active\n",
+                ips_name, i);
          scsi_unregister(sh);
          ips_ha[i] = NULL;
          ips_sh[i] = NULL;
@@ -2833,7 +2841,7 @@
 
    /* FIX stuff that might be wrong */
    scb->sg_list = sg_list;
-   scb->scb_busaddr = VIRT_TO_BUS(scb);
+   scb->scb_busaddr = virt_to_bus(scb);
    scb->bus = scb->scsi_cmd->channel;
    scb->target_id = scb->scsi_cmd->target;
    scb->lun = scb->scsi_cmd->lun;
@@ -2852,13 +2860,13 @@
       return (0);
 
    if (pt->CmdBSize) {
-      scb->data_busaddr = VIRT_TO_BUS(scb->scsi_cmd->request_buffer + sizeof(ips_passthru_t));
+      scb->data_busaddr = virt_to_bus(scb->scsi_cmd->request_buffer + sizeof(ips_passthru_t));
    } else {
       scb->data_busaddr = 0L;
    }
 
    if (scb->cmd.dcdb.op_code == IPS_CMD_DCDB)
-      scb->cmd.dcdb.dcdb_address = cpu_to_le32(VIRT_TO_BUS(&scb->dcdb));
+      scb->cmd.dcdb.dcdb_address = cpu_to_le32(virt_to_bus(&scb->dcdb));
 
    if (pt->CmdBSize) {
       if (scb->cmd.dcdb.op_code == IPS_CMD_DCDB)
@@ -2916,7 +2924,7 @@
 
    /* FIX stuff that might be wrong */
    scb->sg_list = sg_list;
-   scb->scb_busaddr = VIRT_TO_BUS(scb);
+   scb->scb_busaddr = virt_to_bus(scb);
    scb->bus = scb->scsi_cmd->channel;
    scb->target_id = scb->scsi_cmd->target;
    scb->lun = scb->scsi_cmd->lun;
@@ -2959,7 +2967,7 @@
 
       }
 
-      scb->data_busaddr = VIRT_TO_BUS(ha->ioctl_data);
+      scb->data_busaddr = virt_to_bus(ha->ioctl_data);
 
       /* Attempt to copy in the data */
       user_area = *((char **) &scb->scsi_cmd->cmnd[4]);
@@ -2978,7 +2986,7 @@
    }
 
    if (scb->cmd.dcdb.op_code == IPS_CMD_DCDB)
-      scb->cmd.dcdb.dcdb_address = cpu_to_le32(VIRT_TO_BUS(&scb->dcdb));
+      scb->cmd.dcdb.dcdb_address = cpu_to_le32(virt_to_bus(&scb->dcdb));
 
    if (pt->CmdBSize) {
       if (scb->cmd.dcdb.op_code == IPS_CMD_DCDB)
@@ -3393,7 +3401,7 @@
       scb->cmd.flashfw.type = 1;
       scb->cmd.flashfw.direction = 0;
       scb->cmd.flashfw.count = cpu_to_le32(0x800);
-      scb->cmd.flashfw.buffer_addr = cpu_to_le32(VIRT_TO_BUS(buffer));
+      scb->cmd.flashfw.buffer_addr = cpu_to_le32(virt_to_bus(buffer));
       scb->cmd.flashfw.total_packets = 1;
       scb->cmd.flashfw.packet_num = 0;
 
@@ -3569,7 +3577,7 @@
    Scsi_Cmnd            *p;
    Scsi_Cmnd            *q;
    ips_copp_wait_item_t *item;
-   int                   ret;
+   int                   ret, sg_entries = 0;
    int                   intr_status;
    unsigned long         cpu_flags;
    unsigned long         cpu_flags2;
@@ -3771,6 +3779,7 @@
          int                 i;
 
          sg = SC->request_buffer;
+	sg_entries = pci_map_sg(ha->pcidev, sg, SC->use_sg, scsi_to_pci_dma_dir(SC->sc_data_direction));
 
          if (SC->use_sg == 1) {
             if (sg[0].length > ha->max_xfer) {
@@ -3780,12 +3789,12 @@
                scb->data_len = sg[0].length;
 
             scb->dcdb.transfer_length = scb->data_len;
-            scb->data_busaddr = VIRT_TO_BUS(sg[0].address);
+	    scb->data_busaddr = sg_dma_address(&sg[0]);
             scb->sg_len = 0;
          } else {
             /* Check for the first Element being bigger than MAX_XFER */
             if (sg[0].length > ha->max_xfer) {
-               scb->sg_list[0].address = cpu_to_le32(VIRT_TO_BUS(sg[0].address));
+               scb->sg_list[0].address = cpu_to_le32(sg_dma_address(&sg[0]));
                scb->sg_list[0].length = ha->max_xfer;
                scb->data_len = ha->max_xfer;
                scb->breakup = 0; 
@@ -3794,7 +3803,7 @@
             }
             else {
                for (i = 0; i < SC->use_sg; i++) {
-                  scb->sg_list[i].address = cpu_to_le32(VIRT_TO_BUS(sg[i].address));
+                  scb->sg_list[i].address = cpu_to_le32(sg_dma_address(&sg[i]));
                   scb->sg_list[i].length = cpu_to_le32(sg[i].length);
             
                   if (scb->data_len + sg[i].length > ha->max_xfer) {
@@ -3809,13 +3818,13 @@
                }
 
                if (!scb->breakup)
-                  scb->sg_len = SC->use_sg;
+                  scb->sg_len = sg_entries;
                else
                   scb->sg_len = scb->breakup;
             }
 
             scb->dcdb.transfer_length = scb->data_len;
-            scb->data_busaddr = VIRT_TO_BUS(scb->sg_list);
+            scb->data_busaddr = virt_to_bus(scb->sg_list);
          }
       } else {
          if (SC->request_bufflen) {
@@ -3830,7 +3839,7 @@
             }
 
             scb->dcdb.transfer_length = scb->data_len;
-            scb->data_busaddr = VIRT_TO_BUS(SC->request_buffer);
+            scb->data_busaddr = virt_to_bus(SC->request_buffer);
             scb->sg_len = 0;
          } else {
             scb->data_busaddr = 0L;
@@ -4469,11 +4478,11 @@
                if (sg[0].length - (bk_save * ha->max_xfer) > ha->max_xfer) {
                   /* Further breakup required */
                   scb->data_len = ha->max_xfer;
-                  scb->data_busaddr = VIRT_TO_BUS(sg[0].address + (bk_save * ha->max_xfer));
+                  scb->data_busaddr = sg_dma_address(&sg[0] + (bk_save * ha->max_xfer));
                   scb->breakup = bk_save + 1;
                } else {
                   scb->data_len = sg[0].length - (bk_save * ha->max_xfer);
-                  scb->data_busaddr = VIRT_TO_BUS(sg[0].address + (bk_save * ha->max_xfer));
+                  scb->data_busaddr = sg_dma_address(&sg[0] + (bk_save * ha->max_xfer));
                }
 
                scb->dcdb.transfer_length = scb->data_len;
@@ -4490,7 +4499,7 @@
                /* pointed to by bk_save                                             */
                if (scb->sg_break) {
                   scb->sg_len = 1;
-        			   scb->sg_list[0].address = VIRT_TO_BUS(sg[bk_save].address+ha->max_xfer*scb->sg_break);
+        			   scb->sg_list[0].address = sg_dma_address(&sg[bk_save] + ha->max_xfer*scb->sg_break);
                   if (ha->max_xfer > sg[bk_save].length-ha->max_xfer * scb->sg_break) 
                      scb->sg_list[0].length = sg[bk_save].length-ha->max_xfer * scb->sg_break;
                   else 
@@ -4508,7 +4517,7 @@
                } else {
 			         /* ( sg_break == 0 ), so this is our first look at a new sg piece */
                   if (sg[bk_save].length > ha->max_xfer) {
-			            scb->sg_list[0].address = cpu_to_le32(VIRT_TO_BUS(sg[bk_save].address));
+			            scb->sg_list[0].address = cpu_to_le32(sg_dma_address(&sg[bk_save]));
 				         scb->sg_list[0].length = ha->max_xfer;
 				         scb->breakup = bk_save;
 				         scb->sg_break = 1;
@@ -4521,7 +4530,7 @@
       			      scb->sg_break = 0;
                      /*   We're only doing full units here */
 				         for (i = bk_save; i < scb->scsi_cmd->use_sg; i++) {
-					         scb->sg_list[i - bk_save].address = cpu_to_le32(VIRT_TO_BUS(sg[i].address));
+					         scb->sg_list[i - bk_save].address = cpu_to_le32(sg_dma_address(&sg[i]));
 				            scb->sg_list[i - bk_save].length = cpu_to_le32(sg[i].length);
                         if (scb->data_len + sg[i].length > ha->max_xfer) {
 					            scb->breakup = i;  /* sneaky, if not more work, than breakup is 0 */
@@ -4536,7 +4545,7 @@
                /* Also, we need to be sure we don't queue work ( breakup != 0 )
                   if no more sg units for next time */
                scb->dcdb.transfer_length = scb->data_len;
-               scb->data_busaddr = VIRT_TO_BUS(scb->sg_list);
+               scb->data_busaddr = virt_to_bus(scb->sg_list);
             }
                                               
              } else {
@@ -4544,11 +4553,11 @@
             if ((scb->scsi_cmd->request_bufflen - (bk_save * ha->max_xfer)) > ha->max_xfer) {
                /* Further breakup required */
                scb->data_len = ha->max_xfer;
-               scb->data_busaddr = VIRT_TO_BUS(scb->scsi_cmd->request_buffer + (bk_save * ha->max_xfer));
+               scb->data_busaddr = virt_to_bus(scb->scsi_cmd->request_buffer + (bk_save * ha->max_xfer));
                scb->breakup = bk_save + 1;
             } else {
                scb->data_len = scb->scsi_cmd->request_bufflen - (bk_save * ha->max_xfer);
-               scb->data_busaddr = VIRT_TO_BUS(scb->scsi_cmd->request_buffer + (bk_save * ha->max_xfer));
+               scb->data_busaddr = virt_to_bus(scb->scsi_cmd->request_buffer + (bk_save * ha->max_xfer));
             }
 
             scb->dcdb.transfer_length = scb->data_len;
@@ -4589,6 +4598,8 @@
             break;
          } /* end case */
 
+	pci_unmap_sg(ha->pcidev, (struct scatterlist *)scb->scsi_cmd->request_buffer, scb->sg_len, 
+		scsi_to_pci_dma_dir(scb->scsi_cmd->sc_data_direction));
          return ;
       }
 #ifndef NO_IPS_CMDLINE
@@ -4851,7 +4862,7 @@
          } else {
             scb->cmd.logical_info.op_code = IPS_CMD_GET_LD_INFO;
             scb->cmd.logical_info.command_id = IPS_COMMAND_ID(ha, scb);
-            scb->cmd.logical_info.buffer_addr = cpu_to_le32(VIRT_TO_BUS(&ha->adapt->logical_drive_info));
+            scb->cmd.logical_info.buffer_addr = cpu_to_le32(virt_to_bus(&ha->adapt->logical_drive_info));
             scb->cmd.logical_info.reserved = 0;
             scb->cmd.logical_info.reserved2 = 0;
             ret = IPS_SUCCESS;
@@ -4944,14 +4955,14 @@
       case MODE_SENSE:
          scb->cmd.basic_io.op_code = IPS_CMD_ENQUIRY;
          scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb);
-         scb->cmd.basic_io.sg_addr = cpu_to_le32(VIRT_TO_BUS(ha->enq));
+         scb->cmd.basic_io.sg_addr = cpu_to_le32(virt_to_bus(ha->enq));
          ret = IPS_SUCCESS;
          break;
 
       case READ_CAPACITY:
          scb->cmd.logical_info.op_code = IPS_CMD_GET_LD_INFO;
          scb->cmd.logical_info.command_id = IPS_COMMAND_ID(ha, scb);
-         scb->cmd.logical_info.buffer_addr = cpu_to_le32(VIRT_TO_BUS(&ha->adapt->logical_drive_info));
+         scb->cmd.logical_info.buffer_addr = cpu_to_le32(virt_to_bus(&ha->adapt->logical_drive_info));
          scb->cmd.logical_info.reserved = 0;
          scb->cmd.logical_info.reserved2 = 0;
          scb->cmd.logical_info.reserved3 = 0;
@@ -5007,7 +5018,7 @@
 
       ha->dcdb_active[scb->bus-1] |= (1 << scb->target_id);
       scb->cmd.dcdb.command_id = IPS_COMMAND_ID(ha, scb);
-      scb->cmd.dcdb.dcdb_address = cpu_to_le32(VIRT_TO_BUS(&scb->dcdb));
+      scb->cmd.dcdb.dcdb_address = cpu_to_le32(virt_to_bus(&scb->dcdb));
       scb->cmd.dcdb.reserved = 0;
       scb->cmd.dcdb.reserved2 = 0;
       scb->cmd.dcdb.reserved3 = 0;
@@ -5532,16 +5543,16 @@
 
    /* Initialize dummy command bucket */
    ha->dummy->op_code = 0xFF;
-   ha->dummy->ccsar = cpu_to_le32(VIRT_TO_BUS(ha->dummy));
+   ha->dummy->ccsar = cpu_to_le32(virt_to_bus(ha->dummy));
    ha->dummy->command_id = IPS_MAX_CMDS;
 
    /* set bus address of scb */
-   scb->scb_busaddr = VIRT_TO_BUS(scb);
+   scb->scb_busaddr = virt_to_bus(scb);
    scb->sg_list = sg_list;
 
    /* Neptune Fix */
    scb->cmd.basic_io.cccr = cpu_to_le32((u_int32_t) IPS_BIT_ILE);
-   scb->cmd.basic_io.ccsar = cpu_to_le32(VIRT_TO_BUS(ha->dummy));
+   scb->cmd.basic_io.ccsar = cpu_to_le32(virt_to_bus(ha->dummy));
 }
 
 /****************************************************************************/
@@ -6151,7 +6162,7 @@
    ha->adapt->p_status_end = ha->adapt->status + IPS_MAX_CMDS;
    ha->adapt->p_status_tail = ha->adapt->status;
 
-   phys_status_start = VIRT_TO_BUS(ha->adapt->status);
+   phys_status_start = virt_to_bus(ha->adapt->status);
    outl(cpu_to_le32(phys_status_start), ha->io_addr + IPS_REG_SQSR);
    outl(cpu_to_le32(phys_status_start + IPS_STATUS_Q_SIZE), ha->io_addr + IPS_REG_SQER);
    outl(cpu_to_le32(phys_status_start + IPS_STATUS_SIZE), ha->io_addr + IPS_REG_SQHR);
@@ -6180,7 +6191,7 @@
    ha->adapt->p_status_end = ha->adapt->status + IPS_MAX_CMDS;
    ha->adapt->p_status_tail = ha->adapt->status;
 
-   phys_status_start = VIRT_TO_BUS(ha->adapt->status);
+   phys_status_start = virt_to_bus(ha->adapt->status);
    writel(cpu_to_le32(phys_status_start), ha->mem_ptr + IPS_REG_SQSR);
    writel(cpu_to_le32(phys_status_start + IPS_STATUS_Q_SIZE), ha->mem_ptr + IPS_REG_SQER);
    writel(cpu_to_le32(phys_status_start + IPS_STATUS_SIZE), ha->mem_ptr + IPS_REG_SQHR);
@@ -6734,7 +6745,7 @@
    scb->cmd.basic_io.op_code = IPS_CMD_ENQUIRY;
    scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb);
    scb->cmd.basic_io.sg_count = 0;
-   scb->cmd.basic_io.sg_addr = cpu_to_le32(VIRT_TO_BUS(ha->enq));
+   scb->cmd.basic_io.sg_addr = cpu_to_le32(virt_to_bus(ha->enq));
    scb->cmd.basic_io.lba = 0;
    scb->cmd.basic_io.sector_count = 0;
    scb->cmd.basic_io.log_drv = 0;
@@ -6775,7 +6786,7 @@
    scb->cmd.basic_io.op_code = IPS_CMD_GET_SUBSYS;
    scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb);
    scb->cmd.basic_io.sg_count = 0;
-   scb->cmd.basic_io.sg_addr = cpu_to_le32(VIRT_TO_BUS(ha->subsys));
+   scb->cmd.basic_io.sg_addr = cpu_to_le32(virt_to_bus(ha->subsys));
    scb->cmd.basic_io.lba = 0;
    scb->cmd.basic_io.sector_count = 0;
    scb->cmd.basic_io.log_drv = 0;
@@ -6820,7 +6831,7 @@
 
    scb->cmd.basic_io.op_code = IPS_CMD_READ_CONF;
    scb->cmd.basic_io.command_id = IPS_COMMAND_ID(ha, scb);
-   scb->cmd.basic_io.sg_addr = cpu_to_le32(VIRT_TO_BUS(ha->conf));
+   scb->cmd.basic_io.sg_addr = cpu_to_le32(virt_to_bus(ha->conf));
 
    /* send command */
    if (((ret = ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE) ||
@@ -6866,7 +6877,7 @@
    scb->cmd.nvram.command_id = IPS_COMMAND_ID(ha, scb);
    scb->cmd.nvram.page = 5;
    scb->cmd.nvram.write = write;
-   scb->cmd.nvram.buffer_addr = cpu_to_le32(VIRT_TO_BUS(ha->nvram));
+   scb->cmd.nvram.buffer_addr = cpu_to_le32(virt_to_bus(ha->nvram));
    scb->cmd.nvram.reserved = 0;
    scb->cmd.nvram.reserved2 = 0;
 
diff -Nru a/drivers/scsi/ips.h b/drivers/scsi/ips.h
--- a/drivers/scsi/ips.h	Mon Apr 22 15:32:19 2002
+++ b/drivers/scsi/ips.h	Mon Apr 22 15:32:19 2002
@@ -443,7 +443,8 @@
     present : 0,                          \
     unchecked_isa_dma : 0,                \
     use_clustering : ENABLE_CLUSTERING,   \
-    use_new_eh_code : 1                   \
+    use_new_eh_code : 1,                  \
+    highmem_io : 1                        \
 }
 #else
  #define IPS {                            \
diff -Nru a/drivers/scsi/pcmcia/Makefile b/drivers/scsi/pcmcia/Makefile
--- a/drivers/scsi/pcmcia/Makefile	Mon Apr 22 15:32:19 2002
+++ b/drivers/scsi/pcmcia/Makefile	Mon Apr 22 15:32:19 2002
@@ -1,6 +1,4 @@
 #
-# drivers/scsi/pcmcia/Makefile
-#
 # Makefile for the Linux PCMCIA SCSI drivers.
 #
 
@@ -21,18 +19,8 @@
 obj-$(CONFIG_PCMCIA_AHA152X)	+= aha152x_cs.o
 obj-$(CONFIG_PCMCIA_NINJA_SCSI)	+= nsp_cs.o
 
-list-multi	:= qlogic_cs.o fdomain_cs.o aha152x_cs.o
 aha152x_cs-objs	:= aha152x_stub.o aha152x.o
 fdomain_cs-objs	:= fdomain_stub.o fdomain.o
 qlogic_cs-objs	:= qlogic_stub.o qlogicfas.o
 
 include $(TOPDIR)/Rules.make
-
-aha152x_cs.o: $(aha152x_cs-objs)
-	$(LD) -r -o $@ $(aha152x_cs-objs)
-
-fdomain_cs.o: $(fdomain_cs-objs)
-	$(LD) -r -o $@ $(fdomain_cs-objs)
-
-qlogic_cs.o: $(qlogic_cs-objs)
-	$(LD) -r -o $@ $(qlogic_cs-objs)
diff -Nru a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
--- a/drivers/scsi/scsi_debug.c	Mon Apr 22 15:32:18 2002
+++ b/drivers/scsi/scsi_debug.c	Mon Apr 22 15:32:18 2002
@@ -14,8 +14,8 @@
  *
  *   D. Gilbert (dpg) work for MOD device test [20010421]
  *   dpg, work for devfs large number of disks [20010809]
- *   dpg, make more generic [20011123]
  *   dpg, forked for lk 2.5 series [20011216, 20020101]
+ *   dpg, use vmalloc() more inquiry+mode_sense [20020302]
  */
 
 #include <linux/config.h>
@@ -46,7 +46,7 @@
 #include <linux/version.h>
 #endif
 
-static char scsi_debug_version_str[] = "Version: 1.57 (20011216)";
+static char scsi_debug_version_str[] = "Version: 1.59 (20020302)";
 
 #ifndef SCSI_CMD_READ_16
 #define SCSI_CMD_READ_16 0x88
@@ -60,22 +60,26 @@
 #define DEF_DEV_SIZE_MB   8
 #define DEF_FAKE_BLK0   0
 
+#define DEF_OPTS   0
+#define SCSI_DEBUG_OPT_NOISE   1
+#define SCSI_DEBUG_OPT_MEDIUM_ERR   2
+
+#define OPT_MEDIUM_ERR_ADDR   0x1234
+
 static int scsi_debug_num_devs = DEF_NR_FAKE_DEVS;
+static int scsi_debug_opts = DEF_OPTS;
 
 #define NR_HOSTS_PRESENT (((scsi_debug_num_devs - 1) / 7) + 1)
 #define N_HEAD          8
 #define N_SECTOR        32
-#define DISK_READONLY(TGT)      (0)
-#define DISK_REMOVEABLE(TGT)    (0)
+#define DEV_READONLY(TGT)      (0)
+#define DEV_REMOVEABLE(TGT)    (0)
 #define DEVICE_TYPE(TGT) (TYPE_DISK);
 
 #define SCSI_DEBUG_MAILBOXES (scsi_debug_num_devs + 1)
 
 static int scsi_debug_dev_size_mb = DEF_DEV_SIZE_MB;
 #define STORE_SIZE (scsi_debug_dev_size_mb * 1024 * 1024)
-#define STORE_ELEM_ORDER 1
-#define STORE_ELEM_SIZE (PAGE_SIZE * (1 << STORE_ELEM_ORDER))
-#define STORE_ELEMENTS ((STORE_SIZE / STORE_ELEM_SIZE) + 1)
 
 /* default sector size is 512 bytes, 2**9 bytes */
 #define POW2_SECT_SIZE 9
@@ -83,34 +87,24 @@
 
 #define N_CYLINDER (STORE_SIZE / (SECT_SIZE * N_SECTOR * N_HEAD))
 
-static int scsi_debug_fake_blk0 = DEF_FAKE_BLK0;
-
 /* Do not attempt to use a timer to simulate a real disk with latency */
 /* Only use this in the actual kernel, not in the simulator. */
 #define IMMEDIATE
 
-#define SDEBUG_SG_ADDRESS
-
 #define START_PARTITION 4
 
 /* Time to wait before completing a command */
 #define DISK_SPEED     (HZ/10)	/* 100ms */
 #define CAPACITY (N_HEAD * N_SECTOR * N_CYLINDER)
 #define SECT_SIZE_PER(TGT) SECT_SIZE
-#define SECT_PER_ELEM (STORE_ELEM_SIZE / SECT_SIZE)
 
 static int starts[] =
 {N_SECTOR,
  N_HEAD * N_SECTOR,		/* Single cylinder */
  N_HEAD * N_SECTOR * 4,
  0 /* CAPACITY */, 0};
-static int npart = 0;
-
-typedef struct scsi_debug_store_elem {
-	unsigned char * p;
-} Sd_store_elem;
 
-static Sd_store_elem * store_arr = 0;
+static unsigned char * fake_storep;
 
 typedef struct sdebug_dev_info {
 	Scsi_Device * sdp;
@@ -135,13 +129,18 @@
 
 static volatile done_fct_t * do_done = 0;
 
-struct Scsi_Host * SHpnt = NULL;
+static struct Scsi_Host * SHpnt = NULL;
 
+static int scsi_debug_inquiry(unsigned char * cmd, int target,
+			      unsigned char * buff, int bufflen,
+			      Sdebug_dev_info * devip);
+static int scsi_debug_mode_sense(unsigned char * cmd, int target,
+			         unsigned char * buff, int bufflen,
+			         Sdebug_dev_info * devip);
 static int scsi_debug_read(Scsi_Cmnd * SCpnt, int upper_blk, int block, 
 			   int num, int * errstsp, Sdebug_dev_info * devip);
 static int scsi_debug_write(Scsi_Cmnd * SCpnt, int upper_blk, int block, 
 			    int num, int * errstsp, Sdebug_dev_info * devip);
-static void scsi_debug_send_self_command(struct Scsi_Host * shpnt);
 static void scsi_debug_intr_handle(unsigned long);
 static Sdebug_dev_info * devInfoReg(Scsi_Device * sdp);
 static void mk_sense_buffer(Sdebug_dev_info * devip, int index, int key, 
@@ -160,49 +159,23 @@
 #define SENSE_BUFF_LEN 32
 static char sense_buffers[NUM_SENSE_BUFFS][SENSE_BUFF_LEN];
 
-static int made_block0 = 0;
 
-static void scsi_debug_mkblock0(unsigned char * buff, int bufflen,
-				Scsi_Cmnd * SCpnt)
+static inline 
+unsigned char * sdebug_scatg2virt(const struct scatterlist * sclp)
 {
-	int i;
-	struct partition *p;
-
-	memset(buff, 0, bufflen);
-	*((unsigned short *) (buff + 510)) = 0xAA55;
-	p = (struct partition *) (buff + 0x1be);
-	i = 0;
-	while (starts[i + 1]) {
-		int start_cyl, end_cyl;
-
-		start_cyl = starts[i] / N_HEAD / N_SECTOR;
-		end_cyl = (starts[i + 1] - 1) / N_HEAD / N_SECTOR;
-		p->boot_ind = 0;
-
-		p->head = (i == 0 ? 1 : 0);
-		p->sector = 1 | ((start_cyl >> 8) << 6);
-		p->cyl = (start_cyl & 0xff);
-
-		p->end_head = N_HEAD - 1;
-		p->end_sector = N_SECTOR | ((end_cyl >> 8) << 6);
-		p->end_cyl = (end_cyl & 0xff);
-
-		p->start_sect = starts[i];
-		p->nr_sects = starts[i + 1] - starts[i];
-		p->sys_ind = 0x83;	/* Linux ext2 partition */
-		p++;
-		i++;
-	}
-	if (!npart)
-		npart = i;
-	made_block0 = 1;
-	i = (bufflen > STORE_ELEM_SIZE) ? STORE_ELEM_SIZE : bufflen;
-	memcpy(store_arr[0].p, buff, i);
+	if (NULL == sclp)
+		return NULL;
+	else if (sclp->page)
+		return (unsigned char *)page_address(sclp->page) + 
+		       sclp->offset;
+	else
+		return NULL;
 }
 
+static
 int scsi_debug_queuecommand(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *))
 {
-	unchar *cmd = (unchar *) SCpnt->cmnd;
+	unsigned char *cmd = (unsigned char *) SCpnt->cmnd;
 	int block;
 	int upper_blk;
 	unsigned char *buff;
@@ -214,6 +187,12 @@
 	Sdebug_dev_info * devip = NULL;
 	char * sbuff;
 
+	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) {
+		printk(KERN_INFO "scsi_debug: queue_command: cmd ");
+		for (i = 0, num = SCpnt->cmd_len; i < num; ++i)
+	            printk("%02x ", cmd[i]);
+		printk("   use_sg=%d\n", SCpnt->use_sg);
+	}
 	/*
 	 * If we are being notified of the mid-level reposessing a command
 	 * due to timeout, just return.
@@ -222,14 +201,23 @@
 		return 0;
 	}
 
-	buff = (unsigned char *) SCpnt->request_buffer;
+	if (SCpnt->use_sg) { /* just use first element */
+		struct scatterlist *sgpnt = (struct scatterlist *)
+						SCpnt->request_buffer;
+
+		buff = sdebug_scatg2virt(&sgpnt[0]);
+		bufflen = sgpnt[0].length;
+		/* READ and WRITE process scatterlist themselves */
+	}
+	else 
+		buff = (unsigned char *) SCpnt->request_buffer;
 
         /*
          * If a command comes for the ID of the host itself, just print
          * a silly message and return.
          */
         if(target == 7) {
-                printk("How do you do!\n");
+                printk(KERN_WARNING "How do you do!\n");
                 SCpnt->result = 0;
                 done(SCpnt);
                 return 0;
@@ -243,7 +231,7 @@
 #if 0
 	printk(KERN_INFO "sdebug:qc: host_no=%d, id=%d, sdp=%p, cmd=0x%x\n",
 	       (int)SCpnt->device->host->host_no, (int)SCpnt->device->id,
-	       SCpnt->device, (int)(unsigned char)*cmd);
+	       SCpnt->device, (int)*cmd);
 #endif
 	if (NULL == SCpnt->device->hostdata) {
 		devip = devInfoReg(SCpnt->device);
@@ -257,6 +245,13 @@
 	devip = SCpnt->device->hostdata;
 
 	switch (*cmd) {
+	case INQUIRY:     /* mandatory */
+		scsi_debug_errsts = scsi_debug_inquiry(cmd, target, buff, 
+						       bufflen, devip);
+		/* assume INQUIRY called first so setup max_cmd_len */
+		if (SCpnt->host->max_cmd_len != SCSI_DEBUG_MAX_CMD_LEN)
+			SCpnt->host->max_cmd_len = SCSI_DEBUG_MAX_CMD_LEN;
+		break;
 	case REQUEST_SENSE:	/* mandatory */
 		SCSI_LOG_LLQUEUE(3, printk("Request sense...\n"));
 		if (devip) {
@@ -294,23 +289,6 @@
 		}
 		scsi_debug_errsts = 0;
 		break;
-	case INQUIRY:     /* mandatory */
-		SCSI_LOG_LLQUEUE(3, printk("Inquiry...(%p %d)\n", buff, bufflen));
-		memset(buff, 0, bufflen);
-		buff[0] = DEVICE_TYPE(target);
-		buff[1] = DISK_REMOVEABLE(target) ? 0x80 : 0;	
-				/* Removable disk */
-		buff[2] = 2;	/* claim SCSI 2 */
-		buff[4] = 36 - 5;
-		memcpy(&buff[8], "Linux   ", 8);
-		memcpy(&buff[16], "scsi_debug      ", 16);
-		memcpy(&buff[32], "0002", 4);
-		scsi_debug_errsts = 0;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,10)
-		if (SCpnt->host->max_cmd_len != SCSI_DEBUG_MAX_CMD_LEN)
-			SCpnt->host->max_cmd_len = SCSI_DEBUG_MAX_CMD_LEN;
-#endif
-		break;
 	case SEND_DIAGNOSTIC:     /* mandatory */
 		SCSI_LOG_LLQUEUE(3, printk("Send Diagnostic\n"));
 		if (buff)
@@ -318,7 +296,8 @@
 		scsi_debug_errsts = 0;
 		break;
 	case TEST_UNIT_READY:     /* mandatory */
-		SCSI_LOG_LLQUEUE(3, printk("Test unit ready(%p %d)\n", buff, bufflen));
+		SCSI_LOG_LLQUEUE(3, printk("Test unit ready(%p %d)\n", 
+					   buff, bufflen));
 		if (buff)
 			memset(buff, 0, bufflen);
 		scsi_debug_errsts = 0;
@@ -424,19 +403,15 @@
 		(done) (SCpnt);	
 		return 0;
 	case MODE_SENSE:
-		/*
-		 * Used to detect write protected status.
-		 */
-		scsi_debug_errsts = 0;
-		memset(buff, 0, 6);
+	case MODE_SENSE_10:
+		scsi_debug_errsts = 
+		    scsi_debug_mode_sense(cmd, target, buff, bufflen, devip);
 		break;
 	default:
 #if 0
-		SCSI_LOG_LLQUEUE(3, printk("Unknown command %d\n", *cmd));
-		SCpnt->result = DID_NO_CONNECT << 16;
-		done(SCpnt);
-		return 0;
-#else
+		printk(KERN_INFO "scsi_debug: Unsupported command, "
+		       "opcode=0x%x\n", (int)cmd[0]);
+#endif
 		if (check_reset(SCpnt, devip)) {
 			done(SCpnt);
 			return 0;
@@ -445,7 +420,6 @@
 				    (CHECK_CONDITION << 1);
 		mk_sense_buffer(devip, 2, ILLEGAL_REQUEST, 0x20, 0, 14);
 		break;
-#endif
 	}
 
 	spin_lock_irqsave(&mailbox_lock, iflags);
@@ -485,10 +459,11 @@
 	spin_unlock_irqrestore(&mailbox_lock, iflags);
 	add_timer(&timeout[i]);
 	if (!done)
-		printk("scsi_debug_queuecommand: done can't be NULL\n");
+		printk(KERN_ERR "scsi_debug_queuecommand: "
+		       "done can't be NULL\n");
 
 #if 0
-	printk("Sending command (%d %x %d %d)...", i, done, 
+	printk(KERN_INFO "Sending command (%d %x %d %d)...", i, done, 
 	       timeout[i].expires, jiffies);
 #endif
 #endif
@@ -496,6 +471,14 @@
 	return 0;
 }
 
+static int scsi_debug_ioctl(Scsi_Device *dev, int cmd, void *arg)
+{
+	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) {
+		printk(KERN_INFO "scsi_debug: ioctl: cmd=0x%x\n", cmd);
+	}
+	return -ENOTTY;
+}
+
 static int check_reset(Scsi_Cmnd * SCpnt, Sdebug_dev_info * devip)
 {
 	if (devip->reset) {
@@ -508,21 +491,188 @@
 	return 0;
 }
 
-static inline 
-unsigned char * sdebug_scatg2virt(const struct scatterlist * sclp)
-{
-	if (NULL == sclp)
-		return NULL;
-	else if (sclp->page)
-		return (unsigned char *)page_address(sclp->page) + 
-		       sclp->offset;
+#define SDEBUG_MAX_INQ_SZ 58
+
+static int scsi_debug_inquiry(unsigned char * cmd, int target,
+			      unsigned char * buff, int bufflen,
+			      Sdebug_dev_info * devip)
+{
+	unsigned char pq_pdt;
+	unsigned char arr[SDEBUG_MAX_INQ_SZ];
+	int min_len = bufflen > SDEBUG_MAX_INQ_SZ ? 
+			SDEBUG_MAX_INQ_SZ : bufflen;
+
+	SCSI_LOG_LLQUEUE(3, printk("Inquiry...(%p %d)\n", buff, bufflen));
+	if (bufflen < cmd[4])
+		printk(KERN_INFO "scsi_debug: inquiry: bufflen=%d "
+		       "< alloc_length=%d\n", bufflen, (int)cmd[4]);
+	memset(buff, 0, bufflen);
+	memset(arr, 0, SDEBUG_MAX_INQ_SZ);
+	pq_pdt = DEVICE_TYPE(target);
+	arr[0] = pq_pdt;
+	if (0x2 & cmd[1]) {  /* CMDDT bit set */
+		mk_sense_buffer(devip, 1, ILLEGAL_REQUEST, 0x24, 0, 14);
+		return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1);
+	}
+	else if (0x1 & cmd[1]) {  /* EVPD bit set */
+		if (0 == cmd[2]) { /* supported vital product data pages */
+			arr[3] = 1;
+			arr[4] = 0x80; /* ... only unit serial number */
+		}
+		else if (0x80 == cmd[2]) { /* unit serial number */
+			arr[1] = 0x80;
+			arr[3] = 4;
+			arr[4] = '1'; arr[5] = '2'; arr[6] = '3';
+			arr[7] = '4';
+		}
+		else {
+			/* Illegal request, invalid field in cdb */
+			mk_sense_buffer(devip, 1, ILLEGAL_REQUEST, 0x24, 0, 14);
+			return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1);
+		}
+		memcpy(buff, arr, min_len); 
+		return 0;
+	}
+	/* drops through here for a standard inquiry */
+	arr[1] = DEV_REMOVEABLE(target) ? 0x80 : 0;	/* Removable disk */
+	arr[2] = 3;	/* claim SCSI 3 */
+	arr[4] = SDEBUG_MAX_INQ_SZ - 5;
+	arr[7] = 0x3a; /* claim: WBUS16, SYNC, LINKED + CMDQUE */
+	memcpy(&arr[8], "Linux   ", 8);
+	memcpy(&arr[16], "scsi_debug      ", 16);
+	memcpy(&arr[32], "0003", 4);
+	memcpy(buff, arr, min_len);
+	return 0;
+}
+
+/* <<Following mode page info copied from ST318451LW>> */ 
+
+static int sdebug_err_recov_pg(unsigned char * p, int pcontrol, int target)
+{	/* Read-Write Error Recovery page for mode_sense */
+	unsigned char err_recov_pg[] = {0x1, 0xa, 0xc0, 11, 240, 0, 0, 0, 
+					5, 0, 0xff, 0xff};
+
+	memcpy(p, err_recov_pg, sizeof(err_recov_pg));
+	if (1 == pcontrol)
+		memset(p + 2, 0, sizeof(err_recov_pg) - 2);
+	return sizeof(err_recov_pg);
+}
+
+static int sdebug_disconnect_pg(unsigned char * p, int pcontrol, int target)
+{ 	/* Disconnect-Reconnect page for mode_sense */
+	unsigned char disconnect_pg[] = {0x2, 0xe, 128, 128, 0, 10, 0, 0, 
+					 0, 0, 0, 0, 0, 0, 0, 0};
+
+	memcpy(p, disconnect_pg, sizeof(disconnect_pg));
+	if (1 == pcontrol)
+		memset(p + 2, 0, sizeof(disconnect_pg) - 2);
+	return sizeof(disconnect_pg);
+}
+
+static int sdebug_caching_pg(unsigned char * p, int pcontrol, int target)
+{ 	/* Caching page for mode_sense */
+	unsigned char caching_pg[] = {0x8, 18, 0x14, 0, 0xff, 0xff, 0, 0, 
+		0xff, 0xff, 0xff, 0xff, 0x80, 0x14, 0, 0,     0, 0, 0, 0};
+
+	memcpy(p, caching_pg, sizeof(caching_pg));
+	if (1 == pcontrol)
+		memset(p + 2, 0, sizeof(caching_pg) - 2);
+	return sizeof(caching_pg);
+}
+
+static int sdebug_ctrl_m_pg(unsigned char * p, int pcontrol, int target)
+{ 	/* Control mode page for mode_sense */
+	unsigned char ctrl_m_pg[] = {0xa, 10, 2, 0, 0, 0, 0, 0,
+				     0, 0, 0x2, 0x4b};
+
+	memcpy(p, ctrl_m_pg, sizeof(ctrl_m_pg));
+	if (1 == pcontrol)
+		memset(p + 2, 0, sizeof(ctrl_m_pg) - 2);
+	return sizeof(ctrl_m_pg);
+}
+
+
+#define SDEBUG_MAX_MSENSE_SZ 256
+
+static int scsi_debug_mode_sense(unsigned char * cmd, int target,
+			         unsigned char * buff, int bufflen,
+			         Sdebug_dev_info * devip)
+{
+	unsigned char dbd;
+	int pcontrol, pcode;
+	unsigned char dev_spec;
+	int alloc_len, msense_6, offset, len;
+	unsigned char * ap;
+	unsigned char arr[SDEBUG_MAX_MSENSE_SZ];
+	int min_len = bufflen > SDEBUG_MAX_MSENSE_SZ ? 
+			SDEBUG_MAX_MSENSE_SZ : bufflen;
+
+	SCSI_LOG_LLQUEUE(3, printk("Mode sense ...(%p %d)\n", buff, bufflen));
+	dbd = cmd[1] & 0x8;
+	pcontrol = (cmd[2] & 0xc0) >> 6;
+	pcode = cmd[2] & 0x3f;
+	msense_6 = (MODE_SENSE == cmd[0]);
+	alloc_len = msense_6 ? cmd[4] : ((cmd[7] << 8) | cmd[6]);
+	/* printk(KERN_INFO "msense: dbd=%d pcontrol=%d pcode=%d "
+		"msense_6=%d alloc_len=%d\n", dbd, pcontrol, pcode, "
+		"msense_6, alloc_len); */
+	if (bufflen < alloc_len)
+		printk(KERN_INFO "scsi_debug: mode_sense: bufflen=%d "
+		       "< alloc_length=%d\n", bufflen, alloc_len);
+	memset(buff, 0, bufflen);
+	memset(arr, 0, SDEBUG_MAX_MSENSE_SZ);
+	if (0x3 == pcontrol) {  /* Saving values not supported */
+		mk_sense_buffer(devip, 1, ILLEGAL_REQUEST, 0x39, 0, 14);
+		return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1);
+	}
+	dev_spec = DEV_READONLY(target) ? 0x80 : 0x0;
+	if (msense_6) {
+		arr[2] = dev_spec;
+		offset = 4;
+	}
 	else {
-#ifdef SDEBUG_SG_ADDRESS
-		return sclp->address;
-#else
-		return NULL;
-#endif
+		arr[3] = dev_spec;
+		offset = 8;
+	}
+	ap = arr + offset;
+
+	switch (pcode) {
+	case 0x1:	/* Read-Write error recovery page, direct access */
+		len = sdebug_err_recov_pg(ap, pcontrol, target);
+		offset += len;
+		break;
+	case 0x2:	/* Disconnect-Reconnect page, all devices */
+		len = sdebug_disconnect_pg(ap, pcontrol, target);
+		offset += len;
+		break;
+	case 0x8:	/* Caching page, direct access */
+		len = sdebug_caching_pg(ap, pcontrol, target);
+		offset += len;
+		break;
+	case 0xa:	/* Control Mode page, all devices */
+		len = sdebug_ctrl_m_pg(ap, pcontrol, target);
+		offset += len;
+		break;
+	case 0x3f:	/* Read all Mode pages */
+		len = sdebug_err_recov_pg(ap, pcontrol, target);
+		len += sdebug_disconnect_pg(ap + len, pcontrol, target);
+		len += sdebug_caching_pg(ap + len, pcontrol, target);
+		len += sdebug_ctrl_m_pg(ap + len, pcontrol, target);
+		offset += len;
+		break;
+	default:
+		mk_sense_buffer(devip, 1, ILLEGAL_REQUEST, 0x24, 0, 14);
+		return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1);
+	}
+	if (msense_6)
+		arr[0] = offset - 1;
+	else {
+		offset -= 2;
+		arr[0] = (offset >> 8) & 0xff; 
+		arr[1] = offset & 0xff; 
 	}
+	memcpy(buff, arr, min_len);
+	return 0;
 }
 
 static int scsi_debug_read(Scsi_Cmnd * SCpnt, int upper_blk, int block, 
@@ -549,11 +699,19 @@
 			usleep(delay);
 	}
 #endif
-
+	if ((SCSI_DEBUG_OPT_MEDIUM_ERR & scsi_debug_opts) &&
+	    (block >= OPT_MEDIUM_ERR_ADDR) && 
+	    (block < (OPT_MEDIUM_ERR_ADDR + num))) {
+		*errstsp = (COMMAND_COMPLETE << 8) |
+			   (CHECK_CONDITION << 1);
+		mk_sense_buffer(devip, 1, MEDIUM_ERROR, 0x11, 0, 14);
+		/* claim unrecoverable read error */
+		return 1;
+	}
 	read_lock_irqsave(&sdebug_atomic_rw, iflags);
         sgcount = 0;
 	nbytes = bufflen;
-	/* printk("scsi_debug_read: block=%d, tot_bufflen=%d\n", 
+	/* printk(KERN_INFO "scsi_debug_read: block=%d, tot_bufflen=%d\n", 
 	       block, bufflen); */
 	if (SCpnt->use_sg) {
 		sgcount = 0;
@@ -563,30 +721,7 @@
 	}
 	*errstsp = 0;
 	do {
-		int resid, k, off, len, rem, blk;
-		unsigned char * bp;
-
-		/* If this is block 0, then we want to read the partition
-		 * table for this device.  Let's make one up */
-		if (scsi_debug_fake_blk0 && (block == 0) && (! made_block0)) {
-			scsi_debug_mkblock0(buff, bufflen, SCpnt);
-			*errstsp = 0;
-			break;
-		}
-		bp = buff;
-		blk = block;
-		for (resid = bufflen; resid > 0; resid -= len) {
-			k = blk / SECT_PER_ELEM; 
-			off = (blk % SECT_PER_ELEM) * SECT_SIZE;
-			rem = STORE_ELEM_SIZE - off;
-			len = (resid > rem) ? rem : resid;
-/* printk("sdr: blk=%d k=%d off=%d rem=%d resid"
-			       "=%d len=%d  sgcount=%d\n", blk, k, 
-				off, rem, resid, len, sgcount); */
-			memcpy(bp, store_arr[k].p + off, len);
-			bp += len;
-			blk += len / SECT_SIZE;
-		}
+		memcpy(buff, fake_storep + (block * SECT_SIZE), bufflen);
 #if 0
 		/* Simulate a disk change */
 		if (block == 0xfff0) {
@@ -612,7 +747,8 @@
 			}
 		}
 		else if (nbytes > 0)
-			printk("sdebug_read: unexpected nbytes=%d\n", nbytes);
+			printk(KERN_WARNING "sdebug_read: unexpected "
+			       "nbytes=%d\n", nbytes);
 	} while (nbytes);
 	read_unlock_irqrestore(&sdebug_atomic_rw, iflags);
 	return 0;
@@ -645,20 +781,7 @@
 	}
 	*errstsp = 0;
 	do {
-		int resid, k, off, len, rem, blk;
-		unsigned char * bp;
-
-		bp = buff;
-		blk = block;
-		for (resid = bufflen; resid > 0; resid -= len) {
-			k = blk / SECT_PER_ELEM; 
-			off = (blk % SECT_PER_ELEM) * SECT_SIZE;
-			rem = STORE_ELEM_SIZE - off;
-			len = (resid > rem) ? rem : resid;
-			memcpy(store_arr[k].p + off, bp, len);
-			bp += len;
-			blk += len / SECT_SIZE;
-		}
+		memcpy(fake_storep + (block * SECT_SIZE), buff, bufflen);
 
 		nbytes -= bufflen;
 		if (SCpnt->use_sg) {
@@ -670,52 +793,13 @@
 			}
 		}
 		else if (nbytes > 0)
-			printk("sdebug_write: unexpected nbytes=%d\n", nbytes);
+			printk(KERN_WARNING "sdebug_write: "
+			       "unexpected nbytes=%d\n", nbytes);
 	} while (nbytes);
 	write_unlock_irqrestore(&sdebug_atomic_rw, iflags);
 	return 0;
 }
 
-static void scsi_debug_send_self_command(struct Scsi_Host * shpnt)
-{
-	static unsigned char cmd[6] =
-	{TEST_UNIT_READY, 0, 0, 0, 0, 0};
-
-        Scsi_Request  * scp;
-        Scsi_Device   * sdev;
-        
-        printk("Allocating host dev\n");
-        sdev = scsi_get_host_dev(shpnt);
-        if(sdev==NULL)
-        {
-        	printk("Out of memory.\n");
-        	return;
-        }
-        
-        printk("Got %p. Allocating command block\n", sdev);
-        scp  = scsi_allocate_request(sdev);
-        printk("Got %p\n", scp);
-        
-        if(scp==NULL)
-        {
-        	printk("Out of memory.\n");
-        	goto bail;
-        }
-
-        scp->sr_cmd_len = 6;
-        scp->sr_use_sg = 0;
-        
-        printk("Sending command\n");
-        scsi_wait_req (scp, (void *) cmd, (void *) NULL,
-                       0, 100, 3);
-        
-        printk("Releasing command\n");
-        scsi_release_request(scp);
-bail:
-	printk("Freeing device\n");
-        scsi_free_host_dev(sdev);
-}
-
 /* A "high" level interrupt handler.  This should be called once per jiffy
  * to simulate a regular scsi disk.  We use a timer to do this. */
 
@@ -734,18 +818,19 @@
 	SCint[indx] = NULL;
 
 	if (!my_done) {
-		printk("scsi_debug_intr_handle: Unexpected interrupt\n");
+		printk(KERN_ERR "scsi_debug_intr_handle: Unexpected "
+		       "interrupt\n");
 		return;
 	}
 #if 0
-	printk("In intr_handle...");
-	printk("...done %d %x %d %d\n", i, my_done, to, jiffies);
-	printk("In intr_handle: %d %x %x\n", i, SCtmp, my_done);
+	printk(KERN_INFO "In intr_handle...");
+	printk(KERN_INFO "...done %d %x %d %d\n", i, my_done, to, jiffies);
+	printk(KERN_INFO "In intr_handle: %d %x %x\n", i, SCtmp, my_done);
 #endif
 
 	my_done(SCtmp);
 #if 0
-	printk("Called done.\n");
+	printk(KERN_INFO "Called done.\n");
 #endif
 }
 
@@ -753,14 +838,13 @@
 
 static int do_init(void)
 {
-	int sz;
+	int sz = STORE_SIZE;
 
 	starts[3] = CAPACITY;
-	sz = sizeof(Sd_store_elem) * STORE_ELEMENTS;
-	store_arr = kmalloc(sz, GFP_ATOMIC);
-	if (NULL == store_arr)
+	fake_storep = vmalloc(sz);
+	if (NULL == fake_storep)
 		return 1;
-	memset(store_arr, 0, sz);
+	memset(fake_storep, 0, sz);
 
 	sz = sizeof(done_fct_t) * SCSI_DEBUG_MAILBOXES;
 	do_done = kmalloc(sz, GFP_ATOMIC);
@@ -783,8 +867,8 @@
 	return 0;
 
 out:
-	if (store_arr)
-		kfree(store_arr);
+	if (fake_storep)
+		vfree(fake_storep);
 	if (do_done)
 		kfree((void *)do_done);
 	if (timeout)
@@ -799,34 +883,31 @@
 	kfree(SCint);
 	kfree(timeout);
 	kfree((void *)do_done);
-	kfree(store_arr);
+	vfree(fake_storep);
 }
 
 
-int scsi_debug_detect(Scsi_Host_Template * tpnt)
+static int scsi_debug_detect(Scsi_Host_Template * tpnt)
 {
-	int k, num, sz;
+	int k, sz;
 
+	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
+		printk(KERN_INFO "scsi_debug: detect\n");
 	if (0 == initialized) {
 		++initialized;
 		sz = sizeof(Sdebug_dev_info) * scsi_debug_num_devs;
 		devInfop = kmalloc(sz, GFP_ATOMIC);
 		if (NULL == devInfop) {
-			printk("scsi_debug_detect: out of memory, 0.5\n");
+			printk(KERN_ERR "scsi_debug_detect: out of "
+			       "memory, 0.5\n");
 			return 0;
 		}
 		memset(devInfop, 0, sz);
 		if (do_init()) {
-			printk("scsi_debug_detect: out of memory, 0\n");
+			printk(KERN_ERR "scsi_debug_detect: out of memory"
+			       ", 0\n");
 			return 0;
 		}
-		for (k = 0; k < STORE_ELEMENTS; ++k) {
-			store_arr[k].p = (unsigned char *)
-				__get_free_pages(GFP_ATOMIC, STORE_ELEM_ORDER);
-			if (0 == store_arr[k].p)
-				goto detect_err;
-			memset(store_arr[k].p, 0, STORE_ELEM_SIZE);
-		}
 		for (k = 0; k < NUM_SENSE_BUFFS; ++k)
 			sense_buffers[k][0] = 0x70;
 		for (k = 0; k < NR_HOSTS_PRESENT; k++) {
@@ -836,43 +917,21 @@
 		return NR_HOSTS_PRESENT;
 	}
 	else {
-		printk("scsi_debug_detect: called again\n");
+		printk(KERN_WARNING "scsi_debug_detect: called again\n");
 		return 0;
 	}
-
-detect_err:
-	num = k;
-	for (k = 0; k < STORE_ELEMENTS; ++k) {
-		if (0 != store_arr[k].p) {
-			free_pages((unsigned long)store_arr[k].p, 
-				   STORE_ELEM_ORDER);
-			store_arr[k].p = NULL;
-		}
-	}
-	printk("scsi_debug_detect: out of memory: %d out of %d bytes\n",
-	       (int)(num * STORE_ELEM_SIZE), 
-	       (int)(scsi_debug_dev_size_mb * 1024 * 1024));
-	return 0;
 }
 
 
 static int num_releases = 0;
 
-int scsi_debug_release(struct Scsi_Host * hpnt)
+static int scsi_debug_release(struct Scsi_Host * hpnt)
 {
-	int k;
-	
+	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
+		printk(KERN_INFO "scsi_debug: release\n");
 	scsi_unregister(hpnt);
 	if (++num_releases != NR_HOSTS_PRESENT)
 		return 0;
-
-	for (k = 0; k < STORE_ELEMENTS; ++k) {
-		if (0 != store_arr[k].p) {
-			free_pages((unsigned long)store_arr[k].p, 
-				   STORE_ELEM_ORDER);
-			store_arr[k].p = NULL;
-		}
-	}
 	do_end();
 	kfree(devInfop);
 	return 0;
@@ -922,8 +981,10 @@
 	sbuff[13] = asq;
 }
 
-int scsi_debug_abort(Scsi_Cmnd * SCpnt)
+static int scsi_debug_abort(Scsi_Cmnd * SCpnt)
 {
+	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
+		printk(KERN_INFO "scsi_debug: abort\n");
 #if 1
 	++num_aborts;
 	return SUCCESS;
@@ -947,8 +1008,10 @@
 #endif
 }
 
-int scsi_debug_biosparam(Disk * disk, kdev_t dev, int *info)
+static int scsi_debug_biosparam(Disk * disk, kdev_t dev, int *info)
 {
+	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
+		printk(KERN_INFO "scsi_debug: biosparam\n");
 	/* int size = disk->capacity; */
 	info[0] = N_HEAD;
 	info[1] = N_SECTOR;
@@ -958,36 +1021,13 @@
 	return 0;
 }
 
-#if 0
-int scsi_debug_reset(Scsi_Cmnd * SCpnt, unsigned int why)
-{
-	int i;
-	unsigned long iflags;
-
-	void (*my_done) (Scsi_Cmnd *);
-	printk("Bus unlocked by reset - %d\n", why);
-	scsi_debug_lockup = 0;
-	for (i = 0; i < SCSI_DEBUG_MAILBOXES; i++) {
-		if (SCint[i] == NULL)
-			continue;
-		SCint[i]->result = DID_RESET << 16;
-		my_done = do_done[i];
-		my_done(SCint[i]);
-		spin_lock_irqsave(&mailbox_lock, iflags);
-		SCint[i] = NULL;
-		do_done[i] = NULL;
-		timeout[i].function = NULL;
-		spin_unlock_irqrestore(&mailbox_lock, iflags);
-	}
-	return SCSI_RESET_SUCCESS;
-}
-#endif
-
-int scsi_debug_device_reset(Scsi_Cmnd * SCpnt)
+static int scsi_debug_device_reset(Scsi_Cmnd * SCpnt)
 {
 	Scsi_Device * sdp;
 	int k;
 
+	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
+		printk(KERN_INFO "scsi_debug: device_reset\n");
 	++num_dev_resets;
 	if (SCpnt && ((sdp = SCpnt->device))) {
 		for (k = 0; k < scsi_debug_num_devs; ++k) {
@@ -1000,12 +1040,14 @@
 	return SUCCESS;
 }
 
-int scsi_debug_bus_reset(Scsi_Cmnd * SCpnt)
+static int scsi_debug_bus_reset(Scsi_Cmnd * SCpnt)
 {
 	Scsi_Device * sdp;
 	struct Scsi_Host * hp;
 	int k;
 
+	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
+		printk(KERN_INFO "scsi_debug: bus_reset\n");
 	++num_bus_resets;
 	if (SCpnt && ((sdp = SCpnt->device)) && ((hp = sdp->host))) {
 		for (k = 0; k < scsi_debug_num_devs; ++k) {
@@ -1016,10 +1058,12 @@
 	return SUCCESS;
 }
 
-int scsi_debug_host_reset(Scsi_Cmnd * SCpnt)
+static int scsi_debug_host_reset(Scsi_Cmnd * SCpnt)
 {
 	int k;
 
+	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
+		printk(KERN_INFO "scsi_debug: host_reset\n");
 	++num_host_resets;
 	for (k = 0; k < scsi_debug_num_devs; ++k)
 		devInfop[k].reset = 1;
@@ -1037,7 +1081,7 @@
             scsi_debug_num_devs = tmp;
         return 1;
     } else {
-        printk("scsi_debug_num_devs: usage scsi_debug_num_devs=<n> "
+        printk(KERN_INFO "scsi_debug_num_devs: usage scsi_debug_num_devs=<n> "
                "(<n> can be from 1 to around 2000)\n");
         return 0;
     }
@@ -1054,13 +1098,32 @@
             scsi_debug_dev_size_mb = tmp;
         return 1;
     } else {
-        printk("scsi_debug_dev_size_mb: usage scsi_debug_dev_size_mb=<n>\n"
+        printk(KERN_INFO "scsi_debug_dev_size_mb: usage "
+	       "scsi_debug_dev_size_mb=<n>\n"
                "    (<n> is number of MB ram shared by all devs\n");
         return 0;
     }
 }
 
 __setup("scsi_debug_dev_size_mb=", scsi_debug_dev_size_mb_setup);
+
+static int __init scsi_debug_opts_setup(char *str)
+{   
+    int tmp; 
+    
+    if (get_option(&str, &tmp) == 1) {
+        if (tmp > 0)
+            scsi_debug_opts = tmp;
+        return 1;
+    } else {
+        printk(KERN_INFO "scsi_debug_opts: usage "
+	       "scsi_debug_opts=<n>\n"
+               "    (1->noise, 2->medium_error, 4->... (can be or-ed)\n");
+        return 0;
+    }
+}
+
+__setup("scsi_debug_opts=", scsi_debug_opts_setup);
 #endif
 
 MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert");
@@ -1069,6 +1132,8 @@
 MODULE_PARM_DESC(scsi_debug_num_devs, "number of SCSI devices to simulate");
 MODULE_PARM(scsi_debug_dev_size_mb, "i");
 MODULE_PARM_DESC(scsi_debug_dev_size_mb, "size in MB of ram shared by devs");
+MODULE_PARM(scsi_debug_opts, "i");
+MODULE_PARM_DESC(scsi_debug_opts, "1->noise, 2->medium_error, 4->...");
 
 #ifdef MODULE_LICENSE
 MODULE_LICENSE("GPL");
@@ -1076,19 +1141,20 @@
 
 static char sdebug_info[256];
 
-const char * scsi_debug_info(struct Scsi_Host * shp)
+static const char * scsi_debug_info(struct Scsi_Host * shp)
 {
 	sprintf(sdebug_info, "scsi_debug, %s, num_devs=%d, "
-		"dev_size_mb=%d\n", scsi_debug_version_str,
-		scsi_debug_num_devs, scsi_debug_dev_size_mb);
+		"dev_size_mb=%d, opts=0x%x", scsi_debug_version_str,
+		scsi_debug_num_devs, scsi_debug_dev_size_mb,
+		scsi_debug_opts);
 	return sdebug_info;
 }
 
 /* scsi_debug_proc_info
  * Used if the driver currently has no own support for /proc/scsi
  */
-int scsi_debug_proc_info(char *buffer, char **start, off_t offset,
-			 int length, int inode, int inout)
+static int scsi_debug_proc_info(char *buffer, char **start, off_t offset,
+				int length, int inode, int inout)
 {
 	int len, pos, begin;
 	int orig_length;
@@ -1096,55 +1162,28 @@
 	orig_length = length;
 
 	if (inout == 1) {
-		/* First check for the Signature */
-		if (length >= 10 && strncmp(buffer, "scsi_debug", 10) == 0) {
-			buffer += 11;
-			length -= 11;
-
-			if (buffer[length - 1] == '\n') {
-				buffer[length - 1] = '\0';
-				length--;
-			}
-			/*
-			 * OK, we are getting some kind of command.  Figure out
-			 * what we are supposed to do here.  Simulate bus lockups
-			 * to test our reset capability.
-			 */
-			if (length == 4 && strncmp(buffer, "test", length) == 0) {
-                                printk("Testing send self command %p\n", SHpnt);
-                                scsi_debug_send_self_command(SHpnt);
-                                return orig_length;
-                        }
-			if (length == 6 && strncmp(buffer, "lockup", length) == 0) {
-				scsi_debug_lockup = 1;
-				return orig_length;
-			}
-			if (length == 6 && strncmp(buffer, "unlock", length) == 0) {
-				scsi_debug_lockup = 0;
-				return orig_length;
-			}
-			printk("Unknown command:%s (%d)\n", buffer, length);
-		} else
-			printk("Wrong Signature:%10s\n", (char *) buffer);
-
-		return -EINVAL;
+		char arr[16];
+		int minLen = length > 15 ? 15 : length;
 
+		if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
+			return -EACCES;
+		memcpy(arr, buffer, minLen);
+		arr[minLen] = '\0';
+		if (1 != sscanf(arr, "%d", &pos))
+			return -EINVAL;
+		scsi_debug_opts = pos;
+		return length;
 	}
 	begin = 0;
 	pos = len = sprintf(buffer, "scsi_debug adapter driver, %s\n"
-	    "num_devs=%d, shared (ram) size=%d MB, sector_size=%d bytes\n" 
-	    "cylinders=%d, heads=%d, sectors=%d\n"
+	    "num_devs=%d, shared (ram) size=%d MB, opts=0x%x\n"
+	    "sector_size=%d bytes, cylinders=%d, heads=%d, sectors=%d\n"
 	    "number of aborts=%d, device_reset=%d, bus_resets=%d, " 
 	    "host_resets=%d\n",
 	    scsi_debug_version_str, scsi_debug_num_devs, 
-	    scsi_debug_dev_size_mb, SECT_SIZE,
+	    scsi_debug_dev_size_mb, scsi_debug_opts, SECT_SIZE,
 	    N_CYLINDER, N_HEAD, N_SECTOR,
 	    num_aborts, num_dev_resets, num_bus_resets, num_host_resets);
-#if 0
-	 "This driver is not a real scsi driver, but it plays one on TV.\n"
-	 "It is very handy for debugging specific problems because you\n"
-			 "can simulate a variety of error conditions\n");
-#endif
 	if (pos < offset) {
 		len = 0;
 		begin = pos;
diff -Nru a/drivers/scsi/scsi_debug.h b/drivers/scsi/scsi_debug.h
--- a/drivers/scsi/scsi_debug.h	Mon Apr 22 15:32:19 2002
+++ b/drivers/scsi/scsi_debug.h	Mon Apr 22 15:32:19 2002
@@ -3,16 +3,16 @@
 #include <linux/types.h>
 #include <linux/kdev_t.h>
 
-int scsi_debug_detect(Scsi_Host_Template *);
-int scsi_debug_command(Scsi_Cmnd *);
-int scsi_debug_queuecommand(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *));
-int scsi_debug_abort(Scsi_Cmnd *);
-int scsi_debug_biosparam(Disk *, kdev_t, int[]);
-int scsi_debug_bus_reset(Scsi_Cmnd *);
-int scsi_debug_dev_reset(Scsi_Cmnd *);
-int scsi_debug_host_reset(Scsi_Cmnd *);
-int scsi_debug_proc_info(char *, char **, off_t, int, int, int);
-const char * scsi_debug_info(struct Scsi_Host *);
+static int scsi_debug_detect(Scsi_Host_Template *);
+/* static int scsi_debug_command(Scsi_Cmnd *); */
+static int scsi_debug_queuecommand(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *));
+static int scsi_debug_abort(Scsi_Cmnd *);
+static int scsi_debug_biosparam(Disk *, kdev_t, int[]);
+static int scsi_debug_bus_reset(Scsi_Cmnd *);
+static int scsi_debug_device_reset(Scsi_Cmnd *);
+static int scsi_debug_host_reset(Scsi_Cmnd *);
+static int scsi_debug_proc_info(char *, char **, off_t, int, int, int);
+static const char * scsi_debug_info(struct Scsi_Host *);
 
 #ifndef NULL
 #define NULL 0
@@ -31,6 +31,7 @@
 		    info:              scsi_debug_info,		\
 		    detect:            scsi_debug_detect,	\
 		    release:           scsi_debug_release,	\
+		    ioctl:             scsi_debug_ioctl,	\
 		    queuecommand:      scsi_debug_queuecommand, \
 		    eh_abort_handler:  scsi_debug_abort,	\
 		    eh_bus_reset_handler: scsi_debug_bus_reset,	\
diff -Nru a/drivers/scsi/scsicam.c b/drivers/scsi/scsicam.c
--- a/drivers/scsi/scsicam.c	Mon Apr 22 15:32:18 2002
+++ b/drivers/scsi/scsicam.c	Mon Apr 22 15:32:18 2002
@@ -126,7 +126,7 @@
 	unsigned int logical_end, physical_end, ext_physical_end;
 
 
-	if (*(unsigned short *) (buf + 66) == 0xAA55) {
+	if (*(unsigned short *) (buf + 64) == 0xAA55) {
 		for (largest_cyl = -1, i = 0; i < 4; ++i, ++p) {
 			if (!p->sys_ind)
 				continue;
diff -Nru a/drivers/scsi/st.c b/drivers/scsi/st.c
--- a/drivers/scsi/st.c	Mon Apr 22 15:32:18 2002
+++ b/drivers/scsi/st.c	Mon Apr 22 15:32:18 2002
@@ -924,8 +924,8 @@
 	int dev = TAPE_NR(inode->i_rdev);
 
 	write_lock(&st_dev_arr_lock);
-	STp = scsi_tapes[dev];
-	if (dev >= st_template.dev_max || STp == NULL) {
+	if (dev >= st_template.dev_max || scsi_tapes == NULL ||
+	    ((STp = scsi_tapes[dev]) == NULL)) {
 		write_unlock(&st_dev_arr_lock);
 		return (-ENXIO);
 	}
diff -Nru a/drivers/scsi/sym53c8xx_2/Makefile b/drivers/scsi/sym53c8xx_2/Makefile
--- a/drivers/scsi/sym53c8xx_2/Makefile	Mon Apr 22 15:32:19 2002
+++ b/drivers/scsi/sym53c8xx_2/Makefile	Mon Apr 22 15:32:19 2002
@@ -1,14 +1,11 @@
-# File: drivers/sym53c8xx/Makefile
 # Makefile for the NCR/SYMBIOS/LSI 53C8XX PCI SCSI controllers driver.
 
-list-multi := sym53c8xx.o
+O_TARGET := sym53c8xx_2.o
+
 sym53c8xx-objs := sym_fw.o sym_glue.o sym_hipd.o sym_malloc.o sym_misc.o sym_nvram.o
 obj-$(CONFIG_SCSI_SYM53C8XX_2) := sym53c8xx.o
 
 EXTRA_CFLAGS += -I.
-
-sym53c8xx.o: $(sym53c8xx-objs)
-	$(LD) -r -o $@ $(sym53c8xx-objs)
 
 include $(TOPDIR)/Rules.make
 
diff -Nru a/drivers/usb/class/audio.c b/drivers/usb/class/audio.c
--- a/drivers/usb/class/audio.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/usb/class/audio.c	Mon Apr 22 15:32:19 2002
@@ -3523,7 +3523,7 @@
 	unsigned char *p1;
 	unsigned int i, j;
 
-	if (test_and_set_bit(unitid, &state->unitbitmap)) {
+	if (test_and_set_bit(unitid, state->unitbitmap)) {
 		printk(KERN_INFO "usbaudio: mixer path revisits unit %d\n", unitid);
 		return;
 	}
@@ -3571,7 +3571,7 @@
 		return;		
 
 	case PROCESSING_UNIT:
-		if (p1[0] < 13 || p1[0] < 13+p1[6] || p1[0] < 13+p1[6]+p1[11+p1[6]] || p1[0] < 13+p1[6]+p1[11+p1[6]]+p1[13+p1[6]+p1[11+p1[6]]]) {
+		if (p1[0] < 13 || p1[0] < 13+p1[6] || p1[0] < 13+p1[6]+p1[11+p1[6]]) {
 			printk(KERN_ERR "usbaudio: unit %u: invalid PROCESSING_UNIT descriptor\n", unitid);
 			return;
 		}
@@ -3613,7 +3613,7 @@
 	state.buffer = buffer;
 	state.buflen = buflen;
 	state.ctrlif = ctrlif;
-	set_bit(oterm[3], &state.unitbitmap);  /* mark terminal ID as visited */
+	set_bit(oterm[3], state.unitbitmap);  /* mark terminal ID as visited */
 	printk(KERN_DEBUG "usbaudio: constructing mixer for Terminal %u type 0x%04x\n",
 	       oterm[3], oterm[4] | (oterm[5] << 8));
 	usb_audio_recurseunit(&state, oterm[7]);
diff -Nru a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
--- a/drivers/usb/class/cdc-acm.c	Mon Apr 22 15:32:18 2002
+++ b/drivers/usb/class/cdc-acm.c	Mon Apr 22 15:32:18 2002
@@ -144,7 +144,7 @@
 	struct usb_device *dev;				/* the coresponding usb device */
 	struct usb_interface *iface;			/* the interfaces - +0 control +1 data */
 	struct tty_struct *tty;				/* the coresponding tty */
-	struct urb ctrlurb, readurb, writeurb;		/* urbs */
+	struct urb *ctrlurb, *readurb, *writeurb;	/* urbs */
 	struct acm_line line;				/* line coding (bits, stop, parity) */
 	struct tq_struct tqueue;			/* task queue for line discipline waking up */
 	unsigned int ctrlin;				/* input control lines (DCD, DSR, RI, break, overruns) */
@@ -316,12 +316,12 @@
 
         unlock_kernel();
 
-	acm->ctrlurb.dev = acm->dev;
-	if (usb_submit_urb(&acm->ctrlurb, GFP_KERNEL))
+	acm->ctrlurb->dev = acm->dev;
+	if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL))
 		dbg("usb_submit_urb(ctrl irq) failed");
 
-	acm->readurb.dev = acm->dev;
-	if (usb_submit_urb(&acm->readurb, GFP_KERNEL))
+	acm->readurb->dev = acm->dev;
+	if (usb_submit_urb(acm->readurb, GFP_KERNEL))
 		dbg("usb_submit_urb(read bulk) failed");
 
 	acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS);
@@ -342,12 +342,15 @@
 	if (!--acm->used) {
 		if (acm->dev) {
 			acm_set_control(acm, acm->ctrlout = 0);
-			usb_unlink_urb(&acm->ctrlurb);
-			usb_unlink_urb(&acm->writeurb);
-			usb_unlink_urb(&acm->readurb);
+			usb_unlink_urb(acm->ctrlurb);
+			usb_unlink_urb(acm->writeurb);
+			usb_unlink_urb(acm->readurb);
 		} else {
 			tty_unregister_devfs(&acm_tty_driver, acm->minor);
 			acm_table[acm->minor] = NULL;
+			usb_free_urb(acm->ctrlurb);
+			usb_free_urb(acm->readurb);
+			usb_free_urb(acm->writeurb);
 			kfree(acm);
 		}
 	}
@@ -359,20 +362,20 @@
 	struct acm *acm = tty->driver_data;
 
 	if (!ACM_READY(acm)) return -EINVAL;
-	if (acm->writeurb.status == -EINPROGRESS) return 0;
+	if (acm->writeurb->status == -EINPROGRESS) return 0;
 	if (!count) return 0;
 
 	count = (count > acm->writesize) ? acm->writesize : count;
 
 	if (from_user)
-		copy_from_user(acm->writeurb.transfer_buffer, buf, count);
+		copy_from_user(acm->writeurb->transfer_buffer, buf, count);
 	else
-		memcpy(acm->writeurb.transfer_buffer, buf, count);
+		memcpy(acm->writeurb->transfer_buffer, buf, count);
 
-	acm->writeurb.transfer_buffer_length = count;
-	acm->writeurb.dev = acm->dev;
+	acm->writeurb->transfer_buffer_length = count;
+	acm->writeurb->dev = acm->dev;
 
-	if (usb_submit_urb(&acm->writeurb, GFP_KERNEL))
+	if (usb_submit_urb(acm->writeurb, GFP_KERNEL))
 		dbg("usb_submit_urb(write bulk) failed");
 
 	return count;
@@ -382,14 +385,14 @@
 {
 	struct acm *acm = tty->driver_data;
 	if (!ACM_READY(acm)) return -EINVAL;
-	return acm->writeurb.status == -EINPROGRESS ? 0 : acm->writesize;
+	return acm->writeurb->status == -EINPROGRESS ? 0 : acm->writesize;
 }
 
 static int acm_tty_chars_in_buffer(struct tty_struct *tty)
 {
 	struct acm *acm = tty->driver_data;
 	if (!ACM_READY(acm)) return -EINVAL;
-	return acm->writeurb.status == -EINPROGRESS ? acm->writeurb.transfer_buffer_length : 0;
+	return acm->writeurb->status == -EINPROGRESS ? acm->writeurb->transfer_buffer_length : 0;
 }
 
 static void acm_tty_throttle(struct tty_struct *tty)
@@ -404,8 +407,8 @@
 	struct acm *acm = tty->driver_data;
 	if (!ACM_READY(acm)) return;
 	acm->throttle = 0;
-	if (acm->readurb.status != -EINPROGRESS)
-		acm_read_bulk(&acm->readurb);
+	if (acm->readurb->status != -EINPROGRESS)
+		acm_read_bulk(acm->readurb);
 }
 
 static void acm_tty_break_ctl(struct tty_struct *tty, int state)
@@ -585,16 +588,38 @@
 			return NULL;
 		}
 
-		FILL_INT_URB(&acm->ctrlurb, dev, usb_rcvintpipe(dev, epctrl->bEndpointAddress),
+		acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
+		if (!acm->ctrlurb) {
+			err("out of memory");
+			kfree(acm);
+			return NULL;
+		}
+		acm->readurb = usb_alloc_urb(0, GFP_KERNEL);
+		if (!acm->readurb) {
+			err("out of memory");
+			usb_free_urb(acm->ctrlurb);
+			kfree(acm);
+			return NULL;
+		}
+		acm->writeurb = usb_alloc_urb(0, GFP_KERNEL);
+		if (!acm->writeurb) {
+			err("out of memory");
+			usb_free_urb(acm->readurb);
+			usb_free_urb(acm->ctrlurb);
+			kfree(acm);
+			return NULL;
+		}
+
+		usb_fill_int_urb(acm->ctrlurb, dev, usb_rcvintpipe(dev, epctrl->bEndpointAddress),
 			buf, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval);
 
-		FILL_BULK_URB(&acm->readurb, dev, usb_rcvbulkpipe(dev, epread->bEndpointAddress),
+		usb_fill_bulk_urb(acm->readurb, dev, usb_rcvbulkpipe(dev, epread->bEndpointAddress),
 			buf += ctrlsize, readsize, acm_read_bulk, acm);
-		acm->readurb.transfer_flags |= USB_NO_FSBR;
+		acm->readurb->transfer_flags |= USB_NO_FSBR;
 
-		FILL_BULK_URB(&acm->writeurb, dev, usb_sndbulkpipe(dev, epwrite->bEndpointAddress),
+		usb_fill_bulk_urb(acm->writeurb, dev, usb_sndbulkpipe(dev, epwrite->bEndpointAddress),
 			buf += readsize, acm->writesize, acm_write_bulk, acm);
-		acm->writeurb.transfer_flags |= USB_NO_FSBR;
+		acm->writeurb->transfer_flags |= USB_NO_FSBR;
 
 		printk(KERN_INFO "ttyACM%d: USB ACM device\n", minor);
 
@@ -625,11 +650,11 @@
 
 	acm->dev = NULL;
 
-	usb_unlink_urb(&acm->ctrlurb);
-	usb_unlink_urb(&acm->readurb);
-	usb_unlink_urb(&acm->writeurb);
+	usb_unlink_urb(acm->ctrlurb);
+	usb_unlink_urb(acm->readurb);
+	usb_unlink_urb(acm->writeurb);
 
-	kfree(acm->ctrlurb.transfer_buffer);
+	kfree(acm->ctrlurb->transfer_buffer);
 
 	usb_driver_release_interface(&acm_driver, acm->iface + 0);
 	usb_driver_release_interface(&acm_driver, acm->iface + 1);
@@ -637,6 +662,9 @@
 	if (!acm->used) {
 		tty_unregister_devfs(&acm_tty_driver, acm->minor);
 		acm_table[acm->minor] = NULL;
+		usb_free_urb(acm->ctrlurb);
+		usb_free_urb(acm->readurb);
+		usb_free_urb(acm->writeurb);
 		kfree(acm);
 		return;
 	}
diff -Nru a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
--- a/drivers/usb/core/devio.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/usb/core/devio.c	Mon Apr 22 15:32:19 2002
@@ -297,7 +297,9 @@
 }
 
 /*
- * interface claiming
+ * interface claims are made only at the request of user level code,
+ * which can also release them (explicitly or by closing files).
+ * they're also undone when devices disconnect.
  */
 
 static void *driver_probe(struct usb_device *dev, unsigned int intf,
@@ -310,8 +312,20 @@
 {
 	struct dev_state *ps = (struct dev_state *)context;
 
-	if (ps)
-		ps->ifclaimed = 0;
+	if (!ps)
+		return;
+
+	/* this waits till synchronous requests complete */
+	down_write (&ps->devsem);
+
+	/* prevent new I/O requests */
+	ps->dev = 0;
+	ps->ifclaimed = 0;
+
+	/* force async requests to complete */
+	destroy_all_async (ps);
+
+	up_write (&ps->devsem);
 }
 
 struct usb_driver usbdevfs_driver = {
diff -Nru a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
--- a/drivers/usb/core/hcd.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/usb/core/hcd.c	Mon Apr 22 15:32:19 2002
@@ -310,7 +310,7 @@
 
 	// serial number
 	} else if (id == 1) {
-		strcpy (buf, hcd->bus_name);
+		strcpy (buf, hcd->self.bus_name);
 
 	// product description
 	} else if (id == 2) {
@@ -406,7 +406,7 @@
 	case DeviceOutRequest | USB_REQ_SET_ADDRESS:
 		// wValue == urb->dev->devaddr
 		dbg ("%s root hub device address %d",
-			hcd->bus_name, wValue);
+			hcd->self.bus_name, wValue);
 		break;
 
 	/* INTERFACE REQUESTS (no defined feature/status flags) */
@@ -520,7 +520,7 @@
 					&& rh_status_urb (hcd, urb) != 0) {
 				/* another driver snuck in? */
 				dbg ("%s, can't resubmit roothub status urb?",
-					hcd->bus_name);
+					hcd->self.bus_name);
 				spin_unlock_irqrestore (&hcd_data_lock, flags);
 				BUG ();
 			}
@@ -1051,8 +1051,7 @@
 	usb_init_bus (&hcd->self);
 	hcd->self.op = &hcd_operations;
 	hcd->self.hcpriv = (void *) hcd;
-	hcd->bus = &hcd->self;
-	hcd->bus_name = dev->slot_name;
+	hcd->self.bus_name = dev->slot_name;
 	hcd->product_desc = dev->name;
 
 	INIT_LIST_HEAD (&hcd->dev_list);
@@ -1089,16 +1088,15 @@
 	hcd = pci_get_drvdata(dev);
 	if (!hcd)
 		return;
-	info ("remove: %s, state %x", hcd->bus_name, hcd->state);
+	info ("remove: %s, state %x", hcd->self.bus_name, hcd->state);
 
 	if (in_interrupt ()) BUG ();
 
-	hub = hcd->bus->root_hub;
+	hub = hcd->self.root_hub;
 	hcd->state = USB_STATE_QUIESCING;
 
-	dbg ("%s: roothub graceful disconnect", hcd->bus_name);
+	dbg ("%s: roothub graceful disconnect", hcd->self.bus_name);
 	usb_disconnect (&hub);
-	// usb_disconnect (&hcd->bus->root_hub);
 
 	hcd->driver->stop (hcd);
 	hcd->state = USB_STATE_HALT;
@@ -1113,10 +1111,9 @@
 			pci_resource_len (dev, hcd->region));
 	}
 
-	usb_deregister_bus (hcd->bus);
+	usb_deregister_bus (&hcd->self);
 	if (atomic_read (&hcd->self.refcnt) != 1)
-		err ("usb_hcd_pci_remove %s, count != 1", hcd->bus_name);
-	hcd->bus = NULL;
+		err ("usb_hcd_pci_remove %s, count != 1", hcd->self.bus_name);
 
 	hcd->driver->hcd_free (hcd);
 }
@@ -1164,7 +1161,7 @@
 	int			retval;
 
 	hcd = pci_get_drvdata(dev);
-	info ("suspend %s to state %d", hcd->bus_name, state);
+	info ("suspend %s to state %d", hcd->self.bus_name, state);
 
 	pci_save_state (dev, hcd->pci_state);
 
@@ -1193,12 +1190,12 @@
 	int			retval;
 
 	hcd = pci_get_drvdata(dev);
-	info ("resume %s", hcd->bus_name);
+	info ("resume %s", hcd->self.bus_name);
 
 	/* guard against multiple resumes (APM bug?) */
 	atomic_inc (&hcd->resume_count);
 	if (atomic_read (&hcd->resume_count) != 1) {
-		err ("concurrent PCI resumes for %s", hcd->bus_name);
+		err ("concurrent PCI resumes for %s", hcd->self.bus_name);
 		retval = 0;
 		goto done;
 	}
@@ -1215,7 +1212,7 @@
 
 	retval = hcd->driver->resume (hcd);
 	if (!HCD_IS_RUNNING (hcd->state)) {
-		dbg ("resume %s failure, retval %d", hcd->bus_name, retval);
+		dbg ("resume %s failure, retval %d", hcd->self.bus_name, retval);
 		hc_died (hcd);
 // FIXME:  recover, reset etc.
 	} else {
@@ -1290,7 +1287,7 @@
 		list_for_each (urblist, &dev->urb_list) {
 			urb = list_entry (urblist, struct urb, urb_list);
 			dbg ("shutdown %s urb %p pipe %x, current status %d",
-				hcd->bus_name, urb, urb->pipe, urb->status);
+				hcd->self.bus_name, urb, urb->pipe, urb->status);
 			if (urb->status == -EINPROGRESS)
 				urb->status = -ESHUTDOWN;
 		}
@@ -1534,7 +1531,7 @@
 	 * since we report some queuing/setup errors ourselves
 	 */
 	urb = usb_get_urb (urb);
-	if (urb->dev == hcd->bus->root_hub)
+	if (urb->dev == hcd->self.root_hub)
 		status = rh_urb_enqueue (hcd, urb);
 	else
 		status = hcd->driver->urb_enqueue (hcd, urb, mem_flags);
@@ -1686,7 +1683,7 @@
 			&& HCD_IS_RUNNING (hcd->state)
 			&& !retval) {
 		dbg ("%s: wait for giveback urb %p",
-			hcd->bus_name, urb);
+			hcd->self.bus_name, urb);
 		wait_for_completion (&splice.done);
 	} else if ((urb->transfer_flags & USB_ASYNC_UNLINK) && retval == 0) {
 		return -EINPROGRESS;
@@ -1698,7 +1695,7 @@
 bye:
 	if (retval)
 		dbg ("%s: hcd_unlink_urb fail %d",
-		    hcd ? hcd->bus_name : "(no bus?)",
+		    hcd ? hcd->self.bus_name : "(no bus?)",
 		    retval);
 	return retval;
 }
@@ -1731,7 +1728,7 @@
 	/* device driver problem with refcounts? */
 	if (!list_empty (&dev->urb_list)) {
 		dbg ("free busy dev, %s devnum %d (bug!)",
-			hcd->bus_name, udev->devnum);
+			hcd->self.bus_name, udev->devnum);
 		return -EINVAL;
 	}
 
diff -Nru a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
--- a/drivers/usb/core/hcd.h	Mon Apr 22 15:32:19 2002
+++ b/drivers/usb/core/hcd.h	Mon Apr 22 15:32:19 2002
@@ -35,10 +35,8 @@
 	/*
 	 * housekeeping
 	 */
-	struct usb_bus		*bus;		/* FIXME only use "self" */
 	struct usb_bus		self;		/* hcd is-a bus */
 
-	const char		*bus_name;
 	const char		*product_desc;	/* product/vendor string */
 	const char		*description;	/* "ehci-hcd" etc */
 
diff -Nru a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
--- a/drivers/usb/core/hub.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/usb/core/hub.c	Mon Apr 22 15:32:19 2002
@@ -765,23 +765,23 @@
 		 * devices by location for diagnostics, tools, etc.  The
 		 * string is a path along hub ports, from the root.  Each
 		 * device's id will be stable until USB is re-cabled, and
-		 * hubs are often labled with these port numbers.
+		 * hubs are often labeled with these port numbers.
 		 *
-		 * Initial size: "/NN" times five hubs + NUL = 16 bytes max
+		 * Initial size: ".NN" times five hubs + NUL = 16 bytes max
 		 * (quite rare, since most hubs have 4-6 ports).
 		 */
 		pdev = dev->parent;
-		if (pdev->devpath [1] != '\0')	/* parent not root */
+		if (pdev->devpath [0] != '/')	/* parent not root */
 			len = snprintf (dev->devpath, sizeof dev->devpath,
-				"%s/%d", pdev->devpath, port + 1);
-		else	/* root == "/", root port 2 == "/2" */
+				"%s.%d", pdev->devpath, port + 1);
+		else	/* root == "/", root port 2 == "2", port 3 that hub "/2.3" */
 			len = snprintf (dev->devpath, sizeof dev->devpath,
-				"/%d", port + 1);
+				"%d", port + 1);
 		if (len == sizeof dev->devpath)
 			warn ("devpath size! usb/%03d/%03d path %s",
 				dev->bus->busnum, dev->devnum, dev->devpath);
-		info("new USB device on bus %d path %s, assigned address %d",
-			dev->bus->busnum, dev->devpath, dev->devnum);
+		info("new USB device %s-%s, assigned address %d",
+			dev->bus->bus_name, dev->devpath, dev->devnum);
 
 		/* put the device in the global device tree */
 		dev->dev.parent = &dev->parent->dev;
@@ -964,7 +964,7 @@
 	/* Send me a signal to get me die (for debugging) */
 	do {
 		usb_hub_events();
-		interruptible_sleep_on(&khubd_wait);
+		wait_event_interruptible(khubd_wait, !list_empty(&hub_event_list)); 
 	} while (!signal_pending(current));
 
 	dbg("usb_hub_thread exiting");
@@ -1122,7 +1122,7 @@
 					dev->devpath,
 					sizeof(dev->descriptor), ret);
         
-			clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+			clear_bit(dev->devnum, dev->bus->devmap.devicemap);
 			dev->devnum = -1;
 			return -EIO;
 		}
@@ -1131,7 +1131,7 @@
 		if (ret < 0) {
 			err("unable to get configuration (error=%d)", ret);
 			usb_destroy_configuration(dev);
-			clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+			clear_bit(dev->devnum, dev->bus->devmap.devicemap);
 			dev->devnum = -1;
 			return 1;
 		}
diff -Nru a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
--- a/drivers/usb/core/usb.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/usb/core/usb.c	Mon Apr 22 15:32:19 2002
@@ -1784,7 +1784,7 @@
 
 	/* Free the device number and remove the /proc/bus/usb entry */
 	if (dev->devnum > 0) {
-		clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+		clear_bit(dev->devnum, dev->bus->devmap.devicemap);
 		usbfs_remove_device(dev);
 		put_device(&dev->dev);
 	}
@@ -2407,56 +2407,6 @@
 	return err;
 }
 
-/**
- * usb_make_path - returns device path in the hub tree
- * @dev: the device whose path is being constructed
- * @buf: where to put the string
- * @size: how big is "buf"?
- * Context: !in_interrupt ()
- *
- * Returns length of the string (>= 0) or out of memory status (< 0).
- *
- * NOTE:  prefer to use use dev->devpath directly.
- */
-int usb_make_path(struct usb_device *dev, char *buf, size_t size)
-{
-	struct usb_device *pdev = dev->parent;
-	char *tmp;
-	char *port;
-	int i;
-
-	if (!(port = kmalloc(size, GFP_KERNEL)))
-		return -ENOMEM;
-	if (!(tmp = kmalloc(size, GFP_KERNEL))) {
-		kfree(port);
-		return -ENOMEM;
-	}
-
-	*port = 0;
-	while (pdev) {
-		for (i = 0; i < pdev->maxchild; i++)
-			if (pdev->children[i] == dev)
-				break;
-
-		if (pdev->children[i] != dev) {
-			kfree(port);
-			kfree(tmp);
-			return -ENODEV;
-		}
-
-		strcpy(tmp, port);
-		snprintf(port, size, strlen(port) ? "%d.%s" : "%d", i + 1, tmp);
-
-		dev = pdev;
-		pdev = dev->parent;
-	}
-
-	snprintf(buf, size, "usb%d:%s", dev->bus->busnum, port);
-	kfree(port);
-	kfree(tmp);
-	return strlen(buf);
-}
-
 /*
  * By the time we get here, the device has gotten a new device ID
  * and is in the default state. We need to identify the thing and
@@ -2484,7 +2434,7 @@
 	if (err < 0) {
 		err("USB device not accepting new address=%d (error=%d)",
 			dev->devnum, err);
-		clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+		clear_bit(dev->devnum, dev->bus->devmap.devicemap);
 		dev->devnum = -1;
 		return 1;
 	}
@@ -2497,7 +2447,7 @@
 			err("USB device not responding, giving up (error=%d)", err);
 		else
 			err("USB device descriptor short read (expected %i, got %i)", 8, err);
-		clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+		clear_bit(dev->devnum, dev->bus->devmap.devicemap);
 		dev->devnum = -1;
 		return 1;
 	}
@@ -2512,7 +2462,7 @@
 			err("USB device descriptor short read (expected %Zi, got %i)",
 				sizeof(dev->descriptor), err);
 	
-		clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+		clear_bit(dev->devnum, dev->bus->devmap.devicemap);
 		dev->devnum = -1;
 		return 1;
 	}
@@ -2521,7 +2471,7 @@
 	if (err < 0) {
 		err("unable to get device %d configuration (error=%d)",
 			dev->devnum, err);
-		clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+		clear_bit(dev->devnum, dev->bus->devmap.devicemap);
 		dev->devnum = -1;
 		return 1;
 	}
@@ -2531,7 +2481,7 @@
 	if (err) {
 		err("failed to set device %d default configuration (error=%d)",
 			dev->devnum, err);
-		clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+		clear_bit(dev->devnum, dev->bus->devmap.devicemap);
 		dev->devnum = -1;
 		return 1;
 	}
diff -Nru a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
--- a/drivers/usb/host/ehci-dbg.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/usb/host/ehci-dbg.c	Mon Apr 22 15:32:19 2002
@@ -55,7 +55,7 @@
 			strcat(buf, tmp);
 		}
 		dbg ("%s: %s portroute %s", 
-			ehci->hcd.bus_name, label,
+			ehci->hcd.self.bus_name, label,
 			buf);
 	}
 }
diff -Nru a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
--- a/drivers/usb/host/ehci-hcd.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/usb/host/ehci-hcd.c	Mon Apr 22 15:32:19 2002
@@ -248,7 +248,7 @@
 	ehci->tasklet.data = (unsigned long) ehci;
 
 	/* wire up the root hub */
-	hcd->bus->root_hub = udev = usb_alloc_dev (NULL, hcd->bus);
+	hcd->self.root_hub = udev = usb_alloc_dev (NULL, &hcd->self);
 	if (!udev) {
 done2:
 		ehci_mem_cleanup (ehci);
@@ -288,8 +288,7 @@
 		while (readl (&ehci->regs->status) & (STS_ASS | STS_PSS))
 			udelay (100);
 		ehci_reset (ehci);
-		// usb_disconnect (udev); 
-		hcd->bus->root_hub = 0;
+		hcd->self.root_hub = 0;
 		usb_free_dev (udev); 
 		retval = -ENODEV;
 		goto done2;
@@ -304,7 +303,7 @@
 {
 	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
 
-	dbg ("%s: stop", hcd->bus_name);
+	dbg ("%s: stop", hcd->self.bus_name);
 
 	if (hcd->state == USB_STATE_RUNNING)
 		ehci_ready (ehci);
@@ -339,7 +338,7 @@
 	int			ports;
 	int			i;
 
-	dbg ("%s: suspend to %d", hcd->bus_name, state);
+	dbg ("%s: suspend to %d", hcd->self.bus_name, state);
 
 	ports = HCS_N_PORTS (ehci->hcs_params);
 
@@ -356,7 +355,7 @@
 		if ((temp & PORT_PE) == 0
 				|| (temp & PORT_OWNER) != 0)
 			continue;
-dbg ("%s: suspend port %d", hcd->bus_name, i);
+dbg ("%s: suspend port %d", hcd->self.bus_name, i);
 		temp |= PORT_SUSPEND;
 		writel (temp, &ehci->regs->port_status [i]);
 	}
@@ -380,7 +379,7 @@
 	int			ports;
 	int			i;
 
-	dbg ("%s: resume", hcd->bus_name);
+	dbg ("%s: resume", hcd->self.bus_name);
 
 	ports = HCS_N_PORTS (ehci->hcs_params);
 
@@ -400,7 +399,7 @@
 		if ((temp & PORT_PE) == 0
 				|| (temp & PORT_SUSPEND) != 0)
 			continue;
-dbg ("%s: resume port %d", hcd->bus_name, i);
+dbg ("%s: resume port %d", hcd->self.bus_name, i);
 		temp |= PORT_RESUME;
 		writel (temp, &ehci->regs->port_status [i]);
 		readl (&ehci->regs->command);	/* unblock posted writes */
@@ -472,7 +471,7 @@
 
 	/* PCI errors [4.15.2.4] */
 	if (unlikely ((status & STS_FATAL) != 0)) {
-		err ("%s: fatal error, state %x", hcd->bus_name, hcd->state);
+		err ("%s: fatal error, state %x", hcd->self.bus_name, hcd->state);
 		ehci_reset (ehci);
 		// generic layer kills/unlinks all urbs
 		// then tasklet cleans up the rest
@@ -547,7 +546,7 @@
 	unsigned long		flags;
 
 	dbg ("%s urb_dequeue %p qh state %d",
-		hcd->bus_name, urb, qh->qh_state);
+		hcd->self.bus_name, urb, qh->qh_state);
 
 	switch (usb_pipetype (urb->pipe)) {
 	case PIPE_CONTROL:
@@ -608,7 +607,7 @@
 
 	/* ASSERT:  nobody can be submitting urbs for this any more */
 
-	dbg ("%s: free_config devnum %d", hcd->bus_name, udev->devnum);
+	dbg ("%s: free_config devnum %d", hcd->self.bus_name, udev->devnum);
 
 	spin_lock_irqsave (&ehci->lock, flags);
 	for (i = 0; i < 32; i++) {
@@ -645,7 +644,7 @@
 					spin_lock_irqsave (&ehci->lock, flags);
 				}
 			}
-			qh_unput (ehci, qh);
+			qh_put (ehci, qh);
 		}
 	}
 
diff -Nru a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
--- a/drivers/usb/host/ehci-hub.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/usb/host/ehci-hub.c	Mon Apr 22 15:32:19 2002
@@ -41,14 +41,14 @@
 	/* if reset finished and it's still not enabled -- handoff */
 	if (!(port_status & PORT_PE)) {
 		dbg ("%s port %d full speed, give to companion, 0x%x",
-			ehci->hcd.bus_name, index + 1, port_status);
+			ehci->hcd.self.bus_name, index + 1, port_status);
 
 		// what happens if HCS_N_CC(params) == 0 ?
 		port_status |= PORT_OWNER;
 		writel (port_status, &ehci->regs->port_status [index]);
 
 	} else
-		dbg ("%s port %d high speed", ehci->hcd.bus_name, index + 1);
+		dbg ("%s port %d high speed", ehci->hcd.self.bus_name, index + 1);
 
 	return port_status;
 }
@@ -306,11 +306,11 @@
 			if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT
 					&& PORT_USB11 (temp)) {
 				dbg ("%s port %d low speed, give to companion",
-					hcd->bus_name, wIndex + 1);
+					hcd->self.bus_name, wIndex + 1);
 				temp |= PORT_OWNER;
 			} else {
 				vdbg ("%s port %d reset",
-					hcd->bus_name, wIndex + 1);
+					hcd->self.bus_name, wIndex + 1);
 				temp |= PORT_RESET;
 				temp &= ~PORT_PE;
 
diff -Nru a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c
--- a/drivers/usb/host/ehci-mem.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/usb/host/ehci-mem.c	Mon Apr 22 15:32:19 2002
@@ -98,16 +98,16 @@
 }
 
 /* to share a qh (cpu threads, or hc) */
-static inline struct ehci_qh *qh_put (/* ehci, */ struct ehci_qh *qh)
+static inline struct ehci_qh *qh_get (/* ehci, */ struct ehci_qh *qh)
 {
-	// dbg ("put %p (%d++)", qh, qh->refcount.counter);
+	// dbg ("get %p (%d++)", qh, qh->refcount.counter);
 	atomic_inc (&qh->refcount);
 	return qh;
 }
 
-static void qh_unput (struct ehci_hcd *ehci, struct ehci_qh *qh)
+static void qh_put (struct ehci_hcd *ehci, struct ehci_qh *qh)
 {
-	// dbg ("unput %p (--%d)", qh, qh->refcount.counter);
+	// dbg ("put %p (--%d)", qh, qh->refcount.counter);
 	if (!atomic_dec_and_test (&qh->refcount))
 		return;
 	/* clean qtds first, and know this is not linked */
diff -Nru a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
--- a/drivers/usb/host/ehci-q.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/usb/host/ehci-q.c	Mon Apr 22 15:32:19 2002
@@ -194,7 +194,7 @@
 			    ? PCI_DMA_FROMDEVICE
 			    : PCI_DMA_TODEVICE);
 	if (likely (urb->hcpriv != 0)) {
-		qh_unput (ehci, (struct ehci_qh *) urb->hcpriv);
+		qh_put (ehci, (struct ehci_qh *) urb->hcpriv);
 		urb->hcpriv = 0;
 	}
 
@@ -733,7 +733,7 @@
 		epnum |= 0x10;
 
 	vdbg ("%s: submit_async urb %p len %d ep %d-%s qtd %p [qh %p]",
-		ehci->hcd.bus_name, urb, urb->transfer_buffer_length,
+		ehci->hcd.self.bus_name, urb, urb->transfer_buffer_length,
 		epnum & 0x0f, (epnum & 0x10) ? "in" : "out",
 		qtd, dev ? dev->ep [epnum] : (void *)~0);
 
@@ -815,9 +815,9 @@
 	 * the HC and TT handle it when the TT has a buffer ready.
 	 */
 	if (likely (qh != 0)) {
-		urb->hcpriv = qh_put (qh);
+		urb->hcpriv = qh_get (qh);
 		if (likely (qh->qh_state == QH_STATE_IDLE))
-			qh_link_async (ehci, qh_put (qh));
+			qh_link_async (ehci, qh_get (qh));
 	}
 	spin_unlock_irqrestore (&ehci->lock, flags);
 	if (unlikely (!qh))
@@ -835,7 +835,7 @@
 
 	qh->qh_state = QH_STATE_IDLE;
 	qh->qh_next.qh = 0;
-	qh_unput (ehci, qh);			// refcount from reclaim 
+	qh_put (ehci, qh);			// refcount from reclaim 
 	ehci->reclaim = 0;
 	ehci->reclaim_ready = 0;
 
@@ -847,7 +847,7 @@
 			&& HCD_IS_RUNNING (ehci->hcd.state))
 		qh_link_async (ehci, qh);
 	else
-		qh_unput (ehci, qh);		// refcount from async list
+		qh_put (ehci, qh);		// refcount from async list
 }
 
 
@@ -872,7 +872,7 @@
 #endif
 
 	qh->qh_state = QH_STATE_UNLINK;
-	ehci->reclaim = qh = qh_put (qh);
+	ehci->reclaim = qh = qh_get (qh);
 
 	// dbg_qh ("start unlink", ehci, qh);
 
@@ -937,14 +937,14 @@
 			/* clean any finished work for this qh */
 			if (!list_empty (&qh->qtd_list)) {
 				// dbg_qh ("scan_async", ehci, qh);
-				qh = qh_put (qh);
+				qh = qh_get (qh);
 				spin_unlock_irqrestore (&ehci->lock, flags);
 
 				/* concurrent unlink could happen here */
 				qh_completions (ehci, &qh->qtd_list, 1);
 
 				spin_lock_irqsave (&ehci->lock, flags);
-				qh_unput (ehci, qh);
+				qh_put (ehci, qh);
 			}
 
 			/* unlink idle entries (reduces PCI usage) */
diff -Nru a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
--- a/drivers/usb/host/ehci-sched.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/usb/host/ehci-sched.c	Mon Apr 22 15:32:19 2002
@@ -224,7 +224,7 @@
 
 	do {
 		periodic_unlink (ehci, frame, qh);
-		qh_unput (ehci, qh);
+		qh_put (ehci, qh);
 		frame += period;
 	} while (frame < ehci->periodic_size);
 
@@ -345,7 +345,7 @@
 		qh->hw_next = EHCI_LIST_END;
 		qh->usecs = usecs;
 
-		urb->hcpriv = qh_put (qh);
+		urb->hcpriv = qh_get (qh);
 		status = -ENOSPC;
 
 		/* pick a set of schedule slots, link the QH into them */
@@ -393,7 +393,7 @@
 // AND handle it already being (implicitly) linked into this frame
 					BUG ();
 				} else {
-					ehci->pshadow [frame].qh = qh_put (qh);
+					ehci->pshadow [frame].qh = qh_get (qh);
 					ehci->periodic [frame] =
 						QH_NEXT (qh->qh_dma);
 				}
@@ -1113,8 +1113,8 @@
 				temp = q.qh->qh_next;
 				type = Q_NEXT_TYPE (q.qh->hw_next);
 				flags = intr_complete (ehci, frame,
-						qh_put (q.qh), flags);
-				qh_unput (ehci, q.qh);
+						qh_get (q.qh), flags);
+				qh_put (ehci, q.qh);
 				q = temp;
 				break;
 			case Q_TYPE_FSTN:
diff -Nru a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c
--- a/drivers/usb/host/ohci-dbg.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/usb/host/ohci-dbg.c	Mon Apr 22 15:32:19 2002
@@ -89,7 +89,7 @@
 			continue;
 		printed = 1;
 		printk (KERN_DEBUG "%s, ohci %s frame %2d:",
-				label, ohci->hcd.bus_name, i);
+				label, ohci->hcd.self.bus_name, i);
 		while (*ed_p != 0 && j--) {
 			struct ed *ed = dma_to_ed (ohci, le32_to_cpup(ed_p));
 			printk (" %p/%08x;", ed, ed->hwINFO);
@@ -99,7 +99,7 @@
 	}
 	if (!printed)
 		printk (KERN_DEBUG "%s, ohci %s, empty periodic schedule\n",
-				label, ohci->hcd.bus_name);
+				label, ohci->hcd.self.bus_name);
 }
 #endif
 
@@ -229,7 +229,7 @@
 
 static void ohci_dump (struct ohci_hcd *controller, int verbose)
 {
-	dbg ("OHCI controller %s state", controller->hcd.bus_name);
+	dbg ("OHCI controller %s state", controller->hcd.self.bus_name);
 
 	// dumps some of the state we know about
 	ohci_dump_status (controller);
diff -Nru a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
--- a/drivers/usb/host/ohci-hcd.c	Mon Apr 22 15:32:18 2002
+++ b/drivers/usb/host/ohci-hcd.c	Mon Apr 22 15:32:18 2002
@@ -379,7 +379,7 @@
 	writel (OHCI_INTR_MIE, &ohci->regs->intrdisable);
 
 	dbg ("USB HC reset_hc %s: ctrl = 0x%x ;",
-		ohci->hcd.bus_name,
+		ohci->hcd.self.bus_name,
 		readl (&ohci->regs->control));
 
   	/* Reset USB (needed by some controllers) */
@@ -449,7 +449,7 @@
 	mdelay ((roothub_a (ohci) >> 23) & 0x1fe);
  
 	/* connect the virtual root hub */
-	ohci->hcd.bus->root_hub = udev = usb_alloc_dev (NULL, ohci->hcd.bus);
+	ohci->hcd.self.root_hub = udev = usb_alloc_dev (NULL, &ohci->hcd.self);
 	ohci->hcd.state = USB_STATE_READY;
 	if (!udev) {
 	    ohci->disabled = 1;
@@ -491,7 +491,7 @@
 
 	if (ints & OHCI_INTR_UE) {
 		ohci->disabled++;
-		err ("OHCI Unrecoverable Error, %s disabled", hcd->bus_name);
+		err ("OHCI Unrecoverable Error, %s disabled", hcd->self.bus_name);
 		// e.g. due to PCI Master/Target Abort
 
 #ifdef	DEBUG
@@ -530,7 +530,7 @@
 	struct ohci_hcd		*ohci = hcd_to_ohci (hcd);
 
 	dbg ("%s: stop %s controller%s",
-		hcd->bus_name,
+		hcd->self.bus_name,
 		hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS),
 		ohci->disabled ? " (disabled)" : ""
 		);
@@ -571,7 +571,7 @@
 				&& hcd->pdev->device == 0x740c) {
 			ohci->flags = OHCI_QUIRK_AMD756;
 			info ("%s: AMD756 erratum 4 workaround",
-				hcd->bus_name);
+				hcd->self.bus_name);
 		}
 
 		/* Apple's OHCI driver has a lot of bizarre workarounds
@@ -581,7 +581,7 @@
 		else if (hcd->pdev->vendor == 0x1045
 				&& hcd->pdev->device == 0xc861) {
 			info ("%s: WARNING: OPTi workarounds unavailable",
-				hcd->bus_name);
+				hcd->self.bus_name);
 		}
 	}
 #else
@@ -601,7 +601,7 @@
 	}
 
 	if (hc_start (ohci) < 0) {
-		err ("can't start %s", ohci->hcd.bus_name);
+		err ("can't start %s", ohci->hcd.self.bus_name);
 		ohci_stop (hcd);
 		return -EBUSY;
 	}
@@ -623,13 +623,13 @@
 	u16			cmd;
 
 	if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER) {
-		dbg ("can't suspend %s (state is %s)", hcd->bus_name,
+		dbg ("can't suspend %s (state is %s)", hcd->self.bus_name,
 			hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS));
 		return -EIO;
 	}
 
 	/* act as if usb suspend can always be used */
-	dbg ("%s: suspend to %d", hcd->bus_name, state);
+	dbg ("%s: suspend to %d", hcd->self.bus_name, state);
 	ohci->sleeping = 1;
 
 	/* First stop processing */
@@ -664,16 +664,16 @@
 
 	switch (readl (&ohci->regs->control) & OHCI_CTRL_HCFS) {
 		case OHCI_USB_RESET:
-			dbg ("%s suspend->reset ?", hcd->bus_name);
+			dbg ("%s suspend->reset ?", hcd->self.bus_name);
 			break;
 		case OHCI_USB_RESUME:
-			dbg ("%s suspend->resume ?", hcd->bus_name);
+			dbg ("%s suspend->resume ?", hcd->self.bus_name);
 			break;
 		case OHCI_USB_OPER:
-			dbg ("%s suspend->operational ?", hcd->bus_name);
+			dbg ("%s suspend->operational ?", hcd->self.bus_name);
 			break;
 		case OHCI_USB_SUSPEND:
-			dbg ("%s suspended", hcd->bus_name);
+			dbg ("%s suspended", hcd->self.bus_name);
 			break;
 	}
 
@@ -711,8 +711,8 @@
 
 	ohci->disabled = 1;
 	ohci->sleeping = 0;
-	if (ohci->hcd.bus->root_hub)
-		usb_disconnect (&ohci->hcd.bus->root_hub);
+	if (ohci->hcd.self.root_hub)
+		usb_disconnect (&ohci->hcd.self.root_hub);
 	
 	/* empty the interrupt branches */
 	for (i = 0; i < NUM_INTS; i++) ohci->ohci_int_load [i] = 0;
@@ -728,10 +728,10 @@
 	ohci->ed_bulktail    = NULL;
 
 	if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 0) {
-		err ("can't restart %s, %d", ohci->hcd.bus_name, temp);
+		err ("can't restart %s, %d", ohci->hcd.self.bus_name, temp);
 		return temp;
 	} else
-		dbg ("restart %s completed", ohci->hcd.bus_name);
+		dbg ("restart %s completed", ohci->hcd.self.bus_name);
 	return 0;
 }
 
@@ -767,13 +767,13 @@
 	switch (temp) {
 
 	case OHCI_USB_RESET:	// lost power
-		info ("USB restart: %s", hcd->bus_name);
+		info ("USB restart: %s", hcd->self.bus_name);
 		retval = hc_restart (ohci);
 		break;
 
 	case OHCI_USB_SUSPEND:	// host wakeup
 	case OHCI_USB_RESUME:	// remote wakeup
-		info ("USB continue: %s from %s wakeup", hcd->bus_name,
+		info ("USB continue: %s from %s wakeup", hcd->self.bus_name,
 			 (temp == OHCI_USB_SUSPEND)
 				? "host" : "remote");
 		ohci->hc_control = OHCI_USB_RESUME;
@@ -786,7 +786,7 @@
 		temp = readl (&ohci->regs->control);
 		temp = ohci->hc_control & OHCI_CTRL_HCFS;
 		if (temp != OHCI_USB_RESUME) {
-			err ("controller %s won't resume", hcd->bus_name);
+			err ("controller %s won't resume", hcd->self.bus_name);
 			ohci->disabled = 1;
 			retval = -EIO;
 			break;
@@ -836,7 +836,7 @@
 		break;
 
 	default:
-		warn ("odd PCI resume for %s", hcd->bus_name);
+		warn ("odd PCI resume for %s", hcd->self.bus_name);
 	}
 	return retval;
 }
diff -Nru a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
--- a/drivers/usb/host/ohci-hub.c	Mon Apr 22 15:32:18 2002
+++ b/drivers/usb/host/ohci-hub.c	Mon Apr 22 15:32:18 2002
@@ -41,7 +41,7 @@
 #define dbg_port(hc,label,num,value) \
 	dbg ("%s: %s roothub.portstatus [%d] " \
 		"= 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s", \
-		hc->hcd.bus_name, label, num, temp, \
+		hc->hcd.self.bus_name, label, num, temp, \
 		(temp & RH_PS_PRSC) ? " PRSC" : "", \
 		(temp & RH_PS_OCIC) ? " OCIC" : "", \
 		(temp & RH_PS_PSSC) ? " PSSC" : "", \
@@ -71,7 +71,7 @@
 
 	ports = roothub_a (ohci) & RH_A_NDP; 
 	if (ports > MAX_ROOT_PORTS) {
-		err ("%s: bogus NDP=%d", hcd->bus_name, ports);
+		err ("%s: bogus NDP=%d", hcd->self.bus_name, ports);
 		err ("rereads as NDP=%d",
 			readl (&ohci->regs->roothub.a) & RH_A_NDP);
 		/* retry later; "should not happen" */
diff -Nru a/drivers/usb/host/uhci.c b/drivers/usb/host/uhci.c
--- a/drivers/usb/host/uhci.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/usb/host/uhci.c	Mon Apr 22 15:32:19 2002
@@ -2799,6 +2799,7 @@
 
 	uhci->bus = bus;
 	bus->hcpriv = uhci;
+	bus->bus_name = dev->slot_name;
 
 	usb_register_bus(uhci->bus);
 
diff -Nru a/drivers/usb/host/usb-ohci.c b/drivers/usb/host/usb-ohci.c
--- a/drivers/usb/host/usb-ohci.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/usb/host/usb-ohci.c	Mon Apr 22 15:32:19 2002
@@ -2400,6 +2400,7 @@
 		return NULL;
 	}
 	ohci->bus->hcpriv = (void *) ohci;
+	ohci->bus->bus_name = dev->slot_name;
 
 	return ohci;
 } 
diff -Nru a/drivers/usb/host/usb-uhci.c b/drivers/usb/host/usb-uhci.c
--- a/drivers/usb/host/usb-uhci.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/usb/host/usb-uhci.c	Mon Apr 22 15:32:19 2002
@@ -2977,6 +2977,7 @@
 
 	s->bus = bus;
 	bus->hcpriv = s;
+	bus->bus_name = dev->slot_name;
 
 	/* UHCI specs says devices must have 2 ports, but goes on to say */
 	/* they may have more but give no way to determine how many they */
diff -Nru a/drivers/usb/image/Config.help b/drivers/usb/image/Config.help
--- a/drivers/usb/image/Config.help	Mon Apr 22 15:32:18 2002
+++ b/drivers/usb/image/Config.help	Mon Apr 22 15:32:18 2002
@@ -1,14 +1,3 @@
-CONFIG_USB_DC2XX
-  Say Y here if you want to connect this type of still camera to your
-  computer's USB port.  See <file:Documentation/usb/dc2xx.txt> for
-  more information; some non-Kodak cameras may also work with this
-  driver, given application support (such as <http://www.gphoto.org/>).
-
-  This code is also available as a module ( = code which can be
-  inserted in and removed from the running kernel whenever you want).
-  The module will be called dc2xx.o. If you want to compile it as a
-  module, say M here and read <file:Documentation/modules.txt>.
-
 CONFIG_USB_MDC800
   Say Y here if you want to connect this type of still camera to
   your computer's USB port. This driver can be used with gphoto 0.4.3
diff -Nru a/drivers/usb/image/Config.in b/drivers/usb/image/Config.in
--- a/drivers/usb/image/Config.in	Mon Apr 22 15:32:19 2002
+++ b/drivers/usb/image/Config.in	Mon Apr 22 15:32:19 2002
@@ -2,7 +2,6 @@
 # USB Imageing devices configuration
 #
 comment 'USB Imaging devices'
-dep_tristate '  USB Kodak DC-2xx Camera support' CONFIG_USB_DC2XX $CONFIG_USB
 dep_tristate '  USB Mustek MDC800 Digital Camera support (EXPERIMENTAL)' CONFIG_USB_MDC800 $CONFIG_USB $CONFIG_EXPERIMENTAL
 dep_tristate '  USB Scanner support' CONFIG_USB_SCANNER $CONFIG_USB
 dep_tristate '  Microtek X6USB scanner support' CONFIG_USB_MICROTEK $CONFIG_USB $CONFIG_SCSI
@@ -10,10 +9,10 @@
 
 # Turn on CONFIG_USB_IMAGE if any of the drivers are compiled into the kernel to
 # make our Makefile logic a bit simpler
-if [ "$CONFIG_USB_DC2XX" = "y" -o "$CONFIG_USB_MDC800" = "y" ]; then
+if [ "$CONFIG_USB_MDC800" = "y" -o "$CONFIG_USB_SCANNER" = "y" ]; then
    define_bool CONFIG_USB_IMAGE y
 fi
-if [ "$CONFIG_USB_SCANNER" = "y" -o "$CONFIG_USB_MICROTEK" = "y" -o "$CONFIG_USB_HPUSBSCSI" = "y" ]; then
+if [ "$CONFIG_USB_MICROTEK" = "y" -o "$CONFIG_USB_HPUSBSCSI" = "y" ]; then
    define_bool CONFIG_USB_IMAGE y
 fi
    
diff -Nru a/drivers/usb/image/Makefile b/drivers/usb/image/Makefile
--- a/drivers/usb/image/Makefile	Mon Apr 22 15:32:18 2002
+++ b/drivers/usb/image/Makefile	Mon Apr 22 15:32:18 2002
@@ -4,7 +4,6 @@
 
 O_TARGET	:= usb-image.o
 
-obj-$(CONFIG_USB_DC2XX)		+= dc2xx.o
 obj-$(CONFIG_USB_MDC800)	+= mdc800.o
 obj-$(CONFIG_USB_HPUSBSCSI)	+= hpusbscsi.o
 obj-$(CONFIG_USB_MICROTEK)	+= microtek.o
diff -Nru a/drivers/usb/image/dc2xx.c b/drivers/usb/image/dc2xx.c
--- a/drivers/usb/image/dc2xx.c	Mon Apr 22 15:32:19 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,538 +0,0 @@
-/*
- * Copyright (C) 1999-2000 by David Brownell <dbrownell@users.sourceforge.net>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
- 
- 
-/*
- * USB driver for Kodak DC-2XX series digital still cameras
- *
- * The protocol here is the same as the one going over a serial line, but
- * it uses USB for speed.  Set up /dev/kodak, get gphoto (www.gphoto.org),
- * and have fun!
- *
- * This should also work for a number of other digital (non-Kodak) cameras,
- * by adding the vendor and product IDs to the table below.  They'll need
- * to be the sort using USB just as a fast bulk data channel.
- */
-
-/*
- * HISTORY
- *
- * 26 August, 1999 -- first release (0.1), works with my DC-240.
- * 	The DC-280 (2Mpixel) should also work, but isn't tested.
- *	If you use gphoto, make sure you have the USB updates.
- *	Lives in a 2.3.14 or so Linux kernel, in drivers/usb.
- * 31 August, 1999 -- minor update to recognize DC-260 and handle
- *	its endpoints being in a different order.  Note that as
- *	of gPhoto 0.36pre, the USB updates are integrated.
- * 12 Oct, 1999 -- handle DC-280 interface class (0xff not 0x0);
- *	added timeouts to bulk_msg calls.  Minor updates, docs.
- * 03 Nov, 1999 -- update for 2.3.25 kernel API changes.
- * 08 Jan, 2000 .. multiple camera support
- * 12 Aug, 2000 .. add some real locking, remove an Oops
- * 10 Oct, 2000 .. usb_device_id table created. 
- * 01 Nov, 2000 .. usb_device_id support added by Adam J. Richter
- * 08 Apr, 2001 .. Identify version on module load. gb
- *
- * Thanks to:  the folk who've provided USB product IDs, sent in
- * patches, and shared their successes!
- */
-
-#include <linux/config.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/signal.h>
-#include <linux/errno.h>
-#include <linux/miscdevice.h>
-#include <linux/random.h>
-#include <linux/poll.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/devfs_fs_kernel.h>
-
-#ifdef CONFIG_USB_DEBUG
-	#define DEBUG
-#else
-	#undef DEBUG
-#endif
-#include <linux/usb.h>
-
-
-/* /dev/usb dir. */
-extern devfs_handle_t usb_devfs_handle;			
-
-/*
- * Version Information
- */
-#define DRIVER_VERSION "v1.0.0"
-#define DRIVER_AUTHOR "David Brownell, <dbrownell@users.sourceforge.net>"
-#define DRIVER_DESC "USB Camera Driver for Kodak DC-2xx series cameras"
-
-
-/* current USB framework handles max of 16 USB devices per driver */
-#define	MAX_CAMERAS		16
-
-/* USB char devs use USB_MAJOR and from USB_CAMERA_MINOR_BASE up */
-#define	USB_CAMERA_MINOR_BASE	80
-
-
-// XXX remove packet size limit, now that bulk transfers seem fixed
-
-/* Application protocol limit is 0x8002; USB has disliked that limit! */
-#define	MAX_PACKET_SIZE		0x2000		/* e.g. image downloading */
-
-#define	MAX_READ_RETRY		5		/* times to retry reads */
-#define	MAX_WRITE_RETRY		5		/* times to retry writes */
-#define	RETRY_TIMEOUT		(HZ)		/* sleep between retries */
-
-
-/* table of cameras that work through this driver */
-static struct usb_device_id camera_table [] = {
-	/* These have the same application level protocol */  
-	{ USB_DEVICE(0x040a, 0x0120) },		// Kodak DC-240
-	{ USB_DEVICE(0x040a, 0x0130) },		// Kodak DC-280
-	{ USB_DEVICE(0x040a, 0x0131) },		// Kodak DC-5000
-	{ USB_DEVICE(0x040a, 0x0132) },		// Kodak DC-3400
-
-	/* These have a different application level protocol which
-	 * is part of the Flashpoint "DigitaOS".  That supports some
-	 * non-camera devices, and some non-Kodak cameras.
-	 * Use this driver to get USB and "OpenDis" to talk.
-	 */  
-	{ USB_DEVICE(0x040a, 0x0100) },		// Kodak DC-220
-	{ USB_DEVICE(0x040a, 0x0110) },		// Kodak DC-260
-	{ USB_DEVICE(0x040a, 0x0111) },		// Kodak DC-265
-	{ USB_DEVICE(0x040a, 0x0112) },		// Kodak DC-290
-	{ USB_DEVICE(0xf003, 0x6002) },		// HP PhotoSmart C500
-	{ USB_DEVICE(0x03f0, 0x4102) },		// HP PhotoSmart C618
-	{ USB_DEVICE(0x0a17, 0x1001) },		// Pentax EI-200
-
-	/* Other USB devices may well work here too, so long as they
-	 * just stick to half duplex bulk packet exchanges.  That
-	 * means, among other things, no iso or interrupt endpoints.
-	 */
-
-	{ }					/* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE (usb, camera_table);
-
-
-struct camera_state {
-	struct usb_device	*dev;		/* USB device handle */
-	int			inEP;		/* read endpoint */
-	int			outEP;		/* write endpoint */
-	const struct usb_device_id	*info;	/* DC-240, etc */
-	int			subminor;	/* which minor dev #? */
-	struct semaphore	sem;		/* locks this struct */
-
-	/* this is non-null iff the device is open */
-	char			*buf;		/* buffer for I/O */
-
-	devfs_handle_t		devfs;		/* devfs device */
-
-	/* always valid */
-	wait_queue_head_t	wait;		/* for timed waits */
-};
-
-/* Support multiple cameras, possibly of different types.  */
-static struct camera_state *minor_data [MAX_CAMERAS];
-
-/* make this an rwlock if contention becomes an issue */
-static DECLARE_MUTEX (state_table_mutex);
-
-static ssize_t camera_read (struct file *file,
-	char *buf, size_t len, loff_t *ppos)
-{
-	struct camera_state	*camera;
-	int			retries;
-	int			retval = 0;
-
-	if (len > MAX_PACKET_SIZE)
-		return -EINVAL;
-
-	camera = (struct camera_state *) file->private_data;
-	down (&camera->sem);
-	if (!camera->dev) {
-		up (&camera->sem);
-		return -ENODEV;
-	}
-
-	/* Big reads are common, for image downloading.  Smaller ones
-	 * are also common (even "directory listing" commands don't
-	 * send very much data).  We preserve packet boundaries here,
-	 * they matter in the application protocol.
-	 */
-	for (retries = 0; retries < MAX_READ_RETRY; retries++) {
-		int			count;
-
-		if (signal_pending (current)) {
-			retval = -EINTR;
-			break;
-		}
-
-		retval = usb_bulk_msg (camera->dev,
-			  usb_rcvbulkpipe (camera->dev, camera->inEP),
-			  camera->buf, len, &count, HZ*10);
-
-		dbg ("read (%Zd) - 0x%x %d", len, retval, count);
-
-		if (!retval) {
-			if (copy_to_user (buf, camera->buf, count))
-				retval = -EFAULT;
-			else
-				retval = count;
-			break;
-		}
-		if (retval != -ETIMEDOUT)
-			break;
-		interruptible_sleep_on_timeout (&camera->wait, RETRY_TIMEOUT);
-
-		dbg ("read (%Zd) - retry", len);
-	}
-	up (&camera->sem);
-	return retval;
-}
-
-static ssize_t camera_write (struct file *file,
-	const char *buf, size_t len, loff_t *ppos)
-{
-	struct camera_state	*camera;
-	ssize_t			bytes_written = 0;
-
-	if (len > MAX_PACKET_SIZE)
-		return -EINVAL;
-
-	camera = (struct camera_state *) file->private_data;
-	down (&camera->sem);
-	if (!camera->dev) {
-		up (&camera->sem);
-		return -ENODEV;
-	}
-	
-	/* most writes will be small: simple commands, sometimes with
-	 * parameters.  putting images (like borders) into the camera
-	 * would be the main use of big writes.
-	 */
-	while (len > 0) {
-		char		*obuf = camera->buf;
-		int		maxretry = MAX_WRITE_RETRY;
-		unsigned long	copy_size, thistime;
-
-		/* it's not clear that retrying can do any good ... or that
-		 * fragmenting application packets into N writes is correct.
-		 */
-		thistime = copy_size = len;
-		if (copy_from_user (obuf, buf, copy_size)) {
-			bytes_written = -EFAULT;
-			break;
-		}
-		while (thistime) {
-			int		result;
-			int		count;
-
-			if (signal_pending (current)) {
-				if (!bytes_written)
-					bytes_written = -EINTR;
-				goto done;
-			}
-
-			result = usb_bulk_msg (camera->dev,
-				 usb_sndbulkpipe (camera->dev, camera->outEP),
-				 obuf, thistime, &count, HZ*10);
-
-			if (result)
-				dbg ("write USB err - %d", result);
-
-			if (count) {
-				obuf += count;
-				thistime -= count;
-				maxretry = MAX_WRITE_RETRY;
-				continue;
-			} else if (!result)
-				break;
-				
-			if (result == -ETIMEDOUT) {	/* NAK - delay a bit */
-				if (!maxretry--) {
-					if (!bytes_written)
-						bytes_written = -ETIME;
-					goto done;
-				}
-                                interruptible_sleep_on_timeout (&camera->wait,
-					RETRY_TIMEOUT);
-				continue;
-			} 
-			if (!bytes_written)
-				bytes_written = -EIO;
-			goto done;
-		}
-		bytes_written += copy_size;
-		len -= copy_size;
-		buf += copy_size;
-	}
-done:
-	up (&camera->sem);
-	dbg ("wrote %Zd", bytes_written); 
-	return bytes_written;
-}
-
-static int camera_open (struct inode *inode, struct file *file)
-{
-	struct camera_state	*camera = NULL;
-	int			subminor;
-	int			value = 0;
-
-	down (&state_table_mutex);
-	subminor = minor (inode->i_rdev) - USB_CAMERA_MINOR_BASE;
-	if (subminor < 0 || subminor >= MAX_CAMERAS
-			|| !(camera = minor_data [subminor])) {
-		up (&state_table_mutex);
-		return -ENODEV;
-	}
-	down (&camera->sem);
-	up (&state_table_mutex);
-
-	if (camera->buf) {
-		value = -EBUSY;
-		goto done;
-	}
-
-	if (!(camera->buf = (char *) kmalloc (MAX_PACKET_SIZE, GFP_KERNEL))) {
-		value = -ENOMEM;
-		goto done;
-	}
-
-	dbg ("open #%d", subminor); 
-
-	file->private_data = camera;
-done:
-	up (&camera->sem);
-	return value;
-}
-
-static int camera_release (struct inode *inode, struct file *file)
-{
-	struct camera_state	*camera;
-	int			subminor;
-
-	camera = (struct camera_state *) file->private_data;
-	down (&state_table_mutex);
-	down (&camera->sem);
-
-	if (camera->buf) {
-		kfree (camera->buf);
-		camera->buf = 0;
-	}
-	subminor = camera->subminor;
-
-	/* If camera was unplugged with open file ... */
-	if (!camera->dev) {
-		minor_data [subminor] = NULL;
-		kfree (camera);
-	} else
-		up (&camera->sem);
-	
-	up (&state_table_mutex);
-
-	dbg ("close #%d", subminor); 
-
-	return 0;
-}
-
-	/* XXX should define some ioctls to expose camera type
-	 * to applications ... what USB exposes should suffice.
-	 * apps should be able to see the camera type.
-	 */
-static /* const */ struct file_operations usb_camera_fops = {
-	    /* Uses GCC initializer extension; simpler to maintain */
-	owner:		THIS_MODULE,
-	read:		camera_read,
-	write:		camera_write,
-	open:		camera_open,
-	release:	camera_release,
-};
-
-
-
-static void *
-camera_probe (struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *camera_info)
-{
-	int				i;
-	struct usb_interface_descriptor	*interface;
-	struct usb_endpoint_descriptor	*endpoint;
-	int				direction, ep;
-	char name[8];
-	struct camera_state		*camera = NULL;
-
-
-	/* these have one config, one interface */
-	if (dev->descriptor.bNumConfigurations != 1
-			|| dev->config[0].bNumInterfaces != 1) {
-		dbg ("Bogus camera config info");
-		return NULL;
-	}
-
-	/* models differ in how they report themselves */
-	interface = &dev->actconfig->interface[ifnum].altsetting[0];
-	if ((interface->bInterfaceClass != USB_CLASS_PER_INTERFACE
-		&& interface->bInterfaceClass != USB_CLASS_VENDOR_SPEC)
-			|| interface->bInterfaceSubClass != 0
-			|| interface->bInterfaceProtocol != 0
-			|| interface->bNumEndpoints != 2
-			) {
-		dbg ("Bogus camera interface info");
-		return NULL;
-	}
-
-
-	/* select "subminor" number (part of a minor number) */
-	down (&state_table_mutex);
-	for (i = 0; i < MAX_CAMERAS; i++) {
-		if (!minor_data [i])
-			break;
-	}
-	if (i >= MAX_CAMERAS) {
-		info ("Ignoring additional USB Camera");
-		goto bye;
-	}
-
-	/* allocate & init camera state */
-	camera = minor_data [i] = kmalloc (sizeof *camera, GFP_KERNEL);
-	if (!camera) {
-		err ("no memory!");
-		goto bye;
-	}
-
-	init_MUTEX (&camera->sem);
-	camera->info = camera_info;
-	camera->subminor = i;
-	camera->buf = NULL;
-	init_waitqueue_head (&camera->wait);
-
-
-	/* get input and output endpoints (either order) */
-	endpoint = interface->endpoint;
-	camera->outEP = camera->inEP =  -1;
-
-	ep = endpoint [0].bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
-	direction = endpoint [0].bEndpointAddress & USB_ENDPOINT_DIR_MASK;
-	if (direction == USB_DIR_IN)
-		camera->inEP = ep;
-	else
-		camera->outEP = ep;
-
-	ep = endpoint [1].bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
-	direction = endpoint [1].bEndpointAddress & USB_ENDPOINT_DIR_MASK;
-	if (direction == USB_DIR_IN)
-		camera->inEP = ep;
-	else
-		camera->outEP = ep;
-
-	if (camera->outEP == -1 || camera->inEP == -1
-			|| endpoint [0].bmAttributes != USB_ENDPOINT_XFER_BULK
-			|| endpoint [1].bmAttributes != USB_ENDPOINT_XFER_BULK
-			) {
-		dbg ("Bogus endpoints");
-		goto error;
-	}
-
-	info ("USB Camera #%d connected, major/minor %d/%d", camera->subminor,
-		USB_MAJOR, USB_CAMERA_MINOR_BASE + camera->subminor);
-
-	camera->dev = dev;
-	usb_inc_dev_use (dev);
-
-	/* If we have devfs, register the device */
-	sprintf(name, "dc2xx%d", camera->subminor);
-	camera->devfs = devfs_register(usb_devfs_handle, name,
-				       DEVFS_FL_DEFAULT, USB_MAJOR,
-				       USB_CAMERA_MINOR_BASE + camera->subminor,
-				       S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP |
-				       S_IWGRP, &usb_camera_fops, NULL);
-
-	goto bye;
-
-error:
-	minor_data [camera->subminor] = NULL;
-	kfree (camera);
-	camera = NULL;
-bye:
-	up (&state_table_mutex);
-	return camera;
-}
-
-static void camera_disconnect(struct usb_device *dev, void *ptr)
-{
-	struct camera_state	*camera = (struct camera_state *) ptr;
-	int			subminor = camera->subminor;
-
-	down (&state_table_mutex);
-	down (&camera->sem);
-
-	devfs_unregister(camera->devfs); 
-
-	/* If camera's not opened, we can clean up right away.
-	 * Else apps see a disconnect on next I/O; the release cleans.
-	 */
-	if (!camera->buf) {
-		minor_data [subminor] = NULL;
-		kfree (camera);
-		camera = NULL;
-	} else
-		camera->dev = NULL;
-
-	info ("USB Camera #%d disconnected", subminor);
-	usb_dec_dev_use (dev);
-
-	if (camera != NULL)
-		up (&camera->sem);
-	up (&state_table_mutex);
-}
-
-static /* const */ struct usb_driver camera_driver = {
-	name:		"dc2xx",
-
-	id_table:	camera_table,
-	probe:		camera_probe,
-	disconnect:	camera_disconnect,
-
-	fops:		&usb_camera_fops,
-	minor:		USB_CAMERA_MINOR_BASE
-};
-
-
-int __init usb_dc2xx_init(void)
-{
- 	if (usb_register (&camera_driver) < 0)
- 		return -1;
-	info(DRIVER_VERSION ":" DRIVER_DESC);
-	return 0;
-}
-
-void __exit usb_dc2xx_cleanup(void)
-{
-	usb_deregister (&camera_driver);
-}
-
-module_init (usb_dc2xx_init);
-module_exit (usb_dc2xx_cleanup);
-
-MODULE_AUTHOR( DRIVER_AUTHOR );
-MODULE_DESCRIPTION( DRIVER_DESC );
-MODULE_LICENSE("GPL");
-
diff -Nru a/drivers/usb/misc/tiglusb.c b/drivers/usb/misc/tiglusb.c
--- a/drivers/usb/misc/tiglusb.c	Mon Apr 22 15:32:18 2002
+++ b/drivers/usb/misc/tiglusb.c	Mon Apr 22 15:32:18 2002
@@ -33,7 +33,7 @@
 /*
  * Version Information
  */
-#define DRIVER_VERSION "1.02"
+#define DRIVER_VERSION "1.03"
 #define DRIVER_AUTHOR  "Romain Lievin <roms@lpg.ticalc.org> & Julien Blache <jb@jblache.org>"
 #define DRIVER_DESC    "TI-GRAPH LINK USB (aka SilverLink) driver"
 #define DRIVER_LICENSE "GPL"
@@ -383,6 +383,7 @@
 	wake_up (&s->wait);
 	if (s->state == _started)
 		sleep_on (&s->remove_ok);
+	down (&s->mutex);
 	s->dev = NULL;
 	s->opened = 0;
 
@@ -479,7 +480,7 @@
 
 /* --------------------------------------------------------------------- */
 
-__setup ("tipar=", tiglusb_setup);
+__setup ("tiusb=", tiglusb_setup);
 module_init (tiglusb_init);
 module_exit (tiglusb_cleanup);
 
diff -Nru a/drivers/usb/net/Config.in b/drivers/usb/net/Config.in
--- a/drivers/usb/net/Config.in	Mon Apr 22 15:32:19 2002
+++ b/drivers/usb/net/Config.in	Mon Apr 22 15:32:19 2002
@@ -7,10 +7,10 @@
 else
    dep_tristate '  USB CATC NetMate-based Ethernet device support (EXPERIMENTAL)' CONFIG_USB_CATC $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL
    dep_tristate '  USB CDC Ethernet support (EXPERIMENTAL)' CONFIG_USB_CDCETHER $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL
-   dep_tristate '  USB KLSI KL5USB101-based ethernet device support (EXPERIMENTAL)' CONFIG_USB_KAWETH $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL
-   dep_tristate '  USB Pegasus/Pegasus-II based ethernet device support (EXPERIMENTAL)' CONFIG_USB_PEGASUS $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL
+   dep_tristate '  USB KLSI KL5USB101-based ethernet device support' CONFIG_USB_KAWETH $CONFIG_USB $CONFIG_NET
+   dep_tristate '  USB Pegasus/Pegasus-II based ethernet device support' CONFIG_USB_PEGASUS $CONFIG_USB $CONFIG_NET
    dep_tristate '  USB RTL8150 based ethernet device support (EXPERIMENTAL)' CONFIG_USB_RTL8150 $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL
-   dep_tristate '  USB-to-USB Networking cable device support (EXPERIMENTAL)' CONFIG_USB_USBNET $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL
+   dep_tristate '  USB-to-USB Networking cable device support' CONFIG_USB_USBNET $CONFIG_USB $CONFIG_NET
 fi
 
 # Turn on CONFIG_USB_NET if any of the drivers are compiled into the kernel to
diff -Nru a/drivers/usb/net/catc.c b/drivers/usb/net/catc.c
--- a/drivers/usb/net/catc.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/usb/net/catc.c	Mon Apr 22 15:32:19 2002
@@ -278,7 +278,7 @@
 			atomic_dec(&catc->recq_sz);
 			dbg("getting extra packet");
 			urb->dev = catc->usbdev;
-			if ((status = usb_submit_urb(urb, GFP_KERNEL)) < 0) {
+			if ((status = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
 				dbg("submit(rx_urb) status %d", status);
 			}
 		} else {
@@ -329,7 +329,7 @@
 				atomic_inc(&catc->recq_sz);
 		} else {
 			catc->rx_urb->dev = catc->usbdev;
-			if ((status = usb_submit_urb(catc->rx_urb, GFP_KERNEL)) < 0) {
+			if ((status = usb_submit_urb(catc->rx_urb, GFP_ATOMIC)) < 0) {
 				err("submit(rx_urb) status %d", status);
 			}
 		} 
@@ -351,7 +351,7 @@
 	catc->tx_urb->transfer_buffer = catc->tx_buf[catc->tx_idx];
 	catc->tx_urb->dev = catc->usbdev;
 
-	if ((status = usb_submit_urb(catc->tx_urb, GFP_KERNEL)) < 0)
+	if ((status = usb_submit_urb(catc->tx_urb, GFP_ATOMIC)) < 0)
 		err("submit(tx_urb), status %d", status);
 
 	catc->tx_idx = !catc->tx_idx;
@@ -655,7 +655,6 @@
 {
         struct catc *catc = dev->priv;
         u32 cmd;
-	char tmp[40];
         
         if (get_user(cmd, (u32 *)useraddr))
                 return -EFAULT;
@@ -666,8 +665,7 @@
                 struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO};
                 strncpy(info.driver, SHORT_DRIVER_DESC, ETHTOOL_BUSINFO_LEN);
                 strncpy(info.version, DRIVER_VERSION, ETHTOOL_BUSINFO_LEN);
-		sprintf(tmp, "usb%d:%d", catc->usbdev->bus->busnum, catc->usbdev->devnum);
-                strncpy(info.bus_info, tmp,ETHTOOL_BUSINFO_LEN);
+		usb_make_path (catc->usbdev, info.bus_info, sizeof info.bus_info);
                 if (copy_to_user(useraddr, &info, sizeof(info)))
                         return -EFAULT;
                 return 0;
@@ -909,9 +907,9 @@
 		f5u011_rxmode(catc, catc->rxmode);
 	}
 	dbg("Init done.");
-	printk(KERN_INFO "%s: %s USB Ethernet at usb%d:%d.%d, ",
+	printk(KERN_INFO "%s: %s USB Ethernet at usb-%s-%s/%d, ",
 	       netdev->name, (catc->is_f5u011) ? "Belkin F5U011" : "CATC EL1210A NetMate",
-	       usbdev->bus->busnum, usbdev->devnum, ifnum);
+	       usbdev->bus->bus_name, usbdev->devpath, ifnum);
 	for (i = 0; i < 5; i++) printk("%2.2x:", netdev->dev_addr[i]);
 	printk("%2.2x.\n", netdev->dev_addr[i]);
 	return catc;
diff -Nru a/drivers/usb/net/kaweth.c b/drivers/usb/net/kaweth.c
--- a/drivers/usb/net/kaweth.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/usb/net/kaweth.c	Mon Apr 22 15:32:19 2002
@@ -5,17 +5,18 @@
  *     (c) 2000 Interlan Communications
  *     (c) 2000 Stephane Alnet
  *     (C) 2001 Brad Hards
+ *     (C) 2002 Oliver Neukum
  *
  *     Original author: The Zapman <zapman@interlan.net>
- *     Inspired by, and much credit goes to Michael Rothwell 
+ *     Inspired by, and much credit goes to Michael Rothwell
  *     <rothwell@interlan.net> for the test equipment, help, and patience
  *     Based off of (and with thanks to) Petko Manolov's pegaus.c driver.
- *     Also many thanks to Joel Silverman and Ed Surprenant at Kawasaki 
+ *     Also many thanks to Joel Silverman and Ed Surprenant at Kawasaki
  *     for providing the firmware and driver resources.
  *
  *     This program is free software; you can redistribute it and/or
  *     modify it under the terms of the GNU General Public License as
- *     published by the Free Software Foundation; either version 2, or 
+ *     published by the Free Software Foundation; either version 2, or
  *     (at your option) any later version.
  *
  *     This program is distributed in the hope that it will be useful,
@@ -25,7 +26,7 @@
  *
  *     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. 
+ *     Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  *
  ****************************************************************/
 
@@ -36,8 +37,8 @@
  * Fix bugs from previous two steps
  * Snoop other OSs for any tricks we're not doing
  * SMP locking
- * Reduce arbitrary timeouts 
- * Smart multicast support 
+ * Reduce arbitrary timeouts
+ * Smart multicast support
  * Temporary MAC change support
  * Tunable SOFs parameter - ioctl()?
  * Ethernet stats collection
@@ -99,8 +100,14 @@
 
 #define KAWETH_SOFS_TO_WAIT			0x05
 
+#define INTBUFFERSIZE				4
 
-MODULE_AUTHOR("Michael Zappe <zapman@interlan.net>, Stephane Alnet <stephane@u-picardie.fr> and Brad Hards <bhards@bigpond.net.au>");
+#define STATE_OFFSET				0
+#define STATE_MASK				0x40
+#define	STATE_SHIFT				5
+
+
+MODULE_AUTHOR("Michael Zappe <zapman@interlan.net>, Stephane Alnet <stephane@u-picardie.fr>, Brad Hards <bhards@bigpond.net.au> and Oliver Neukum <oliver@neukum.org>");
 MODULE_DESCRIPTION("KL5USB101 USB Ethernet driver");
 MODULE_LICENSE("GPL");
 
@@ -118,21 +125,21 @@
  *     usb_device_id
  ****************************************************************/
 static struct usb_device_id usb_klsi_table[] = {
-	{ USB_DEVICE(0x03e8, 0x0008) }, /* AOX Endpoints USB Ethernet */ 
+	{ USB_DEVICE(0x03e8, 0x0008) }, /* AOX Endpoints USB Ethernet */
 	{ USB_DEVICE(0x04bb, 0x0901) }, /* I-O DATA USB-ET/T */
-	{ USB_DEVICE(0x0506, 0x03e8) }, /* 3Com 3C19250 */ 
-	{ USB_DEVICE(0x0557, 0x2002) }, /* ATEN USB Ethernet */ 
-	{ USB_DEVICE(0x0557, 0x4000) }, /* D-Link DSB-650C */ 
+	{ USB_DEVICE(0x0506, 0x03e8) }, /* 3Com 3C19250 */
+	{ USB_DEVICE(0x0557, 0x2002) }, /* ATEN USB Ethernet */
+	{ USB_DEVICE(0x0557, 0x4000) }, /* D-Link DSB-650C */
 	{ USB_DEVICE(0x0565, 0x0002) }, /* Peracom Enet */
 	{ USB_DEVICE(0x0565, 0x0003) }, /* Optus@Home UEP1045A */
-	{ USB_DEVICE(0x0565, 0x0005) }, /* Peracom Enet2 */ 
+	{ USB_DEVICE(0x0565, 0x0005) }, /* Peracom Enet2 */
 	{ USB_DEVICE(0x05e9, 0x0008) }, /* KLSI KL5KUSB101B */
 	{ USB_DEVICE(0x05e9, 0x0009) }, /* KLSI KL5KUSB101B (Board change) */
-	{ USB_DEVICE(0x066b, 0x2202) }, /* Linksys USB10T */ 
-	{ USB_DEVICE(0x06e1, 0x0008) }, /* ADS USB-10BT */ 
-	{ USB_DEVICE(0x06e1, 0x0009) }, /* ADS USB-10BT */ 
-	{ USB_DEVICE(0x0707, 0x0100) }, /* SMC 2202USB */ 
-	{ USB_DEVICE(0x07aa, 0x0001) }, /* Correga K.K. */ 
+	{ USB_DEVICE(0x066b, 0x2202) }, /* Linksys USB10T */
+	{ USB_DEVICE(0x06e1, 0x0008) }, /* ADS USB-10BT */
+	{ USB_DEVICE(0x06e1, 0x0009) }, /* ADS USB-10BT */
+	{ USB_DEVICE(0x0707, 0x0100) }, /* SMC 2202USB */
+	{ USB_DEVICE(0x07aa, 0x0001) }, /* Correga K.K. */
 	{ USB_DEVICE(0x07b8, 0x4000) }, /* D-Link DU-E10 */
 	{ USB_DEVICE(0x0846, 0x1001) }, /* NetGear EA-101 */
 	{ USB_DEVICE(0x0846, 0x1002) }, /* NetGear EA-101 */
@@ -144,9 +151,9 @@
 	{ USB_DEVICE(0x10bd, 0x1427) }, /* ASANTE USB To Ethernet Adapter */
 	{ USB_DEVICE(0x1342, 0x0204) }, /* Mobility USB-Ethernet Adapter */
 	{ USB_DEVICE(0x13d2, 0x0400) }, /* Shark Pocket Adapter */
-	{ USB_DEVICE(0x1645, 0x0005) }, /* Entrega E45 */ 
-	{ USB_DEVICE(0x1645, 0x0008) }, /* Entrega USB Ethernet Adapter */ 
-	{ USB_DEVICE(0x1645, 0x8005) }, /* PortGear Ethernet Adapter */ 
+	{ USB_DEVICE(0x1645, 0x0005) }, /* Entrega E45 */
+	{ USB_DEVICE(0x1645, 0x0008) }, /* Entrega USB Ethernet Adapter */
+	{ USB_DEVICE(0x1645, 0x8005) }, /* PortGear Ethernet Adapter */
 	{ USB_DEVICE(0x2001, 0x4000) }, /* D-link DSB-650C */
 	{} /* Null terminator */
 };
@@ -200,17 +207,23 @@
 	spinlock_t device_lock;
 
 	__u32 status;
+	int end;
+	int removed;
+	int suspend_lowmem;
+	int linkstate;
 
 	struct usb_device *dev;
 	struct net_device *net;
-	wait_queue_head_t control_wait;
+	wait_queue_head_t term_wait;
 
 	struct urb *rx_urb;
 	struct urb *tx_urb;
-	
+	struct urb *irq_urb;
+
 	__u8 firmware_buf[KAWETH_FIRMWARE_BUF_SIZE];
 	__u8 tx_buf[KAWETH_BUF_SIZE];
 	__u8 rx_buf[KAWETH_BUF_SIZE];
+	__u8 intbuffer[INTBUFFERSIZE];
 	__u16 packet_filter_bitmap;
 
 	struct kaweth_ethernet_configuration configuration;
@@ -223,13 +236,13 @@
  *     kaweth_control
  ****************************************************************/
 static int kaweth_control(struct kaweth_device *kaweth,
-			  unsigned int pipe, 
-			  __u8 request, 
-			  __u8 requesttype, 
-			  __u16 value, 
+			  unsigned int pipe,
+			  __u8 request,
+			  __u8 requesttype,
+			  __u16 value,
 			  __u16 index,
-			  void *data, 
-			  __u16 size, 
+			  void *data,
+			  __u16 size,
 			  int timeout)
 {
 	struct usb_ctrlrequest *dr;
@@ -247,7 +260,7 @@
 		kaweth_dbg("kmalloc() failed");
 		return -ENOMEM;
 	}
-	
+
 	dr->bRequestType= requesttype;
 	dr->bRequest = request;
 	dr->wValue = cpu_to_le16p(&value);
@@ -354,19 +367,19 @@
 /****************************************************************
  *     kaweth_download_firmware
  ****************************************************************/
-static int kaweth_download_firmware(struct kaweth_device *kaweth, 
-				    __u8 *data, 
+static int kaweth_download_firmware(struct kaweth_device *kaweth,
+				    __u8 *data,
 				    __u16 data_len,
 				    __u8 interrupt,
 				    __u8 type)
-{	
+{
 	if(data_len > KAWETH_FIRMWARE_BUF_SIZE)	{
 		kaweth_err("Firmware too big: %d", data_len);
 		return -ENOSPC;
 	}
-	
+
 	memcpy(kaweth->firmware_buf, data, data_len);
-	
+
 	kaweth->firmware_buf[2] = (data_len & 0xFF) - 7;
 	kaweth->firmware_buf[3] = data_len >> 8;
 	kaweth->firmware_buf[4] = type;
@@ -375,8 +388,8 @@
 	kaweth_dbg("High: %i, Low:%i", kaweth->firmware_buf[3],
 		   kaweth->firmware_buf[2]);
 
-	kaweth_dbg("Downloading firmware at %p to kaweth device at %p", 
-	    data, 
+	kaweth_dbg("Downloading firmware at %p to kaweth device at %p",
+	    data,
 	    kaweth);
 	kaweth_dbg("Firmware length: %d", data_len);
 
@@ -405,7 +418,7 @@
 	kaweth->firmware_buf[5] = interrupt;
 	kaweth->firmware_buf[6] = 0x00;
 	kaweth->firmware_buf[7] = 0x00;
-	
+
 	kaweth_dbg("Triggering firmware");
 
 	return kaweth_control(kaweth,
@@ -429,12 +442,12 @@
 	kaweth_dbg("kaweth_reset(%p)", kaweth);
 	result = kaweth_control(kaweth,
 				usb_sndctrlpipe(kaweth->dev, 0),
-				USB_REQ_SET_CONFIGURATION, 
-				0, 
+				USB_REQ_SET_CONFIGURATION,
+				0,
 				kaweth->dev->config[0].bConfigurationValue,
-				0, 
-				NULL, 
-				0, 
+				0,
+				NULL,
+				0,
 				KAWETH_CONTROL_TIMEOUT);
 
 	udelay(10000);
@@ -445,17 +458,40 @@
 }
 
 static void kaweth_usb_receive(struct urb *);
+static void kaweth_resubmit_rx_urb(struct kaweth_device *, int);
+
+/****************************************************************
+	int_callback
+*****************************************************************/
+static void int_callback(struct urb *u)
+{
+	struct kaweth_device *kaweth = u->context;
+	int act_state;
+
+	/* we abuse the interrupt urb for rebsubmitting under low memory saving a timer */
+	if (kaweth->suspend_lowmem)
+		kaweth_resubmit_rx_urb(kaweth, GFP_ATOMIC);
+
+	/* we check the link state to report changes */
+	if (kaweth->linkstate != (act_state = ( kaweth->intbuffer[STATE_OFFSET] | STATE_MASK) >> STATE_SHIFT)) {
+		if (!act_state)
+			netif_carrier_on(kaweth->net);
+		else
+			netif_carrier_off(kaweth->net);
+
+		kaweth->linkstate = act_state;
+	}
+
+}
 
 /****************************************************************
  *     kaweth_resubmit_rx_urb
  ****************************************************************/
-static inline void kaweth_resubmit_rx_urb(struct kaweth_device *kaweth,
+static void kaweth_resubmit_rx_urb(struct kaweth_device *kaweth,
 						int mem_flags)
 {
 	int result;
 
-	memset(kaweth->rx_urb, 0, sizeof(*kaweth->rx_urb));
-
 	FILL_BULK_URB(kaweth->rx_urb,
 		      kaweth->dev,
 		      usb_rcvbulkpipe(kaweth->dev, 1),
@@ -465,7 +501,11 @@
 		      kaweth);
 
 	if((result = usb_submit_urb(kaweth->rx_urb, mem_flags))) {
+		if (result == -ENOMEM)
+			kaweth->suspend_lowmem = 1;
 		kaweth_err("resubmitting rx_urb %d failed", result);
+	} else {
+		kaweth->suspend_lowmem = 0;
 	}
 }
 
@@ -478,23 +518,30 @@
 {
 	struct kaweth_device *kaweth = urb->context;
 	struct net_device *net = kaweth->net;
-	
+
 	int count = urb->actual_length;
 	int count2 = urb->transfer_buffer_length;
-			
+
 	__u16 pkt_len = le16_to_cpup((u16 *)kaweth->rx_buf);
 
 	struct sk_buff *skb;
 
-	if(kaweth->status & KAWETH_STATUS_CLOSING) {
+	if(unlikely(urb->status == -ECONNRESET || urb->status == -ECONNABORTED))
+	/* we are killed - set a flag and wake the disconnect handler */
+	{
+		kaweth->end = 1;
+		wake_up(&kaweth->term_wait);
 		return;
 	}
-	
-	if(urb->status && urb->status != -EREMOTEIO && count != 1) { 
+
+	if (kaweth->status & KAWETH_STATUS_CLOSING)
+		return;
+
+	if(urb->status && urb->status != -EREMOTEIO && count != 1) {
 		kaweth_err("%s RX status: %d count: %d packet_len: %d",
-                           net->name, 
+                           net->name,
 			   urb->status,
-			   count, 
+			   count,
 			   (int)pkt_len);
 		kaweth_resubmit_rx_urb(kaweth, GFP_ATOMIC);
                 return;
@@ -508,7 +555,7 @@
 		        kaweth_resubmit_rx_urb(kaweth, GFP_ATOMIC);
                         return;
                 }
-		
+
 		if(!(skb = dev_alloc_skb(pkt_len+2))) {
 		        kaweth_resubmit_rx_urb(kaweth, GFP_ATOMIC);
                         return;
@@ -517,13 +564,13 @@
 		skb->dev = net;
 
 		eth_copy_and_sum(skb, kaweth->rx_buf + 2, pkt_len, 0);
-		
+
 		skb_put(skb, pkt_len);
 
 		skb->protocol = eth_type_trans(skb, net);
-		
+
 		netif_rx(skb);
-		
+
 		kaweth->stats.rx_packets++;
 		kaweth->stats.rx_bytes += pkt_len;
 	}
@@ -546,6 +593,18 @@
 
 	kaweth_resubmit_rx_urb(kaweth, GFP_KERNEL);
 
+	FILL_INT_URB(
+		kaweth->irq_urb,
+		kaweth->dev,
+		usb_rcvintpipe(kaweth->dev, 3),
+		kaweth->intbuffer,
+		INTBUFFERSIZE,
+		int_callback,
+		kaweth,
+		HZ/4);
+
+	usb_submit_urb(kaweth->irq_urb, GFP_KERNEL);
+
 	netif_start_queue(net);
 
 	kaweth_async_set_rx_mode(kaweth);
@@ -563,6 +622,7 @@
 
 	kaweth->status |= KAWETH_STATUS_CLOSING;
 
+	usb_unlink_urb(kaweth->irq_urb);
 	usb_unlink_urb(kaweth->rx_urb);
 
 	kaweth->status &= ~KAWETH_STATUS_CLOSING;
@@ -602,11 +662,18 @@
 {
 	struct kaweth_device *kaweth = net->priv;
 	int count = skb->len;
-	
+
 	int res;
 
 	spin_lock(&kaweth->device_lock);
 
+	if (kaweth->removed) {
+	/* our device is undergoing disconnection - we bail out */
+		spin_unlock(&kaweth->device_lock);
+		dev_kfree_skb(skb);
+		return 0;
+	}
+
 	kaweth_async_set_rx_mode(kaweth);
 	netif_stop_queue(net);
 
@@ -614,8 +681,6 @@
 
 	memcpy(kaweth->tx_buf + 2, skb->data, skb->len);
 
-	memset(kaweth->tx_urb, 0, sizeof(*kaweth->tx_urb));
-
 	FILL_BULK_URB(kaweth->tx_urb,
 		      kaweth->dev,
 		      usb_sndbulkpipe(kaweth->dev, 2),
@@ -623,15 +688,17 @@
 		      count + 2,
 		      kaweth_usb_transmit_complete,
 		      kaweth);
+	kaweth->end = 0;
+	kaweth->tx_urb->transfer_flags |= USB_ASYNC_UNLINK;
 
 	if((res = usb_submit_urb(kaweth->tx_urb, GFP_ATOMIC)))
 	{
 		kaweth_warn("kaweth failed tx_urb %d", res);
 		kaweth->stats.tx_errors++;
-		
+
 		netif_start_queue(net);
-	} 
-	else 
+	}
+	else
 	{
 		kaweth->stats.tx_packets++;
 		kaweth->stats.tx_bytes += skb->len;
@@ -651,7 +718,7 @@
 static void kaweth_set_rx_mode(struct net_device *net)
 {
 	struct kaweth_device *kaweth = net->priv;
-	
+
 	__u16 packet_filter_bitmap = KAWETH_PACKET_FILTER_DIRECTED |
                                      KAWETH_PACKET_FILTER_BROADCAST |
 		                     KAWETH_PACKET_FILTER_MULTICAST;
@@ -662,7 +729,7 @@
 
 	if (net->flags & IFF_PROMISC) {
 		packet_filter_bitmap |= KAWETH_PACKET_FILTER_PROMISCUOUS;
-	} 
+	}
 	else if ((net->mc_count) || (net->flags & IFF_ALLMULTI)) {
 		packet_filter_bitmap |= KAWETH_PACKET_FILTER_ALL_MULTICAST;
 	}
@@ -677,7 +744,7 @@
 static void kaweth_async_set_rx_mode(struct kaweth_device *kaweth)
 {
 	__u16 packet_filter_bitmap = kaweth->packet_filter_bitmap;
-	kaweth->packet_filter_bitmap = 0;	
+	kaweth->packet_filter_bitmap = 0;
 	if(packet_filter_bitmap == 0) return;
 
 	{
@@ -737,14 +804,14 @@
 	int result = 0;
 
 	kaweth_dbg("Kawasaki Device Probe (Device number:%d): 0x%4.4x:0x%4.4x:0x%4.4x",
-		 dev->devnum, 
-		 (int)dev->descriptor.idVendor, 
+		 dev->devnum,
+		 (int)dev->descriptor.idVendor,
 		 (int)dev->descriptor.idProduct,
 		 (int)dev->descriptor.bcdDevice);
 
 	kaweth_dbg("Device at %p", dev);
 
-	kaweth_dbg("Descriptor length: %x type: %x", 
+	kaweth_dbg("Descriptor length: %x type: %x",
 		 (int)dev->descriptor.bLength,
 		 (int)dev->descriptor.bDescriptorType);
 
@@ -757,7 +824,8 @@
 
 	kaweth->dev = dev;
 	spin_lock_init(&kaweth->device_lock);
-		
+	init_waitqueue_head(&kaweth->term_wait);
+
 	kaweth_dbg("Resetting.");
 
 	kaweth_reset(kaweth);
@@ -772,20 +840,20 @@
 	} else {
 		/* Download the firmware */
 		kaweth_info("Downloading firmware...");
-		if ((result = kaweth_download_firmware(kaweth, 
-						      kaweth_new_code, 
-						      len_kaweth_new_code, 
-						      100, 
+		if ((result = kaweth_download_firmware(kaweth,
+						      kaweth_new_code,
+						      len_kaweth_new_code,
+						      100,
 						      2)) < 0) {
 			kaweth_err("Error downloading firmware (%d)", result);
 			kfree(kaweth);
 			return NULL;
 		}
 
-		if ((result = kaweth_download_firmware(kaweth, 
-						      kaweth_new_code_fix, 
-						      len_kaweth_new_code_fix, 
-						      100, 
+		if ((result = kaweth_download_firmware(kaweth,
+						      kaweth_new_code_fix,
+						      len_kaweth_new_code_fix,
+						      100,
 						      3)) < 0) {
 			kaweth_err("Error downloading firmware fix (%d)", result);
 			kfree(kaweth);
@@ -845,7 +913,7 @@
 		 (int)kaweth->configuration.hw_addr[5]);
 
 	if(!memcmp(&kaweth->configuration.hw_addr,
-                   &bcast_addr, 
+                   &bcast_addr,
 		   sizeof(bcast_addr))) {
 		kaweth_err("Firmware not functioning properly, no net device created");
 		kfree(kaweth);
@@ -856,13 +924,13 @@
 		kaweth_dbg("Error setting URB size");
 		return kaweth;
 	}
-	
+
 	if(kaweth_set_sofs_wait(kaweth, KAWETH_SOFS_TO_WAIT) < 0) {
 		kaweth_err("Error setting SOFS wait");
 		return kaweth;
 	}
 
-	result = kaweth_set_receive_filter(kaweth, 
+	result = kaweth_set_receive_filter(kaweth,
                                            KAWETH_PACKET_FILTER_DIRECTED |
                                            KAWETH_PACKET_FILTER_BROADCAST |
                                            KAWETH_PACKET_FILTER_MULTICAST);
@@ -871,11 +939,18 @@
 		kaweth_err("Error setting receive filter");
 		return kaweth;
 	}
-	
+
 	kaweth_dbg("Initializing net device.");
 
 	kaweth->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!kaweth->tx_urb)
+		goto err_no_urb;
 	kaweth->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!kaweth->rx_urb)
+		goto err_only_tx;
+	kaweth->irq_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!kaweth->irq_urb)
+		goto err_tx_and_rx;
 
 	kaweth->net = init_etherdev(0, 0);
 	if (!kaweth->net) {
@@ -884,17 +959,17 @@
 	}
 
 	memcpy(kaweth->net->broadcast, &bcast_addr, sizeof(bcast_addr));
-	memcpy(kaweth->net->dev_addr, 
+	memcpy(kaweth->net->dev_addr,
                &kaweth->configuration.hw_addr,
                sizeof(kaweth->configuration.hw_addr));
-	 
+
 	kaweth->net->priv = kaweth;
 	kaweth->net->open = kaweth_open;
 	kaweth->net->stop = kaweth_close;
 
 	kaweth->net->watchdog_timeo = KAWETH_TX_TIMEOUT;
 	kaweth->net->tx_timeout = kaweth_tx_timeout;
-	
+
 	kaweth->net->do_ioctl = kaweth_ioctl;
 	kaweth->net->hard_start_xmit = kaweth_start_xmit;
 	kaweth->net->set_multicast_list = kaweth_set_rx_mode;
@@ -904,10 +979,18 @@
 	memset(&kaweth->stats, 0, sizeof(kaweth->stats));
 
 	kaweth_info("kaweth interface created at %s", kaweth->net->name);
-								
+
 	kaweth_dbg("Kaweth probe returning.");
 
 	return kaweth;
+
+err_tx_and_rx:
+	usb_free_urb(kaweth->rx_urb);
+err_only_tx:
+	usb_free_urb(kaweth->tx_urb);
+err_no_urb:
+	kfree(kaweth);
+	return NULL;
 }
 
 /****************************************************************
@@ -923,9 +1006,20 @@
 		kaweth_warn("unregistering non-existant device");
 		return;
 	}
-	usb_unlink_urb(kaweth->tx_urb);
+
+	kaweth->removed = 1;
+	usb_unlink_urb(kaweth->irq_urb);
 	usb_unlink_urb(kaweth->rx_urb);
 
+	/* we need to wait for the urb to be cancelled, if it is active */
+	spin_lock(&kaweth->device_lock);
+	if (usb_unlink_urb(kaweth->tx_urb) == -EINPROGRESS) {
+		spin_unlock(&kaweth->device_lock);
+		wait_event(kaweth->term_wait, kaweth->end);
+	} else {
+		spin_unlock(&kaweth->device_lock);
+	}
+
 	if(kaweth->net) {
 		if(kaweth->net->flags & IFF_UP) {
 			kaweth_dbg("Closing net device");
@@ -944,7 +1038,7 @@
 
 
 // FIXME this completion stuff is a modified clone of
-// an OLD version of some stuff in usb.c ... 
+// an OLD version of some stuff in usb.c ...
 struct usb_api_data {
 	wait_queue_head_t wqh;
 	int done;
@@ -974,7 +1068,7 @@
 
         init_waitqueue_head(&awd.wqh);
         awd.done = 0;
-        
+
         set_current_state(TASK_INTERRUPTIBLE);
         add_wait_queue(&awd.wqh, &wait);
         urb->context = &awd;
@@ -1021,7 +1115,7 @@
         int retv;
         int length;
 
-        urb = usb_alloc_urb(0, GFP_KERNEL);
+        urb = usb_alloc_urb(0, GFP_NOIO);
         if (!urb)
                 return -ENOMEM;
 
@@ -1057,6 +1151,7 @@
 
 module_init(kaweth_init);
 module_exit(kaweth_exit);
+
 
 
 
diff -Nru a/drivers/usb/net/pegasus.c b/drivers/usb/net/pegasus.c
--- a/drivers/usb/net/pegasus.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/usb/net/pegasus.c	Mon Apr 22 15:32:19 2002
@@ -41,7 +41,6 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
-
 #include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/init.h>
@@ -59,7 +58,7 @@
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v0.5.2 (2002/03/21)"
+#define DRIVER_VERSION "v0.5.4 (2002/04/11)"
 #define DRIVER_AUTHOR "Petko Manolov <petkan@users.sourceforge.net>"
 #define DRIVER_DESC "Pegasus/Pegasus II USB Ethernet driver"
 
@@ -85,105 +84,103 @@
 	{match_flags: USB_DEVICE_ID_MATCH_DEVICE, idVendor:vid, idProduct:pid},
 #include "pegasus.h"
 #undef	PEGASUS_DEV
-	{ }
+	{}
 };
 
-
-MODULE_AUTHOR( DRIVER_AUTHOR );
-MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
 MODULE_PARM(loopback, "i");
 MODULE_PARM(mii_mode, "i");
 MODULE_PARM_DESC(loopback, "Enable MAC loopback mode (bit 0)");
 MODULE_PARM_DESC(mii_mode, "Enable HomePNA mode (bit 0),default=MII mode = 0");
 
-MODULE_DEVICE_TABLE (usb, pegasus_ids);
-
+MODULE_DEVICE_TABLE(usb, pegasus_ids);
 
-static int update_eth_regs_async( pegasus_t * );
+static int update_eth_regs_async(pegasus_t *);
 /* Aargh!!! I _really_ hate such tweaks */
-static void ctrl_callback( struct urb *urb )
+static void ctrl_callback(struct urb *urb)
 {
-	pegasus_t	*pegasus = urb->context;
+	pegasus_t *pegasus = urb->context;
 
-	if ( !pegasus )
+	if (!pegasus)
 		return;
 
-	switch ( urb->status ) {
-		case 0:
-			if ( pegasus->flags & ETH_REGS_CHANGE ) {
-				pegasus->flags &= ~ETH_REGS_CHANGE;
-				pegasus->flags |= ETH_REGS_CHANGED;
-				update_eth_regs_async( pegasus );
-				return;
-			}
-			break;
-		case -EINPROGRESS:
+	switch (urb->status) {
+	case 0:
+		if (pegasus->flags & ETH_REGS_CHANGE) {
+			pegasus->flags &= ~ETH_REGS_CHANGE;
+			pegasus->flags |= ETH_REGS_CHANGED;
+			update_eth_regs_async(pegasus);
 			return;
-		case -ENOENT:
-			break;
-		default:
-			warn("%s: status %d", __FUNCTION__, urb->status);
+		}
+		break;
+	case -EINPROGRESS:
+		return;
+	case -ENOENT:
+		break;
+	default:
+		warn("%s: status %d", __FUNCTION__, urb->status);
 	}
 	pegasus->flags &= ~ETH_REGS_CHANGED;
-	wake_up(&pegasus->ctrl_wait );
+	wake_up(&pegasus->ctrl_wait);
 }
 
-
-static int get_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data)
+static int get_registers(pegasus_t * pegasus, __u16 indx, __u16 size,
+			 void *data)
 {
-	int	ret;
+	int ret;
 	unsigned char *buffer;
 	DECLARE_WAITQUEUE(wait, current);
 
-	buffer = kmalloc(size,GFP_KERNEL);
+	buffer = kmalloc(size, GFP_KERNEL);
 	if (!buffer) {
 		err("unable to allocate memory for configuration descriptors");
 		return 0;
 	}
-	memcpy(buffer,data,size);
+	memcpy(buffer, data, size);
 
 	add_wait_queue(&pegasus->ctrl_wait, &wait);
 	set_current_state(TASK_UNINTERRUPTIBLE);
-	while ( pegasus->flags & ETH_REGS_CHANGED )
+	while (pegasus->flags & ETH_REGS_CHANGED)
 		schedule();
 	remove_wait_queue(&pegasus->ctrl_wait, &wait);
 	set_current_state(TASK_RUNNING);
 
 	pegasus->dr.bRequestType = PEGASUS_REQT_READ;
 	pegasus->dr.bRequest = PEGASUS_REQ_GET_REGS;
-	pegasus->dr.wValue = cpu_to_le16 (0);
+	pegasus->dr.wValue = cpu_to_le16(0);
 	pegasus->dr.wIndex = cpu_to_le16p(&indx);
 	pegasus->dr.wLength = cpu_to_le16p(&size);
 	pegasus->ctrl_urb->transfer_buffer_length = size;
 
-	FILL_CONTROL_URB( pegasus->ctrl_urb, pegasus->usb,
-			  usb_rcvctrlpipe(pegasus->usb,0),
-			  (char *)&pegasus->dr,
-			  buffer, size, ctrl_callback, pegasus );
+	FILL_CONTROL_URB(pegasus->ctrl_urb, pegasus->usb,
+			 usb_rcvctrlpipe(pegasus->usb, 0),
+			 (char *) &pegasus->dr,
+			 buffer, size, ctrl_callback, pegasus);
 
-	add_wait_queue( &pegasus->ctrl_wait, &wait );
-	set_current_state( TASK_UNINTERRUPTIBLE );
+	add_wait_queue(&pegasus->ctrl_wait, &wait);
+	set_current_state(TASK_UNINTERRUPTIBLE);
 
 	/* using ATOMIC, we'd never wake up if we slept */
-	if ( (ret = usb_submit_urb( pegasus->ctrl_urb, GFP_ATOMIC )) ) {
+	if ((ret = usb_submit_urb(pegasus->ctrl_urb, GFP_ATOMIC))) {
 		err("%s: BAD CTRLs %d", __FUNCTION__, ret);
 		goto out;
 	}
 
 	schedule();
 out:
-	remove_wait_queue( &pegasus->ctrl_wait, &wait );
-	memcpy(data,buffer,size);
+	remove_wait_queue(&pegasus->ctrl_wait, &wait);
+	memcpy(data, buffer, size);
 	kfree(buffer);
 
 	return ret;
 }
 
-
-static int set_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data)
+static int set_registers(pegasus_t * pegasus, __u16 indx, __u16 size,
+			 void *data)
 {
-	int	ret;
+	int ret;
 	unsigned char *buffer;
 	DECLARE_WAITQUEUE(wait, current);
 
@@ -196,47 +193,46 @@
 
 	add_wait_queue(&pegasus->ctrl_wait, &wait);
 	set_current_state(TASK_UNINTERRUPTIBLE);
-	while ( pegasus->flags & ETH_REGS_CHANGED )
+	while (pegasus->flags & ETH_REGS_CHANGED)
 		schedule();
 	remove_wait_queue(&pegasus->ctrl_wait, &wait);
 	set_current_state(TASK_RUNNING);
 
 	pegasus->dr.bRequestType = PEGASUS_REQT_WRITE;
 	pegasus->dr.bRequest = PEGASUS_REQ_SET_REGS;
-	pegasus->dr.wValue = cpu_to_le16 (0);
-	pegasus->dr.wIndex = cpu_to_le16p( &indx );
-	pegasus->dr.wLength = cpu_to_le16p( &size );
+	pegasus->dr.wValue = cpu_to_le16(0);
+	pegasus->dr.wIndex = cpu_to_le16p(&indx);
+	pegasus->dr.wLength = cpu_to_le16p(&size);
 	pegasus->ctrl_urb->transfer_buffer_length = size;
 
-	FILL_CONTROL_URB( pegasus->ctrl_urb, pegasus->usb,
-			  usb_sndctrlpipe(pegasus->usb,0),
-			  (char *)&pegasus->dr,
-			  buffer, size, ctrl_callback, pegasus );
-			  
-	add_wait_queue( &pegasus->ctrl_wait, &wait );
-	set_current_state( TASK_UNINTERRUPTIBLE );
+	FILL_CONTROL_URB(pegasus->ctrl_urb, pegasus->usb,
+			 usb_sndctrlpipe(pegasus->usb, 0),
+			 (char *) &pegasus->dr,
+			 buffer, size, ctrl_callback, pegasus);
 
-	if ( (ret = usb_submit_urb( pegasus->ctrl_urb, GFP_ATOMIC )) ) {
+	add_wait_queue(&pegasus->ctrl_wait, &wait);
+	set_current_state(TASK_UNINTERRUPTIBLE);
+
+	if ((ret = usb_submit_urb(pegasus->ctrl_urb, GFP_ATOMIC))) {
 		err("%s: BAD CTRL %d", __FUNCTION__, ret);
 		goto out;
 	}
-	
+
 	schedule();
 out:
-	remove_wait_queue( &pegasus->ctrl_wait, &wait );
+	remove_wait_queue(&pegasus->ctrl_wait, &wait);
 	kfree(buffer);
-	
+
 	return ret;
 }
 
-
-static int set_register( pegasus_t *pegasus, __u16 indx, __u8 data )
+static int set_register(pegasus_t * pegasus, __u16 indx, __u8 data)
 {
-	int	ret;
+	int ret;
 	unsigned char *buffer;
 	__u16 dat = data;
 	DECLARE_WAITQUEUE(wait, current);
-	
+
 	buffer = kmalloc(1, GFP_KERNEL);
 	if (!buffer) {
 		err("unable to allocate memory for configuration descriptors");
@@ -246,129 +242,126 @@
 
 	add_wait_queue(&pegasus->ctrl_wait, &wait);
 	set_current_state(TASK_UNINTERRUPTIBLE);
-	while ( pegasus->flags & ETH_REGS_CHANGED )
+	while (pegasus->flags & ETH_REGS_CHANGED)
 		schedule();
 	remove_wait_queue(&pegasus->ctrl_wait, &wait);
 	set_current_state(TASK_RUNNING);
 
 	pegasus->dr.bRequestType = PEGASUS_REQT_WRITE;
 	pegasus->dr.bRequest = PEGASUS_REQ_SET_REG;
-	pegasus->dr.wValue = cpu_to_le16p( &dat);
-	pegasus->dr.wIndex = cpu_to_le16p( &indx );
-	pegasus->dr.wLength = cpu_to_le16( 1 );
+	pegasus->dr.wValue = cpu_to_le16p(&dat);
+	pegasus->dr.wIndex = cpu_to_le16p(&indx);
+	pegasus->dr.wLength = cpu_to_le16(1);
 	pegasus->ctrl_urb->transfer_buffer_length = 1;
 
-	FILL_CONTROL_URB( pegasus->ctrl_urb, pegasus->usb,
-			  usb_sndctrlpipe(pegasus->usb,0),
-			  (char *)&pegasus->dr,
-			  buffer, 1, ctrl_callback, pegasus );
+	FILL_CONTROL_URB(pegasus->ctrl_urb, pegasus->usb,
+			 usb_sndctrlpipe(pegasus->usb, 0),
+			 (char *) &pegasus->dr,
+			 buffer, 1, ctrl_callback, pegasus);
 
-	add_wait_queue( &pegasus->ctrl_wait, &wait );
-	set_current_state( TASK_UNINTERRUPTIBLE );
+	add_wait_queue(&pegasus->ctrl_wait, &wait);
+	set_current_state(TASK_UNINTERRUPTIBLE);
 
-	if ( (ret = usb_submit_urb( pegasus->ctrl_urb, GFP_ATOMIC )) ) {
+	if ((ret = usb_submit_urb(pegasus->ctrl_urb, GFP_ATOMIC))) {
 		err("%s: BAD CTRL %d", __FUNCTION__, ret);
 		goto out;
 	}
 
 	schedule();
 out:
-	remove_wait_queue( &pegasus->ctrl_wait, &wait );
+	remove_wait_queue(&pegasus->ctrl_wait, &wait);
 	kfree(buffer);
 
 	return ret;
 }
 
-
-static int update_eth_regs_async( pegasus_t *pegasus )
+static int update_eth_regs_async(pegasus_t * pegasus)
 {
-	int	ret;
+	int ret;
 
 	pegasus->dr.bRequestType = PEGASUS_REQT_WRITE;
 	pegasus->dr.bRequest = PEGASUS_REQ_SET_REGS;
 	pegasus->dr.wValue = 0;
-	pegasus->dr.wIndex =  cpu_to_le16(EthCtrl0);
+	pegasus->dr.wIndex = cpu_to_le16(EthCtrl0);
 	pegasus->dr.wLength = cpu_to_le16(3);
 	pegasus->ctrl_urb->transfer_buffer_length = 3;
 
-	FILL_CONTROL_URB( pegasus->ctrl_urb, pegasus->usb,
-			  usb_sndctrlpipe(pegasus->usb,0),
-			  (char *)&pegasus->dr,
-			  pegasus->eth_regs, 3, ctrl_callback, pegasus );
+	FILL_CONTROL_URB(pegasus->ctrl_urb, pegasus->usb,
+			 usb_sndctrlpipe(pegasus->usb, 0),
+			 (char *) &pegasus->dr,
+			 pegasus->eth_regs, 3, ctrl_callback, pegasus);
+
+	if ((ret = usb_submit_urb(pegasus->ctrl_urb, GFP_ATOMIC)))
+		err("%s: BAD CTRL %d, flgs %x", __FUNCTION__, ret,
+		    pegasus->flags);
 
-	if ( (ret = usb_submit_urb( pegasus->ctrl_urb, GFP_ATOMIC )) )
-		err("%s: BAD CTRL %d, flgs %x",__FUNCTION__,ret,pegasus->flags);
-
-	return	ret;
+	return ret;
 }
 
-
-static int read_mii_word( pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 *regd )
+static int read_mii_word(pegasus_t * pegasus, __u8 phy, __u8 indx, __u16 * regd)
 {
-	int	i;
-	__u8	data[4] = { phy, 0, 0, indx };
-	__u16  regdi;
-	
-	set_register( pegasus, PhyCtrl, 0 );
-	set_registers( pegasus, PhyAddr, sizeof(data), data );
-	set_register( pegasus, PhyCtrl, (indx | PHY_READ) );
+	int i;
+	__u8 data[4] = { phy, 0, 0, indx };
+	__u16 regdi;
+
+	set_register(pegasus, PhyCtrl, 0);
+	set_registers(pegasus, PhyAddr, sizeof(data), data);
+	set_register(pegasus, PhyCtrl, (indx | PHY_READ));
 	for (i = 0; i < REG_TIMEOUT; i++) {
 		get_registers(pegasus, PhyCtrl, 1, data);
-		if ( data[0] & PHY_DONE ) 
+		if (data[0] & PHY_DONE)
 			break;
 	}
-	if ( i < REG_TIMEOUT ) {
-		get_registers( pegasus, PhyData, 2, &regdi );
+	if (i < REG_TIMEOUT) {
+		get_registers(pegasus, PhyData, 2, &regdi);
 		*regd = le16_to_cpu(regdi);
-		return	0;
+		return 0;
 	}
 	warn("%s: failed", __FUNCTION__);
-	
+
 	return 1;
 }
 
-
-static int write_mii_word( pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 regd )
+static int write_mii_word(pegasus_t * pegasus, __u8 phy, __u8 indx, __u16 regd)
 {
-	int	i;
-	__u8	data[4] = { phy, 0, 0, indx };
-	
-	*(data + 1) = cpu_to_le16p( &regd );
-	set_register( pegasus, PhyCtrl, 0 );
-	set_registers( pegasus, PhyAddr, 4, data );
-	set_register( pegasus, PhyCtrl, (indx | PHY_WRITE) );
+	int i;
+	__u8 data[4] = { phy, 0, 0, indx };
+
+	*(data + 1) = cpu_to_le16p(&regd);
+	set_register(pegasus, PhyCtrl, 0);
+	set_registers(pegasus, PhyAddr, 4, data);
+	set_register(pegasus, PhyCtrl, (indx | PHY_WRITE));
 	for (i = 0; i < REG_TIMEOUT; i++) {
 		get_registers(pegasus, PhyCtrl, 1, data);
-		if ( data[0] & PHY_DONE ) 
+		if (data[0] & PHY_DONE)
 			break;
 	}
-	if ( i < REG_TIMEOUT )
-		return	0;
+	if (i < REG_TIMEOUT)
+		return 0;
 	warn("%s: failed", __FUNCTION__);
 
 	return 1;
 }
 
-
-static int read_eprom_word( pegasus_t *pegasus, __u8 index, __u16 *retdata )
+static int read_eprom_word(pegasus_t * pegasus, __u8 index, __u16 * retdata)
 {
-	int	i;
+	int i;
 	__u8 tmp;
 	__u16 retdatai;
-	
-	set_register( pegasus, EpromCtrl, 0 );
-	set_register( pegasus, EpromOffset, index );
-	set_register( pegasus, EpromCtrl, EPROM_READ); 
-
-	for ( i=0; i < REG_TIMEOUT; i++ ) {
-		get_registers( pegasus, EpromCtrl, 1, &tmp );
-		if ( tmp & EPROM_DONE )
+
+	set_register(pegasus, EpromCtrl, 0);
+	set_register(pegasus, EpromOffset, index);
+	set_register(pegasus, EpromCtrl, EPROM_READ);
+
+	for (i = 0; i < REG_TIMEOUT; i++) {
+		get_registers(pegasus, EpromCtrl, 1, &tmp);
+		if (tmp & EPROM_DONE)
 			break;
 	}
-	if ( i < REG_TIMEOUT ) {
-		get_registers( pegasus, EpromData, 2, &retdatai );
-		*retdata = le16_to_cpu (retdatai);
-		return	0;
+	if (i < REG_TIMEOUT) {
+		get_registers(pegasus, EpromData, 2, &retdatai);
+		*retdata = le16_to_cpu(retdatai);
+		return 0;
 	}
 	warn("%s: failed", __FUNCTION__);
 
@@ -376,355 +369,372 @@
 }
 
 #ifdef	PEGASUS_WRITE_EEPROM
-static inline void enable_eprom_write( pegasus_t *pegasus )
+static inline void enable_eprom_write(pegasus_t * pegasus)
 {
-	__u8	tmp;
+	__u8 tmp;
 
-	get_registers( pegasus, EthCtrl2, 1, &tmp );
-	set_register( pegasus, EthCtrl2, tmp | EPROM_WR_ENABLE );
+	get_registers(pegasus, EthCtrl2, 1, &tmp);
+	set_register(pegasus, EthCtrl2, tmp | EPROM_WR_ENABLE);
 }
 
-
-static inline void disable_eprom_write( pegasus_t *pegasus )
+static inline void disable_eprom_write(pegasus_t * pegasus)
 {
-	__u8 	tmp;
+	__u8 tmp;
 
-	get_registers( pegasus, EthCtrl2, 1, &tmp );
-	set_register( pegasus, EpromCtrl, 0 );
-	set_register( pegasus, EthCtrl2, tmp & ~EPROM_WR_ENABLE );
+	get_registers(pegasus, EthCtrl2, 1, &tmp);
+	set_register(pegasus, EpromCtrl, 0);
+	set_register(pegasus, EthCtrl2, tmp & ~EPROM_WR_ENABLE);
 }
 
-
-static int write_eprom_word( pegasus_t *pegasus, __u8 index, __u16 data )
+static int write_eprom_word(pegasus_t * pegasus, __u8 index, __u16 data)
 {
-	int	i, tmp;
-	__u8	d[4] = {0x3f, 0, 0, EPROM_WRITE};
+	int i, tmp;
+	__u8 d[4] = { 0x3f, 0, 0, EPROM_WRITE };
 
-	set_registers( pegasus, EpromOffset, 4, d );
-	enable_eprom_write( pegasus );
-	set_register( pegasus, EpromOffset, index );
-	set_registers( pegasus, EpromData, 2, &data );
-	set_register( pegasus, EpromCtrl, EPROM_WRITE );
+	set_registers(pegasus, EpromOffset, 4, d);
+	enable_eprom_write(pegasus);
+	set_register(pegasus, EpromOffset, index);
+	set_registers(pegasus, EpromData, 2, &data);
+	set_register(pegasus, EpromCtrl, EPROM_WRITE);
 
-	for ( i=0; i < REG_TIMEOUT; i++ ) {
-		get_registers( pegasus, EpromCtrl, 1, &tmp );
-		if ( tmp & EPROM_DONE )
+	for (i = 0; i < REG_TIMEOUT; i++) {
+		get_registers(pegasus, EpromCtrl, 1, &tmp);
+		if (tmp & EPROM_DONE)
 			break;
 	}
-	disable_eprom_write( pegasus );
-	if ( i < REG_TIMEOUT )
-		return	0;
+	disable_eprom_write(pegasus);
+	if (i < REG_TIMEOUT)
+		return 0;
 	warn("%s: failed", __FUNCTION__);
-	return	-1;
+	return -1;
 }
-#endif	/* PEGASUS_WRITE_EEPROM */
+#endif				/* PEGASUS_WRITE_EEPROM */
 
-static inline void get_node_id( pegasus_t *pegasus, __u8 *id )
+static inline void get_node_id(pegasus_t * pegasus, __u8 * id)
 {
-	int	i;
+	int i;
 	__u16 w16;
-	
+
 	for (i = 0; i < 3; i++) {
-		read_eprom_word( pegasus, i, &w16);
-		((__u16 *) id)[i] = cpu_to_le16p (&w16);
+		read_eprom_word(pegasus, i, &w16);
+		((__u16 *) id)[i] = cpu_to_le16p(&w16);
 	}
 }
 
-
-static void set_ethernet_addr( pegasus_t *pegasus )
+static void set_ethernet_addr(pegasus_t * pegasus)
 {
-	__u8	node_id[6];
+	__u8 node_id[6];
 
 	get_node_id(pegasus, node_id);
-	set_registers( pegasus, EthID, sizeof(node_id), node_id );
-	memcpy( pegasus->net->dev_addr, node_id, sizeof(node_id) );
+	set_registers(pegasus, EthID, sizeof(node_id), node_id);
+	memcpy(pegasus->net->dev_addr, node_id, sizeof(node_id));
 }
 
-
-static inline int reset_mac( pegasus_t *pegasus )
+static inline int reset_mac(pegasus_t * pegasus)
 {
-	__u8	data = 0x8;
-	int	i;
+	__u8 data = 0x8;
+	int i;
 
 	set_register(pegasus, EthCtrl1, data);
 	for (i = 0; i < REG_TIMEOUT; i++) {
 		get_registers(pegasus, EthCtrl1, 1, &data);
 		if (~data & 0x08) {
-			if (loopback & 1) 
+			if (loopback & 1)
 				break;
-			if ( mii_mode && (pegasus->features & HAS_HOME_PNA) )
-				set_register( pegasus, Gpio1, 0x34 );
+			if (mii_mode && (pegasus->features & HAS_HOME_PNA))
+				set_register(pegasus, Gpio1, 0x34);
 			else
-				set_register( pegasus, Gpio1, 0x26 );
-			set_register( pegasus, Gpio0, pegasus->features );
-			set_register( pegasus, Gpio0, DEFAULT_GPIO_SET );
+				set_register(pegasus, Gpio1, 0x26);
+			set_register(pegasus, Gpio0, pegasus->features);
+			set_register(pegasus, Gpio0, DEFAULT_GPIO_SET);
 			break;
 		}
 	}
-	if ( i == REG_TIMEOUT )
+	if (i == REG_TIMEOUT)
 		return 1;
 
 	if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS ||
 	    usb_dev_id[pegasus->dev_index].vendor == VENDOR_DLINK) {
-		__u16	auxmode;
-		read_mii_word(pegasus, 1, MII_TPISTATUS, &auxmode);
-		write_mii_word(pegasus, 1, MII_TPISTATUS, auxmode | 4);
+		set_register(pegasus, Gpio0, 0x24);
+		set_register(pegasus, Gpio0, 0x26);
 	}
 	if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_ELCON) {
-		__u16   auxmode;
+		__u16 auxmode;
 		read_mii_word(pegasus, 3, 0x1b, &auxmode);
 		write_mii_word(pegasus, 3, 0x1b, auxmode | 4);
 	}
 
-	return	0;
+	return 0;
 }
 
-
-static int enable_net_traffic( struct net_device *dev, struct usb_device *usb )
+static int enable_net_traffic(struct net_device *dev, struct usb_device *usb)
 {
-	__u16	linkpart, bmsr;
-	__u8	data[4];
+	__u16 linkpart, bmsr;
+	__u8 data[4];
 	pegasus_t *pegasus = dev->priv;
 
-
 	read_mii_word(pegasus, pegasus->phy, MII_BMSR, &bmsr);
 	read_mii_word(pegasus, pegasus->phy, MII_BMSR, &bmsr);
-	if ( !(bmsr & 4) && !loopback )
-		warn( "%s: link NOT established (%04x) - check the cable.",
-			dev->name, bmsr );
-	if ( read_mii_word(pegasus, pegasus->phy, MII_LPA, &linkpart) )
+	if (!(bmsr & BMSR_LSTATUS) && !loopback)
+		warn("%s: link NOT established (%04x) - check the cable.",
+		     dev->name, bmsr);
+	if (read_mii_word(pegasus, pegasus->phy, MII_LPA, &linkpart))
 		return 2;
-	if ( !(linkpart & 1) )
-		warn( "link partner stat %x", linkpart );
+	if (!(linkpart & 1))
+		warn("link partner stat %x", linkpart);
 
 	data[0] = 0xc9;
 	data[1] = 0;
-	if ( linkpart & (ADVERTISE_100FULL | ADVERTISE_10FULL) )
-		data[1] |= 0x20; /* set full duplex */
-	if ( linkpart & (ADVERTISE_100FULL | ADVERTISE_100HALF) )
-		data[1] |= 0x10; /* set 100 Mbps */
-	if ( mii_mode )
+	if (linkpart & (ADVERTISE_100FULL | ADVERTISE_10FULL))
+		data[1] |= 0x20;	/* set full duplex */
+	if (linkpart & (ADVERTISE_100FULL | ADVERTISE_100HALF))
+		data[1] |= 0x10;	/* set 100 Mbps */
+	if (mii_mode)
 		data[1] = 0;
 	data[2] = (loopback & 1) ? 0x09 : 0x01;
 
-	memcpy( pegasus->eth_regs, data, sizeof(data) );
+	memcpy(pegasus->eth_regs, data, sizeof(data));
+	set_registers(pegasus, EthCtrl0, 3, data);
 
-	set_registers( pegasus, EthCtrl0, 3, data );
+	if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS ||
+	    usb_dev_id[pegasus->dev_index].vendor == VENDOR_DLINK) {
+		u16 auxmode;
+		read_mii_word(pegasus, 0, 0x1b, &auxmode);
+		write_mii_word(pegasus, 0, 0x1b, auxmode | 4);
+	}
 
 	return 0;
 }
 
-
-static void read_bulk_callback( struct urb *urb )
+static void read_bulk_callback(struct urb *urb)
 {
 	pegasus_t *pegasus = urb->context;
 	struct net_device *net;
-	int count = urb->actual_length, res;
+	int count = urb->actual_length;
 	int rx_status;
-	struct sk_buff	*skb;
+	struct sk_buff *skb;
 	__u16 pkt_len;
 
-	if ( !pegasus || !(pegasus->flags & PEGASUS_RUNNING) )
+	if (!pegasus || !(pegasus->flags & PEGASUS_RUNNING))
 		return;
 
 	net = pegasus->net;
-	if ( !netif_device_present(net) )
+	if (!netif_device_present(net))
 		return;
 
-	if ( pegasus->flags & PEGASUS_RX_BUSY ) {
-		pegasus->stats.rx_errors++;
-		dbg("pegasus Rx busy");
-		return;
-	}
-	pegasus->flags |= PEGASUS_RX_BUSY;
-
-	switch ( urb->status ) {
-		case 0:
-			break;
-		case -ETIMEDOUT:
-			dbg( "reset MAC" );
-			pegasus->flags &= ~PEGASUS_RX_BUSY;
-			break;
-		default:
-			dbg( "%s: RX status %d", net->name, urb->status );
-			goto goon;
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ETIMEDOUT:
+		dbg("reset MAC");
+		pegasus->flags &= ~PEGASUS_RX_BUSY;
+		break;
+	default:
+		dbg("%s: RX status %d", net->name, urb->status);
+		goto goon;
 	}
 
-	if ( !count )
+	if (!count)
 		goto goon;
 
-	rx_status = le32_to_cpu(*(int *)(pegasus->rx_buff + count - 4));
-	if ( rx_status & 0x000e0000 ) {
+	rx_status = le32_to_cpu(*(int *)(urb->transfer_buffer + count - 4));
+	if (rx_status & 0x000e0000) {
 		dbg("%s: RX packet error %x", net->name, rx_status & 0xe0000);
 		pegasus->stats.rx_errors++;
-		if ( rx_status & 0x060000 )
+		if (rx_status & 0x060000)
 			pegasus->stats.rx_length_errors++;
-		if ( rx_status & 0x080000 )
+		if (rx_status & 0x080000)
 			pegasus->stats.rx_crc_errors++;
-		if ( rx_status & 0x100000 )
+		if (rx_status & 0x100000)
 			pegasus->stats.rx_frame_errors++;
 		goto goon;
 	}
-
 	pkt_len = (rx_status & 0xfff) - 8;
 
-	if ( !(skb = dev_alloc_skb(pkt_len+2)) )
-		goto goon;
+	tasklet_schedule(&pegasus->rx_tl);
 
+	if (!pegasus->rx_skb)
+		return;
+
+	skb_put(pegasus->rx_skb, pkt_len);
+	pegasus->rx_skb->protocol = eth_type_trans(pegasus->rx_skb, net);
+	netif_rx(pegasus->rx_skb);
+
+	if (!(skb = dev_alloc_skb(PEGASUS_MTU + 2))) {
+		pegasus->rx_skb = NULL;
+		return;
+	}
+	
 	skb->dev = net;
 	skb_reserve(skb, 2);
-	eth_copy_and_sum(skb, pegasus->rx_buff, pkt_len, 0);
-	skb_put(skb, pkt_len);
-
-	skb->protocol = eth_type_trans(skb, net);
-	netif_rx(skb);
+	pegasus->rx_skb = skb;
 	pegasus->stats.rx_packets++;
 	pegasus->stats.rx_bytes += pkt_len;
-
 goon:
-	FILL_BULK_URB( pegasus->rx_urb, pegasus->usb,
-			usb_rcvbulkpipe(pegasus->usb, 1),
-			pegasus->rx_buff, PEGASUS_MAX_MTU, 
-			read_bulk_callback, pegasus );
-	if ( (res = usb_submit_urb(pegasus->rx_urb, GFP_ATOMIC)) )
-		warn("%s: failed submint rx_urb %d", __FUNCTION__, res);
-	pegasus->flags &= ~PEGASUS_RX_BUSY;
+	FILL_BULK_URB(pegasus->rx_urb, pegasus->usb,
+		      usb_rcvbulkpipe(pegasus->usb, 1),
+		      pegasus->rx_skb->data, PEGASUS_MTU + 8,
+		      read_bulk_callback, pegasus);
+	if (usb_submit_urb(pegasus->rx_urb, GFP_ATOMIC))
+		pegasus->flags |= PEGASUS_RX_URB_FAIL;
+	else
+		pegasus->flags &= ~PEGASUS_RX_URB_FAIL;
 }
 
+static void rx_fixup(unsigned long data)
+{
+	pegasus_t *pegasus;
+
+	pegasus = (pegasus_t *)data;
 
-static void write_bulk_callback( struct urb *urb )
+	if (pegasus->flags & PEGASUS_RX_URB_FAIL) {
+		goto try_again;
+	}
+	if (pegasus->rx_skb)
+		return;
+	if (!(pegasus->rx_skb = dev_alloc_skb(PEGASUS_MTU + 2))) {
+		tasklet_schedule(&pegasus->rx_tl);
+		return;
+	}
+	FILL_BULK_URB(pegasus->rx_urb, pegasus->usb,
+ 	              usb_rcvbulkpipe(pegasus->usb, 1),
+	              pegasus->rx_skb->data, PEGASUS_MTU + 8,
+	              read_bulk_callback, pegasus);	
+try_again:
+	if (usb_submit_urb(pegasus->rx_urb, GFP_ATOMIC)) {
+		pegasus->flags |= PEGASUS_RX_URB_FAIL;
+		tasklet_schedule(&pegasus->rx_tl);
+	} else {
+		pegasus->flags &= ~PEGASUS_RX_URB_FAIL;
+	}
+}
+
+static void write_bulk_callback(struct urb *urb)
 {
 	pegasus_t *pegasus = urb->context;
 
-	if ( !pegasus || !(pegasus->flags & PEGASUS_RUNNING) )
+	if (!pegasus || !(pegasus->flags & PEGASUS_RUNNING))
 		return;
 
-	if ( !netif_device_present(pegasus->net) )
+	if (!netif_device_present(pegasus->net))
 		return;
-		
-	if ( urb->status )
+
+	if (urb->status)
 		info("%s: TX status %d", pegasus->net->name, urb->status);
 
 	pegasus->net->trans_start = jiffies;
-	netif_wake_queue( pegasus->net );
+	netif_wake_queue(pegasus->net);
 }
 
 #ifdef	PEGASUS_USE_INTR
-static void intr_callback( struct urb *urb )
+static void intr_callback(struct urb *urb)
 {
 	pegasus_t *pegasus = urb->context;
 	struct net_device *net;
-	__u8	*d;
+	__u8 *d;
 
-	if ( !pegasus )
+	if (!pegasus)
 		return;
-		
-	switch ( urb->status ) {
-		case 0:
-			break;
-		case -ENOENT:
-			return;
-		default:
-			info("intr status %d", urb->status);
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ENOENT:
+		return;
+	default:
+		info("intr status %d", urb->status);
 	}
 
 	d = urb->transfer_buffer;
 	net = pegasus->net;
-	if ( d[0] & 0xfc ) {
+	if (d[0] & 0xfc) {
 		pegasus->stats.tx_errors++;
-		if ( d[0] & TX_UNDERRUN )
+		if (d[0] & TX_UNDERRUN)
 			pegasus->stats.tx_fifo_errors++;
-		if ( d[0] & (EXCESSIVE_COL | JABBER_TIMEOUT) )
+		if (d[0] & (EXCESSIVE_COL | JABBER_TIMEOUT))
 			pegasus->stats.tx_aborted_errors++;
-		if ( d[0] & LATE_COL )
+		if (d[0] & LATE_COL)
 			pegasus->stats.tx_window_errors++;
-		if ( d[0] & (NO_CARRIER | LOSS_CARRIER) )
+		if (d[0] & (NO_CARRIER | LOSS_CARRIER))
 			pegasus->stats.tx_carrier_errors++;
 	}
 }
 #endif
 
-static void pegasus_tx_timeout( struct net_device *net )
+static void pegasus_tx_timeout(struct net_device *net)
 {
 	pegasus_t *pegasus = net->priv;
 
-	if ( !pegasus )
+	if (!pegasus)
 		return;
-		
+
 	warn("%s: Tx timed out.", net->name);
 	pegasus->tx_urb->transfer_flags |= USB_ASYNC_UNLINK;
-	usb_unlink_urb( pegasus->tx_urb );
+	usb_unlink_urb(pegasus->tx_urb);
 	pegasus->stats.tx_errors++;
 }
 
-
-static int pegasus_start_xmit( struct sk_buff *skb, struct net_device *net )
+static int pegasus_start_xmit(struct sk_buff *skb, struct net_device *net)
 {
-	pegasus_t	*pegasus = net->priv;
-	int 	count = ((skb->len+2) & 0x3f) ? skb->len+2 : skb->len+3;
-	int 	res;
+	pegasus_t *pegasus = net->priv;
+	int count = ((skb->len + 2) & 0x3f) ? skb->len + 2 : skb->len + 3;
+	int res;
 	__u16 l16 = skb->len;
-	
-	netif_stop_queue( net );
-		
-	((__u16 *)pegasus->tx_buff)[0] = cpu_to_le16( l16 );
-	memcpy(pegasus->tx_buff+2, skb->data, skb->len);
-	FILL_BULK_URB( pegasus->tx_urb, pegasus->usb,
-			usb_sndbulkpipe(pegasus->usb, 2),
-			pegasus->tx_buff, PEGASUS_MAX_MTU, 
-			write_bulk_callback, pegasus );
-	pegasus->tx_urb->transfer_buffer_length = count;
+
+	netif_stop_queue(net);
+
+	((__u16 *) pegasus->tx_buff)[0] = cpu_to_le16(l16);
+	memcpy(pegasus->tx_buff + 2, skb->data, skb->len);
+	FILL_BULK_URB(pegasus->tx_urb, pegasus->usb,
+		      usb_sndbulkpipe(pegasus->usb, 2),
+		      pegasus->tx_buff, count,
+		      write_bulk_callback, pegasus);
 	if ((res = usb_submit_urb(pegasus->tx_urb, GFP_ATOMIC))) {
 		warn("failed tx_urb %d", res);
 		pegasus->stats.tx_errors++;
-		netif_start_queue( net );
+		netif_start_queue(net);
 	} else {
 		pegasus->stats.tx_packets++;
 		pegasus->stats.tx_bytes += skb->len;
 		net->trans_start = jiffies;
 	}
-
 	dev_kfree_skb(skb);
 
 	return 0;
 }
 
-
-static struct net_device_stats *pegasus_netdev_stats( struct net_device *dev )
+static struct net_device_stats *pegasus_netdev_stats(struct net_device *dev)
 {
-	return &((pegasus_t *)dev->priv)->stats;
+	return &((pegasus_t *) dev->priv)->stats;
 }
 
-
-static inline void disable_net_traffic( pegasus_t *pegasus )
+static inline void disable_net_traffic(pegasus_t * pegasus)
 {
-	int 	tmp=0;
+	int tmp = 0;
 
-	set_registers( pegasus, EthCtrl0, 2, &tmp );
+	set_registers(pegasus, EthCtrl0, 2, &tmp);
 }
 
-
-static inline void get_interrupt_interval( pegasus_t *pegasus )
+static inline void get_interrupt_interval(pegasus_t * pegasus)
 {
-	__u8	data[2];
+	__u8 data[2];
 
-	read_eprom_word( pegasus, 4, (__u16 *)data );
-	if ( data[1] < 0x80 ) {
-		info( "intr interval will be changed from %ums to %ums",
-		     data[1], 0x80 );
+	read_eprom_word(pegasus, 4, (__u16 *) data);
+	if (data[1] < 0x80) {
+		info("intr interval will be changed from %ums to %ums",
+		     data[1], 0x80);
 		data[1] = 0x80;
 #ifdef	PEGASUS_WRITE_EEPROM
-		write_eprom_word( pegasus, 4, *(__u16 *)data );
+		write_eprom_word(pegasus, 4, *(__u16 *) data);
 #endif
 	}
 	pegasus->intr_interval = data[1];
 }
 
-
 static void set_carrier(struct net_device *net)
 {
-	pegasus_t	*pegasus;
-	short		tmp;
+	pegasus_t *pegasus;
+	short tmp;
 
 	pegasus = net->priv;
 	read_mii_word(pegasus, pegasus->phy, MII_BMSR, &tmp);
@@ -732,33 +742,37 @@
 		netif_carrier_on(net);
 	else
 		netif_carrier_off(net);
-	
-}
 
+}
 
 static int pegasus_open(struct net_device *net)
 {
-	pegasus_t *pegasus = (pegasus_t *)net->priv;
-	int	res;
+	pegasus_t *pegasus = (pegasus_t *) net->priv;
+	int res;
+
+	if (!(pegasus->rx_skb = dev_alloc_skb(PEGASUS_MTU + 2)))
+		return -ENOMEM;
+	pegasus->rx_skb->dev = net;
+	skb_reserve(pegasus->rx_skb, 2);
 
 	down(&pegasus->sem);
-	FILL_BULK_URB( pegasus->rx_urb, pegasus->usb,
-			usb_rcvbulkpipe(pegasus->usb, 1),
-			pegasus->rx_buff, PEGASUS_MAX_MTU, 
-			read_bulk_callback, pegasus );
-	if ( (res = usb_submit_urb(pegasus->rx_urb, GFP_KERNEL)) )
+	FILL_BULK_URB(pegasus->rx_urb, pegasus->usb,
+		      usb_rcvbulkpipe(pegasus->usb, 1),
+		      pegasus->rx_skb->data, PEGASUS_MTU + 8,
+		      read_bulk_callback, pegasus);
+	if ((res = usb_submit_urb(pegasus->rx_urb, GFP_KERNEL)))
 		warn("%s: failed rx_urb %d", __FUNCTION__, res);
 #ifdef	PEGASUS_USE_INTR
-	FILL_INT_URB( pegasus->intr_urb, pegasus->usb,
-			usb_rcvintpipe(pegasus->usb, 3),
-			pegasus->intr_buff, sizeof(pegasus->intr_buff),
-			intr_callback, pegasus, pegasus->intr_interval );
-	if ( (res = usb_submit_urb(pegasus->intr_urb, GFP_KERNEL)) )
+	FILL_INT_URB(pegasus->intr_urb, pegasus->usb,
+		     usb_rcvintpipe(pegasus->usb, 3),
+		     pegasus->intr_buff, sizeof(pegasus->intr_buff),
+		     intr_callback, pegasus, pegasus->intr_interval);
+	if ((res = usb_submit_urb(pegasus->intr_urb, GFP_KERNEL)))
 		warn("%s: failed intr_urb %d", __FUNCTION__, res);
 #endif
-	netif_start_queue( net );
+	netif_start_queue(net);
 	pegasus->flags |= PEGASUS_RUNNING;
-	if ( (res = enable_net_traffic(net, pegasus->usb)) ) {
+	if ((res = enable_net_traffic(net, pegasus->usb))) {
 		err("can't enable_net_traffic() - %d", res);
 		res = -EIO;
 		goto exit;
@@ -771,125 +785,122 @@
 	return res;
 }
 
-
-static int pegasus_close( struct net_device *net )
+static int pegasus_close(struct net_device *net)
 {
-	pegasus_t	*pegasus = net->priv;
+	pegasus_t *pegasus = net->priv;
 
 	down(&pegasus->sem);
 	pegasus->flags &= ~PEGASUS_RUNNING;
-	netif_stop_queue( net );
-	if ( !(pegasus->flags & PEGASUS_UNPLUG) )
-		disable_net_traffic( pegasus );
-
-	usb_unlink_urb( pegasus->rx_urb );
-	usb_unlink_urb( pegasus->tx_urb );
-	usb_unlink_urb( pegasus->ctrl_urb );
+	netif_stop_queue(net);
+	if (!(pegasus->flags & PEGASUS_UNPLUG))
+		disable_net_traffic(pegasus);
+
+	usb_unlink_urb(pegasus->rx_urb);
+	usb_unlink_urb(pegasus->tx_urb);
+	usb_unlink_urb(pegasus->ctrl_urb);
 #ifdef	PEGASUS_USE_INTR
-	usb_unlink_urb( pegasus->intr_urb );
+	usb_unlink_urb(pegasus->intr_urb);
 #endif
 	up(&pegasus->sem);
 
 	return 0;
 }
 
-
 static int pegasus_ethtool_ioctl(struct net_device *net, void *uaddr)
 {
-	pegasus_t	*pegasus;
-	int		cmd;
-	char		tmp[128];
+	pegasus_t *pegasus;
+	int cmd;
+	char tmp[128];
 
 	pegasus = net->priv;
-	if (get_user(cmd, (int *)uaddr))
+	if (get_user(cmd, (int *) uaddr))
 		return -EFAULT;
 	switch (cmd) {
-	case ETHTOOL_GDRVINFO: {
-		struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO};
-		strncpy(info.driver, DRIVER_DESC, ETHTOOL_BUSINFO_LEN);
-		strncpy(info.version, DRIVER_VERSION, ETHTOOL_BUSINFO_LEN);
-		sprintf(tmp, "usb%d:%d", pegasus->usb->bus->busnum,
-		        pegasus->usb->devnum);
-		strncpy(info.bus_info, tmp, ETHTOOL_BUSINFO_LEN);
-		if (copy_to_user(uaddr, &info, sizeof(info)))
-			return -EFAULT;
-		return 0;
-	}
-	case ETHTOOL_GSET: {
-		struct ethtool_cmd ecmd;
-		short	lpa, bmcr;
-
-		if (copy_from_user(&ecmd, uaddr, sizeof(ecmd)))
-			return -EFAULT;
-		ecmd.supported = (SUPPORTED_10baseT_Half |
-		                 SUPPORTED_10baseT_Full |
-		                 SUPPORTED_100baseT_Half |
-		                 SUPPORTED_100baseT_Full |
-		                 SUPPORTED_Autoneg |
-		                 SUPPORTED_TP |
-		                 SUPPORTED_MII);
-		ecmd.port = PORT_TP;
-		ecmd.transceiver = XCVR_INTERNAL;
-		ecmd.phy_address = pegasus->phy;
-		read_mii_word(pegasus, pegasus->phy, MII_BMCR, &bmcr);
-		read_mii_word(pegasus, pegasus->phy, MII_LPA, &lpa);
-		if (bmcr & BMCR_ANENABLE) {
-			ecmd.autoneg = AUTONEG_ENABLE;
-			ecmd.speed = lpa & (LPA_100HALF|LPA_100FULL) ?
-		                     SPEED_100 : SPEED_10;
-			if (ecmd.speed == SPEED_100)
-				ecmd.duplex = lpa & LPA_100FULL ?
-				              DUPLEX_FULL : DUPLEX_HALF;
-			else
-				ecmd.duplex = lpa & LPA_10FULL ?
-				              DUPLEX_FULL : DUPLEX_HALF;
-		} else {
-			ecmd.autoneg = AUTONEG_DISABLE;
-			ecmd.speed = bmcr & BMCR_SPEED100 ? 
-			             SPEED_100 : SPEED_10;
-			ecmd.duplex = bmcr & BMCR_FULLDPLX ?
-			              DUPLEX_FULL : DUPLEX_HALF;
+	case ETHTOOL_GDRVINFO:{
+			struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+			strncpy(info.driver, DRIVER_DESC, ETHTOOL_BUSINFO_LEN);
+			strncpy(info.version, DRIVER_VERSION,
+				ETHTOOL_BUSINFO_LEN);
+			sprintf(tmp, "usb%d:%d", pegasus->usb->bus->busnum,
+				pegasus->usb->devnum);
+			strncpy(info.bus_info, tmp, ETHTOOL_BUSINFO_LEN);
+			if (copy_to_user(uaddr, &info, sizeof(info)))
+				return -EFAULT;
+			return 0;
+		}
+	case ETHTOOL_GSET:{
+			struct ethtool_cmd ecmd;
+			short lpa, bmcr;
+
+			if (copy_from_user(&ecmd, uaddr, sizeof(ecmd)))
+				return -EFAULT;
+			ecmd.supported = (SUPPORTED_10baseT_Half |
+					  SUPPORTED_10baseT_Full |
+					  SUPPORTED_100baseT_Half |
+					  SUPPORTED_100baseT_Full |
+					  SUPPORTED_Autoneg |
+					  SUPPORTED_TP | SUPPORTED_MII);
+			ecmd.port = PORT_TP;
+			ecmd.transceiver = XCVR_INTERNAL;
+			ecmd.phy_address = pegasus->phy;
+			read_mii_word(pegasus, pegasus->phy, MII_BMCR, &bmcr);
+			read_mii_word(pegasus, pegasus->phy, MII_LPA, &lpa);
+			if (bmcr & BMCR_ANENABLE) {
+				ecmd.autoneg = AUTONEG_ENABLE;
+				ecmd.speed = lpa & (LPA_100HALF | LPA_100FULL) ?
+				    SPEED_100 : SPEED_10;
+				if (ecmd.speed == SPEED_100)
+					ecmd.duplex = lpa & LPA_100FULL ?
+					    DUPLEX_FULL : DUPLEX_HALF;
+				else
+					ecmd.duplex = lpa & LPA_10FULL ?
+					    DUPLEX_FULL : DUPLEX_HALF;
+			} else {
+				ecmd.autoneg = AUTONEG_DISABLE;
+				ecmd.speed = bmcr & BMCR_SPEED100 ?
+				    SPEED_100 : SPEED_10;
+				ecmd.duplex = bmcr & BMCR_FULLDPLX ?
+				    DUPLEX_FULL : DUPLEX_HALF;
+			}
+			if (copy_to_user(uaddr, &ecmd, sizeof(ecmd)))
+				return -EFAULT;
+
+			return 0;
+		}
+	case ETHTOOL_SSET:{
+			return -EOPNOTSUPP;
+		}
+	case ETHTOOL_GLINK:{
+			struct ethtool_value edata = { ETHTOOL_GLINK };
+			edata.data = netif_carrier_ok(net);
+			if (copy_to_user(uaddr, &edata, sizeof(edata)))
+				return -EFAULT;
+			return 0;
 		}
-		if (copy_to_user(uaddr, &ecmd, sizeof(ecmd)))
-			return -EFAULT;
-		
-		return 0;
-	}
-	case ETHTOOL_SSET: {
-		return -EOPNOTSUPP;
-	}
-	case ETHTOOL_GLINK: {
-		struct ethtool_value edata = {ETHTOOL_GLINK};
-		edata.data = netif_carrier_ok(net);
-		if (copy_to_user(uaddr, &edata, sizeof(edata)))
-			return -EFAULT;
-		return 0;
-	}
 	default:
 		return -EOPNOTSUPP;
 	}
 }
 
-
-static int pegasus_ioctl( struct net_device *net, struct ifreq *rq, int cmd )
+static int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
 {
-	__u16 *data = (__u16 *)&rq->ifr_data;
-	pegasus_t	*pegasus = net->priv;
-	int	res;
+	__u16 *data = (__u16 *) & rq->ifr_data;
+	pegasus_t *pegasus = net->priv;
+	int res;
 
 	down(&pegasus->sem);
-	switch(cmd) {
+	switch (cmd) {
 	case SIOCETHTOOL:
 		res = pegasus_ethtool_ioctl(net, rq->ifr_data);
 		break;
 	case SIOCDEVPRIVATE:
 		data[0] = pegasus->phy;
-	case SIOCDEVPRIVATE+1:
-		read_mii_word(pegasus, data[0], data[1]&0x1f, &data[3]);
+	case SIOCDEVPRIVATE + 1:
+		read_mii_word(pegasus, data[0], data[1] & 0x1f, &data[3]);
 		res = 0;
 		break;
-	case SIOCDEVPRIVATE+2:
-		if ( !capable(CAP_NET_ADMIN) ) {
+	case SIOCDEVPRIVATE + 2:
+		if (!capable(CAP_NET_ADMIN)) {
 			up(&pegasus->sem);
 			return -EPERM;
 		}
@@ -903,8 +914,7 @@
 	return res;
 }
 
-
-static void pegasus_set_multicast( struct net_device *net )
+static void pegasus_set_multicast(struct net_device *net)
 {
 	pegasus_t *pegasus = net->priv;
 
@@ -914,7 +924,7 @@
 		pegasus->eth_regs[EthCtrl2] |= RX_PROMISCUOUS;
 		info("%s: Promiscuous mode enabled", net->name);
 	} else if ((net->mc_count > multicast_filter_limit) ||
-			(net->flags & IFF_ALLMULTI)) {
+		   (net->flags & IFF_ALLMULTI)) {
 		pegasus->eth_regs[EthCtrl0] |= RX_MULTICAST;
 		pegasus->eth_regs[EthCtrl2] &= ~RX_PROMISCUOUS;
 		info("%s set allmulti", net->name);
@@ -924,98 +934,97 @@
 	}
 
 	pegasus->flags |= ETH_REGS_CHANGE;
-	ctrl_callback( pegasus->ctrl_urb );
+	ctrl_callback(pegasus->ctrl_urb);
 
 	netif_wake_queue(net);
 }
 
-
-static __u8 mii_phy_probe( pegasus_t *pegasus )
+static __u8 mii_phy_probe(pegasus_t * pegasus)
 {
-	int	i;
-	__u16	tmp;
+	int i;
+	__u16 tmp;
 
-	for ( i=0; i < 32; i++ ) {
-		read_mii_word( pegasus, i, MII_BMSR, &tmp );
-		if ( tmp == 0 || tmp == 0xffff || (tmp & BMSR_MEDIA) == 0 )
+	for (i = 0; i < 32; i++) {
+		read_mii_word(pegasus, i, MII_BMSR, &tmp);
+		if (tmp == 0 || tmp == 0xffff || (tmp & BMSR_MEDIA) == 0)
 			continue;
 		else
-			return	i;
+			return i;
 	}
 
-	return	0xff;
+	return 0xff;
 }
 
-
-static inline void setup_pegasus_II( pegasus_t *pegasus )
+static inline void setup_pegasus_II(pegasus_t * pegasus)
 {
-	set_register( pegasus, Reg1d, 0 );
-	set_register( pegasus, Reg7b, 2 );
-	if ( pegasus->features & HAS_HOME_PNA  && mii_mode )
-		set_register( pegasus, Reg81, 6 );
+	set_register(pegasus, Reg1d, 0);
+	set_register(pegasus, Reg7b, 2);
+	if (pegasus->features & HAS_HOME_PNA && mii_mode)
+		set_register(pegasus, Reg81, 6);
 	else
-		set_register( pegasus, Reg81, 2 );
+		set_register(pegasus, Reg81, 2);
 }
 
-
-static void * pegasus_probe( struct usb_device *dev, unsigned int ifnum,
-			     const struct usb_device_id *id)
+static void *pegasus_probe(struct usb_device *dev, unsigned int ifnum,
+			   const struct usb_device_id *id)
 {
-	struct net_device	*net;
-	pegasus_t		*pegasus;
-	int			dev_index = id - pegasus_ids;
+	struct net_device *net;
+	pegasus_t *pegasus;
+	int dev_index = id - pegasus_ids;
 
 	if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) {
 		err("usb_set_configuration() failed");
 		return NULL;
 	}
-	if(!(pegasus = kmalloc(sizeof(struct pegasus), GFP_KERNEL))) {
+	if (!(pegasus = kmalloc(sizeof(struct pegasus), GFP_KERNEL))) {
 		err("out of memory allocating device structure");
 		return NULL;
 	}
 
-	usb_inc_dev_use( dev );
+	usb_inc_dev_use(dev);
 	memset(pegasus, 0, sizeof(struct pegasus));
 	pegasus->dev_index = dev_index;
-	init_waitqueue_head( &pegasus->ctrl_wait );
+	init_waitqueue_head(&pegasus->ctrl_wait);
 
 	pegasus->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL);
 	if (!pegasus->ctrl_urb) {
-		kfree (pegasus);
+		kfree(pegasus);
 		return NULL;
 	}
 	pegasus->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
 	if (!pegasus->rx_urb) {
-		usb_free_urb (pegasus->ctrl_urb);
-		kfree (pegasus);
+		usb_free_urb(pegasus->ctrl_urb);
+		kfree(pegasus);
 		return NULL;
 	}
 	pegasus->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
 	if (!pegasus->tx_urb) {
-		usb_free_urb (pegasus->rx_urb);
-		usb_free_urb (pegasus->ctrl_urb);
-		kfree (pegasus);
+		usb_free_urb(pegasus->rx_urb);
+		usb_free_urb(pegasus->ctrl_urb);
+		kfree(pegasus);
 		return NULL;
 	}
 	pegasus->intr_urb = usb_alloc_urb(0, GFP_KERNEL);
 	if (!pegasus->intr_urb) {
-		usb_free_urb (pegasus->tx_urb);
-		usb_free_urb (pegasus->rx_urb);
-		usb_free_urb (pegasus->ctrl_urb);
-		kfree (pegasus);
+		usb_free_urb(pegasus->tx_urb);
+		usb_free_urb(pegasus->rx_urb);
+		usb_free_urb(pegasus->ctrl_urb);
+		kfree(pegasus);
 		return NULL;
 	}
 
-	net = init_etherdev( NULL, 0 );
-	if ( !net ) {
-		usb_free_urb (pegasus->tx_urb);
-		usb_free_urb (pegasus->rx_urb);
-		usb_free_urb (pegasus->ctrl_urb);
-		kfree( pegasus );
+	net = init_etherdev(NULL, 0);
+	if (!net) {
+		usb_free_urb(pegasus->tx_urb);
+		usb_free_urb(pegasus->rx_urb);
+		usb_free_urb(pegasus->ctrl_urb);
+		kfree(pegasus);
 		return NULL;
 	}
 
 	init_MUTEX(&pegasus->sem);
+	tasklet_init(&pegasus->rx_tl, rx_fixup, (unsigned long)pegasus);
+	
 	down(&pegasus->sem);
 	pegasus->usb = dev;
 	pegasus->net = net;
@@ -1033,32 +1042,32 @@
 
 	pegasus->features = usb_dev_id[dev_index].private;
 #ifdef	PEGASUS_USE_INTR
-	get_interrupt_interval( pegasus );
+	get_interrupt_interval(pegasus);
 #endif
-	if ( reset_mac(pegasus) ) {
+	if (reset_mac(pegasus)) {
 		err("can't reset MAC");
-		unregister_netdev( pegasus->net );
-		usb_free_urb (pegasus->tx_urb);
-		usb_free_urb (pegasus->rx_urb);
-		usb_free_urb (pegasus->ctrl_urb);
+		unregister_netdev(pegasus->net);
+		usb_free_urb(pegasus->tx_urb);
+		usb_free_urb(pegasus->rx_urb);
+		usb_free_urb(pegasus->ctrl_urb);
 		kfree(pegasus->net);
 		kfree(pegasus);
 		pegasus = NULL;
 		goto exit;
 	}
 
-	info( "%s: %s", net->name, usb_dev_id[dev_index].name );
+	info("%s: %s", net->name, usb_dev_id[dev_index].name);
 
-	set_ethernet_addr( pegasus );
+	set_ethernet_addr(pegasus);
 
-	if ( pegasus->features & PEGASUS_II ) {
-		info( "setup Pegasus II specific registers" );
-		setup_pegasus_II( pegasus );
+	if (pegasus->features & PEGASUS_II) {
+		info("setup Pegasus II specific registers");
+		setup_pegasus_II(pegasus);
 	}
-	
-	pegasus->phy = mii_phy_probe( pegasus );
-	if ( pegasus->phy == 0xff ) {
-		warn( "can't locate MII phy, using default" );
+
+	pegasus->phy = mii_phy_probe(pegasus);
+	if (pegasus->phy == 0xff) {
+		warn("can't locate MII phy, using default");
 		pegasus->phy = 1;
 	}
 exit:
@@ -1066,19 +1075,18 @@
 	return pegasus;
 }
 
-
-static void pegasus_disconnect( struct usb_device *dev, void *ptr )
+static void pegasus_disconnect(struct usb_device *dev, void *ptr)
 {
 	struct pegasus *pegasus = ptr;
 
-	if ( !pegasus ) {
+	if (!pegasus) {
 		warn("unregistering non-existant device");
 		return;
 	}
 
 	pegasus->flags |= PEGASUS_UNPLUG;
-	unregister_netdev( pegasus->net );
-	usb_dec_dev_use( dev );
+	unregister_netdev(pegasus->net);
+	usb_dec_dev_use(dev);
 	usb_unlink_urb(pegasus->intr_urb);
 	usb_unlink_urb(pegasus->tx_urb);
 	usb_unlink_urb(pegasus->rx_urb);
@@ -1087,12 +1095,13 @@
 	usb_free_urb(pegasus->tx_urb);
 	usb_free_urb(pegasus->rx_urb);
 	usb_free_urb(pegasus->ctrl_urb);
-	kfree( pegasus->net );
-	kfree( pegasus );
+	if (pegasus->rx_skb)
+		dev_kfree_skb(pegasus->rx_skb);
+	kfree(pegasus->net);
+	kfree(pegasus);
 	pegasus = NULL;
 }
 
-
 static struct usb_driver pegasus_driver = {
 	name:		"pegasus",
 	probe:		pegasus_probe,
@@ -1103,13 +1112,13 @@
 int __init pegasus_init(void)
 {
 	info(DRIVER_VERSION ":" DRIVER_DESC);
-	return usb_register( &pegasus_driver );
+	return usb_register(&pegasus_driver);
 }
 
 void __exit pegasus_exit(void)
 {
-	usb_deregister( &pegasus_driver );
+	usb_deregister(&pegasus_driver);
 }
 
-module_init( pegasus_init );
-module_exit( pegasus_exit );
+module_init(pegasus_init);
+module_exit(pegasus_exit);
diff -Nru a/drivers/usb/net/pegasus.h b/drivers/usb/net/pegasus.h
--- a/drivers/usb/net/pegasus.h	Mon Apr 22 15:32:19 2002
+++ b/drivers/usb/net/pegasus.h	Mon Apr 22 15:32:19 2002
@@ -22,8 +22,7 @@
 #define	PEGASUS_II		0x80000000
 #define	HAS_HOME_PNA		0x40000000
 
-#define	PEGASUS_MTU		1500
-#define	PEGASUS_MAX_MTU		1536
+#define	PEGASUS_MTU		1536
 
 #define	EPROM_WRITE		0x01
 #define	EPROM_READ		0x02
@@ -45,6 +44,7 @@
 #define	CTRL_URB_RUNNING	0x00000010
 #define	CTRL_URB_SLEEP		0x00000020
 #define	PEGASUS_UNPLUG		0x00000040
+#define	PEGASUS_RX_URB_FAIL	0x00000080
 #define	ETH_REGS_CHANGE		0x40000000
 #define	ETH_REGS_CHANGED	0x80000000
 
@@ -98,13 +98,14 @@
 	unsigned		features;
 	int			dev_index;
 	int			intr_interval;
+	struct tasklet_struct	rx_tl;
 	struct urb		*ctrl_urb, *rx_urb, *tx_urb, *intr_urb;
+	struct sk_buff		*rx_skb;
 	struct usb_ctrlrequest	dr;
 	wait_queue_head_t	ctrl_wait;
 	struct semaphore	sem;
-	unsigned char		rx_buff[PEGASUS_MAX_MTU];
-	unsigned char		tx_buff[PEGASUS_MAX_MTU];
 	unsigned char		intr_buff[8];
+	__u8			tx_buff[PEGASUS_MTU];
 	__u8			eth_regs[4];
 	__u8			phy;
 	__u8			gpio_res;
@@ -236,7 +237,7 @@
 		LINKSYS_GPIO_RESET | HAS_HOME_PNA )
 PEGASUS_DEV( "Linksys USB Ethernet Adapter", VENDOR_LINKSYS, 0x2206,
 		LINKSYS_GPIO_RESET )
-PEGASUS_DEV( "Linksys USB USB10TX", VENDOR_LINKSYS, 0x400b,
+PEGASUS_DEV( "Linksys USB USB100TX", VENDOR_LINKSYS, 0x400b,
 		LINKSYS_GPIO_RESET | PEGASUS_II )
 PEGASUS_DEV( "Linksys USB10TX", VENDOR_LINKSYS, 0x200c,
 		LINKSYS_GPIO_RESET | PEGASUS_II )	
diff -Nru a/drivers/usb/net/rtl8150.c b/drivers/usb/net/rtl8150.c
--- a/drivers/usb/net/rtl8150.c	Mon Apr 22 15:32:18 2002
+++ b/drivers/usb/net/rtl8150.c	Mon Apr 22 15:32:18 2002
@@ -21,14 +21,11 @@
 #include <linux/usb.h>
 #include <asm/uaccess.h>
 
-
-
 /* Version Information */
-#define DRIVER_VERSION "v0.5.0 (2002/03/28)"
+#define DRIVER_VERSION "v0.5.4 (2002/04/11)"
 #define DRIVER_AUTHOR "Petko Manolov <petkan@users.sourceforge.net>"
 #define DRIVER_DESC "rtl8150 based usb-ethernet driver"
 
-
 #define	IRD			0x0120
 #define	MAR			0x0126
 #define	CR			0x012e
@@ -53,64 +50,65 @@
 #define	PHY_WRITE		0x20
 #define	PHY_GO			0x40
 
+#define	MII_TIMEOUT		10
+
 #define	RTL8150_REQT_READ	0xc0
 #define	RTL8150_REQT_WRITE	0x40
 #define	RTL8150_REQ_GET_REGS	0x05
 #define	RTL8150_REQ_SET_REGS	0x05
 
-#define	RTL8150_MTU		1500
-#define	RTL8150_MAX_MTU		1536
-
+#define	RTL8150_MTU		1540
 #define	RTL8150_TX_TIMEOUT	(HZ)
+#define	RX_SKB_POOL_SIZE	4
 
 /* rtl8150 flags */
-#define	RTL8150_FLAG_HWCRC	0
+#define	RTL8150_HW_CRC		0
 #define	RX_REG_SET		1
 #define	RTL8150_UNPLUG		2
-
+#define	RX_URB_FAIL		3
 
 /* Define these values to match your device */
 #define VENDOR_ID_REALTEK		0x0bda
 #define PRODUCT_ID_RTL8150		0x8150
 
 /* table of devices that work with this driver */
-static struct usb_device_id rtl8150_table [] = {
-	{ USB_DEVICE(VENDOR_ID_REALTEK, PRODUCT_ID_RTL8150) },
-	{ }				
+static struct usb_device_id rtl8150_table[] = {
+	{USB_DEVICE(VENDOR_ID_REALTEK, PRODUCT_ID_RTL8150)},
+	{}
 };
 
-MODULE_DEVICE_TABLE (usb, rtl8150_table);
-
+MODULE_DEVICE_TABLE(usb, rtl8150_table);
 
 struct rtl8150 {
-	unsigned int		flags;
-	struct usb_device	*udev;
-	struct usb_interface	*interface;
-	struct semaphore	sem;
-	struct net_device_stats	stats;
-	struct net_device	*netdev;
-	struct urb		*rx_urb, *tx_urb, *intr_urb, *ctrl_urb;
-	struct usb_ctrlrequest	dr;
-	int			intr_interval;
-	u16			rx_creg;
-	u8			rx_buff[RTL8150_MAX_MTU];
-	u8			tx_buff[RTL8150_MAX_MTU];
-	u8			intr_buff[8];
-	u8			phy;
+	unsigned long flags;
+	struct usb_device *udev;
+	struct semaphore sem;
+	struct tasklet_struct tl;
+	struct net_device_stats stats;
+	struct net_device *netdev;
+	struct urb *rx_urb, *tx_urb, *intr_urb, *ctrl_urb;
+	struct sk_buff *tx_skb, *rx_skb;
+	struct sk_buff *rx_skb_pool[RX_SKB_POOL_SIZE];
+	spinlock_t rx_pool_lock;
+	struct usb_ctrlrequest dr;
+	int intr_interval;
+	u16 rx_creg;
+	u8 intr_buff[8];
+	u8 phy;
 };
 
-typedef	struct rtl8150	rtl8150_t;
-
+typedef struct rtl8150 rtl8150_t;
 
 /* the global usb devfs handle */
 extern devfs_handle_t usb_devfs_handle;
 unsigned long multicast_filter_limit = 32;
 
-
+static void fill_skb_pool(rtl8150_t *);
+static void free_skb_pool(rtl8150_t *);
+static struct sk_buff *pull_skb(rtl8150_t *);
 static void rtl8150_disconnect(struct usb_device *dev, void *ptr);
-static void * rtl8150_probe(struct usb_device *dev, unsigned int ifnum,
-			    const struct usb_device_id *id);
-
+static void *rtl8150_probe(struct usb_device *dev, unsigned int ifnum,
+			   const struct usb_device_id *id);
 
 static struct usb_driver rtl8150_driver = {
 	name:		"rtl8150",
@@ -119,33 +117,29 @@
 	id_table:	rtl8150_table,
 };
 
-
-
 /*
 **
 **	device related part of the code
 **
 */
-static int get_registers(rtl8150_t *dev, u16 indx, u16 size, void *data)
+static int get_registers(rtl8150_t * dev, u16 indx, u16 size, void *data)
 {
-	return usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev,0),
-				RTL8150_REQ_GET_REGS, RTL8150_REQT_READ,
-				indx, 0, data, size, HZ/2);
+	return usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
+			       RTL8150_REQ_GET_REGS, RTL8150_REQT_READ,
+			       indx, 0, data, size, HZ / 2);
 }
 
-
-static int set_registers(rtl8150_t *dev, u16 indx, u16 size, void *data)
+static int set_registers(rtl8150_t * dev, u16 indx, u16 size, void *data)
 {
-	return usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev,0),
-				RTL8150_REQ_SET_REGS, RTL8150_REQT_WRITE,
-				indx, 0, data, size, HZ/2);
+	return usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
+			       RTL8150_REQ_SET_REGS, RTL8150_REQT_WRITE,
+			       indx, 0, data, size, HZ / 2);
 }
 
-
 static void ctrl_callback(struct urb *urb)
 {
-	rtl8150_t	*dev;
-	
+	rtl8150_t *dev;
+
 	switch (urb->status) {
 	case 0:
 		break;
@@ -160,23 +154,22 @@
 	clear_bit(RX_REG_SET, &dev->flags);
 }
 
-
-static int async_set_registers(rtl8150_t *dev, u16 indx, u16 size, void *data)
+static int async_set_registers(rtl8150_t * dev, u16 indx, u16 size, void *data)
 {
-	int	ret;
+	int ret;
 
 	if (test_bit(RX_REG_SET, &dev->flags))
 		return -EAGAIN;
-	
+
 	dev->dr.bRequestType = RTL8150_REQT_WRITE;
 	dev->dr.bRequest = RTL8150_REQ_SET_REGS;
 	dev->dr.wValue = cpu_to_le16(indx);
 	dev->dr.wIndex = 0;
 	dev->dr.wLength = cpu_to_le16(size);
 	dev->ctrl_urb->transfer_buffer_length = size;
-	FILL_CONTROL_URB(dev->ctrl_urb, dev->udev, usb_sndctrlpipe(dev->udev,0),
-	                 (char*)&dev->dr, &dev->rx_creg, size,
-	                 ctrl_callback, dev);
+	FILL_CONTROL_URB(dev->ctrl_urb, dev->udev,
+			 usb_sndctrlpipe(dev->udev, 0), (char *) &dev->dr,
+			 &dev->rx_creg, size, ctrl_callback, dev);
 	if ((ret = usb_submit_urb(dev->ctrl_urb, GFP_ATOMIC)))
 		err("control request submission failed: %d", ret);
 	else
@@ -185,11 +178,10 @@
 	return ret;
 }
 
-
-static int read_mii_word(rtl8150_t *dev, u8 phy, __u8 indx, u16 *reg)
+static int read_mii_word(rtl8150_t * dev, u8 phy, __u8 indx, u16 * reg)
 {
-	int	i;
-	u8	data[3], tmp;
+	int i;
+	u8 data[3], tmp;
 
 	data[0] = phy;
 	data[1] = data[2] = 0;
@@ -200,9 +192,9 @@
 	set_registers(dev, PHYCNT, 1, &tmp);
 	do {
 		get_registers(dev, PHYCNT, 1, data);
-	} while ((data[0] & PHY_GO) && (i++ < HZ));
+	} while ((data[0] & PHY_GO) && (i++ < MII_TIMEOUT));
 
-	if (i < HZ) {
+	if (i < MII_TIMEOUT) {
 		get_registers(dev, PHYDAT, 2, data);
 		*reg = le16_to_cpup(data);
 		return 0;
@@ -210,11 +202,10 @@
 		return 1;
 }
 
-
-static int write_mii_word(rtl8150_t *dev, u8 phy, __u8 indx, u16 reg)
+static int write_mii_word(rtl8150_t * dev, u8 phy, __u8 indx, u16 reg)
 {
-	int	i;
-	u8	data[3], tmp;
+	int i;
+	u8 data[3], tmp;
 
 	data[0] = phy;
 	*(data + 1) = cpu_to_le16p(&reg);
@@ -225,39 +216,36 @@
 	set_registers(dev, PHYCNT, 1, &tmp);
 	do {
 		get_registers(dev, PHYCNT, 1, data);
-	} while((data[0] & PHY_GO) && (i++ < HZ));
+	} while ((data[0] & PHY_GO) && (i++ < MII_TIMEOUT));
 
-	if (i < HZ)
+	if (i < MII_TIMEOUT)
 		return 0;
 	else
 		return 1;
 }
 
-
-static inline void set_ethernet_addr(rtl8150_t *dev)
+static inline void set_ethernet_addr(rtl8150_t * dev)
 {
-	u8	node_id[6];
+	u8 node_id[6];
 
 	get_registers(dev, IRD, sizeof(node_id), node_id);
 	memcpy(dev->netdev->dev_addr, node_id, sizeof(node_id));
 }
 
-
-static int rtl8150_reset(rtl8150_t *dev)
+static int rtl8150_reset(rtl8150_t * dev)
 {
-	u8	data=0x10;
-	int	i=HZ;
+	u8 data = 0x10;
+	int i = HZ;
 
 	set_registers(dev, CR, 1, &data);
 	do {
 		get_registers(dev, CR, 1, &data);
 	} while ((data & 0x10) && --i);
-	
-	return (i > 0) ? 0 : -1;
-}
 
+	return (i > 0) ? 1 : 0;
+}
 
-static int alloc_all_urbs(rtl8150_t *dev)
+static int alloc_all_urbs(rtl8150_t * dev)
 {
 	dev->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
 	if (!dev->rx_urb)
@@ -284,8 +272,7 @@
 	return 1;
 }
 
-
-static void free_all_urbs(rtl8150_t *dev)
+static void free_all_urbs(rtl8150_t * dev)
 {
 	usb_free_urb(dev->rx_urb);
 	usb_free_urb(dev->tx_urb);
@@ -293,8 +280,7 @@
 	usb_free_urb(dev->ctrl_urb);
 }
 
-
-static void unlink_all_urbs(rtl8150_t *dev)
+static void unlink_all_urbs(rtl8150_t * dev)
 {
 	usb_unlink_urb(dev->rx_urb);
 	usb_unlink_urb(dev->tx_urb);
@@ -302,30 +288,28 @@
 	usb_unlink_urb(dev->ctrl_urb);
 }
 
-
 static void read_bulk_callback(struct urb *urb)
 {
-	rtl8150_t	*dev;
-	unsigned	pkt_len, res;
-	struct sk_buff	*skb;
+	rtl8150_t *dev;
+	unsigned pkt_len, res;
+	struct sk_buff *skb;
 	struct net_device *netdev;
-	u16		rx_stat;
+	u16 rx_stat;
 
 	dev = urb->context;
-	if (!dev) {
-		warn("!dev");
+	if (!dev)
+		return;
+	if (test_bit(RTL8150_UNPLUG, &dev->flags))
 		return;
-	}
 	netdev = dev->netdev;
-	if (!netif_device_present(netdev)) {
-		warn("netdev is not present");
+	if (!netif_device_present(netdev))
 		return;
-	}
+
 	switch (urb->status) {
 	case 0:
 		break;
 	case -ENOENT:
-		return;
+		return;	/* urb's in unlink state */
 	case -ETIMEDOUT:
 		warn("reset needed may be?..");
 		goto goon;
@@ -334,35 +318,76 @@
 		goto goon;
 	}
 
+	tasklet_schedule(&dev->tl);
+
+	if (!dev->rx_skb) {
+		/* lost packets++ */
+		return;
+	}
+
 	res = urb->actual_length;
-	rx_stat = le16_to_cpu(*(short*)(dev->rx_buff + res - 4));
+	rx_stat = le16_to_cpu(*(short *)(urb->transfer_buffer + res - 4));
 	pkt_len = res - 4;
 
-	if (!(skb = dev_alloc_skb(pkt_len + 2))) 
-		goto goon;
-	skb->dev = netdev;
-	skb_reserve(skb, 2);
-	eth_copy_and_sum(skb, dev->rx_buff, pkt_len, 0);
-	skb_put(skb, pkt_len);
-	skb->protocol = eth_type_trans(skb, netdev);
-	netif_rx(skb);
+	skb_put(dev->rx_skb, pkt_len);
+	dev->rx_skb->protocol = eth_type_trans(dev->rx_skb, netdev);
+	netif_rx(dev->rx_skb);
 	dev->stats.rx_packets++;
 	dev->stats.rx_bytes += pkt_len;
+	
+	skb = pull_skb(dev);
+	if (!skb) {
+		dev->rx_skb = NULL;
+		return;
+	}
+
+	skb->dev = netdev;
+	skb_reserve(skb, 2);
+	dev->rx_skb = skb;
 goon:
-	FILL_BULK_URB(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev,1),
-	              dev->rx_buff, RTL8150_MAX_MTU, read_bulk_callback, dev);
-	if ((res=usb_submit_urb(dev->rx_urb, GFP_ATOMIC)))
-		warn("%s: Rx urb submission failed %d", netdev->name, res);
+	FILL_BULK_URB(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1),
+		      dev->rx_skb->data, RTL8150_MTU, read_bulk_callback, dev);
+	if (usb_submit_urb(dev->rx_urb, GFP_ATOMIC))
+		set_bit(RX_URB_FAIL, &dev->flags);
+	else
+		clear_bit(RX_URB_FAIL, &dev->flags);
 }
 
+static void rx_fixup(unsigned long data)
+{
+	rtl8150_t *dev;
+	struct sk_buff *skb;
+
+	dev = (rtl8150_t *)data;
+
+	fill_skb_pool(dev);
+	if (test_bit(RX_URB_FAIL, &dev->flags))
+		goto try_again;
+	if (dev->rx_skb)
+		return;
+	if (!(skb = pull_skb(dev))) {
+		tasklet_schedule(&dev->tl);
+		return;
+	}
+	dev->rx_skb = skb;
+	FILL_BULK_URB(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1),
+		      dev->rx_skb->data, RTL8150_MTU, read_bulk_callback, dev);
+try_again:
+	if (usb_submit_urb(dev->rx_urb, GFP_ATOMIC)) {
+		set_bit(RX_URB_FAIL, &dev->flags);
+		tasklet_schedule(&dev->tl);
+	 } else
+		clear_bit(RX_URB_FAIL, &dev->flags);
+}
 
 static void write_bulk_callback(struct urb *urb)
 {
-	rtl8150_t	*dev;
+	rtl8150_t *dev;
 
 	dev = urb->context;
 	if (!dev)
 		return;
+	dev_kfree_skb_irq(dev->tx_skb);
 	if (!netif_device_present(dev->netdev))
 		return;
 	if (urb->status)
@@ -371,43 +396,94 @@
 	netif_wake_queue(dev->netdev);
 }
 
-
 void intr_callback(struct urb *urb)
 {
-	rtl8150_t	*dev;
+	rtl8150_t *dev;
 
 	dev = urb->context;
 	if (!dev)
 		return;
 	switch (urb->status) {
-		case 0:
-			break;
-		case -ENOENT:
-			return;
-		default:
-			info("%s: intr status %d", dev->netdev->name,
-			     urb->status);
+	case 0:
+		break;
+	case -ENOENT:
+		return;
+	default:
+		info("%s: intr status %d", dev->netdev->name, urb->status);
 	}
 }
 
-
 /*
 **
 **	network related part of the code
 **
 */
 
+static void fill_skb_pool(rtl8150_t *dev)
+{
+	struct sk_buff *skb;
+	int i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->rx_pool_lock, flags);
+	for (i = 0; i < RX_SKB_POOL_SIZE; i++) {
+		if (dev->rx_skb_pool[i])
+			continue;
+		skb = dev_alloc_skb(RTL8150_MTU + 2);
+		if (!skb) {
+			spin_unlock_irqrestore(&dev->rx_pool_lock, flags);
+			return;
+		}
+		skb->dev = dev->netdev;
+		skb_reserve(skb, 2);
+		dev->rx_skb_pool[i] = skb;
+	}
+	spin_unlock_irqrestore(&dev->rx_pool_lock, flags);
+}
+
+static void free_skb_pool(rtl8150_t *dev)
+{
+	int i;
+
+	spin_lock_irq(&dev->rx_pool_lock);
+	for (i = 0; i < RX_SKB_POOL_SIZE; i++)
+		if (dev->rx_skb_pool[i])
+			dev_kfree_skb(dev->rx_skb_pool[i]);
+	spin_unlock_irq(&dev->rx_pool_lock);
+}
+
+static struct sk_buff *pull_skb(rtl8150_t *dev)
+{
+	struct sk_buff *skb;
+	int i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->rx_pool_lock, flags);
+	for (i = 0; i < RX_SKB_POOL_SIZE; i++) {
+		if (dev->rx_skb_pool[i]) {
+			skb = dev->rx_skb_pool[i];
+			dev->rx_skb_pool[i] = NULL;
+			spin_unlock_irqrestore(&dev->rx_pool_lock, flags);
+			return skb;
+		}
+	}
+	spin_unlock_irqrestore(&dev->rx_pool_lock, flags);
+	return NULL;
+}
 
-static int enable_net_traffic(rtl8150_t *dev)
+static int enable_net_traffic(rtl8150_t * dev)
 {
-	u8	cr, tcr, rcr, msr;
+	u8 cr, tcr, rcr, msr;
 
-	if (rtl8150_reset(dev)) {
+	if (!rtl8150_reset(dev)) {
 		warn("%s - device reset failed", __FUNCTION__);
 	}
-	dev->rx_creg = rcr = 0x9e;	/* bit7=1 attach Rx info at the end */
-	tcr = 0xd8;		/* bit0=1 no CRC at the end of the frame */
+	/* RCR bit7=1 attach Rx info at the end;  =0 HW CRC (which is broken) */
+	dev->rx_creg = rcr = 0x9e;
+	tcr = 0xd8;
 	cr = 0x0c;
+	if (!(rcr & 0x80))
+		set_bit(RTL8150_HW_CRC, &dev->flags);
 	set_registers(dev, RCR, 1, &rcr);
 	set_registers(dev, TCR, 1, &tcr);
 	set_registers(dev, CR, 1, &cr);
@@ -416,26 +492,23 @@
 	return 0;
 }
 
-
-static void disable_net_traffic(rtl8150_t *dev)
+static void disable_net_traffic(rtl8150_t * dev)
 {
-	u8	cr;
+	u8 cr;
 
 	get_registers(dev, CR, 1, &cr);
 	cr &= 0xf3;
 	set_registers(dev, CR, 1, &cr);
 }
 
-
 static struct net_device_stats *rtl8150_netdev_stats(struct net_device *dev)
 {
-	return &((rtl8150_t *)dev->priv)->stats;
+	return &((rtl8150_t *) dev->priv)->stats;
 }
 
-
 static void rtl8150_tx_timeout(struct net_device *netdev)
 {
-	rtl8150_t	*dev;
+	rtl8150_t *dev;
 
 	dev = netdev->priv;
 	if (!dev)
@@ -446,10 +519,9 @@
 	dev->stats.tx_errors++;
 }
 
-
 static void rtl8150_set_multicast(struct net_device *netdev)
 {
-	rtl8150_t	*dev;
+	rtl8150_t *dev;
 
 	dev = netdev->priv;
 	netif_stop_queue(netdev);
@@ -457,7 +529,7 @@
 		dev->rx_creg |= 0x0001;
 		info("%s: promiscuous mode", netdev->name);
 	} else if ((netdev->mc_count > multicast_filter_limit) ||
-	           (netdev->flags & IFF_ALLMULTI)) {
+		   (netdev->flags & IFF_ALLMULTI)) {
 		dev->rx_creg &= 0xfffe;
 		dev->rx_creg |= 0x0002;
 		info("%s: allmulti set", netdev->name);
@@ -469,22 +541,19 @@
 	netif_wake_queue(netdev);
 }
 
-
 static int rtl8150_start_xmit(struct sk_buff *skb, struct net_device *netdev)
 {
-	rtl8150_t	*dev;
-	int		count, res;
+	rtl8150_t *dev;
+	int count, res;
 
 	netif_stop_queue(netdev);
 	dev = netdev->priv;
 	count = (skb->len < 60) ? 60 : skb->len;
 	count = (count & 0x3f) ? count : count + 1;
-	memcpy(dev->tx_buff, skb->data, skb->len);
-	FILL_BULK_URB(dev->tx_urb, dev->udev, usb_sndbulkpipe(dev->udev,2),
-		      dev->tx_buff, RTL8150_MAX_MTU, write_bulk_callback, dev);
-	dev->tx_urb->transfer_buffer_length = count;
-
-	if ((res = usb_submit_urb(dev->tx_urb, GFP_KERNEL))) {
+	dev->tx_skb = skb;
+	FILL_BULK_URB(dev->tx_urb, dev->udev, usb_sndbulkpipe(dev->udev, 2),
+		      skb->data, count, write_bulk_callback, dev);
+	if ((res = usb_submit_urb(dev->tx_urb, GFP_ATOMIC))) {
 		warn("failed tx_urb %d\n", res);
 		dev->stats.tx_errors++;
 		netif_start_queue(netdev);
@@ -493,31 +562,33 @@
 		dev->stats.tx_bytes += skb->len;
 		netdev->trans_start = jiffies;
 	}
-	dev_kfree_skb(skb);
 
 	return 0;
 }
 
-
 static int rtl8150_open(struct net_device *netdev)
 {
-	rtl8150_t	*dev;
-	int		res;
-	
+	rtl8150_t *dev;
+	int res;
+
 	dev = netdev->priv;
 	if (dev == NULL) {
 		return -ENODEV;
 	}
 
+	dev->rx_skb = pull_skb(dev);
+	if (!dev->rx_skb)
+		return -ENOMEM;
+
 	down(&dev->sem);
-	FILL_BULK_URB(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev,1),
-			dev->rx_buff, RTL8150_MAX_MTU, read_bulk_callback, dev);
-	if ((res=usb_submit_urb(dev->rx_urb, GFP_KERNEL)))
+	FILL_BULK_URB(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1),
+		      dev->rx_skb->data, RTL8150_MTU, read_bulk_callback, dev);
+	if ((res = usb_submit_urb(dev->rx_urb, GFP_KERNEL)))
 		warn("%s: rx_urb submit failed: %d", __FUNCTION__, res);
-	FILL_INT_URB(dev->intr_urb, dev->udev, usb_rcvintpipe(dev->udev,3),
-			dev->intr_buff, sizeof(dev->intr_buff), intr_callback,
-			dev, dev->intr_interval);
-	if ((res=usb_submit_urb(dev->intr_urb, GFP_KERNEL)))
+	FILL_INT_URB(dev->intr_urb, dev->udev, usb_rcvintpipe(dev->udev, 3),
+		     dev->intr_buff, sizeof(dev->intr_buff), intr_callback,
+		     dev, dev->intr_interval);
+	if ((res = usb_submit_urb(dev->intr_urb, GFP_KERNEL)))
 		warn("%s: intr_urb submit failed: %d", __FUNCTION__, res);
 	netif_start_queue(netdev);
 	enable_net_traffic(dev);
@@ -526,7 +597,6 @@
 	return res;
 }
 
-
 static int rtl8150_close(struct net_device *netdev)
 {
 	rtl8150_t *dev;
@@ -537,53 +607,47 @@
 		return -ENODEV;
 
 	down(&dev->sem);
+	netif_stop_queue(netdev);
 	if (!test_bit(RTL8150_UNPLUG, &dev->flags))
 		disable_net_traffic(dev);
 	unlink_all_urbs(dev);
-	netif_stop_queue(netdev);
 	up(&dev->sem);
 
-
 	return res;
 }
 
-
 static int rtl8150_ethtool_ioctl(struct net_device *netdev, void *uaddr)
 {
-	rtl8150_t	*dev;
-	int		cmd;
-	char		tmp[128];
+	rtl8150_t *dev;
+	int cmd;
 
 	dev = netdev->priv;
-	if (get_user(cmd, (int *)uaddr))
+	if (get_user(cmd, (int *) uaddr))
 		return -EFAULT;
 
 	switch (cmd) {
-	case ETHTOOL_GDRVINFO: {
-		struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO};
-		
+	case ETHTOOL_GDRVINFO:{
+		struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+
 		strncpy(info.driver, DRIVER_DESC, ETHTOOL_BUSINFO_LEN);
 		strncpy(info.version, DRIVER_VERSION, ETHTOOL_BUSINFO_LEN);
-		sprintf(tmp, "usb%d:%d", dev->udev->bus->busnum,
-		        dev->udev->devnum);
-		strncpy(info.bus_info, tmp, ETHTOOL_BUSINFO_LEN);
+		usb_make_path(dev->udev, info.bus_info, sizeof info.bus_info);
 		if (copy_to_user(uaddr, &info, sizeof(info)))
 			return -EFAULT;
 		return 0;
-	}
-	case ETHTOOL_GSET: {
+		}
+	case ETHTOOL_GSET:{
 		struct ethtool_cmd ecmd;
-		short   lpa, bmcr;
+		short lpa, bmcr;
 
 		if (copy_from_user(&ecmd, uaddr, sizeof(ecmd)))
 			return -EFAULT;
 		ecmd.supported = (SUPPORTED_10baseT_Half |
-		                  SUPPORTED_10baseT_Full |
-		                  SUPPORTED_100baseT_Half |
-		                  SUPPORTED_100baseT_Full |
-		                  SUPPORTED_Autoneg |
-		                  SUPPORTED_TP |
-		                  SUPPORTED_MII);
+				  SUPPORTED_10baseT_Full |
+				  SUPPORTED_100baseT_Half |
+				  SUPPORTED_100baseT_Full |
+				  SUPPORTED_Autoneg |
+				  SUPPORTED_TP | SUPPORTED_MII);
 		ecmd.port = PORT_TP;
 		ecmd.transceiver = XCVR_INTERNAL;
 		ecmd.phy_address = dev->phy;
@@ -595,45 +659,44 @@
 			             SPEED_100 : SPEED_10;
 			if (ecmd.speed == SPEED_100)
 				ecmd.duplex = (lpa & LPA_100FULL) ?
-				              DUPLEX_FULL : DUPLEX_HALF;
+				    DUPLEX_FULL : DUPLEX_HALF;
 			else
 				ecmd.duplex = (lpa & LPA_10FULL) ?
-				              DUPLEX_FULL : DUPLEX_HALF;
+				    DUPLEX_FULL : DUPLEX_HALF;
 		} else {
 			ecmd.autoneg = AUTONEG_DISABLE;
 			ecmd.speed = (bmcr & BMCR_SPEED100) ?
-			             SPEED_100 : SPEED_10;
+			    SPEED_100 : SPEED_10;
 			ecmd.duplex = (bmcr & BMCR_FULLDPLX) ?
-			              DUPLEX_FULL : DUPLEX_HALF;
+			    DUPLEX_FULL : DUPLEX_HALF;
 		}
 		if (copy_to_user(uaddr, &ecmd, sizeof(ecmd)))
 			return -EFAULT;
 		return 0;
-	}
+		}
 	case ETHTOOL_SSET:
 		return -ENOTSUPP;
-	case ETHTOOL_GLINK: {
-		struct ethtool_value edata = {ETHTOOL_GLINK};
+	case ETHTOOL_GLINK:{
+		struct ethtool_value edata = { ETHTOOL_GLINK };
 
 		edata.data = netif_carrier_ok(netdev);
 		if (copy_to_user(uaddr, &edata, sizeof(edata)))
 			return -EFAULT;
 		return 0;
-	}
+		}
 	default:
 		return -EOPNOTSUPP;
 	}
 }
 
-
-static int rtl8150_ioctl (struct net_device *netdev, struct ifreq *rq, int cmd)
+static int rtl8150_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd)
 {
 	rtl8150_t *dev;
-	u16	*data;
-	int	res;
+	u16 *data;
+	int res;
 
 	dev = netdev->priv;
-	data = (u16 *)&rq->ifr_data;
+	data = (u16 *) & rq->ifr_data;
 	res = 0;
 
 	down(&dev->sem);
@@ -643,10 +706,10 @@
 		break;
 	case SIOCDEVPRIVATE:
 		data[0] = dev->phy;
-	case SIOCDEVPRIVATE+1:
+	case SIOCDEVPRIVATE + 1:
 		read_mii_word(dev, dev->phy, (data[1] & 0x1f), &data[3]);
 		break;
-	case SIOCDEVPRIVATE+2:
+	case SIOCDEVPRIVATE + 2:
 		if (!capable(CAP_NET_ADMIN)) {
 			up(&dev->sem);
 			return -EPERM;
@@ -660,27 +723,25 @@
 	return res;
 }
 
-
-static void * rtl8150_probe(struct usb_device *udev, unsigned int ifnum,
-			    const struct usb_device_id *id)
+static void *rtl8150_probe(struct usb_device *udev, unsigned int ifnum,
+			   const struct usb_device_id *id)
 {
 	rtl8150_t *dev;
 	struct net_device *netdev;
 
-	udev->config[0].bConfigurationValue = 1;
 	if (usb_set_configuration(udev, udev->config[0].bConfigurationValue)) {
 		err("usb_set_configuration() failed");
 		return NULL;
 	}
 	if ((udev->descriptor.idVendor != VENDOR_ID_REALTEK) ||
 	    (udev->descriptor.idProduct != PRODUCT_ID_RTL8150)) {
-	    	err("Not the one we are interested about");
+		err("Not the one we are interested about");
 		return NULL;
 	}
 	dev = kmalloc(sizeof(rtl8150_t), GFP_KERNEL);
 	if (!dev) {
-		err ("Out of memory");
-		goto exit;
+		err("Out of memory");
+		return NULL;
 	} else
 		memset(dev, 0, sizeof(rtl8150_t));
 
@@ -688,11 +749,14 @@
 	if (!netdev) {
 		kfree(dev);
 		err("Oh boy, out of memory again?!?");
-		dev = NULL;
-		goto exit;
+		return NULL;
 	}
-		
+
 	init_MUTEX(&dev->sem);
+	tasklet_init(&dev->tl, rx_fixup, (unsigned long)dev);
+	spin_lock_init(&dev->rx_pool_lock);
+	
+	down(&dev->sem);
 	dev->udev = udev;
 	dev->netdev = netdev;
 	SET_MODULE_OWNER(netdev);
@@ -708,23 +772,29 @@
 	netdev->mtu = RTL8150_MTU;
 	dev->intr_interval = 100;	/* 100ms */
 
-	if (rtl8150_reset(dev) || !alloc_all_urbs(dev)) {
+	if (!alloc_all_urbs(dev)) {
+		err("out of memory");
+		goto err;
+	}
+	if (!rtl8150_reset(dev)) {
 		err("couldn't reset the device");
 		free_all_urbs(dev);
-		unregister_netdev(dev->netdev);
-		kfree(netdev);
-		kfree(dev);
-		dev = NULL;
-		goto exit;
+		goto err;
 	}
-
+	fill_skb_pool(dev);
 	set_ethernet_addr(dev);
 	info("%s: rtl8150 is detected", netdev->name);
-exit:
+	
+	up(&dev->sem);
 	return dev;
+err:
+	unregister_netdev(dev->netdev);
+	up(&dev->sem);
+	kfree(netdev);
+	kfree(dev);
+	return NULL;
 }
 
-
 static void rtl8150_disconnect(struct usb_device *udev, void *ptr)
 {
 	rtl8150_t *dev;
@@ -734,26 +804,25 @@
 	unregister_netdev(dev->netdev);
 	unlink_all_urbs(dev);
 	free_all_urbs(dev);
+	free_skb_pool(dev);
+	if (dev->rx_skb)
+		dev_kfree_skb(dev->rx_skb);
 	kfree(dev->netdev);
 	kfree(dev);
 	dev->netdev = NULL;
 	dev = NULL;
 }
 
-
-
 static int __init usb_rtl8150_init(void)
 {
 	info(DRIVER_DESC " " DRIVER_VERSION);
 	return usb_register(&rtl8150_driver);
 }
 
-
 static void __exit usb_rtl8150_exit(void)
 {
 	usb_deregister(&rtl8150_driver);
 }
-
 
 module_init(usb_rtl8150_init);
 module_exit(usb_rtl8150_exit);
diff -Nru a/drivers/usb/net/usbnet.c b/drivers/usb/net/usbnet.c
--- a/drivers/usb/net/usbnet.c	Mon Apr 22 15:32:18 2002
+++ b/drivers/usb/net/usbnet.c	Mon Apr 22 15:32:18 2002
@@ -538,25 +538,25 @@
 
 		// allocate the skb for the individual packet
 		gl_skb = alloc_skb (size, GFP_ATOMIC);
-		if (gl_skb == 0)
-			return 0;
+		if (gl_skb) {
 
-		// copy the packet data to the new skb
-		memcpy (gl_skb->data, packet->packet_data, size);
+			// copy the packet data to the new skb
+			memcpy (gl_skb->data, packet->packet_data, size);
 
-		// set skb data size
-		gl_skb->len = size;
-		gl_skb->dev = &dev->net;
+			// set skb data size
+			gl_skb->len = size;
+			gl_skb->dev = &dev->net;
 
-		// determine the packet's protocol ID
-		gl_skb->protocol = eth_type_trans (gl_skb, &dev->net);
+			// determine the packet's protocol ID
+			gl_skb->protocol = eth_type_trans (gl_skb, &dev->net);
 
-		// update the status
-		dev->stats.rx_packets++;
-		dev->stats.rx_bytes += size;
+			// update the status
+			dev->stats.rx_packets++;
+			dev->stats.rx_bytes += size;
 
-		// notify os of the received packet
-		status = netif_rx (gl_skb);
+			// notify os of the received packet
+			status = netif_rx (gl_skb);
+		}
 
 		// advance to the next packet
 		packet = (struct gl_packet *)
@@ -834,10 +834,10 @@
 static inline void nc_dump_usbctl (struct usbnet *dev, u16 usbctl)
 {
 #ifdef DEBUG
-	devdbg (dev, "net1080 %03d/%03d usbctl 0x%x:%s%s%s%s%s;"
+	devdbg (dev, "net1080 %s-%s usbctl 0x%x:%s%s%s%s%s;"
 			" this%s%s;"
 			" other%s%s; r/o 0x%x",
-		dev->udev->bus->busnum, dev->udev->devnum,
+		dev->udev->bus->bus_name, dev->udev->devpath,
 		usbctl,
 		(usbctl & USBCTL_ENABLE_LANG) ? " lang" : "",
 		(usbctl & USBCTL_ENABLE_MFGR) ? " mfgr" : "",
@@ -879,10 +879,10 @@
 static inline void nc_dump_status (struct usbnet *dev, u16 status)
 {
 #ifdef DEBUG
-	devdbg (dev, "net1080 %03d/%03d status 0x%x:"
+	devdbg (dev, "net1080 %s-%s status 0x%x:"
 			" this (%c) PKT=%d%s%s%s;"
 			" other PKT=%d%s%s%s; unspec 0x%x",
-		dev->udev->bus->busnum, dev->udev->devnum,
+		dev->udev->bus->bus_name, dev->udev->devpath,
 		status,
 
 		// XXX the packet counts don't seem right
@@ -917,8 +917,8 @@
 static inline void nc_dump_ttl (struct usbnet *dev, u16 ttl)
 {
 #ifdef DEBUG
-	devdbg (dev, "net1080 %03d/%03d ttl 0x%x this = %d, other = %d",
-		dev->udev->bus->busnum, dev->udev->devnum,
+	devdbg (dev, "net1080 %s-%s ttl 0x%x this = %d, other = %d",
+		dev->udev->bus->bus_name, dev->udev->devpath,
 		ttl,
 
 		TTL_THIS (ttl),
@@ -941,7 +941,8 @@
 	// nc_dump_registers (dev);
 
 	if ((retval = nc_register_read (dev, REG_STATUS, vp)) < 0) {
-		dbg ("can't read dev %d status: %d", dev->udev->devnum, retval);
+		dbg ("can't read %s-%s status: %d",
+			dev->udev->bus->bus_name, dev->udev->devpath, retval);
 		goto done;
 	}
 	status = *vp;
@@ -1504,10 +1505,9 @@
 
 	// put into "known safe" state
 	if (info->reset && (retval = info->reset (dev)) < 0) {
-		devinfo (dev, "open reset fail (%d) usbnet bus%d%s, %s",
+		devinfo (dev, "open reset fail (%d) usbnet usb-%s-%s, %s",
 			retval,
-			// FIXME busnum is unstable
-			dev->udev->bus->busnum, dev->udev->devpath,
+			dev->udev->bus->bus_name, dev->udev->devpath,
 			info->description);
 		goto done;
 	}
@@ -1557,9 +1557,7 @@
 		strncpy (info.version, DRIVER_VERSION, sizeof info.version);
 		strncpy (info.fw_version, dev->driver_info->description,
 			sizeof info.fw_version);
-		snprintf (info.bus_info, sizeof info.bus_info, "USB bus%d%s",
-			/* FIXME busnums are bogus/unstable IDs */
-			dev->udev->bus->busnum, dev->udev->devpath);
+		usb_make_path (dev->udev, info.bus_info, sizeof info.bus_info);
 		if (copy_to_user (useraddr, &info, sizeof (info)))
 			return -EFAULT;
 		return 0;
@@ -1847,9 +1845,8 @@
 {
 	struct usbnet	*dev = (struct usbnet *) ptr;
 
-	devinfo (dev, "unregister usbnet bus%d%s, %s",
-		// FIXME busnum is unstable
-		udev->bus->busnum, udev->devpath,
+	devinfo (dev, "unregister usbnet usb-%s-%s, %s",
+		udev->bus->bus_name, udev->devpath,
 		dev->driver_info->description);
 	
 	unregister_netdev (&dev->net);
@@ -1938,9 +1935,8 @@
 	net->do_ioctl = usbnet_ioctl;
 
 	register_netdev (&dev->net);
-	devinfo (dev, "register usbnet bus%d%s, %s",
-		// FIXME busnum is unstable
-		udev->bus->busnum, udev->devpath,
+	devinfo (dev, "register usbnet usb-%s-%s, %s",
+		udev->bus->bus_name, udev->devpath,
 		dev->driver_info->description);
 
 	// ok, it's ready to go.
diff -Nru a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
--- a/drivers/usb/serial/ftdi_sio.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/usb/serial/ftdi_sio.c	Mon Apr 22 15:32:19 2002
@@ -197,6 +197,7 @@
 };
 
 static struct usb_serial_device_type ftdi_8U232AM_device = {
+	owner:			THIS_MODULE,
 	name:			"FTDI 8U232AM",
 	id_table:		id_table_8U232AM,
 	num_interrupt_in:	0,
diff -Nru a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c
--- a/drivers/usb/serial/keyspan_pda.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/usb/serial/keyspan_pda.c	Mon Apr 22 15:32:19 2002
@@ -798,6 +798,7 @@
 
 #ifdef KEYSPAN
 static struct usb_serial_device_type keyspan_pda_fake_device = {
+	owner:			THIS_MODULE,
 	name:			"Keyspan PDA - (prerenumeration)",
 	id_table:		id_table_fake,
 	num_interrupt_in:	NUM_DONT_CARE,
@@ -810,17 +811,19 @@
 
 #ifdef XIRCOM
 static struct usb_serial_device_type xircom_pgs_fake_device = {
-        name:                   "Xircom / Entregra PGS - (prerenumeration)",
-        id_table:               id_table_fake_xircom,
-        num_interrupt_in:       NUM_DONT_CARE,
-        num_bulk_in:            NUM_DONT_CARE,
-        num_bulk_out:           NUM_DONT_CARE,
-        num_ports:              1,
-        startup:                keyspan_pda_fake_startup,
+	owner:			THIS_MODULE,
+	name:			"Xircom / Entregra PGS - (prerenumeration)",
+	id_table:		id_table_fake_xircom,
+	num_interrupt_in:	NUM_DONT_CARE,
+	num_bulk_in:		NUM_DONT_CARE,
+	num_bulk_out:		NUM_DONT_CARE,
+	num_ports:		1,
+	startup:		keyspan_pda_fake_startup,
 };
 #endif
 
 static struct usb_serial_device_type keyspan_pda_device = {
+	owner:			THIS_MODULE,
 	name:			"Keyspan PDA",
 	id_table:		id_table_std,
 	num_interrupt_in:	1,
diff -Nru a/drivers/usb/serial/usbserial.c b/drivers/usb/serial/usbserial.c
--- a/drivers/usb/serial/usbserial.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/usb/serial/usbserial.c	Mon Apr 22 15:32:19 2002
@@ -15,6 +15,10 @@
  *
  * See Documentation/usb/usb-serial.txt for more information on using this driver
  *
+ * (04/10/2002) gkh
+ *	added serial_read_proc function which creates a
+ *	/proc/tty/driver/usb-serial file.
+ *
  * (03/27/2002) gkh
  *	Got USB serial console code working properly and merged into the main
  *	version of the tree.  Thanks to Randy Dunlap for the initial version
@@ -340,7 +344,7 @@
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v1.4"
+#define DRIVER_VERSION "v1.5"
 #define DRIVER_AUTHOR "Greg Kroah-Hartman, greg@kroah.com, http://www.kroah.com/linux-usb/"
 #define DRIVER_DESC "USB Serial Driver core"
 
@@ -844,6 +848,48 @@
 		generic_shutdown(serial);
 }
 
+static int serial_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+	struct usb_serial *serial;
+	int length = 0;
+	int i;
+	off_t begin = 0;
+	char tmp[40];
+
+	dbg(__FUNCTION__);
+	length += sprintf (page, "usbserinfo:1.0 driver:%s\n", DRIVER_VERSION);
+	for (i = 0; i < SERIAL_TTY_MINORS && length < PAGE_SIZE; ++i) {
+		serial = get_serial_by_minor(i);
+		if (serial == NULL)
+			continue;
+
+		length += sprintf (page+length, "%d:", i);
+		if (serial->type->owner)
+			length += sprintf (page+length, " module:%s", serial->type->owner->name);
+		length += sprintf (page+length, " name:\"%s\"", serial->type->name);
+		length += sprintf (page+length, " vendor:%04x product:%04x", serial->vendor, serial->product);
+		length += sprintf (page+length, " num_ports:%d", serial->num_ports);
+		length += sprintf (page+length, " port:%d", i - serial->minor + 1);
+
+		usb_make_path(serial->dev, tmp, sizeof(tmp));
+		length += sprintf (page+length, " path:%s", tmp);
+			
+		length += sprintf (page+length, "\n");
+		if ((length + begin) > (off + count))
+			goto done;
+		if ((length + begin) < off) {
+			begin += length;
+			length = 0;
+		}
+	}
+	*eof = 1;
+done:
+	if (off >= (length + begin))
+		return 0;
+	*start = page + (off-begin);
+	return ((count < begin+length-off) ? count : begin+length-off);
+}
+
 /*****************************************************************************
  * generic devices specific driver functions
  *****************************************************************************/
@@ -1491,6 +1537,7 @@
 	unthrottle:		serial_unthrottle,
 	break_ctl:		serial_break,
 	chars_in_buffer:	serial_chars_in_buffer,
+	read_proc:		serial_read_proc,
 };
 
 
diff -Nru a/drivers/usb/storage/Makefile b/drivers/usb/storage/Makefile
--- a/drivers/usb/storage/Makefile	Mon Apr 22 15:32:19 2002
+++ b/drivers/usb/storage/Makefile	Mon Apr 22 15:32:19 2002
@@ -10,16 +10,14 @@
 
 obj-$(CONFIG_USB_STORAGE)	+= usb-storage.o
 
-usb-storage-obj-$(CONFIG_USB_STORAGE_DEBUG)			+= debug.o
-usb-storage-obj-$(CONFIG_USB_STORAGE_HP8200e)			+= shuttle_usbat.o
-usb-storage-obj-$(CONFIG_USB_STORAGE_SDDR09)			+= sddr09.o
-usb-storage-obj-$(CONFIG_USB_STORAGE_FREECOM)			+= freecom.o
-usb-storage-obj-$(CONFIG_USB_STORAGE_SHUTTLE_SMARTMEDIA)	+= shuttle_sm.o
-usb-storage-obj-$(CONFIG_USB_STORAGE_SHUTTLE_COMPACTFLASH)	+= shuttle_cf.o
-usb-storage-obj-$(CONFIG_USB_STORAGE_DPCM)			+= dpcm.o
-usb-storage-obj-$(CONFIG_USB_STORAGE_ISD200)			+= isd200.o
-usb-storage-obj-$(CONFIG_USB_STORAGE_DATAFAB)			+= datafab.o
-usb-storage-obj-$(CONFIG_USB_STORAGE_JUMPSHOT)			+= jumpshot.o
+usb-storage-obj-$(CONFIG_USB_STORAGE_DEBUG)	+= debug.o
+usb-storage-obj-$(CONFIG_USB_STORAGE_HP8200e)	+= shuttle_usbat.o
+usb-storage-obj-$(CONFIG_USB_STORAGE_SDDR09)	+= sddr09.o
+usb-storage-obj-$(CONFIG_USB_STORAGE_FREECOM)	+= freecom.o
+usb-storage-obj-$(CONFIG_USB_STORAGE_DPCM)	+= dpcm.o
+usb-storage-obj-$(CONFIG_USB_STORAGE_ISD200)	+= isd200.o
+usb-storage-obj-$(CONFIG_USB_STORAGE_DATAFAB)	+= datafab.o
+usb-storage-obj-$(CONFIG_USB_STORAGE_JUMPSHOT)	+= jumpshot.o
 
 usb-storage-objs :=	scsiglue.o protocol.o transport.o usb.o \
 			initializers.o $(usb-storage-obj-y)
diff -Nru a/drivers/video/anakinfb.c b/drivers/video/anakinfb.c
--- a/drivers/video/anakinfb.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/video/anakinfb.c	Mon Apr 22 15:32:19 2002
@@ -180,7 +180,7 @@
 {
 	memset(&fb_info, 0, sizeof(struct fb_info));
 	strcpy(fb_info.modename, "AnakinFB");
-	fb_info.node = -1;
+	fb_info.node = NODEV;
 	fb_info.flags = FBINFO_FLAG_DEFAULT;
 	fb_info.fbops = &anakinfb_ops;
 	fb_info.disp = &display;
@@ -192,7 +192,12 @@
 
 	memset(&display, 0, sizeof(struct display));
 	anakinfb_get_var(&display.var, 0, &fb_info);
-	display.screen_base = ioremap(VGA_START, VGA_SIZE);
+	if (!(request_mem_region(VGA_START, VGA_SIZE, "vga")))
+		return -ENOMEM;
+	if (!(display.screen_base = ioremap(VGA_START, VGA_SIZE))) {
+		release_mem_region(VGA_START, VGA_SIZE);
+		return -EIO;
+	}
 	display.visual = FB_VISUAL_TRUECOLOR;
 	display.type = FB_TYPE_PACKED_PIXELS;
 	display.type_aux = 0;
@@ -209,8 +214,11 @@
 	display.dispsw = &fbcon_dummy;
 #endif
 
-	if (register_framebuffer(&fb_info) < 0)
+	if (register_framebuffer(&fb_info) < 0) {
+		iounmap(display.screen_base);
+		release_mem_region(VGA_START, VGA_SIZE);
 		return -EINVAL;
+	}
 
 	MOD_INC_USE_COUNT;
 	return 0;
diff -Nru a/drivers/video/cyber2000fb.c b/drivers/video/cyber2000fb.c
--- a/drivers/video/cyber2000fb.c	Mon Apr 22 15:32:19 2002
+++ b/drivers/video/cyber2000fb.c	Mon Apr 22 15:32:19 2002
@@ -1704,8 +1704,8 @@
 	 * Use MCLK from BIOS. FIXME: what about hotplug?
 	 */
 #ifndef __arm__
-	cfb->mclk_mult = cyber2000_grphr(MCLK_MULT, cfb);
-	cfb->mclk_div  = cyber2000_grphr(MCLK_DIV, cfb);
+	cfb->mclk_mult = cyber2000_grphr(EXT_MCLK_MULT, cfb);
+	cfb->mclk_div  = cyber2000_grphr(EXT_MCLK_DIV, cfb);
 #else
 	/*
 	 * MCLK on the NetWinder and the Shark is fixed at 75MHz
diff -Nru a/drivers/video/neofb.c b/drivers/video/neofb.c
--- a/drivers/video/neofb.c	Mon Apr 22 15:32:18 2002
+++ b/drivers/video/neofb.c	Mon Apr 22 15:32:18 2002
@@ -2365,7 +2365,7 @@
   if (!options || !*options)
     return 0;
 
-  for (this_opt=strtok(options,","); this_opt; this_opt=strtok(NULL,","))
+  while ((this_opt = strsep(&options,",")) != NULL)
     {
       if (!*this_opt) continue;
 
diff -Nru a/fs/Config.in b/fs/Config.in
--- a/fs/Config.in	Mon Apr 22 15:32:18 2002
+++ b/fs/Config.in	Mon Apr 22 15:32:18 2002
@@ -124,6 +124,7 @@
    if [ "$CONFIG_NFSD_V3" = "y" -o "$CONFIG_NFS_V3" = "y" ]; then
      define_bool CONFIG_LOCKD_V4 y
    fi
+   define_tristate CONFIG_EXPORTFS $CONFIG_NFSD
 
    dep_tristate 'SMB file system support (to mount Windows shares etc.)' CONFIG_SMB_FS $CONFIG_INET
    if [ "$CONFIG_SMB_FS" != "n" ]; then
diff -Nru a/fs/Makefile b/fs/Makefile
--- a/fs/Makefile	Mon Apr 22 15:32:18 2002
+++ b/fs/Makefile	Mon Apr 22 15:32:18 2002
@@ -45,6 +45,7 @@
 subdir-$(CONFIG_HFS_FS)		+= hfs
 subdir-$(CONFIG_VXFS_FS)	+= freevxfs
 subdir-$(CONFIG_NFS_FS)		+= nfs
+subdir-$(CONFIG_EXPORTFS)	+= exportfs
 subdir-$(CONFIG_NFSD)		+= nfsd
 subdir-$(CONFIG_LOCKD)		+= lockd
 subdir-$(CONFIG_NLS)		+= nls
diff -Nru a/fs/bfs/bfs_defs.h b/fs/bfs/bfs_defs.h
--- a/fs/bfs/bfs_defs.h	Mon Apr 22 15:32:18 2002
+++ b/fs/bfs/bfs_defs.h	Mon Apr 22 15:32:18 2002
@@ -1,14 +1,3 @@
-#define su_lasti	u.bfs_sb.si_lasti
-#define su_blocks	u.bfs_sb.si_blocks
-#define su_freeb	u.bfs_sb.si_freeb
-#define su_freei	u.bfs_sb.si_freei
-#define su_lf_ioff	u.bfs_sb.si_lf_ioff
-#define su_lf_sblk	u.bfs_sb.si_lf_sblk
-#define su_lf_eblk	u.bfs_sb.si_lf_eblk
-#define su_imap		u.bfs_sb.si_imap
-#define su_sbh		u.bfs_sb.si_sbh
-#define su_bfs_sb	u.bfs_sb.si_bfs_sb
-
 #define printf(format, args...) \
 	printk(KERN_ERR "BFS-fs: %s(): " format, __FUNCTION__, ## args)
 
diff -Nru a/fs/bfs/dir.c b/fs/bfs/dir.c
--- a/fs/bfs/dir.c	Mon Apr 22 15:32:19 2002
+++ b/fs/bfs/dir.c	Mon Apr 22 15:32:19 2002
@@ -78,20 +78,21 @@
 	int err;
 	struct inode * inode;
 	struct super_block * s = dir->i_sb;
+	struct bfs_sb_info * info = BFS_SB(s);
 	unsigned long ino;
 
 	inode = new_inode(s);
 	if (!inode)
 		return -ENOSPC;
 	lock_kernel();
-	ino = find_first_zero_bit(s->su_imap, s->su_lasti);
-	if (ino > s->su_lasti) {
+	ino = find_first_zero_bit(info->si_imap, info->si_lasti);
+	if (ino > info->si_lasti) {
 		unlock_kernel();
 		iput(inode);
 		return -ENOSPC;
 	}
-	set_bit(ino, s->su_imap);	
-	s->su_freei--;
+	set_bit(ino, info->si_imap);	
+	info->si_freei--;
 	inode->i_uid = current->fsuid;
 	inode->i_gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid;
 	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
@@ -318,11 +319,9 @@
 {
 	unsigned long block, offset;
 	struct buffer_head * bh;
-	struct bfs_sb_info * info;
 	struct bfs_dirent * de;
 
 	*res_dir = NULL;
-	info = &dir->i_sb->u.bfs_sb;
 	if (namelen > BFS_NAMELEN)
 		return NULL;
 	bh = NULL;
diff -Nru a/fs/bfs/file.c b/fs/bfs/file.c
--- a/fs/bfs/file.c	Mon Apr 22 15:32:19 2002
+++ b/fs/bfs/file.c	Mon Apr 22 15:32:19 2002
@@ -60,10 +60,11 @@
 	long phys;
 	int err;
 	struct super_block *sb = inode->i_sb;
+	struct bfs_sb_info *info = BFS_SB(sb);
 	struct bfs_inode_info *bi = BFS_I(inode);
-	struct buffer_head *sbh = sb->su_sbh;
+	struct buffer_head *sbh = info->si_sbh;
 
-	if (block < 0 || block > sb->su_blocks)
+	if (block < 0 || block > info->si_blocks)
 		return -EIO;
 
 	phys = bi->i_sblock + block;
@@ -89,12 +90,12 @@
 
 	/* if the last data block for this file is the last allocated block, we can
 	   extend the file trivially, without moving it anywhere */
-	if (bi->i_eblock == sb->su_lf_eblk) {
+	if (bi->i_eblock == info->si_lf_eblk) {
 		dprintf("c=%d, b=%08lx, phys=%08lx (simple extension)\n", 
 				create, block, phys);
 		map_bh(bh_result, sb, phys);
-		sb->su_freeb -= phys - bi->i_eblock;
-		sb->su_lf_eblk = bi->i_eblock = phys;
+		info->si_freeb -= phys - bi->i_eblock;
+		info->si_lf_eblk = bi->i_eblock = phys;
 		mark_inode_dirty(inode);
 		mark_buffer_dirty(sbh);
 		err = 0;
@@ -102,7 +103,7 @@
 	}
 
 	/* Ok, we have to move this entire file to the next free block */
-	phys = sb->su_lf_eblk + 1;
+	phys = info->si_lf_eblk + 1;
 	if (bi->i_sblock) { /* if data starts on block 0 then there is no data */
 		err = bfs_move_blocks(inode->i_sb, bi->i_sblock, 
 				bi->i_eblock, phys);
@@ -116,11 +117,11 @@
 	dprintf("c=%d, b=%08lx, phys=%08lx (moved)\n", create, block, phys);
 	bi->i_sblock = phys;
 	phys += block;
-	sb->su_lf_eblk = bi->i_eblock = phys;
+	info->si_lf_eblk = bi->i_eblock = phys;
 
 	/* this assumes nothing can write the inode back while we are here
 	 * and thus update inode->i_blocks! (XXX)*/
-	sb->su_freeb -= bi->i_eblock - bi->i_sblock + 1 - inode->i_blocks;
+	info->si_freeb -= bi->i_eblock - bi->i_sblock + 1 - inode->i_blocks;
 	mark_inode_dirty(inode);
 	mark_buffer_dirty(sbh);
 	map_bh(bh_result, sb, phys);
diff -Nru a/fs/bfs/inode.c b/fs/bfs/inode.c
--- a/fs/bfs/inode.c	Mon Apr 22 15:32:19 2002
+++ b/fs/bfs/inode.c	Mon Apr 22 15:32:19 2002
@@ -39,7 +39,7 @@
 	struct buffer_head * bh;
 	int block, off;
 
-	if (ino < BFS_ROOT_INO || ino > inode->i_sb->su_lasti) {
+	if (ino < BFS_ROOT_INO || ino > BFS_SB(inode->i_sb)->si_lasti) {
 		printf("Bad inode number %s:%08lx\n", inode->i_sb->s_id, ino);
 		make_bad_inode(inode);
 		return;
@@ -91,7 +91,7 @@
 	struct buffer_head * bh;
 	int block, off;
 
-	if (ino < BFS_ROOT_INO || ino > inode->i_sb->su_lasti) {
+	if (ino < BFS_ROOT_INO || ino > BFS_SB(inode->i_sb)->si_lasti) {
 		printf("Bad inode number %s:%08lx\n", inode->i_sb->s_id, ino);
 		return;
 	}
@@ -137,10 +137,11 @@
 	struct buffer_head * bh;
 	int block, off;
 	struct super_block * s = inode->i_sb;
+	struct bfs_sb_info * info = BFS_SB(s);
 
 	dprintf("ino=%08lx\n", inode->i_ino);
 
-	if (inode->i_ino < BFS_ROOT_INO || inode->i_ino > inode->i_sb->su_lasti) {
+	if (inode->i_ino < BFS_ROOT_INO || inode->i_ino > info->si_lasti) {
 		printf("invalid ino=%08lx\n", inode->i_ino);
 		return;
 	}
@@ -159,9 +160,9 @@
 	off = (ino - BFS_ROOT_INO)%BFS_INODES_PER_BLOCK;
 	di = (struct bfs_inode *)bh->b_data + off;
 	if (di->i_ino) {
-		s->su_freeb += BFS_FILEBLOCKS(di);
-		s->su_freei++;
-		clear_bit(di->i_ino, s->su_imap);
+		info->si_freeb += BFS_FILEBLOCKS(di);
+		info->si_freei++;
+		clear_bit(di->i_ino, info->si_imap);
 		dump_imap("delete_inode", s);
 	}
 	di->i_ino = 0;
@@ -172,9 +173,9 @@
 	/* if this was the last file, make the previous 
 	   block "last files last block" even if there is no real file there,
 	   saves us 1 gap */
-	if (s->su_lf_eblk == BFS_I(inode)->i_eblock) {
-		s->su_lf_eblk = BFS_I(inode)->i_sblock - 1;
-		mark_buffer_dirty(s->su_sbh);
+	if (info->si_lf_eblk == BFS_I(inode)->i_eblock) {
+		info->si_lf_eblk = BFS_I(inode)->i_sblock - 1;
+		mark_buffer_dirty(info->si_sbh);
 	}
 	unlock_kernel();
 	clear_inode(inode);
@@ -182,18 +183,22 @@
 
 static void bfs_put_super(struct super_block *s)
 {
-	brelse(s->su_sbh);
-	kfree(s->su_imap);
+	struct bfs_sb_info *info = BFS_SB(s);
+	brelse(info->si_sbh);
+	kfree(info->si_imap);
+	kfree(info);
+	s->u.generic_sbp = NULL;
 }
 
 static int bfs_statfs(struct super_block *s, struct statfs *buf)
 {
+	struct bfs_sb_info *info = BFS_SB(s);
 	buf->f_type = BFS_MAGIC;
 	buf->f_bsize = s->s_blocksize;
-	buf->f_blocks = s->su_blocks;
-	buf->f_bfree = buf->f_bavail = s->su_freeb;
-	buf->f_files = s->su_lasti + 1 - BFS_ROOT_INO;
-	buf->f_ffree = s->su_freei;
+	buf->f_blocks = info->si_blocks;
+	buf->f_bfree = buf->f_bavail = info->si_freeb;
+	buf->f_files = info->si_lasti + 1 - BFS_ROOT_INO;
+	buf->f_ffree = info->si_freei;
 	buf->f_fsid.val[0] = kdev_t_to_nr(s->s_dev);
 	buf->f_namelen = BFS_NAMELEN;
 	return 0;
@@ -202,7 +207,7 @@
 static void bfs_write_super(struct super_block *s)
 {
 	if (!(s->s_flags & MS_RDONLY))
-		mark_buffer_dirty(s->su_sbh);
+		mark_buffer_dirty(BFS_SB(s)->si_sbh);
 	s->s_dirt = 0;
 }
 
@@ -267,14 +272,14 @@
 
 	if (!tmpbuf)
 		return;
-	for (i=s->su_lasti; i>=0; i--) {
+	for (i=BFS_SB(s)->si_lasti; i>=0; i--) {
 		if (i>PAGE_SIZE-100) break;
-		if (test_bit(i, s->su_imap))
+		if (test_bit(i, BFS_SB(s)->si_imap))
 			strcat(tmpbuf, "1");
 		else
 			strcat(tmpbuf, "0");
 	}
-	printk(KERN_ERR "BFS-fs: %s: lasti=%08lx <%s>\n", prefix, s->su_lasti, tmpbuf);
+	printk(KERN_ERR "BFS-fs: %s: lasti=%08lx <%s>\n", prefix, BFS_SB(s)->si_lasti, tmpbuf);
 	free_page((unsigned long)tmpbuf);
 #endif
 }
@@ -285,6 +290,13 @@
 	struct bfs_super_block * bfs_sb;
 	struct inode * inode;
 	int i, imap_len;
+	struct bfs_sb_info * info;
+
+	info = kmalloc(sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+	s->u.generic_sbp = info;
+	memset(info, 0, sizeof(*info));
 
 	sb_set_blocksize(s, BFS_BSIZE);
 
@@ -302,49 +314,49 @@
 		printf("%s is unclean, continuing\n", s->s_id);
 
 	s->s_magic = BFS_MAGIC;
-	s->su_bfs_sb = bfs_sb;
-	s->su_sbh = bh;
-	s->su_lasti = (bfs_sb->s_start - BFS_BSIZE)/sizeof(struct bfs_inode) 
+	info->si_bfs_sb = bfs_sb;
+	info->si_sbh = bh;
+	info->si_lasti = (bfs_sb->s_start - BFS_BSIZE)/sizeof(struct bfs_inode) 
 			+ BFS_ROOT_INO - 1;
 
-	imap_len = s->su_lasti/8 + 1;
-	s->su_imap = kmalloc(imap_len, GFP_KERNEL);
-	if (!s->su_imap)
+	imap_len = info->si_lasti/8 + 1;
+	info->si_imap = kmalloc(imap_len, GFP_KERNEL);
+	if (!info->si_imap)
 		goto out;
-	memset(s->su_imap, 0, imap_len);
+	memset(info->si_imap, 0, imap_len);
 	for (i=0; i<BFS_ROOT_INO; i++) 
-		set_bit(i, s->su_imap);
+		set_bit(i, info->si_imap);
 
 	s->s_op = &bfs_sops;
 	inode = iget(s, BFS_ROOT_INO);
 	if (!inode) {
-		kfree(s->su_imap);
+		kfree(info->si_imap);
 		goto out;
 	}
 	s->s_root = d_alloc_root(inode);
 	if (!s->s_root) {
 		iput(inode);
-		kfree(s->su_imap);
+		kfree(info->si_imap);
 		goto out;
 	}
 
-	s->su_blocks = (bfs_sb->s_end + 1)>>BFS_BSIZE_BITS; /* for statfs(2) */
-	s->su_freeb = (bfs_sb->s_end + 1 - bfs_sb->s_start)>>BFS_BSIZE_BITS;
-	s->su_freei = 0;
-	s->su_lf_eblk = 0;
-	s->su_lf_sblk = 0;
-	s->su_lf_ioff = 0;
-	for (i=BFS_ROOT_INO; i<=s->su_lasti; i++) {
+	info->si_blocks = (bfs_sb->s_end + 1)>>BFS_BSIZE_BITS; /* for statfs(2) */
+	info->si_freeb = (bfs_sb->s_end + 1 - bfs_sb->s_start)>>BFS_BSIZE_BITS;
+	info->si_freei = 0;
+	info->si_lf_eblk = 0;
+	info->si_lf_sblk = 0;
+	info->si_lf_ioff = 0;
+	for (i=BFS_ROOT_INO; i<=info->si_lasti; i++) {
 		inode = iget(s,i);
 		if (BFS_I(inode)->i_dsk_ino == 0)
-			s->su_freei++;
+			info->si_freei++;
 		else {
-			set_bit(i, s->su_imap);
-			s->su_freeb -= inode->i_blocks;
-			if (BFS_I(inode)->i_eblock > s->su_lf_eblk) {
-				s->su_lf_eblk = BFS_I(inode)->i_eblock;
-				s->su_lf_sblk = BFS_I(inode)->i_sblock;
-				s->su_lf_ioff = BFS_INO2OFF(i);
+			set_bit(i, info->si_imap);
+			info->si_freeb -= inode->i_blocks;
+			if (BFS_I(inode)->i_eblock > info->si_lf_eblk) {
+				info->si_lf_eblk = BFS_I(inode)->i_eblock;
+				info->si_lf_sblk = BFS_I(inode)->i_sblock;
+				info->si_lf_ioff = BFS_INO2OFF(i);
 			}
 		}
 		iput(inode);
@@ -358,6 +370,8 @@
 
 out:
 	brelse(bh);
+	kfree(info);
+	s->u.generic_sbp = NULL;
 	return -EINVAL;
 }
 
diff -Nru a/fs/binfmt_misc.c b/fs/binfmt_misc.c
--- a/fs/binfmt_misc.c	Mon Apr 22 15:32:19 2002
+++ b/fs/binfmt_misc.c	Mon Apr 22 15:32:19 2002
@@ -38,7 +38,7 @@
 
 typedef struct {
 	struct list_head list;
-	int flags;			/* type, status, etc. */
+	unsigned long flags;		/* type, status, etc. */
 	int offset;			/* offset of magic */
 	int size;			/* size of magic/mask */
 	char *magic;			/* magic or filename extension */
diff -Nru a/fs/dcache.c b/fs/dcache.c
--- a/fs/dcache.c	Mon Apr 22 15:32:18 2002
+++ b/fs/dcache.c	Mon Apr 22 15:32:18 2002
@@ -239,12 +239,15 @@
  * return it. Otherwise return NULL. Notice that if inode is a directory
  * there can be only one alias and it can be unhashed only if it has
  * no children.
+ *
+ * If the inode has a DCACHE_DISCONNECTED alias, then prefer
+ * any other hashed alias over that one.
  */
 
 struct dentry * d_find_alias(struct inode *inode)
 {
 	struct list_head *head, *next, *tmp;
-	struct dentry *alias;
+	struct dentry *alias, *discon_alias=NULL;
 
 	spin_lock(&dcache_lock);
 	head = &inode->i_dentry;
@@ -254,13 +257,19 @@
 		next = tmp->next;
 		alias = list_entry(tmp, struct dentry, d_alias);
 		if (!list_empty(&alias->d_hash)) {
-			__dget_locked(alias);
-			spin_unlock(&dcache_lock);
-			return alias;
+			if (alias->d_flags & DCACHE_DISCONNECTED)
+				discon_alias = alias;
+			else {
+				__dget_locked(alias);
+				spin_unlock(&dcache_lock);
+				return alias;
+			}
 		}
 	}
+	if (discon_alias)
+		__dget_locked(discon_alias);
 	spin_unlock(&dcache_lock);
-	return NULL;
+	return discon_alias;
 }
 
 /*
@@ -537,6 +546,33 @@
 		prune_dcache(found);
 }
 
+/**
+ * shrink_dcache_anon - further prune the cache
+ * @head: head of d_hash list of dentries to prune
+ *
+ * Prune the dentries that are anonymous
+ *
+ */
+void shrink_dcache_anon(struct list_head *head)
+{
+	struct list_head *lp;
+	int found;
+	do {
+		found = 0;
+		spin_lock(&dcache_lock);
+		list_for_each(lp, head) {
+			struct dentry *this = list_entry(lp, struct dentry, d_hash);
+			if (!atomic_read(&this->d_count)) {
+				list_del(&this->d_lru);
+				list_add_tail(&this->d_lru, &dentry_unused);
+				found++;
+			}
+		}
+		spin_unlock(&dcache_lock);
+		prune_dcache(found);
+	} while(found);
+}
+
 /*
  * This is called from kswapd when we think we need some
  * more memory, but aren't really sure how much. So we
@@ -692,6 +728,112 @@
 }
 
 /**
+ * d_alloc_anon - allocate an anonymous dentry
+ * @inode: inode to allocate the dentry for
+ *
+ * This is similar to d_alloc_root.  It is used by filesystems when
+ * creating a dentry for a given inode, often in the process of 
+ * mapping a filehandle to a dentry.  The returned dentry may be
+ * anonymous, or may have a full name (if the inode was already
+ * in the cache).  The file system may need to make further
+ * efforts to connect this dentry into the dcache properly.
+ *
+ * When called on a directory inode, we must ensure that
+ * the inode only ever has one dentry.  If a dentry is
+ * found, that is returned instead of allocating a new one.
+ *
+ * On successful return, the reference to the inode has been transferred
+ * to the dentry.  If %NULL is returned (indicating kmalloc failure),
+ * the reference on the inode has not been released.
+ */
+
+struct dentry * d_alloc_anon(struct inode *inode)
+{
+	struct dentry *tmp;
+	struct dentry *res;
+
+	if ((res = d_find_alias(inode))) {
+		iput(inode);
+		return res;
+	}
+
+	tmp = d_alloc(NULL, &(const struct qstr) {"",0,0});
+	tmp->d_parent = tmp; /* make sure dput doesn't croak */
+	
+	spin_lock(&dcache_lock);
+	if (S_ISDIR(inode->i_mode) && !list_empty(&inode->i_dentry)) {
+		/* A directory can only have one dentry.
+		 * This (now) has one, so use it.
+		 */
+		res = list_entry(inode->i_dentry.next, struct dentry, d_alias);
+		__dget_locked(res);
+	} else {
+		/* attach a disconnected dentry */
+		res = tmp;
+		tmp = NULL;
+		if (res) {
+			res->d_sb = inode->i_sb;
+			res->d_parent = res;
+			res->d_inode = inode;
+			res->d_flags |= DCACHE_DISCONNECTED;
+			list_add(&res->d_alias, &inode->i_dentry);
+			list_add(&res->d_hash, &inode->i_sb->s_anon);
+		}
+		inode = NULL; /* don't drop reference */
+	}
+	spin_unlock(&dcache_lock);
+
+	if (inode)
+		iput(inode);
+	if (tmp)
+		dput(tmp);
+	return res;
+}
+
+
+/**
+ * d_splice_alias - splice a disconnected dentry into the tree if one exists
+ * @inode:  the inode which may have a disconnected dentry
+ * @dentry: a negative dentry which we want to point to the inode.
+ *
+ * If inode is a directory and has a 'disconnected' dentry (i.e. IS_ROOT and
+ * DCACHE_DISCONNECTED), then d_move that in place of the given dentry
+ * and return it, else simply d_add the inode to the dentry and return NULL.
+ *
+ * This is (will be) needed in the lookup routine of any filesystem that is exportable
+ * (via knfsd) so that we can build dcache paths to directories effectively.
+ *
+ * If a dentry was found and moved, then it is returned.  Otherwise NULL
+ * is returned.  This matches the expected return value of ->lookup.
+ *
+ */
+struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry)
+{
+	struct dentry *new = NULL;
+
+	if (inode && S_ISDIR(inode->i_mode)) {
+		spin_lock(&dcache_lock);
+		if (!list_empty(&inode->i_dentry)) {
+			new = list_entry(inode->i_dentry.next, struct dentry, d_alias);
+			__dget_locked(new);
+			spin_unlock(&dcache_lock);
+			d_rehash(dentry);
+			d_move(new, dentry);
+			iput(inode);
+		} else {
+			/* d_instantiate takes dcache_lock, so we do it by hand */
+			list_add(&dentry->d_alias, &inode->i_dentry);
+			dentry->d_inode = inode;
+			spin_unlock(&dcache_lock);
+			d_rehash(dentry);
+		}
+	} else
+		d_add(dentry, inode);
+	return new;
+}
+
+
+/**
  * d_lookup - search for a dentry
  * @parent: parent dentry
  * @name: qstr of name we wish to find
@@ -915,16 +1057,24 @@
 	list_del(&dentry->d_child);
 	list_del(&target->d_child);
 
-	/* Switch the parents and the names.. */
+	/* Switch the names.. */
 	switch_names(dentry, target);
-	write_lock(&dparent_lock);
-	do_switch(dentry->d_parent, target->d_parent);
-	write_unlock(&dparent_lock);
 	do_switch(dentry->d_name.len, target->d_name.len);
 	do_switch(dentry->d_name.hash, target->d_name.hash);
+	/* ... and switch the parents */
+	write_lock(&dparent_lock);
+	if (IS_ROOT(dentry)) {
+		dentry->d_parent = target->d_parent;
+		target->d_parent = target;
+		INIT_LIST_HEAD(&target->d_child);
+	} else {
+		do_switch(dentry->d_parent, target->d_parent);
+
+		/* And add them back to the (new) parent lists */
+		list_add(&target->d_child, &target->d_parent->d_subdirs);
+	}
+	write_unlock(&dparent_lock);
 
-	/* And add them back to the (new) parent lists */
-	list_add(&target->d_child, &target->d_parent->d_subdirs);
 	list_add(&dentry->d_child, &dentry->d_parent->d_subdirs);
 	spin_unlock(&dcache_lock);
 }
diff -Nru a/fs/devfs/util.c b/fs/devfs/util.c
--- a/fs/devfs/util.c	Mon Apr 22 15:32:18 2002
+++ b/fs/devfs/util.c	Mon Apr 22 15:32:18 2002
@@ -54,6 +54,8 @@
     20020326   Richard Gooch <rgooch@atnf.csiro.au>
                Fixed bitfield data type for <devfs_*alloc_devnum>.
                Made major bitfield type and initialiser 64 bit safe.
+    20020413   Richard Gooch <rgooch@atnf.csiro.au>
+               Fixed shift warning on 64 bit machines.
 */
 #include <linux/module.h>
 #include <linux/init.h>
@@ -136,7 +138,7 @@
 #if BITS_PER_LONG == 32
 #  define INITIALISER64(low,high) (low), (high)
 #else
-#  define INITIALISER64(low,high) ( (high) << 32 | (low) )
+#  define INITIALISER64(low,high) ( (unsigned long) (high) << 32 | (low) )
 #endif
 
 /*  Block majors already assigned:
diff -Nru a/fs/exec.c b/fs/exec.c
--- a/fs/exec.c	Mon Apr 22 15:32:19 2002
+++ b/fs/exec.c	Mon Apr 22 15:32:19 2002
@@ -978,8 +978,7 @@
 	if (current->rlim[RLIMIT_CORE].rlim_cur < binfmt->min_coredump)
 		goto fail;
 
-	memcpy(corename,"core.", 5);
-	corename[4] = '\0';
+	memcpy(corename,"core", 5); /* include trailing \0 */
  	if (core_uses_pid || atomic_read(&current->mm->mm_users) != 1)
  		sprintf(&corename[4], ".%d", current->pid);
 	file = filp_open(corename, O_CREAT | 2 | O_NOFOLLOW, 0600);
diff -Nru a/fs/exportfs/Makefile b/fs/exportfs/Makefile
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/fs/exportfs/Makefile	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,11 @@
+#
+# Makefile for the filesystem export support routines.
+
+O_TARGET := exportfs.o
+
+export-objs := expfs.o
+
+obj-y :=    expfs.o
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff -Nru a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/fs/exportfs/expfs.c	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,516 @@
+
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/smp_lock.h>
+
+/**
+ * find_exported_dentry - helper routine to implement export_operations->decode_fh
+ * @sb:		The &super_block identifying the filesystem
+ * @obj:	An opaque identifier of the object to be found - passed to get_inode
+ * @parent:	An optional opqaue identifier of the parent of the object.
+ * @acceptable:	A function used to test possible &dentries to see of they are acceptable
+ * @context:	A parameter to @acceptable so that it knows on what basis to judge.
+ *
+ * find_exported_dentry is the central helper routine to enable file systems to provide
+ * the decode_fh() export_operation.  It's main task is to take an &inode, find or create an
+ * appropriate &dentry structure, and possibly splice this into the dcache in the
+ * correct place.
+ *
+ * The decode_fh() operation provided by the filesystem should call find_exported_dentry()
+ * with the same parameters that it received except that instead of the file handle fragment,
+ * pointers to opaque identifiers for the object and optionally its parent are passed.
+ * The default decode_fh routine passes one pointer to the start of the filehandle fragment, and
+ * one 8 bytes into the fragment.  It is expected that most filesystems will take this
+ * approach, though the offset to the parent identifier may well be different.
+ *
+ * find_exported_dentry() will call get_dentry to get an dentry pointer from the file system.  If
+ * any &dentry in the d_alias list is acceptable, it will be returned.  Otherwise
+ * find_exported_dentry() will attempt to splice a new &dentry into the dcache using get_name() and
+ * get_parent() to find the appropriate place.
+ *
+ */
+
+struct export_operations export_op_default;
+
+#define	CALL(ops,fun) ((ops->fun)?(ops->fun):export_op_default.fun)
+
+#define dprintk(x, ...) do{}while(0)
+
+struct dentry *
+find_exported_dentry(struct super_block *sb, void *obj, void *parent,
+		     int (*acceptable)(void *context, struct dentry *de),
+		     void *context)
+{
+	struct dentry *result = NULL;
+	struct dentry *target_dir;
+	int err;
+	struct export_operations *nops = sb->s_export_op;
+	struct list_head *le, *head;
+	struct dentry *toput = NULL;
+	int noprogress;
+
+
+	/*
+	 * Attempt to find the inode.
+	 */
+	result = CALL(sb->s_export_op,get_dentry)(sb,obj);
+	err = -ESTALE;
+	if (result == NULL)
+		goto err_out;
+	if (IS_ERR(result)) {
+		err = PTR_ERR(result);
+		goto err_out;
+	}
+	if (S_ISDIR(result->d_inode->i_mode) &&
+	    (result->d_flags & DCACHE_DISCONNECTED)) {
+		/* it is an unconnected directory, we must connect it */
+		;
+	} else {
+		if (acceptable(context, result))
+			return result;
+		if (S_ISDIR(result->d_inode->i_mode)) {
+			/* there is no other dentry, so fail */
+			goto err_result;
+		}
+		/* try any other aliases */
+		spin_lock(&dcache_lock);
+		head = &result->d_inode->i_dentry;
+		list_for_each(le, head) {
+			struct dentry *dentry = list_entry(le, struct dentry, d_alias);
+			dget_locked(dentry);
+			spin_unlock(&dcache_lock);
+			if (toput)
+				dput(toput);
+			toput = NULL;
+			if (dentry != result &&
+			    acceptable(context, dentry)) {
+				dput(result);
+				dentry->d_vfs_flags |= DCACHE_REFERENCED;
+				return dentry;
+			}
+			spin_lock(&dcache_lock);
+			toput = dentry;
+		}
+		spin_unlock(&dcache_lock);
+		if (toput)
+			dput(toput);
+	}			
+
+	/* It's a directory, or we are required to confirm the file's
+	 * location in the tree based on the parent information
+ 	 */
+	dprintk("find_exported_dentry: need to look harder for %d/%d\n",kdev_t_to_nr(sb->s_dev),*(int*)obj);
+	if (S_ISDIR(result->d_inode->i_mode))
+		target_dir = dget(result);
+	else {
+		if (parent == NULL)
+			goto err_result;
+
+		target_dir = CALL(sb->s_export_op,get_dentry)(sb,parent);
+		if (IS_ERR(target_dir))
+			err = PTR_ERR(target_dir);
+		if (target_dir == NULL || IS_ERR(target_dir))
+			goto err_result;
+	}
+	/*
+	 * Now we need to make sure that target_dir is properly connected.
+	 * It may already be, as the flag isn't always updated when connection
+	 * happens.
+	 * So, we walk up parent links until we find a connected directory,
+	 * or we run out of directories.  Then we find the parent, find
+	 * the name of the child in that parent, and do a lookup.
+	 * This should connect the child into the parent
+	 * We then repeat.
+	 */
+
+	/* it is possible that a confused file system might not let us complete the
+	 * path to the root.  For example, if get_parent returns a directory
+	 * in which we cannot find a name for the child.  While this implies a very
+	 * sick filesystem we don't want it to cause knfsd to spin.  Hence the noprogress
+	 * counter.  If we go through the loop 10 times (2 is probably enough) without
+	 * getting anywhere, we just give up
+	 */
+	lock_kernel();
+	noprogress= 0;
+	while (target_dir->d_flags & DCACHE_DISCONNECTED && noprogress++ < 10) {
+		struct dentry *pd = target_dir;
+		read_lock(&dparent_lock);
+		while (!IS_ROOT(pd) &&
+		       (pd->d_parent->d_flags & DCACHE_DISCONNECTED))
+			pd = pd->d_parent;
+
+		dget(pd);
+		read_unlock(&dparent_lock);
+
+		if (!IS_ROOT(pd)) {
+			/* must have found a connected parent - great */
+			pd->d_flags &= ~DCACHE_DISCONNECTED;
+			noprogress = 0;
+		} else if (pd == sb->s_root) {
+			printk(KERN_ERR "export: Eeek filesystem root is not connected, impossible\n");
+			pd->d_flags &= ~DCACHE_DISCONNECTED;
+			noprogress = 0;
+		} else {
+			/* we have hit the top of a disconnected path.  Try
+			 * to find parent and connect
+			 * note: racing with some other process renaming a
+			 * directory isn't much of a problem here.  If someone
+			 * renames the directory, it will end up properly connected,
+			 * which is what we want
+			 */
+			struct dentry *ppd;
+			struct dentry *npd;
+			char nbuf[NAME_MAX+1];
+
+			down(&pd->d_inode->i_sem);
+			ppd = CALL(nops,get_parent)(pd);
+			up(&pd->d_inode->i_sem);
+
+			if (IS_ERR(ppd)) {
+				err = PTR_ERR(ppd);
+				dprintk("find_exported_dentry: get_parent of %ld failed, err %d\n",
+					pd->d_inode->i_ino, err);
+				dput(pd);
+				break;
+			}
+			dprintk("find_exported_dentry: find name of %lu in %lu\n", pd->d_inode->i_ino, ppd->d_inode->i_ino);
+			err = CALL(nops,get_name)(ppd, nbuf, pd);
+			if (err) {
+				dput(ppd);
+				if (err == -ENOENT)
+					/* some race between get_parent and get_name?
+					 * just try again
+					 */
+					continue;
+				dput(pd);
+				break;
+			}
+			dprintk("find_exported_dentry: found name: %s\n", nbuf);
+			down(&ppd->d_inode->i_sem);
+			npd = lookup_one_len(nbuf, ppd, strlen(nbuf));
+			up(&ppd->d_inode->i_sem);
+			if (IS_ERR(npd)) {
+				err = PTR_ERR(npd);
+				dprintk("find_exported_dentry: lookup failed: %d\n", err);
+				dput(ppd);
+				dput(pd);
+				break;
+			}
+			/* we didn't really want npd, we really wanted
+			 * a side-effect of the lookup.
+			 * hopefully, npd == pd, though it isn't really
+			 * a problem if it isn't
+			 */
+			if (npd == pd)
+				noprogress = 0;
+			else
+				printk("find_exported_dentry: npd != pd\n");
+			dput(npd);
+			dput(ppd);
+			if (IS_ROOT(pd)) {
+				/* something went wrong, we will have to give up */
+				dput(pd);
+				break;
+			}
+		}
+		dput(pd);
+	}
+
+	if (target_dir->d_flags & DCACHE_DISCONNECTED) {
+		/* something went wrong - oh-well */
+		if (!err)
+			err = -ESTALE;
+		unlock_kernel();
+		goto err_target;
+	}
+	/* if we weren't after a directory, have one more step to go */
+	if (result != target_dir) {
+		struct dentry *nresult;
+		char nbuf[NAME_MAX+1];
+		err = CALL(nops,get_name)(target_dir, nbuf, result);
+		if (!err) {
+			down(&target_dir->d_inode->i_sem);
+			nresult = lookup_one_len(nbuf, target_dir, strlen(nbuf));
+			up(&target_dir->d_inode->i_sem);
+			if (!IS_ERR(nresult)) {
+				if (nresult->d_inode) {
+					dput(result);
+					result = nresult;
+				} else
+					dput(nresult);
+			}
+		}
+	}
+	dput(target_dir);
+	unlock_kernel();
+	/* now result is properly connected, it is our best bet */
+	if (acceptable(context, result))
+		return result;
+	/* one last try of the aliases.. */
+	spin_lock(&dcache_lock);
+	head = &result->d_inode->i_dentry;
+	list_for_each(le, head) {
+		struct dentry *dentry = list_entry(le, struct dentry, d_alias);
+		dget_locked(dentry);
+		spin_unlock(&dcache_lock);
+		if (toput) dput(toput);
+		if (dentry != result &&
+		    acceptable(context, dentry)) {
+			dput(result);
+			dentry->d_vfs_flags |= DCACHE_REFERENCED;
+			return dentry;
+		}
+		spin_lock(&dcache_lock);
+		toput = dentry;
+	}
+	spin_unlock(&dcache_lock);
+	if (toput)
+		dput(toput);
+
+	/* drat - I just cannot find anything acceptable */
+	dput(result);
+	return ERR_PTR(-ESTALE);
+
+ err_target:
+	dput(target_dir);
+ err_result:
+	dput(result);
+ err_out:
+	return ERR_PTR(err);
+}
+
+
+
+static struct dentry *get_parent(struct dentry *child)
+{
+	/* get_parent cannot be supported generically, the locking
+	 * is too icky.
+	 * instead, we just return EACCES.  If server reboots or inodes
+	 * get flushed, you lose
+	 */
+	return ERR_PTR(-EACCES);
+}
+
+
+struct getdents_callback {
+	char *name;		/* name that was found. It already points to a buffer NAME_MAX+1 is size */
+	unsigned long ino;	/* the inum we are looking for */
+	int found;		/* inode matched? */
+	int sequence;		/* sequence counter */
+};
+
+/*
+ * A rather strange filldir function to capture
+ * the name matching the specified inode number.
+ */
+static int filldir_one(void * __buf, const char * name, int len,
+			loff_t pos, ino_t ino, unsigned int d_type)
+{
+	struct getdents_callback *buf = __buf;
+	int result = 0;
+
+	buf->sequence++;
+	if (buf->ino == ino) {
+		memcpy(buf->name, name, len);
+		buf->name[len] = '\0';
+		buf->found = 1;
+		result = -1;
+	}
+	return result;
+}
+
+/**
+ * get_name - default export_operations->get_name function
+ * @dentry: the directory in which to find a name
+ * @name:   a pointer to a %NAME_MAX+1 char buffer to store the name
+ * @child:  the dentry for the child directory.
+ *
+ * calls readdir on the parent until it finds an entry with
+ * the same inode number as the child, and returns that.
+ */
+static int get_name(struct dentry *dentry, char *name,
+			struct dentry *child)
+{
+	struct inode *dir = dentry->d_inode;
+	int error;
+	struct file file;
+	struct getdents_callback buffer;
+
+	error = -ENOTDIR;
+	if (!dir || !S_ISDIR(dir->i_mode))
+		goto out;
+	error = -EINVAL;
+	if (!dir->i_fop)
+		goto out;
+	/*
+	 * Open the directory ...
+	 */
+	error = init_private_file(&file, dentry, FMODE_READ);
+	if (error)
+		goto out;
+	error = -EINVAL;
+	if (!file.f_op->readdir)
+		goto out_close;
+
+	buffer.name = name;
+	buffer.ino = child->d_inode->i_ino;
+	buffer.found = 0;
+	buffer.sequence = 0;
+	while (1) {
+		int old_seq = buffer.sequence;
+
+		error = vfs_readdir(&file, filldir_one, &buffer);
+
+		if (error < 0)
+			break;
+
+		error = 0;
+		if (buffer.found)
+			break;
+		error = -ENOENT;
+		if (old_seq == buffer.sequence)
+			break;
+	}
+
+out_close:
+	if (file.f_op->release)
+		file.f_op->release(dir, &file);
+out:
+	return error;
+}
+
+
+static struct dentry *export_iget(struct super_block *sb, unsigned long ino, __u32 generation)
+{
+
+	/* iget isn't really right if the inode is currently unallocated!!
+	 * This should really all be done inside each filesystem
+	 *
+	 * ext2fs' read_inode has been strengthed to return a bad_inode if the inode
+	 *   had been deleted.
+	 *
+	 * Currently we don't know the generation for parent directory, so a generation
+	 * of 0 means "accept any"
+	 */
+	struct inode *inode;
+	struct dentry *result;
+	if (ino == 0)
+		return ERR_PTR(-ESTALE);
+	inode = iget(sb, ino);
+	if (inode == NULL)
+		return ERR_PTR(-ENOMEM);
+	if (is_bad_inode(inode)
+	    || (generation && inode->i_generation != generation)
+		) {
+		/* we didn't find the right inode.. */
+		dprintk("fh_verify: Inode %lu, Bad count: %d %d or version  %u %u\n",
+			inode->i_ino,
+			inode->i_nlink, atomic_read(&inode->i_count),
+			inode->i_generation,
+			generation);
+
+		iput(inode);
+		return ERR_PTR(-ESTALE);
+	}
+	/* now to find a dentry.
+	 * If possible, get a well-connected one
+	 */
+	result = d_alloc_anon(inode);
+	if (!result) {
+		iput(inode);
+		return ERR_PTR(-ENOMEM);
+	}
+	result->d_vfs_flags |= DCACHE_REFERENCED;
+	return result;
+}
+
+
+static struct dentry *get_object(struct super_block *sb, void *vobjp)
+{
+	__u32 *objp = vobjp;
+	unsigned long ino = objp[0];
+	__u32 generation = objp[1];
+
+	return export_iget(sb, ino, generation);
+}
+
+
+/**
+ * export_encode_fh - default export_operations->encode_fh function
+ * dentry:  the dentry to encode
+ * fh:      where to store the file handle fragment
+ * max_len: maximum length to store there
+ * connectable: whether to store parent infomation
+ *
+ * This default encode_fh function assumes that the 32 inode number
+ * is suitable for locating an inode, and that the generation number
+ * can be used to check that it is still valid.  It places them in the
+ * filehandle fragment where export_decode_fh expects to find them.
+ */
+static int export_encode_fh(struct dentry *dentry, __u32 *fh, int *max_len,
+		   int connectable)
+{
+	struct inode * inode = dentry->d_inode;
+	struct inode *parent = dentry->d_parent->d_inode;
+	int len = *max_len;
+	int type = 1;
+	
+	if (len < 2 || (connectable && len < 4))
+		return 255;
+
+	len = 2;
+	fh[0] = inode->i_ino;
+	fh[1] = inode->i_generation;
+	if (connectable && !S_ISDIR(inode->i_mode)) {
+		fh[2] = parent->i_ino;
+		fh[3] = parent->i_generation;
+		len = 4;
+		type = 2;
+	}
+	*max_len = len;
+	return type;
+}
+
+
+/**
+ * export_decode_fh - default export_operations->decode_fh function
+ * sb:  The superblock
+ * fh:  pointer to the file handle fragment
+ * fh_len: length of file handle fragment
+ * acceptable: function for testing acceptability of dentrys
+ * context:   context for @acceptable
+ *
+ * This is the default decode_fh() function.
+ * a fileid_type of 1 indicates that the filehandlefragment
+ * just contains an object identifier understood by  get_dentry.
+ * a fileid_type of 2 says that there is also a directory
+ * identifier 8 bytes in to the filehandlefragement.
+ */
+static struct dentry *export_decode_fh(struct super_block *sb, __u32 *fh, int fh_len,
+			      int fileid_type,
+			 int (*acceptable)(void *context, struct dentry *de),
+			 void *context)
+{
+	__u32 parent[2];
+	parent[0] = parent[1] = 0;
+	if (fh_len < 2 || fileid_type > 2)
+		return NULL;
+	if (fileid_type == 2) {
+		if (fh_len > 2) parent[0] = fh[2];
+		if (fh_len > 3) parent[1] = fh[3];
+	}
+	return find_exported_dentry(sb, fh, parent,
+				   acceptable, context);
+}
+
+struct export_operations export_op_default = {
+	decode_fh:	export_decode_fh,
+	encode_fh:	export_encode_fh,
+
+	get_name:	get_name,
+	get_parent:	get_parent,
+	get_dentry:	get_object,
+};
+
+EXPORT_SYMBOL(export_op_default);
+EXPORT_SYMBOL(find_exported_dentry);
diff -Nru a/fs/ext2/inode.c b/fs/ext2/inode.c
--- a/fs/ext2/inode.c	Mon Apr 22 15:32:18 2002
+++ b/fs/ext2/inode.c	Mon Apr 22 15:32:18 2002
@@ -261,6 +261,7 @@
 
 changed:
 	read_unlock(&EXT2_I(inode)->i_meta_lock);
+	brelse(bh);
 	*err = -EAGAIN;
 	goto no_block;
 failure:
diff -Nru a/fs/ext2/namei.c b/fs/ext2/namei.c
--- a/fs/ext2/namei.c	Mon Apr 22 15:32:19 2002
+++ b/fs/ext2/namei.c	Mon Apr 22 15:32:19 2002
@@ -79,9 +79,36 @@
 		if (!inode)
 			return ERR_PTR(-EACCES);
 	}
+	if (inode)
+		return d_splice_alias(inode, dentry);
 	d_add(dentry, inode);
 	return NULL;
 }
+
+struct dentry *ext2_get_parent(struct dentry *child)
+{
+	unsigned long ino;
+	struct dentry *parent;
+	struct inode *inode;
+	struct dentry dotdot;
+
+	dotdot.d_name.name = "..";
+	dotdot.d_name.len = 2;
+
+	ino = ext2_inode_by_name(child->d_inode, &dotdot);
+	if (!ino)
+		return ERR_PTR(-ENOENT);
+	inode = iget(child->d_inode->i_sb, ino);
+
+	if (!inode)
+		return ERR_PTR(-EACCES);
+	parent = d_alloc_anon(inode);
+	if (!parent) {
+		iput(inode);
+		parent = ERR_PTR(-ENOMEM);
+	}
+	return parent;
+} 
 
 /*
  * By the time this is called, we already have created
diff -Nru a/fs/ext2/super.c b/fs/ext2/super.c
--- a/fs/ext2/super.c	Mon Apr 22 15:32:19 2002
+++ b/fs/ext2/super.c	Mon Apr 22 15:32:19 2002
@@ -209,6 +209,16 @@
 	remount_fs:	ext2_remount,
 };
 
+/* Yes, most of these are left as NULL!!
+ * A NULL value implies the default, which works with ext2-like file
+ * systems, but can be improved upon.
+ * Currently only get_parent is required.
+ */
+struct dentry *ext2_get_parent(struct dentry *child);
+static struct export_operations ext2_export_ops = {
+	get_parent: ext2_get_parent,
+};
+
 /*
  * This function has been shamelessly adapted from the msdos fs
  */
@@ -687,6 +697,7 @@
 	 * set up enough so that it can read an inode
 	 */
 	sb->s_op = &ext2_sops;
+	sb->s_export_op = &ext2_export_ops;
 	sb->s_root = d_alloc_root(iget(sb, EXT2_ROOT_INO));
 	if (!sb->s_root || !S_ISDIR(sb->s_root->d_inode->i_mode) ||
 	    !sb->s_root->d_inode->i_blocks || !sb->s_root->d_inode->i_size) {
diff -Nru a/fs/ext3/inode.c b/fs/ext3/inode.c
--- a/fs/ext3/inode.c	Mon Apr 22 15:32:19 2002
+++ b/fs/ext3/inode.c	Mon Apr 22 15:32:19 2002
@@ -414,6 +414,7 @@
 	return NULL;
 
 changed:
+	brelse(bh);
 	*err = -EAGAIN;
 	goto no_block;
 failure:
diff -Nru a/fs/fat/inode.c b/fs/fat/inode.c
--- a/fs/fat/inode.c	Mon Apr 22 15:32:19 2002
+++ b/fs/fat/inode.c	Mon Apr 22 15:32:19 2002
@@ -17,6 +17,7 @@
 #include <linux/smp_lock.h>
 #include <linux/msdos_fs.h>
 #include <linux/fat_cvf.h>
+#include <linux/bitops.h>
 
 //#include <asm/uaccess.h>
 #include <asm/unaligned.h>
@@ -430,7 +431,6 @@
 				int len, int fhtype, int parent)
 {
 	struct inode *inode = NULL;
-	struct list_head *lp;
 	struct dentry *result;
 
 	if (fhtype != 3)
@@ -480,31 +480,14 @@
 	
 	/* now to find a dentry.
 	 * If possible, get a well-connected one
-	 *
-	 * Given the way that we found the inode, it *MUST* be
-	 * well-connected, but it is easiest to just copy the
-	 * code.
 	 */
-	spin_lock(&dcache_lock);
-	list_for_each(lp, &inode->i_dentry) {
-		result = list_entry(lp,struct dentry, d_alias);
-		if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED)) {
-			dget_locked(result);
-			result->d_vfs_flags |= DCACHE_REFERENCED;
-			spin_unlock(&dcache_lock);
-			iput(inode);
-			return result;
-		}
-	}
-	spin_unlock(&dcache_lock);
-	result = d_alloc_root(inode);
+	result = d_alloc_anon(inode);
 	if (result == NULL) {
 		iput(inode);
 		return ERR_PTR(-ENOMEM);
 	}
-	result->d_flags |= DCACHE_NFSD_DISCONNECTED;
+	result->d_vfs_flags |= DCACHE_REFERENCED;
 	return result;
-
 		
 }
 
diff -Nru a/fs/fcntl.c b/fs/fcntl.c
--- a/fs/fcntl.c	Mon Apr 22 15:32:18 2002
+++ b/fs/fcntl.c	Mon Apr 22 15:32:18 2002
@@ -88,6 +88,10 @@
 
 	write_lock(&files->file_lock);
 	
+	error = -EINVAL;
+	if (orig_start >= current->rlim[RLIMIT_NOFILE].rlim_cur)
+		goto out;
+
 repeat:
 	/*
 	 * Someone might have closed fd's in the range
diff -Nru a/fs/intermezzo/dir.c b/fs/intermezzo/dir.c
--- a/fs/intermezzo/dir.c	Mon Apr 22 15:32:19 2002
+++ b/fs/intermezzo/dir.c	Mon Apr 22 15:32:19 2002
@@ -192,7 +192,7 @@
         }
 
         d_instantiate(dentry, inode);
-        dentry->d_flags |= DCACHE_NFSD_DISCONNECTED; /* NFS hack */
+        dentry->d_flags |= DCACHE_DISCONNECTED; /* NFS hack */
 
         EXIT;
         return NULL;
diff -Nru a/fs/jffs/Makefile b/fs/jffs/Makefile
--- a/fs/jffs/Makefile	Mon Apr 22 15:32:18 2002
+++ b/fs/jffs/Makefile	Mon Apr 22 15:32:18 2002
@@ -3,13 +3,6 @@
 #
 # $Id: Makefile,v 1.11 2001/09/25 20:59:41 dwmw2 Exp $
 #
-# Note! Dependencies are done automagically by 'make dep', which also
-# removes any old dependencies. DON'T put your own dependencies here
-# unless it's something special (ie not a .c file).
-#
-# Note 2! The CFLAGS definitions are now in the main makefile...
-
-list-multi := jffs.o
 
 jffs-objs := jffs_fm.o intrep.o
 
diff -Nru a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c
--- a/fs/jfs/jfs_metapage.c	Mon Apr 22 15:32:19 2002
+++ b/fs/jfs/jfs_metapage.c	Mon Apr 22 15:32:19 2002
@@ -91,32 +91,6 @@
 		__lock_metapage(mp);
 }
 
-/* We're currently re-evaluating the method we use to write metadata
- * pages.  Currently, we have to make sure there no dirty buffer_heads
- * hanging around after we free the metadata page, since the same
- * physical disk blocks may be used in a different address space and we
- * can't write old data over the good data.
- *
- * The best way to do this now is with block_invalidate_page.  However,
- * this is only available in the newer kernels and is not exported
- * to modules.  block_flushpage is the next best, but it too is not exported
- * to modules.
- *
- * In a module, about the best we have is generic_buffer_fdatasync.  This
- * synchronously writes any dirty buffers.  This is not optimal, but it will
- * keep old dirty buffers from overwriting newer data.
- */
-static inline void invalidate_page(metapage_t *mp)
-{
-#ifdef MODULE
-	generic_buffer_fdatasync(mp->mapping->host, mp->index, mp->index + 1);
-#else
-	lock_page(mp->page);
-	block_flushpage(mp->page, 0);
-	UnlockPage(mp->page);
-#endif
-}
-
 int __init metapage_init(void)
 {
 	int i;
@@ -515,7 +489,7 @@
 	lock_page(page);
 
 	/* we're done with this page - no need to check for errors */
-	if (page->buffers) {
+	if (page_has_buffers(page)) {
 		writeout_one_page(page);
 		waitfor_one_page(page);
 	}
@@ -559,8 +533,11 @@
 				clear_bit(META_sync, &mp->flag);
 			}
 
-			if (test_bit(META_discard, &mp->flag))
-				invalidate_page(mp);
+			if (test_bit(META_discard, &mp->flag)) {
+				lock_page(mp->page);
+				block_flushpage(mp->page, 0);
+				UnlockPage(mp->page);
+			}
 
 			page_cache_release(mp->page);
 			INCREMENT(mpStat.pagefree);
@@ -593,9 +570,7 @@
 	int l2BlocksPerPage = PAGE_CACHE_SHIFT - ip->i_sb->s_blocksize_bits;
 	struct address_space *mapping = ip->i_mapping;
 	metapage_t *mp;
-#ifndef MODULE
 	struct page *page;
-#endif
 
 	/*
 	 * First, mark metapages to discard.  They will eventually be
@@ -612,27 +587,14 @@
 			/*
 			 * If in the metapage cache, we've got the page locked
 			 */
-#ifdef MODULE
-			UnlockPage(mp->page);
-			generic_buffer_fdatasync(mp->mapping->host, mp->index,
-						 mp->index+1);
-			lock_page(mp->page);
-#else
 			block_flushpage(mp->page, 0);
-#endif
 		} else {
 			spin_unlock(&meta_lock);
-#ifdef MODULE
-			generic_buffer_fdatasync(ip, lblock << l2BlocksPerPage,
-					(lblock + 1) << l2BlocksPerPage);
-#else
-			page = find_lock_page(mapping,
-					      lblock >> l2BlocksPerPage);
+			page = find_lock_page(mapping, lblock>>l2BlocksPerPage);
 			if (page) {
 				block_flushpage(page, 0);
 				UnlockPage(page);
 			}
-#endif
 		}
 	}
 }
diff -Nru a/fs/minix/itree_common.c b/fs/minix/itree_common.c
--- a/fs/minix/itree_common.c	Mon Apr 22 15:32:19 2002
+++ b/fs/minix/itree_common.c	Mon Apr 22 15:32:19 2002
@@ -57,6 +57,7 @@
 
 changed:
 	read_unlock(&pointers_lock);
+	brelse(bh);
 	*err = -EAGAIN;
 	goto no_block;
 failure:
diff -Nru a/fs/nfsd/export.c b/fs/nfsd/export.c
--- a/fs/nfsd/export.c	Mon Apr 22 15:32:19 2002
+++ b/fs/nfsd/export.c	Mon Apr 22 15:32:19 2002
@@ -233,6 +233,10 @@
 	list_add(&exp->ex_fsid_hash, head);
 }
 
+extern struct dentry *
+find_exported_dentry(struct super_block *sb, void *obj, void *parent,
+		     int (*acceptable)(void *context, struct dentry *de),
+		     void *context);
 /*
  * Export a file system.
  */
@@ -316,12 +320,17 @@
 	      || (nxp->ex_flags & NFSEXP_FSID))
 	    &&
 	    (inode->i_sb->s_op->read_inode
+	     || inode->i_sb->s_export_op
 	     || inode->i_sb->s_op->fh_to_dentry)) 
 		/* Ok, we can export it */;
 	else {
 		dprintk("exp_export: export of invalid fs type.\n");
 		goto finish;
 	}
+	if (inode->i_sb->s_export_op &&
+	    !inode->i_sb->s_export_op->find_exported_dentry)
+		inode->i_sb->s_export_op->find_exported_dentry =
+			find_exported_dentry;
 
 	if ((parent = exp_child(clp, inode->i_sb, nd.dentry)) != NULL) {
 		dprintk("exp_export: export not valid (Rule 3).\n");
diff -Nru a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
--- a/fs/nfsd/nfsctl.c	Mon Apr 22 15:32:19 2002
+++ b/fs/nfsd/nfsctl.c	Mon Apr 22 15:32:19 2002
@@ -30,6 +30,7 @@
 #include <linux/nfsd/cache.h>
 #include <linux/nfsd/xdr.h>
 #include <linux/nfsd/syscall.h>
+#include <linux/nfsd/interface.h>
 
 #include <asm/uaccess.h>
 
@@ -98,13 +99,13 @@
 	return size;
 }
 
-static ssize_t TA_open(struct inode *inode, struct file *file)
+static int TA_open(struct inode *inode, struct file *file)
 {
 	file->private_data = NULL;
 	return 0;
 }
 
-static ssize_t TA_release(struct inode *inode, struct file *file)
+static int TA_release(struct inode *inode, struct file *file)
 {
 	void *p = file->private_data;
 	file->private_data = NULL;
@@ -379,6 +380,7 @@
 static int __init init_nfsd(void)
 {
 	printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n");
+
 	nfsd_stat_init();	/* Statistics */
 	nfsd_cache_init();	/* RPC reply cache */
 	nfsd_export_init();	/* Exports table */
diff -Nru a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
--- a/fs/nfsd/nfsfh.c	Mon Apr 22 15:32:19 2002
+++ b/fs/nfsd/nfsfh.c	Mon Apr 22 15:32:19 2002
@@ -6,6 +6,7 @@
  * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
  * Portions Copyright (C) 1999 G. Allen Morris III <gam3@acm.org>
  * Extensive rewrite by Neil Brown <neilb@cse.unsw.edu.au> Southern-Spring 1999
+ * ... and again Southern-Winter 2001 to support export_operations
  */
 
 #include <linux/sched.h>
@@ -29,6 +30,11 @@
 static int nfsd_nr_verified;
 static int nfsd_nr_put;
 
+extern struct export_operations export_op_default;
+
+#define	CALL(ops,fun) ((ops->fun)?(ops->fun):export_op_default.fun)
+
+
 
 struct nfsd_getdents_callback {
 	char *name;		/* name that was found. It already points to a buffer NAME_MAX+1 is size */
@@ -60,15 +66,6 @@
 	return result;
 }
 
-/**
- * nfsd_get_name - default nfsd_operations->get_name function
- * @dentry: the directory in which to find a name
- * @name:   a pointer to a %NAME_MAX+1 char buffer to store the name
- * @child:  the dentry for the child directory.
- *
- * calls readdir on the parent until it finds an entry with
- * the same inode number as the child, and returns that.
- */
 static int nfsd_get_name(struct dentry *dentry, char *name,
 			struct dentry *child)
 {
@@ -120,9 +117,6 @@
 	return error;
 }
 
-/* this should be provided by each filesystem in an nfsd_operations interface as
- * iget isn't really the right interface
- */
 static struct dentry *nfsd_iget(struct super_block *sb, unsigned long ino, __u32 generation)
 {
 
@@ -136,7 +130,6 @@
 	 * of 0 means "accept any"
 	 */
 	struct inode *inode;
-	struct list_head *lp;
 	struct dentry *result;
 	if (ino == 0)
 		return ERR_PTR(-ESTALE);
@@ -159,24 +152,12 @@
 	/* now to find a dentry.
 	 * If possible, get a well-connected one
 	 */
-	spin_lock(&dcache_lock);
-	list_for_each(lp, &inode->i_dentry) {
-		result = list_entry(lp,struct dentry, d_alias);
-		if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED)) {
-			dget_locked(result);
-			result->d_vfs_flags |= DCACHE_REFERENCED;
-			spin_unlock(&dcache_lock);
-			iput(inode);
-			return result;
-		}
-	}
-	spin_unlock(&dcache_lock);
-	result = d_alloc_root(inode);
-	if (result == NULL) {
+	result = d_alloc_anon(inode);
+	if (!result) {
 		iput(inode);
 		return ERR_PTR(-ENOMEM);
 	}
-	result->d_flags |= DCACHE_NFSD_DISCONNECTED;
+	result->d_vfs_flags |= DCACHE_REFERENCED;
 	return result;
 }
 
@@ -215,7 +196,7 @@
 #ifdef NFSD_PARANOIA
 	if (!IS_ROOT(target))
 		printk("nfsd: d_splice with no-root target: %s/%s\n", parent->d_name.name, name->name);
-	if (!(target->d_flags & DCACHE_NFSD_DISCONNECTED))
+	if (!(target->d_flags & DCACHE_DISCONNECTED))
 		printk("nfsd: d_splice with non-DISCONNECTED target: %s/%s\n", parent->d_name.name, name->name);
 #endif
 	tdentry = d_alloc(parent, name);
@@ -223,13 +204,6 @@
 		return -ENOMEM;
 	d_move(target, tdentry);
 
-	/* tdentry will have been made a "child" of target (the parent of target)
-	 * make it an IS_ROOT instead
-	 */
-	spin_lock(&dcache_lock);
-	list_del_init(&tdentry->d_child);
-	tdentry->d_parent = tdentry;
-	spin_unlock(&dcache_lock);
 	d_rehash(target);
 	dput(tdentry);
 
@@ -237,9 +211,9 @@
 	 * the children are connected, but it must be a singluar (non-forking)
 	 * branch
 	 */
-	if (!(parent->d_flags & DCACHE_NFSD_DISCONNECTED)) {
+	if (!(parent->d_flags & DCACHE_DISCONNECTED)) {
 		while (target) {
-			target->d_flags &= ~DCACHE_NFSD_DISCONNECTED;
+			target->d_flags &= ~DCACHE_DISCONNECTED;
 			parent = target;
 			spin_lock(&dcache_lock);
 			if (list_empty(&parent->d_subdirs))
@@ -249,9 +223,12 @@
 #ifdef NFSD_PARANOIA
 				/* must be only child */
 				if (target->d_child.next != &parent->d_subdirs
-				    || target->d_child.prev != &parent->d_subdirs)
+				    || target->d_child.prev != &parent->d_subdirs) {
 					printk("nfsd: d_splice found non-singular disconnected branch: %s/%s\n",
 					       parent->d_name.name, target->d_name.name);
+					spin_unlock(&dcache_lock);
+					return 0;
+				}
 #endif
 			}
 			spin_unlock(&dcache_lock);
@@ -261,7 +238,6 @@
 }
 
 /* this routine finds the dentry of the parent of a given directory
- * it should be in the filesystem accessed by nfsd_operations
  * it assumes lookup("..") works.
  */
 struct dentry *nfsd_findparent(struct dentry *child)
@@ -305,7 +281,7 @@
 			pdentry = d_alloc_root(tdentry->d_inode);
 			if (pdentry) {
 				igrab(tdentry->d_inode);
-				pdentry->d_flags |= DCACHE_NFSD_DISCONNECTED;
+				pdentry->d_flags |= DCACHE_DISCONNECTED;
 			}
 		}
 		if (pdentry == NULL)
@@ -412,14 +388,14 @@
 	down(&sb->s_nfsd_free_path_sem);
 	result = nfsd_get_dentry(sb, datap, len, fhtype, 0);
 	if (IS_ERR(result)
-	    || !(result->d_flags & DCACHE_NFSD_DISCONNECTED)
+	    || !(result->d_flags & DCACHE_DISCONNECTED)
 	    || (!S_ISDIR(result->d_inode->i_mode) && ! needpath)) {
 		up(&sb->s_nfsd_free_path_sem);
 	    
 		err = PTR_ERR(result);
 		if (IS_ERR(result))
 			goto err_out;
-		if ((result->d_flags & DCACHE_NFSD_DISCONNECTED))
+		if ((result->d_flags & DCACHE_DISCONNECTED))
 			nfsdstats.fh_anon++;
 		return result;
 	}
@@ -457,7 +433,7 @@
 		dentry = dget(result);
 	}
 
-	while(dentry->d_flags & DCACHE_NFSD_DISCONNECTED) {
+	while(dentry->d_flags & DCACHE_DISCONNECTED) {
 		/* LOOP INVARIANT */
 		/* haven't found a place in the tree yet, but we do have a free path
 		 * from dentry down to result, and dentry is a directory.
@@ -527,6 +503,46 @@
 }
 
 /*
+ * our acceptability function.
+ * if NOSUBTREECHECK, accept anything
+ * if not, require that we can walk up to exp->ex_dentry
+ * doing some checks on the 'x' bits
+ */
+int nfsd_acceptable(void *expv, struct dentry *dentry)
+{
+	struct svc_export *exp = expv;
+	int rv;
+	struct dentry *tdentry;
+
+	if (exp->ex_flags & NFSEXP_NOSUBTREECHECK)
+		return 1;
+
+	dget(dentry);
+	read_lock(&dparent_lock);
+	for (tdentry = dentry;
+	     tdentry != exp->ex_dentry && ! IS_ROOT(tdentry);
+	     (dget(tdentry->d_parent),
+	      dput(tdentry),
+	      tdentry = tdentry->d_parent)
+		) {
+		/* make sure parents give x permission to user */
+		int err;
+		read_unlock(&dparent_lock);
+		err = permission(tdentry->d_parent->d_inode, S_IXOTH);
+		read_lock(&dparent_lock);
+		if (err < 0)
+			break;
+	}
+	read_unlock(&dparent_lock);
+	if (tdentry != exp->ex_dentry)
+		dprintk("nfsd_acceptable failed at %p %s\n", tdentry, tdentry->d_name.name);
+	rv = (tdentry == exp->ex_dentry);
+	dput(tdentry);
+	return rv;
+}
+
+
+/*
  * Perform sanity checks on the dentry in a client's file handle.
  *
  * Note that the file handle dentry may need to be freed even after
@@ -553,6 +569,8 @@
 		kdev_t xdev = NODEV;
 		ino_t xino = 0;
 		__u32 *datap=NULL;
+		__u32 tfh[3];		/* filehandle fragment for oldstyle filehandles */
+		int fileid_type;
 		int data_left = fh->fh_size/4;
 		int nfsdev;
 		int fsid = 0;
@@ -560,8 +578,8 @@
 		error = nfserr_stale;
 		if (rqstp->rq_vers == 3)
 			error = nfserr_badhandle;
+
 		if (fh->fh_version == 1) {
-			
 			datap = fh->fh_auth;
 			if (--data_left<0) goto out;
 			switch (fh->fh_auth_type) {
@@ -602,7 +620,6 @@
 
 		if (!exp) {
 			/* export entry revoked */
-			nfsdstats.fh_stale++;
 			goto out;
 		}
 
@@ -626,27 +643,35 @@
 		if (rqstp->rq_vers == 3)
 			error = nfserr_badhandle;
 
-		if (fh->fh_version == 1) {
-			/* if fileid_type != 0, and super_operations provide fh_to_dentry lookup,
-			 *  then should use that */
-			switch (fh->fh_fileid_type) {
-			case 0:
-				dentry = dget(exp->ex_dentry);
-				break;
-			default:
-				dentry = find_fh_dentry(exp->ex_dentry->d_sb,
-							datap, data_left, fh->fh_fileid_type,
-							!(exp->ex_flags & NFSEXP_NOSUBTREECHECK));
-			}
-		} else {
-			__u32 tfh[3];
+		if (fh->fh_version != 1) {
 			tfh[0] = fh->ofh_ino;
 			tfh[1] = fh->ofh_generation;
 			tfh[2] = fh->ofh_dirino;
-			dentry = find_fh_dentry(exp->ex_dentry->d_sb,
-						tfh, 3, fh->ofh_dirino?2:1,
-						!(exp->ex_flags & NFSEXP_NOSUBTREECHECK));
+			datap = tfh;
+			data_left = 3;
+			if (fh->ofh_dirino == 0)
+				fileid_type = 1;
+			else
+				fileid_type = 2;
+		} else
+			fileid_type = fh->fh_fileid_type;
+		
+		if (fileid_type == 0)
+			dentry = dget(exp->ex_dentry);
+		else {
+			struct export_operations *nop = exp->ex_mnt->mnt_sb->s_export_op;
+			if (nop)
+				dentry = CALL(nop,decode_fh)(exp->ex_mnt->mnt_sb,
+							     datap, data_left,
+							     fileid_type,
+							     nfsd_acceptable, exp);
+			else
+				dentry = find_fh_dentry(exp->ex_dentry->d_sb,
+							datap, data_left, fileid_type,
+							!(exp->ex_flags & NFSEXP_NOSUBTREECHECK));
 		}
+		if (dentry == NULL)
+			goto out;
 		if (IS_ERR(dentry)) {
 			if (PTR_ERR(dentry) != -EINVAL)
 				error = nfserrno(PTR_ERR(dentry));
@@ -654,7 +679,7 @@
 		}
 #ifdef NFSD_PARANOIA
 		if (S_ISDIR(dentry->d_inode->i_mode) &&
-		    (dentry->d_flags & DCACHE_NFSD_DISCONNECTED)) {
+		    (dentry->d_flags & DCACHE_DISCONNECTED)) {
 			printk("nfsd: find_fh_dentry returned a DISCONNECTED directory: %s/%s\n",
 			       dentry->d_parent->d_name.name, dentry->d_name.name);
 		}
@@ -681,7 +706,7 @@
 	 * write call).
 	 */
 
-	/* When is type ever negative? */
+	/* Type can be negative when creating hardlinks - not to a dir */
 	if (type > 0 && (inode->i_mode & S_IFMT) != type) {
 		error = (type == S_IFDIR)? nfserr_notdir : nfserr_isdir;
 		goto out;
@@ -693,10 +718,14 @@
 
 	/*
 	 * Security: Check that the export is valid for dentry <gam3@acm.org>
+	 *  This is only needed with subtree_check, and if export_operations is
+	 *  not being used -  export_operations does the check via  the "acceptable"
+	 *  callback
 	 */
 	error = 0;
 
-	if (!(exp->ex_flags & NFSEXP_NOSUBTREECHECK)) {
+	if (exp->ex_mnt->mnt_sb->s_export_op == NULL &&
+	    !(exp->ex_flags & NFSEXP_NOSUBTREECHECK)) {
 		if (exp->ex_dentry != dentry) {
 			struct dentry *tdentry = dentry;
 
@@ -718,13 +747,11 @@
 				    && !(tdentry->d_inode->i_mode & S_IXOTH)
 					) {
 					error = nfserr_stale;
-					nfsdstats.fh_stale++;
 					dprintk("fh_verify: no root_squashed access.\n");
 				}
 			} while ((tdentry != tdentry->d_parent));
 			if (exp->ex_dentry != tdentry) {
 				error = nfserr_stale;
-				nfsdstats.fh_stale++;
 				printk("nfsd Security: %s/%s bad export.\n",
 				       dentry->d_parent->d_name.name,
 				       dentry->d_name.name);
@@ -746,9 +773,12 @@
 	}
 #endif
 out:
+	if (error == nfserr_stale)
+		nfsdstats.fh_stale++;
 	return error;
 }
 
+
 /*
  * Compose a file handle for an NFS reply.
  *
@@ -759,6 +789,7 @@
 inline int _fh_update(struct dentry *dentry, struct svc_export *exp,
 		      __u32 *datap, int *maxsize)
 {
+	struct export_operations *nop = exp->ex_mnt->mnt_sb->s_export_op;
 	struct super_block *sb = dentry->d_sb;
 	
 	if (dentry == exp->ex_dentry) {
@@ -766,6 +797,10 @@
 		return 0;
 	}
 
+	if (nop)
+		return CALL(nop,encode_fh)(dentry, datap, maxsize,
+			  !(exp->ex_flags&NFSEXP_NOSUBTREECHECK));
+
 	if (sb->s_op->dentry_to_fh) {
 		int need_parent = !S_ISDIR(dentry->d_inode->i_mode) &&
 			!(exp->ex_flags & NFSEXP_NOSUBTREECHECK);
@@ -870,11 +905,11 @@
 				_fh_update(dentry, exp, datap, &size);
 			fhp->fh_handle.fh_size += size*4;
 		}
+		if (fhp->fh_handle.fh_fileid_type == 255)
+			return nfserr_opnotsupp;
 	}
 
 	nfsd_nr_verified++;
-	if (fhp->fh_handle.fh_fileid_type == 255)
-		return nfserr_opnotsupp;
 	return 0;
 }
 
@@ -906,6 +941,8 @@
 		fhp->fh_handle.fh_fileid_type =
 			_fh_update(dentry, fhp->fh_export, datap, &size);
 		fhp->fh_handle.fh_size += size*4;
+		if (fhp->fh_handle.fh_fileid_type == 255)
+			return nfserr_opnotsupp;
 	}
 out:
 	return 0;
@@ -938,3 +975,4 @@
 	}
 	return;
 }
+
diff -Nru a/fs/nls/nls_base.c b/fs/nls/nls_base.c
--- a/fs/nls/nls_base.c	Mon Apr 22 15:32:18 2002
+++ b/fs/nls/nls_base.c	Mon Apr 22 15:32:18 2002
@@ -1,5 +1,5 @@
 /*
- * linux/fs/nls.c
+ * linux/fs/nls_base.c
  *
  * Native language support--charsets and unicode translations.
  * By Gordon Chaffee 1996, 1997
@@ -93,7 +93,7 @@
 				ip++;
 				n--;
 			} else {
-				op += size;
+				op++;
 				ip += size;
 				n -= size;
 			}
diff -Nru a/fs/proc/base.c b/fs/proc/base.c
--- a/fs/proc/base.c	Mon Apr 22 15:32:18 2002
+++ b/fs/proc/base.c	Mon Apr 22 15:32:18 2002
@@ -37,11 +37,62 @@
 
 #define fake_ino(pid,ino) (((pid)<<16)|(ino))
 
+enum pid_directory_inos {
+	PROC_PID_INO = 2,
+	PROC_PID_STATUS,
+	PROC_PID_MEM,
+	PROC_PID_CWD,
+	PROC_PID_ROOT,
+	PROC_PID_EXE,
+	PROC_PID_FD,
+	PROC_PID_ENVIRON,
+	PROC_PID_CMDLINE,
+	PROC_PID_STAT,
+	PROC_PID_STATM,
+	PROC_PID_MAPS,
+	PROC_PID_CPU,
+	PROC_PID_MOUNTS,
+	PROC_PID_FD_DIR = 0x8000,	/* 0x8000-0xffff */
+};
+
+struct pid_entry {
+	int type;
+	int len;
+	char *name;
+	mode_t mode;
+};
+
+#define E(type,name,mode) {(type),sizeof(name)-1,(name),(mode)}
+static struct pid_entry base_stuff[] = {
+  E(PROC_PID_FD,	"fd",		S_IFDIR|S_IRUSR|S_IXUSR),
+  E(PROC_PID_ENVIRON,	"environ",	S_IFREG|S_IRUSR),
+  E(PROC_PID_STATUS,	"status",	S_IFREG|S_IRUGO),
+  E(PROC_PID_CMDLINE,	"cmdline",	S_IFREG|S_IRUGO),
+  E(PROC_PID_STAT,	"stat",		S_IFREG|S_IRUGO),
+  E(PROC_PID_STATM,	"statm",	S_IFREG|S_IRUGO),
+#ifdef CONFIG_SMP
+  E(PROC_PID_CPU,	"cpu",		S_IFREG|S_IRUGO),
+#endif
+  E(PROC_PID_MAPS,	"maps",		S_IFREG|S_IRUGO),
+  E(PROC_PID_MEM,	"mem",		S_IFREG|S_IRUSR|S_IWUSR),
+  E(PROC_PID_CWD,	"cwd",		S_IFLNK|S_IRWXUGO),
+  E(PROC_PID_ROOT,	"root",		S_IFLNK|S_IRWXUGO),
+  E(PROC_PID_EXE,	"exe",		S_IFLNK|S_IRWXUGO),
+  E(PROC_PID_MOUNTS,	"mounts",	S_IFREG|S_IRUGO),
+  {0,0,NULL,0}
+};
+#undef E
+
 static inline struct task_struct *proc_task(struct inode *inode)
 {
 	return PROC_I(inode)->task;
 }
 
+static inline int proc_type(struct inode *inode)
+{
+	return PROC_I(inode)->type;
+}
+
 ssize_t proc_pid_read_maps(struct task_struct*,struct file*,char*,size_t,loff_t*);
 int proc_pid_stat(struct task_struct*,char*);
 int proc_pid_status(struct task_struct*,char*);
@@ -50,11 +101,28 @@
 
 static int proc_fd_link(struct inode *inode, struct dentry **dentry, struct vfsmount **mnt)
 {
-	struct file *file = PROC_I(inode)->file;
-	if (file) {
-		*mnt = mntget(file->f_vfsmnt);
-		*dentry = dget(file->f_dentry);
-		return 0;
+	struct task_struct *task = proc_task(inode);
+	struct files_struct *files;
+	struct file *file;
+	int fd = proc_type(inode) - PROC_PID_FD_DIR;
+
+	task_lock(task);
+	files = task->files;
+	if (files)
+		atomic_inc(&files->count);
+	task_unlock(task);
+	if (files) {
+		read_lock(&files->file_lock);
+		file = fcheck_files(files, fd);
+		if (file) {
+			*mnt = mntget(file->f_vfsmnt);
+			*dentry = dget(file->f_dentry);
+			read_unlock(&files->file_lock);
+			put_files_struct(files);
+			return 0;
+		}
+		read_unlock(&files->file_lock);
+		put_files_struct(files);
 	}
 	return -ENOENT;
 }
@@ -525,52 +593,6 @@
 	follow_link:	proc_pid_follow_link
 };
 
-struct pid_entry {
-	int type;
-	int len;
-	char *name;
-	mode_t mode;
-};
-
-enum pid_directory_inos {
-	PROC_PID_INO = 2,
-	PROC_PID_STATUS,
-	PROC_PID_MEM,
-	PROC_PID_CWD,
-	PROC_PID_ROOT,
-	PROC_PID_EXE,
-	PROC_PID_FD,
-	PROC_PID_ENVIRON,
-	PROC_PID_CMDLINE,
-	PROC_PID_STAT,
-	PROC_PID_STATM,
-	PROC_PID_MAPS,
-	PROC_PID_CPU,
-	PROC_PID_MOUNTS,
-	PROC_PID_FD_DIR = 0x8000,	/* 0x8000-0xffff */
-};
-
-#define E(type,name,mode) {(type),sizeof(name)-1,(name),(mode)}
-static struct pid_entry base_stuff[] = {
-  E(PROC_PID_FD,	"fd",		S_IFDIR|S_IRUSR|S_IXUSR),
-  E(PROC_PID_ENVIRON,	"environ",	S_IFREG|S_IRUSR),
-  E(PROC_PID_STATUS,	"status",	S_IFREG|S_IRUGO),
-  E(PROC_PID_CMDLINE,	"cmdline",	S_IFREG|S_IRUGO),
-  E(PROC_PID_STAT,	"stat",		S_IFREG|S_IRUGO),
-  E(PROC_PID_STATM,	"statm",	S_IFREG|S_IRUGO),
-#ifdef CONFIG_SMP
-  E(PROC_PID_CPU,	"cpu",		S_IFREG|S_IRUGO),
-#endif
-  E(PROC_PID_MAPS,	"maps",		S_IFREG|S_IRUGO),
-  E(PROC_PID_MEM,	"mem",		S_IFREG|S_IRUSR|S_IWUSR),
-  E(PROC_PID_CWD,	"cwd",		S_IFLNK|S_IRWXUGO),
-  E(PROC_PID_ROOT,	"root",		S_IFLNK|S_IRWXUGO),
-  E(PROC_PID_EXE,	"exe",		S_IFLNK|S_IRWXUGO),
-  E(PROC_PID_MOUNTS,	"mounts",	S_IFREG|S_IRUGO),
-  {0,0,NULL,0}
-};
-#undef E
-
 #define NUMBUF 10
 
 static int proc_readfd(struct file * filp, void * dirent, filldir_t filldir)
@@ -707,7 +729,6 @@
 	/* Common stuff */
 	ei = PROC_I(inode);
 	ei->task = NULL;
-	ei->file = NULL;
 	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
 	inode->i_ino = fake_ino(task->pid, ino);
 
@@ -719,6 +740,7 @@
 	 */
 	get_task_struct(task);
 	ei->task = task;
+	ei->type = ino;
 	inode->i_uid = 0;
 	inode->i_gid = 0;
 	if (ino == PROC_PID_INO || task_dumpable(task)) {
@@ -737,17 +759,12 @@
 
 /* dentry stuff */
 
-static int pid_fd_revalidate(struct dentry * dentry, int flags)
-{
-	return 0;
-}
-
 /*
  *	Exceptional case: normally we are not allowed to unhash a busy
  * directory. In this case, however, we can do it - no aliasing problems
  * due to the way we treat inodes.
  */
-static int pid_base_revalidate(struct dentry * dentry, int flags)
+static int pid_revalidate(struct dentry * dentry, int flags)
 {
 	if (proc_task(dentry->d_inode)->pid)
 		return 1;
@@ -755,9 +772,44 @@
 	return 0;
 }
 
+static int pid_fd_revalidate(struct dentry * dentry, int flags)
+{
+	struct task_struct *task = proc_task(dentry->d_inode);
+	int fd = proc_type(dentry->d_inode) - PROC_PID_FD_DIR;
+	struct files_struct *files;
+
+	task_lock(task);
+	files = task->files;
+	if (files)
+		atomic_inc(&files->count);
+	task_unlock(task);
+	if (files) {
+		read_lock(&files->file_lock);
+		if (fcheck_files(files, fd)) {
+			read_unlock(&files->file_lock);
+			put_files_struct(files);
+			return 1;
+		}
+		read_unlock(&files->file_lock);
+		put_files_struct(files);
+	}
+	d_drop(dentry);
+	return 0;
+}
+
+static void pid_base_iput(struct dentry *dentry, struct inode *inode)
+{
+	struct task_struct *task = proc_task(inode);
+	write_lock_irq(&tasklist_lock);
+	if (task->proc_dentry == dentry)
+		task->proc_dentry = NULL;
+	write_unlock_irq(&tasklist_lock);
+	iput(inode);
+}
+
 static int pid_delete_dentry(struct dentry * dentry)
 {
-	return 1;
+	return proc_task(dentry->d_inode)->pid == 0;
 }
 
 static struct dentry_operations pid_fd_dentry_operations =
@@ -768,44 +820,53 @@
 
 static struct dentry_operations pid_dentry_operations =
 {
+	d_revalidate:	pid_revalidate,
 	d_delete:	pid_delete_dentry,
 };
 
 static struct dentry_operations pid_base_dentry_operations =
 {
-	d_revalidate:	pid_base_revalidate,
+	d_revalidate:	pid_revalidate,
+	d_iput:		pid_base_iput,
 	d_delete:	pid_delete_dentry,
 };
 
 /* Lookups */
-#define MAX_MULBY10	((~0U-9)/10)
+
+static unsigned name_to_int(struct dentry *dentry)
+{
+	const char *name = dentry->d_name.name;
+	int len = dentry->d_name.len;
+	unsigned n = 0;
+
+	if (len > 1 && *name == '0')
+		goto out;
+	while (len-- > 0) {
+		unsigned c = *name++ - '0';
+		if (c > 9)
+			goto out;
+		if (n >= (~0U-9)/10)
+			goto out;
+		n *= 10;
+		n += c;
+	}
+	return n;
+out:
+	return ~0U;
+}
 
 /* SMP-safe */
 static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry)
 {
-	unsigned int fd, c;
 	struct task_struct *task = proc_task(dir);
+	unsigned fd = name_to_int(dentry);
 	struct file * file;
 	struct files_struct * files;
 	struct inode *inode;
 	struct proc_inode *ei;
-	const char *name;
-	int len;
 
-	fd = 0;
-	len = dentry->d_name.len;
-	name = dentry->d_name.name;
-	if (len > 1 && *name == '0') goto out;
-	while (len-- > 0) {
-		c = *name - '0';
-		name++;
-		if (c > 9)
-			goto out;
-		if (fd >= MAX_MULBY10)
-			goto out;
-		fd *= 10;
-		fd += c;
-	}
+	if (fd == ~0U)
+		goto out;
 
 	inode = proc_pid_make_inode(dir->i_sb, task, PROC_PID_FD_DIR+fd);
 	if (!inode)
@@ -818,28 +879,29 @@
 	task_unlock(task);
 	if (!files)
 		goto out_unlock;
+	inode->i_mode = S_IFLNK;
 	read_lock(&files->file_lock);
-	file = ei->file = fcheck_files(files, fd);
+	file = fcheck_files(files, fd);
 	if (!file)
 		goto out_unlock2;
-	get_file(file);
+	if (file->f_mode & 1)
+		inode->i_mode |= S_IRUSR | S_IXUSR;
+	if (file->f_mode & 2)
+		inode->i_mode |= S_IWUSR | S_IXUSR;
 	read_unlock(&files->file_lock);
 	put_files_struct(files);
 	inode->i_op = &proc_pid_link_inode_operations;
 	inode->i_size = 64;
-	inode->i_mode = S_IFLNK;
 	ei->op.proc_get_link = proc_fd_link;
-	if (file->f_mode & 1)
-		inode->i_mode |= S_IRUSR | S_IXUSR;
-	if (file->f_mode & 2)
-		inode->i_mode |= S_IWUSR | S_IXUSR;
 	dentry->d_op = &pid_fd_dentry_operations;
 	d_add(dentry, inode);
+	if (!proc_task(dentry->d_inode)->pid)
+		d_drop(dentry);
 	return NULL;
 
 out_unlock2:
-	put_files_struct(files);
 	read_unlock(&files->file_lock);
+	put_files_struct(files);
 out_unlock:
 	iput(inode);
 out:
@@ -952,6 +1014,8 @@
 	}
 	dentry->d_op = &pid_dentry_operations;
 	d_add(dentry, inode);
+	if (!proc_task(dentry->d_inode)->pid)
+		d_drop(dentry);
 	return NULL;
 
 out:
@@ -992,17 +1056,12 @@
 /* SMP-safe */
 struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry)
 {
-	unsigned int pid, c;
 	struct task_struct *task;
-	const char *name;
 	struct inode *inode;
 	struct proc_inode *ei;
-	int len;
+	unsigned pid;
 
-	pid = 0;
-	name = dentry->d_name.name;
-	len = dentry->d_name.len;
-	if (len == 4 && !memcmp(name, "self", 4)) {
+	if (dentry->d_name.len == 4 && !memcmp(dentry->d_name.name,"self",4)) {
 		inode = new_inode(dir->i_sb);
 		if (!inode)
 			return ERR_PTR(-ENOMEM);
@@ -1017,18 +1076,9 @@
 		d_add(dentry, inode);
 		return NULL;
 	}
-	while (len-- > 0) {
-		c = *name - '0';
-		name++;
-		if (c > 9)
-			goto out;
-		if (pid >= MAX_MULBY10)
-			goto out;
-		pid *= 10;
-		pid += c;
-		if (!pid)
-			goto out;
-	}
+	pid = name_to_int(dentry);
+	if (pid == ~0U)
+		goto out;
 
 	read_lock(&tasklist_lock);
 	task = find_task_by_pid(pid);
@@ -1052,6 +1102,11 @@
 
 	dentry->d_op = &pid_base_dentry_operations;
 	d_add(dentry, inode);
+	read_lock(&tasklist_lock);
+	proc_task(dentry->d_inode)->proc_dentry = dentry;
+	read_unlock(&tasklist_lock);
+	if (!proc_task(dentry->d_inode)->pid)
+		d_drop(dentry);
 	return NULL;
 out:
 	return ERR_PTR(-ENOENT);
@@ -1059,8 +1114,6 @@
 
 void proc_pid_delete_inode(struct inode *inode)
 {
-	if (PROC_I(inode)->file)
-		fput(PROC_I(inode)->file);
 	if (proc_task(inode))
 		put_task_struct(proc_task(inode));
 }
diff -Nru a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c
--- a/fs/reiserfs/inode.c	Mon Apr 22 15:32:19 2002
+++ b/fs/reiserfs/inode.c	Mon Apr 22 15:32:19 2002
@@ -1249,7 +1249,6 @@
 				     int len, int fhtype, int parent) {
     struct cpu_key key ;
     struct inode *inode = NULL ;
-    struct list_head *lp;
     struct dentry *result;
 
     /* fhtype happens to reflect the number of u32s encoded.
@@ -1304,24 +1303,12 @@
     /* now to find a dentry.
      * If possible, get a well-connected one
      */
-    spin_lock(&dcache_lock);
-    list_for_each(lp, &inode->i_dentry) {
-	    result = list_entry(lp,struct dentry, d_alias);
-	    if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED)) {
-		    dget_locked(result);
-		    result->d_vfs_flags |= DCACHE_REFERENCED;
-		    spin_unlock(&dcache_lock);
-		    iput(inode);
-		    return result;
-	    }
-    }
-    spin_unlock(&dcache_lock);
-    result = d_alloc_root(inode);
+    result = d_alloc_anon(inode);
     if (result == NULL) {
 	    iput(inode);
 	    return ERR_PTR(-ENOMEM);
     }
-    result->d_flags |= DCACHE_NFSD_DISCONNECTED;
+    result->d_vfs_flags |= DCACHE_REFERENCED;
     return result;
 
 }
diff -Nru a/fs/super.c b/fs/super.c
--- a/fs/super.c	Mon Apr 22 15:32:19 2002
+++ b/fs/super.c	Mon Apr 22 15:32:19 2002
@@ -51,6 +51,7 @@
 		INIT_LIST_HEAD(&s->s_locked_inodes);
 		INIT_LIST_HEAD(&s->s_files);
 		INIT_LIST_HEAD(&s->s_instances);
+		INIT_LIST_HEAD(&s->s_anon);
 		init_rwsem(&s->s_umount);
 		sema_init(&s->s_lock, 1);
 		down_write(&s->s_umount);
@@ -192,6 +193,7 @@
 	if (root) {
 		sb->s_root = NULL;
 		shrink_dcache_parent(root);
+		shrink_dcache_anon(&sb->s_anon);
 		dput(root);
 		fsync_super(sb);
 		lock_super(sb);
diff -Nru a/fs/sysv/itree.c b/fs/sysv/itree.c
--- a/fs/sysv/itree.c	Mon Apr 22 15:32:19 2002
+++ b/fs/sysv/itree.c	Mon Apr 22 15:32:19 2002
@@ -108,6 +108,7 @@
 	return NULL;
 
 changed:
+	brelse(bh);
 	*err = -EAGAIN;
 	goto no_block;
 failure:
diff -Nru a/include/asm-alpha/mman.h b/include/asm-alpha/mman.h
--- a/include/asm-alpha/mman.h	Mon Apr 22 15:32:18 2002
+++ b/include/asm-alpha/mman.h	Mon Apr 22 15:32:18 2002
@@ -4,6 +4,7 @@
 #define PROT_READ	0x1		/* page can be read */
 #define PROT_WRITE	0x2		/* page can be written */
 #define PROT_EXEC	0x4		/* page can be executed */
+#define PROT_SEM	0x8		/* page may be used for atomic ops */
 #define PROT_NONE	0x0		/* page can not be accessed */
 
 #define MAP_SHARED	0x01		/* Share changes */
diff -Nru a/include/asm-alpha/pgalloc.h b/include/asm-alpha/pgalloc.h
--- a/include/asm-alpha/pgalloc.h	Mon Apr 22 15:32:18 2002
+++ b/include/asm-alpha/pgalloc.h	Mon Apr 22 15:32:18 2002
@@ -12,7 +12,7 @@
 static inline void
 pmd_populate(struct mm_struct *mm, pmd_t *pmd, struct page *pte)
 {
-	pmd_set(pmd, (pte_t *)((pte - mem_map) << PAGE_SHIFT));
+	pmd_set(pmd, (pte_t *)(((pte - mem_map) << PAGE_SHIFT) + PAGE_OFFSET));
 }
 
 static inline void
diff -Nru a/include/asm-alpha/siginfo.h b/include/asm-alpha/siginfo.h
--- a/include/asm-alpha/siginfo.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-alpha/siginfo.h	Mon Apr 22 15:32:19 2002
@@ -108,6 +108,7 @@
 #define SI_ASYNCIO	-4		/* sent by AIO completion */
 #define SI_SIGIO	-5		/* sent by queued SIGIO */
 #define SI_TKILL	-6		/* sent by tkill system call */
+#define SI_DETHREAD	-7		/* sent by execve() killing subsidiary threads */
 
 #define SI_FROMUSER(siptr)	((siptr)->si_code <= 0)
 #define SI_FROMKERNEL(siptr)	((siptr)->si_code > 0)
diff -Nru a/include/asm-alpha/thread_info.h b/include/asm-alpha/thread_info.h
--- a/include/asm-alpha/thread_info.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-alpha/thread_info.h	Mon Apr 22 15:32:19 2002
@@ -20,6 +20,7 @@
 	struct exec_domain	*exec_domain;	/* execution domain */
 	mm_segment_t		addr_limit;	/* thread address space */
 	int			cpu;		/* current CPU */
+	int			preempt_count; /* 0 => preemptable, <0 => BUG */
 
 	int bpt_nsaved;
 	unsigned long bpt_addr[2];		/* breakpoint handling  */
@@ -52,6 +53,8 @@
 #define put_thread_info(ti) put_task_struct((ti)->task)
 
 #endif /* __ASSEMBLY__ */
+
+#define PREEMPT_ACTIVE		0x4000000
 
 /*
  * Thread information flags:
diff -Nru a/include/asm-alpha/unistd.h b/include/asm-alpha/unistd.h
--- a/include/asm-alpha/unistd.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-alpha/unistd.h	Mon Apr 22 15:32:19 2002
@@ -528,6 +528,7 @@
 
 static inline long close(int fd)
 {
+	extern long sys_close(unsigned int);
 	return sys_close(fd);
 }
 
@@ -603,6 +604,6 @@
  * What we want is __attribute__((weak,alias("sys_ni_syscall"))),
  * but it doesn't work on all toolchains, so we just do it by hand
  */
-#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall");
+#define cond_syscall(x) asmlinkage long x(void) __attribute__((weak,alias("sys_ni_syscall")));
 
 #endif /* _ALPHA_UNISTD_H */
diff -Nru a/include/asm-arm/arch-adifcc/system.h b/include/asm-arm/arch-adifcc/system.h
--- a/include/asm-arm/arch-adifcc/system.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-arm/arch-adifcc/system.h	Mon Apr 22 15:32:19 2002
@@ -11,8 +11,7 @@
 static inline void arch_idle(void)
 {
 #if 0
-	if (!hlt_counter)
-		cpu_do_idle(0);
+	cpu_do_idle();
 #endif
 }
 
diff -Nru a/include/asm-arm/arch-anakin/system.h b/include/asm-arm/arch-anakin/system.h
--- a/include/asm-arm/arch-anakin/system.h	Mon Apr 22 15:32:18 2002
+++ b/include/asm-arm/arch-anakin/system.h	Mon Apr 22 15:32:18 2002
@@ -15,8 +15,7 @@
 #ifndef __ASM_ARCH_SYSTEM_H
 #define __ASM_ARCH_SYSTEM_H
 
-static inline void
-arch_idle(void)
+static inline void arch_idle(void)
 {
 }
 
diff -Nru a/include/asm-arm/arch-arc/system.h b/include/asm-arm/arch-arc/system.h
--- a/include/asm-arm/arch-arc/system.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-arm/arch-arc/system.h	Mon Apr 22 15:32:19 2002
@@ -8,9 +8,8 @@
  * published by the Free Software Foundation.
  */
 
-static void arch_idle(void)
+static inline void arch_idle(void)
 {
-	while (!need_resched() && !hlt_counter);
 }
 
 static inline void arch_reset(char mode)
diff -Nru a/include/asm-arm/arch-cl7500/system.h b/include/asm-arm/arch-cl7500/system.h
--- a/include/asm-arm/arch-cl7500/system.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-arm/arch-cl7500/system.h	Mon Apr 22 15:32:19 2002
@@ -7,11 +7,11 @@
 #define __ASM_ARCH_SYSTEM_H
 
 #include <asm/hardware/iomd.h>
+#include <asm/io.h>
 
-static void arch_idle(void)
+static inline void arch_idle(void)
 {
-	while (!need_resched() && !hlt_counter)
-		iomd_writeb(0, IOMD_SUSMODE);
+	iomd_writeb(0, IOMD_SUSMODE);
 }
 
 #define arch_reset(mode)			\
diff -Nru a/include/asm-arm/arch-clps711x/system.h b/include/asm-arm/arch-clps711x/system.h
--- a/include/asm-arm/arch-clps711x/system.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-arm/arch-clps711x/system.h	Mon Apr 22 15:32:19 2002
@@ -22,7 +22,7 @@
 
 #include <asm/hardware/clps7111.h>
 
-static void arch_idle(void)
+static inline void arch_idle(void)
 {
 	clps_writel(1, HALT);
 	__asm__ __volatile__(
diff -Nru a/include/asm-arm/arch-ebsa110/system.h b/include/asm-arm/arch-ebsa110/system.h
--- a/include/asm-arm/arch-ebsa110/system.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-arm/arch-ebsa110/system.h	Mon Apr 22 15:32:19 2002
@@ -20,31 +20,18 @@
  * Instead, we spin, polling the IRQ_STAT register for the occurrence
  * of any interrupt with core clock down to the memory clock.
  */
-static void arch_idle(void)
+static inline void arch_idle(void)
 {
 	const char *irq_stat = (char *)0xff000000;
-	long flags;
 
-	if (!hlt_counter)
-		return;
+	/* disable clock switching */
+	asm volatile ("mcr%? p15, 0, ip, c15, c2, 2");
 
-	do {
-		/* disable interrupts */
-		cli();
-		/* check need_resched here to avoid races */
-		if (need_resched()) {
-			sti();
-			return;
-		}
-		/* disable clock switching */
-		asm volatile ("mcr%? p15, 0, ip, c15, c2, 2");
-		/* wait for an interrupt to occur */
-		while (!*irq_stat);
-		/* enable clock switching */
-		asm volatile ("mcr%? p15, 0, ip, c15, c1, 2");
-		/* allow the interrupt to happen */
-		sti();
-	} while (!need_resched());
+	/* wait for an interrupt to occur */
+	while (!*irq_stat);
+
+	/* enable clock switching */
+	asm volatile ("mcr%? p15, 0, ip, c15, c1, 2");
 }
 
 #define arch_reset(mode)	cpu_reset(0x80000000)
diff -Nru a/include/asm-arm/arch-ebsa285/system.h b/include/asm-arm/arch-ebsa285/system.h
--- a/include/asm-arm/arch-ebsa285/system.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-arm/arch-ebsa285/system.h	Mon Apr 22 15:32:19 2002
@@ -13,26 +13,9 @@
 #include <asm/leds.h>
 #include <asm/mach-types.h>
 
-static void arch_idle(void)
+static inline void arch_idle(void)
 {
-	unsigned long start_idle;
-
-	start_idle = jiffies;
-
-	do {
-		if (need_resched() || hlt_counter)
-			goto slow_out;
-		cpu_do_idle(IDLE_WAIT_FAST);
-	} while (time_before(jiffies, start_idle + HZ/50));
-
-	cpu_do_idle(IDLE_CLOCK_SLOW);
-
-	while (!need_resched() && !hlt_counter) {
-		cpu_do_idle(IDLE_WAIT_SLOW);
-	}
-
-	cpu_do_idle(IDLE_CLOCK_FAST);
-slow_out:
+	cpu_do_idle();
 }
 
 static inline void arch_reset(char mode)
diff -Nru a/include/asm-arm/arch-epxa10db/system.h b/include/asm-arm/arch-epxa10db/system.h
--- a/include/asm-arm/arch-epxa10db/system.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-arm/arch-epxa10db/system.h	Mon Apr 22 15:32:19 2002
@@ -24,13 +24,13 @@
 
 #include <asm/arch/platform.h>
 
-static void arch_idle(void)
+static inline void arch_idle(void)
 {
 	/*
 	 * This should do all the clock switching
 	 * and wait for interrupt tricks
 	 */
-	cpu_do_idle(0);
+	cpu_do_idle();
 }
 
 extern __inline__ void arch_reset(char mode)
diff -Nru a/include/asm-arm/arch-integrator/system.h b/include/asm-arm/arch-integrator/system.h
--- a/include/asm-arm/arch-integrator/system.h	Mon Apr 22 15:32:18 2002
+++ b/include/asm-arm/arch-integrator/system.h	Mon Apr 22 15:32:18 2002
@@ -23,13 +23,13 @@
 
 #include <asm/arch/platform.h>
 
-static void arch_idle(void)
+static inline void arch_idle(void)
 {
 	/*
 	 * This should do all the clock switching
 	 * and wait for interrupt tricks
 	 */
-	cpu_do_idle(0);
+	cpu_do_idle();
 }
 
 static inline void arch_reset(char mode)
diff -Nru a/include/asm-arm/arch-iop310/system.h b/include/asm-arm/arch-iop310/system.h
--- a/include/asm-arm/arch-iop310/system.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-arm/arch-iop310/system.h	Mon Apr 22 15:32:19 2002
@@ -10,10 +10,7 @@
 
 static inline void arch_idle(void)
 {
-	if (!hlt_counter)
-	{
-		cpu_do_idle(0);
-	}
+	cpu_do_idle();
 }
 
 
diff -Nru a/include/asm-arm/arch-l7200/system.h b/include/asm-arm/arch-l7200/system.h
--- a/include/asm-arm/arch-l7200/system.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-arm/arch-l7200/system.h	Mon Apr 22 15:32:19 2002
@@ -12,9 +12,8 @@
 #ifndef __ASM_ARCH_SYSTEM_H
 #define __ASM_ARCH_SYSTEM_H
 
-static void arch_idle(void)
+static inline void arch_idle(void)
 {
-	/* fixme: this needs to be cleaned up (converted from ASM code) --rmk */
 	*(unsigned long *)(IO_BASE + 0x50004) = 1;	/* idle mode */
 }
 
diff -Nru a/include/asm-arm/arch-nexuspci/system.h b/include/asm-arm/arch-nexuspci/system.h
--- a/include/asm-arm/arch-nexuspci/system.h	Mon Apr 22 15:32:18 2002
+++ b/include/asm-arm/arch-nexuspci/system.h	Mon Apr 22 15:32:18 2002
@@ -14,10 +14,9 @@
 #ifndef __ASM_ARCH_SYSTEM_H
 #define __ASM_ARCH_SYSTEM_H
 
-static void arch_idle(void)
+static inline void arch_idle(void)
 {
-	while (!need_resched() && !hlt_counter)
-		cpu_do_idle(IDLE_WAIT_SLOW);
+	cpu_do_idle();
 }
 
 #define arch_reset(mode)	do { } while (0)
diff -Nru a/include/asm-arm/arch-pxa/system.h b/include/asm-arm/arch-pxa/system.h
--- a/include/asm-arm/arch-pxa/system.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-arm/arch-pxa/system.h	Mon Apr 22 15:32:19 2002
@@ -14,13 +14,7 @@
 
 static inline void arch_idle(void)
 {
-	if (!hlt_counter) {
-		int flags;
-		local_irq_save(flags);
-		if(!need_resched())
-			cpu_do_idle(0);
-		local_irq_restore(flags);
-	}
+	cpu_do_idle();
 }
 
 
diff -Nru a/include/asm-arm/arch-rpc/system.h b/include/asm-arm/arch-rpc/system.h
--- a/include/asm-arm/arch-rpc/system.h	Mon Apr 22 15:32:18 2002
+++ b/include/asm-arm/arch-rpc/system.h	Mon Apr 22 15:32:18 2002
@@ -11,26 +11,9 @@
 #include <asm/hardware/iomd.h>
 #include <asm/io.h>
 
-static void arch_idle(void)
+static inline void arch_idle(void)
 {
-	unsigned long start_idle;
-
-	start_idle = jiffies;
-
-	do {
-		if (need_resched() || hlt_counter)
-			goto slow_out;
-		cpu_do_idle(IDLE_WAIT_FAST);
-	} while (time_before(jiffies, start_idle + HZ/50));
-
-	cpu_do_idle(IDLE_CLOCK_SLOW);
-
-	while (!need_resched() && !hlt_counter) {
-		cpu_do_idle(IDLE_WAIT_SLOW);
-	}
-
-	cpu_do_idle(IDLE_CLOCK_FAST);
-slow_out:
+	cpu_do_idle();
 }
 
 static inline void arch_reset(char mode)
diff -Nru a/include/asm-arm/arch-sa1100/system.h b/include/asm-arm/arch-sa1100/system.h
--- a/include/asm-arm/arch-sa1100/system.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-arm/arch-sa1100/system.h	Mon Apr 22 15:32:19 2002
@@ -7,13 +7,7 @@
 
 static inline void arch_idle(void)
 {
-	if (!hlt_counter) {
-		int flags;
-		local_irq_save(flags);
-		if (!need_resched())
-			cpu_do_idle(0);
-		local_irq_restore(flags);
-	}
+	cpu_do_idle();
 }
 
 #ifdef CONFIG_SA1100_VICTOR
diff -Nru a/include/asm-arm/arch-shark/memory.h b/include/asm-arm/arch-shark/memory.h
--- a/include/asm-arm/arch-shark/memory.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-arm/arch-shark/memory.h	Mon Apr 22 15:32:19 2002
@@ -38,6 +38,8 @@
 #define __bus_to_virt__is_a_macro
 #define __bus_to_virt(x)	__phys_to_virt(x)
 
+#define isa_virt_to_bus		virt_to_bus
+
 #define PHYS_TO_NID(addr)	(0)
 
 #endif
diff -Nru a/include/asm-arm/arch-shark/system.h b/include/asm-arm/arch-shark/system.h
--- a/include/asm-arm/arch-shark/system.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-arm/arch-shark/system.h	Mon Apr 22 15:32:19 2002
@@ -21,7 +21,7 @@
 
 }
 
-static void arch_idle(void)
+static inline void arch_idle(void)
 {
 }
 
diff -Nru a/include/asm-arm/arch-tbox/system.h b/include/asm-arm/arch-tbox/system.h
--- a/include/asm-arm/arch-tbox/system.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-arm/arch-tbox/system.h	Mon Apr 22 15:32:19 2002
@@ -6,26 +6,9 @@
 #ifndef __ASM_ARCH_SYSTEM_H
 #define __ASM_ARCH_SYSTEM_H
 
-static void arch_idle(void)
+static inline void arch_idle(void)
 {
-	unsigned long start_idle;
-
-	start_idle = jiffies;
-
-	do {
-		if (need_resched() || hlt_counter)
-			goto slow_out;
-		cpu_do_idle(IDLE_WAIT_FAST);
-	} while (time_before(jiffies, start_idle + HZ/50));
-
-	cpu_do_idle(IDLE_CLOCK_SLOW);
-
-	while (!need_resched() && !hlt_counter) {
-		cpu_do_idle(IDLE_WAIT_SLOW);
-	}
-
-	cpu_do_idle(IDLE_CLOCK_FAST);
-slow_out:
+	cpu_do_idle();
 }
 
 #define arch_reset(mode)	do { } while (0)
diff -Nru a/include/asm-arm/cacheflush.h b/include/asm-arm/cacheflush.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/include/asm-arm/cacheflush.h	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,15 @@
+/*
+ *  linux/include/asm-arm/cacheflush.h
+ *
+ *  Copyright (C) 2000-2002 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef _ASMARM_CACHEFLUSH_H
+#define _ASMARM_CACHEFLUSH_H
+
+#include <asm/proc/cache.h>
+
+#endif
diff -Nru a/include/asm-arm/cpu-multi26.h b/include/asm-arm/cpu-multi26.h
--- a/include/asm-arm/cpu-multi26.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-arm/cpu-multi26.h	Mon Apr 22 15:32:19 2002
@@ -40,7 +40,7 @@
 #define cpu_proc_init()				processor._proc_init()
 #define cpu_proc_fin()				processor._proc_fin()
 #define cpu_do_idle()				do { } while (0)
-#define cpu_switch_mm(pgd,tsk)			processor._set_pgd(pgd)
+#define cpu_switch_mm(pgd,mm)			processor._set_pgd(pgd)
 #define cpu_xchg_1(x,ptr)			processor._xchg_1(x,ptr)
 #define cpu_xchg_4(x,ptr)			processor._xchg_4(x,ptr)
 
diff -Nru a/include/asm-arm/cpu-multi32.h b/include/asm-arm/cpu-multi32.h
--- a/include/asm-arm/cpu-multi32.h	Mon Apr 22 15:32:18 2002
+++ b/include/asm-arm/cpu-multi32.h	Mon Apr 22 15:32:18 2002
@@ -12,8 +12,7 @@
 #include <asm/memory.h>
 #include <asm/page.h>
 
-/* forward-declare task_struct */
-struct task_struct;
+struct mm_struct;
 
 /*
  * Don't change this structure - ASM code
@@ -43,7 +42,7 @@
 	/*
 	 * Idle the processor
 	 */
-	int (*_do_idle)(int mode);
+	int (*_do_idle)(void);
 	/*
 	 * Processor architecture specific
 	 */
@@ -93,7 +92,7 @@
 		/*
 		 * Set the page table
 		 */
-		void (*set_pgd)(unsigned long pgd_phys);
+		void (*set_pgd)(unsigned long pgd_phys, struct mm_struct *mm);
 		/*
 		 * Set a PMD (handling IMP bit 4)
 		 */
@@ -113,7 +112,7 @@
 #define cpu_proc_init()				processor._proc_init()
 #define cpu_proc_fin()				processor._proc_fin()
 #define cpu_reset(addr)				processor.reset(addr)
-#define cpu_do_idle(mode)			processor._do_idle(mode)
+#define cpu_do_idle()				processor._do_idle()
 
 #define cpu_cache_clean_invalidate_all()	processor.cache.clean_invalidate_all()
 #define cpu_cache_clean_invalidate_range(s,e,f)	processor.cache.clean_invalidate_range(s,e,f)
@@ -126,11 +125,11 @@
 #define cpu_icache_invalidate_range(s,e)	processor.icache.invalidate_range(s,e)
 #define cpu_icache_invalidate_page(vp)		processor.icache.invalidate_page(vp)
 
-#define cpu_set_pgd(pgd)			processor.pgtable.set_pgd(pgd)
+#define cpu_set_pgd(pgd,mm)			processor.pgtable.set_pgd(pgd,mm)
 #define cpu_set_pmd(pmdp, pmd)			processor.pgtable.set_pmd(pmdp, pmd)
 #define cpu_set_pte(ptep, pte)			processor.pgtable.set_pte(ptep, pte)
 
-#define cpu_switch_mm(pgd,tsk)			cpu_set_pgd(__virt_to_phys((unsigned long)(pgd)))
+#define cpu_switch_mm(pgd,mm)			cpu_set_pgd(__virt_to_phys((unsigned long)(pgd)),mm)
 
 #define cpu_get_pgd()	\
 	({						\
diff -Nru a/include/asm-arm/cpu-single.h b/include/asm-arm/cpu-single.h
--- a/include/asm-arm/cpu-single.h	Mon Apr 22 15:32:18 2002
+++ b/include/asm-arm/cpu-single.h	Mon Apr 22 15:32:18 2002
@@ -44,15 +44,14 @@
 #include <asm/memory.h>
 #include <asm/page.h>
 
-/* forward declare task_struct */
-struct task_struct;
+struct mm_struct;
 
 /* declare all the functions as extern */
 extern void cpu_data_abort(unsigned long pc);
 extern void cpu_check_bugs(void);
 extern void cpu_proc_init(void);
 extern void cpu_proc_fin(void);
-extern int cpu_do_idle(int mode);
+extern int cpu_do_idle(void);
 
 extern void cpu_cache_clean_invalidate_all(void);
 extern void cpu_cache_clean_invalidate_range(unsigned long address, unsigned long end, int flags);
@@ -65,13 +64,13 @@
 extern void cpu_icache_invalidate_range(unsigned long start, unsigned long end);
 extern void cpu_icache_invalidate_page(void *virt_page);
 
-extern void cpu_set_pgd(unsigned long pgd_phys);
+extern void cpu_set_pgd(unsigned long pgd_phys, struct mm_struct *mm);
 extern void cpu_set_pmd(pmd_t *pmdp, pmd_t pmd);
 extern void cpu_set_pte(pte_t *ptep, pte_t pte);
 
 extern volatile void cpu_reset(unsigned long addr);
 
-#define cpu_switch_mm(pgd,tsk) cpu_set_pgd(__virt_to_phys((unsigned long)(pgd)))
+#define cpu_switch_mm(pgd,mm) cpu_set_pgd(__virt_to_phys((unsigned long)(pgd)),mm)
 
 #define cpu_get_pgd()	\
 	({						\
diff -Nru a/include/asm-arm/hardware/sa1111.h b/include/asm-arm/hardware/sa1111.h
--- a/include/asm-arm/hardware/sa1111.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-arm/hardware/sa1111.h	Mon Apr 22 15:32:19 2002
@@ -26,9 +26,7 @@
 #define SA1111_v2p( x )         ((x) - SA1111_VBASE + SA1111_BASE)
 
 #ifndef __ASSEMBLY__
-
-extern struct resource sa1111_resource;
-#define _SA1111(x)	((x) + sa1111_resource.start)
+#define _SA1111(x)	((x) + sa1111->resource.start)
 #endif
 
 /*
@@ -51,7 +49,8 @@
  */
 #define __CCREG(x)	__REGP(SA1111_VBASE + (x))
 
-/* System Bus Interface (SBI)
+/*
+ * System Bus Interface (SBI)
  *
  * Registers
  *    SKCR	Control Register
@@ -127,28 +126,37 @@
  *    SKPEN1	PWM1 Enable Register
  *    SKPWM1	PWM1 Clock Register
  */
-
-#define _SKPCR		_SA1111(0x0200)
-#define _SKCDR		_SA1111(0x0204)
-#define _SKAUD		_SA1111(0x0208)
-#define _SKPMC		_SA1111(0x020c)
-#define _SKPTC		_SA1111(0x0210)
-#define _SKPEN0		_SA1111(0x0214)
-#define _SKPWM0		_SA1111(0x0218)
-#define _SKPEN1		_SA1111(0x021c)
-#define _SKPWM1		_SA1111(0x0220)
+#define SA1111_SKPCR	0x0200
+#define SA1111_SKCDR	0x0204
+#define SA1111_SKAUD	0x0208
+#define SA1111_SKPMC	0x020c
+#define SA1111_SKPTC	0x0210
+#define SA1111_SKPEN0	0x0214
+#define SA1111_SKPWN0	0x0218
+#define SA1111_SKPEN1	0x021c
+#define SA1111_SKPWM1	0x0220
+
+#define _SKPCR		_SA1111(SA1111_SKPCR)
+#define _SKCDR		_SA1111(SA1111_SKCDR)
+#define _SKAUD		_SA1111(SA1111_SKAUD)
+#define _SKPMC		_SA1111(SA1111_SKPMC)
+#define _SKPTC		_SA1111(SA1111_SKPTC)
+#define _SKPEN0		_SA1111(SA1111_SKPEN0)
+#define _SKPWM0		_SA1111(SA1111_SKPWM0)
+#define _SKPEN1		_SA1111(SA1111_SKPEN1)
+#define _SKPWM1		_SA1111(SA1111_SKPWM1)
 
 #if LANGUAGE == C
 
-#define SKPCR		__CCREG(0x0200)
-#define SKCDR		__CCREG(0x0204)
-#define SKAUD		__CCREG(0x0208)
-#define SKPMC		__CCREG(0x020c)
-#define SKPTC		__CCREG(0x0210)
-#define SKPEN0		__CCREG(0x0214)
-#define SKPWM0		__CCREG(0x0218)
-#define SKPEN1		__CCREG(0x021c)
-#define SKPWM1		__CCREG(0x0220)
+#define SKPCR		__CCREG(SA1111_SKPCR)
+#define SKCDR		__CCREG(SA1111_SKCDR)
+#define SKAUD		__CCREG(SA1111_SKAUD)
+#define SKPMC		__CCREG(SA1111_SKPMC)
+#define SKPTC		__CCREG(SA1111_SKPTC)
+#define SKPEN0		__CCREG(SA1111_SKPEN0)
+#define SKPWM0		__CCREG(SA1111_SKPWM0)
+#define SKPEN1		__CCREG(SA1111_SKPEN1)
+#define SKPWM1		__CCREG(SA1111_SKPWM1)
 
 #endif  /* LANGUAGE == C */
 
@@ -674,5 +682,13 @@
 
 #define PCSSR_S0_SLEEP	(1<<0)
 #define PCSSR_S1_SLEEP	(1<<1)
+
+struct sa1111_device {
+	struct device	dev;
+	struct resource	resource;
+	void		*base;
+};
+
+extern struct sa1111_device *sa1111;
 
 #endif  /* _ASM_ARCH_SA1111 */
diff -Nru a/include/asm-arm/io.h b/include/asm-arm/io.h
--- a/include/asm-arm/io.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-arm/io.h	Mon Apr 22 15:32:19 2002
@@ -22,6 +22,7 @@
 
 #ifdef __KERNEL__
 
+#include <linux/config.h>
 #include <linux/types.h>
 #include <asm/byteorder.h>
 #include <asm/memory.h>
@@ -269,13 +270,22 @@
 extern void consistent_sync(void *vaddr, size_t size, int rw);
 
 /*
- * FIXME: I'm sure these will need to be changed for DISCONTIG
+ * Change "struct page" to physical address.
  */
+#ifdef CONFIG_DISCONTIGMEM
+#define page_to_phys(page)					  \
+	((((page) - page_zone(page)->zone_mem_map) << PAGE_SHIFT) \
+		  + page_zone(page)->zone_start_paddr)
+#else
+#define page_to_phys(page)					  \
+	(PHYS_OFFSET + (((page) - mem_map) << PAGE_SHIFT))
+#endif
+
 /*
- * Change "struct page" to physical address.
+ * We should really eliminate virt_to_bus() here - it's depreciated.
  */
-#define page_to_phys(page)	(PHYS_OFFSET + ((page - mem_map) << PAGE_SHIFT))
-#define page_to_bus(page)	(PHYS_OFFSET + ((page - mem_map) << PAGE_SHIFT))
+#define page_to_bus(page)					  \
+	(virt_to_bus(page_address(page)))
 
 /*
  * can the hardware map this into one segment or not, given no other
diff -Nru a/include/asm-arm/mmu_context.h b/include/asm-arm/mmu_context.h
--- a/include/asm-arm/mmu_context.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-arm/mmu_context.h	Mon Apr 22 15:32:19 2002
@@ -43,7 +43,7 @@
 	  struct task_struct *tsk, unsigned int cpu)
 {
 	if (prev != next)
-		cpu_switch_mm(next->pgd, tsk);
+		cpu_switch_mm(next->pgd, next);
 }
 
 #define activate_mm(prev, next) \
diff -Nru a/include/asm-arm/pci.h b/include/asm-arm/pci.h
--- a/include/asm-arm/pci.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-arm/pci.h	Mon Apr 22 15:32:19 2002
@@ -95,30 +95,25 @@
 	/* nothing to do */
 }
 
-/* Whether pci_unmap_{single,page} is a nop depends upon the
+/*
+ * Whether pci_unmap_{single,page} is a nop depends upon the
  * configuration.
  */
-#ifdef CONFIG_SA1111
-#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)	\
-	dma_addr_t ADDR_NAME;
-#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)		\
-	__u32 LEN_NAME;
-#define pci_unmap_addr(PTR, ADDR_NAME)			\
-	((PTR)->ADDR_NAME)
-#define pci_unmap_addr_set(PTR, ADDR_NAME, VAL)		\
-	(((PTR)->ADDR_NAME) = (VAL))
-#define pci_unmap_len(PTR, LEN_NAME)			\
-	((PTR)->LEN_NAME)
-#define pci_unmap_len_set(PTR, LEN_NAME, VAL)		\
-	(((PTR)->LEN_NAME) = (VAL))
-#else /* !(CONFIG_SA1111) */
+#ifdef CONFIG_PCI
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)	dma_addr_t ADDR_NAME;
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)		__u32 LEN_NAME;
+#define pci_unmap_addr(PTR, ADDR_NAME)		((PTR)->ADDR_NAME)
+#define pci_unmap_addr_set(PTR, ADDR_NAME, VAL)	(((PTR)->ADDR_NAME) = (VAL))
+#define pci_unmap_len(PTR, LEN_NAME)		((PTR)->LEN_NAME)
+#define pci_unmap_len_set(PTR, LEN_NAME, VAL)	(((PTR)->LEN_NAME) = (VAL))
+#else
 #define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)
 #define DECLARE_PCI_UNMAP_LEN(LEN_NAME)
 #define pci_unmap_addr(PTR, ADDR_NAME)		(0)
 #define pci_unmap_addr_set(PTR, ADDR_NAME, VAL)	do { } while (0)
 #define pci_unmap_len(PTR, LEN_NAME)		(0)
 #define pci_unmap_len_set(PTR, LEN_NAME, VAL)	do { } while (0)
-#endif /* CONFIG_SA1111 */
+#endif /* CONFIG_PCI */
 
 /* Map a set of buffers described by scatterlist in streaming
  * mode for DMA.  This is the scather-gather version of the
diff -Nru a/include/asm-arm/pgalloc.h b/include/asm-arm/pgalloc.h
--- a/include/asm-arm/pgalloc.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-arm/pgalloc.h	Mon Apr 22 15:32:19 2002
@@ -10,11 +10,10 @@
 #ifndef _ASMARM_PGALLOC_H
 #define _ASMARM_PGALLOC_H
 
-#include <linux/config.h>
-
 #include <asm/processor.h>
+#include <asm/cacheflush.h>
+#include <asm/tlbflush.h>
 
-#include <asm/proc/cache.h>
 #include <asm/proc/pgalloc.h>
 
 /*
diff -Nru a/include/asm-arm/proc-armo/cache.h b/include/asm-arm/proc-armo/cache.h
--- a/include/asm-arm/proc-armo/cache.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-arm/proc-armo/cache.h	Mon Apr 22 15:32:19 2002
@@ -27,66 +27,3 @@
 
 /* DAG: ARM3 will flush cache on MEMC updates anyway? so don't bother */
 #define clean_cache_area(_start,_size) do { } while (0)
-
-/*
- * TLB flushing:
- *
- *  - flush_tlb_all() flushes all processes TLBs
- *  - flush_tlb_mm(mm) flushes the specified mm context TLB's
- *  - flush_tlb_page(vma, vmaddr) flushes one page
- *  - flush_tlb_range(vma, start, end) flushes a range of pages
- */
-#define flush_tlb_all()				memc_update_all()
-#define flush_tlb_mm(mm)			memc_update_mm(mm)
-#define flush_tlb_range(vma,start,end)		\
-		do { memc_update_mm(vma->vm_mm); (void)(start); (void)(end); } while (0)
-#define flush_tlb_page(vma, vmaddr)		do { } while (0)
-
-/*
- * The following handle the weird MEMC chip
- */
-static inline void memc_update_all(void)
-{
-	struct task_struct *p;
-
-	cpu_memc_update_all(init_mm.pgd);
-	for_each_task(p) {
-		if (!p->mm)
-			continue;
-		cpu_memc_update_all(p->mm->pgd);
-	}
-	processor._set_pgd(current->active_mm->pgd);
-}
-
-static inline void memc_update_mm(struct mm_struct *mm)
-{
-	cpu_memc_update_all(mm->pgd);
-
-	if (mm == current->active_mm)
-		processor._set_pgd(mm->pgd);
-}
-
-static inline void
-memc_clear(struct mm_struct *mm, struct page *page)
-{
-	cpu_memc_update_entry(mm->pgd, (unsigned long) page_address(page), 0);
-
-	if (mm == current->active_mm)
-		processor._set_pgd(mm->pgd);
-}
-
-static inline void
-memc_update_addr(struct mm_struct *mm, pte_t pte, unsigned long vaddr)
-{
-	cpu_memc_update_entry(mm->pgd, pte_val(pte), vaddr);
-
-	if (mm == current->active_mm)
-		processor._set_pgd(mm->pgd);
-}
-
-static inline void
-update_mmu_cache(struct vm_area_struct *vma, unsigned long addr, pte_t pte)
-{
-	struct mm_struct *mm = vma->vm_mm;
-	memc_update_addr(mm, pte, addr);
-}
diff -Nru a/include/asm-arm/proc-armo/tlbflush.h b/include/asm-arm/proc-armo/tlbflush.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/include/asm-arm/proc-armo/tlbflush.h	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,63 @@
+/*
+ * TLB flushing:
+ *
+ *  - flush_tlb_all() flushes all processes TLBs
+ *  - flush_tlb_mm(mm) flushes the specified mm context TLB's
+ *  - flush_tlb_page(vma, vmaddr) flushes one page
+ *  - flush_tlb_range(vma, start, end) flushes a range of pages
+ */
+#define flush_tlb_all()				memc_update_all()
+#define flush_tlb_mm(mm)			memc_update_mm(mm)
+#define flush_tlb_range(vma,start,end)		\
+		do { memc_update_mm(vma->vm_mm); (void)(start); (void)(end); } while (0)
+#define flush_tlb_page(vma, vmaddr)		do { } while (0)
+
+/*
+ * The following handle the weird MEMC chip
+ */
+static inline void memc_update_all(void)
+{
+	struct task_struct *p;
+
+	cpu_memc_update_all(init_mm.pgd);
+	for_each_task(p) {
+		if (!p->mm)
+			continue;
+		cpu_memc_update_all(p->mm->pgd);
+	}
+	processor._set_pgd(current->active_mm->pgd);
+}
+
+static inline void memc_update_mm(struct mm_struct *mm)
+{
+	cpu_memc_update_all(mm->pgd);
+
+	if (mm == current->active_mm)
+		processor._set_pgd(mm->pgd);
+}
+
+static inline void
+memc_clear(struct mm_struct *mm, struct page *page)
+{
+	cpu_memc_update_entry(mm->pgd, (unsigned long) page_address(page), 0);
+
+	if (mm == current->active_mm)
+		processor._set_pgd(mm->pgd);
+}
+
+static inline void
+memc_update_addr(struct mm_struct *mm, pte_t pte, unsigned long vaddr)
+{
+	cpu_memc_update_entry(mm->pgd, pte_val(pte), vaddr);
+
+	if (mm == current->active_mm)
+		processor._set_pgd(mm->pgd);
+}
+
+static inline void
+update_mmu_cache(struct vm_area_struct *vma, unsigned long addr, pte_t pte)
+{
+	struct mm_struct *mm = vma->vm_mm;
+	memc_update_addr(mm, pte, addr);
+}
+
diff -Nru a/include/asm-arm/proc-armv/cache.h b/include/asm-arm/proc-armv/cache.h
--- a/include/asm-arm/proc-armv/cache.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-arm/proc-armv/cache.h	Mon Apr 22 15:32:19 2002
@@ -132,108 +132,3 @@
 	do {								\
 		cpu_icache_invalidate_range((_s), (_e));		\
 	} while (0)
-
-/*
- *	TLB Management
- *	==============
- *
- *	The arch/arm/mm/tlb-*.S files implement these methods.
- *
- *	The TLB specific code is expected to perform whatever tests it
- *	needs to determine if it should invalidate the TLB for each
- *	call.  Start addresses are inclusive and end addresses are
- *	exclusive; it is safe to round these addresses down.
- *
- *	flush_tlb_all()
- *
- *		Invalidate the entire TLB.
- *
- *	flush_tlb_mm(mm)
- *
- *		Invalidate all TLB entries in a particular address
- *		space.
- *		- mm	- mm_struct describing address space
- *
- *	flush_tlb_range(mm,start,end)
- *
- *		Invalidate a range of TLB entries in the specified
- *		address space.
- *		- mm	- mm_struct describing address space
- *		- start - start address (may not be aligned)
- *		- end	- end address (exclusive, may not be aligned)
- *
- *	flush_tlb_page(vaddr,vma)
- *
- *		Invalidate the specified page in the specified address range.
- *		- vaddr - virtual address (may not be aligned)
- *		- vma	- vma_struct describing address range
- *
- *	flush_kern_tlb_page(kaddr)
- *
- *		Invalidate the TLB entry for the specified page.  The address
- *		will be in the kernels virtual memory space.  Current uses
- *		only require the D-TLB to be invalidated.
- *		- kaddr - Kernel virtual memory address
- */
-
-struct cpu_tlb_fns {
-	void (*flush_kern_all)(void);
-	void (*flush_user_mm)(struct mm_struct *);
-	void (*flush_user_range)(unsigned long, unsigned long, struct vm_area_struct *);
-	void (*flush_user_page)(unsigned long, struct vm_area_struct *);
-	void (*flush_kern_page)(unsigned long);
-};
-
-/*
- * Convert calls to our calling convention.
- */
-#define flush_tlb_all()			__cpu_flush_kern_tlb_all()
-#define flush_tlb_mm(mm)		__cpu_flush_user_tlb_mm(mm)
-#define flush_tlb_range(vma,start,end)	__cpu_flush_user_tlb_range(start,end,vma)
-#define flush_tlb_page(vma,vaddr)	__cpu_flush_user_tlb_page(vaddr,vma)
-#define flush_kern_tlb_page(kaddr)	__cpu_flush_kern_tlb_page(kaddr)
-
-/*
- * Now select the calling method
- */
-#ifdef MULTI_TLB
-
-extern struct cpu_tlb_fns cpu_tlb;
-
-#define __cpu_flush_kern_tlb_all	cpu_tlb.flush_kern_all
-#define __cpu_flush_user_tlb_mm		cpu_tlb.flush_user_mm
-#define __cpu_flush_user_tlb_range	cpu_tlb.flush_user_range
-#define __cpu_flush_user_tlb_page	cpu_tlb.flush_user_page
-#define __cpu_flush_kern_tlb_page	cpu_tlb.flush_kern_page
-
-#else
-
-#define __cpu_flush_kern_tlb_all	__glue(_TLB,_flush_kern_tlb_all)
-#define __cpu_flush_user_tlb_mm		__glue(_TLB,_flush_user_tlb_mm)
-#define __cpu_flush_user_tlb_range	__glue(_TLB,_flush_user_tlb_range)
-#define __cpu_flush_user_tlb_page	__glue(_TLB,_flush_user_tlb_page)
-#define __cpu_flush_kern_tlb_page	__glue(_TLB,_flush_kern_tlb_page)
-
-extern void __cpu_flush_kern_tlb_all(void);
-extern void __cpu_flush_user_tlb_mm(struct mm_struct *);
-extern void __cpu_flush_user_tlb_range(unsigned long, unsigned long, struct vm_area_struct *);
-extern void __cpu_flush_user_tlb_page(unsigned long, struct vm_area_struct *);
-extern void __cpu_flush_kern_tlb_page(unsigned long);
-
-#endif
-
-/*
- * if PG_dcache_dirty is set for the page, we need to ensure that any
- * cache entries for the kernels virtual memory range are written
- * back to the page.
- */
-extern void update_mmu_cache(struct vm_area_struct *vma, unsigned long addr, pte_t pte);
-
-/*
- * Old ARM MEMC stuff.  This supports the reversed mapping handling that
- * we have on the older 26-bit machines.  We don't have a MEMC chip, so...
- */
-#define memc_update_all()		do { } while (0)
-#define memc_update_mm(mm)		do { } while (0)
-#define memc_update_addr(mm,pte,log)	do { } while (0)
-#define memc_clear(mm,physaddr)		do { } while (0)
diff -Nru a/include/asm-arm/proc-armv/tlbflush.h b/include/asm-arm/proc-armv/tlbflush.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/include/asm-arm/proc-armv/tlbflush.h	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,125 @@
+/*
+ *  linux/include/asm-arm/proc-armv/tlbflush.h
+ *
+ *  Copyright (C) 1999-2002 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/*
+ *	TLB Management
+ *	==============
+ *
+ *	The arch/arm/mm/tlb-*.S files implement these methods.
+ *
+ *	The TLB specific code is expected to perform whatever tests it
+ *	needs to determine if it should invalidate the TLB for each
+ *	call.  Start addresses are inclusive and end addresses are
+ *	exclusive; it is safe to round these addresses down.
+ *
+ *	flush_tlb_all()
+ *
+ *		Invalidate the entire TLB.
+ *
+ *	flush_tlb_mm(mm)
+ *
+ *		Invalidate all TLB entries in a particular address
+ *		space.
+ *		- mm	- mm_struct describing address space
+ *
+ *	flush_tlb_range(mm,start,end)
+ *
+ *		Invalidate a range of TLB entries in the specified
+ *		address space.
+ *		- mm	- mm_struct describing address space
+ *		- start - start address (may not be aligned)
+ *		- end	- end address (exclusive, may not be aligned)
+ *
+ *	flush_tlb_page(vaddr,vma)
+ *
+ *		Invalidate the specified page in the specified address range.
+ *		- vaddr - virtual address (may not be aligned)
+ *		- vma	- vma_struct describing address range
+ *
+ *	flush_kern_tlb_page(kaddr)
+ *
+ *		Invalidate the TLB entry for the specified page.  The address
+ *		will be in the kernels virtual memory space.  Current uses
+ *		only require the D-TLB to be invalidated.
+ *		- kaddr - Kernel virtual memory address
+ */
+
+struct cpu_tlb_fns {
+	void (*flush_kern_all)(void);
+	void (*flush_user_mm)(struct mm_struct *);
+	void (*flush_user_range)(unsigned long, unsigned long, struct vm_area_struct *);
+	void (*flush_user_page)(unsigned long, struct vm_area_struct *);
+	void (*flush_kern_range)(unsigned long, unsigned long);
+	void (*flush_kern_page)(unsigned long);
+};
+
+/*
+ * Convert calls to our calling convention.
+ */
+#define flush_tlb_all()			__cpu_flush_kern_tlb_all()
+#define flush_tlb_mm(mm)		__cpu_flush_user_tlb_mm(mm)
+#define flush_tlb_range(vma,start,end)	__cpu_flush_user_tlb_range(start,end,vma)
+#define flush_tlb_page(vma,vaddr)	__cpu_flush_user_tlb_page(vaddr,vma)
+#define flush_tlb_kernel_range(s,e)	__cpu_flush_kern_tlb_range(s,e)
+#define flush_tlb_kernel_page(kaddr)	__cpu_flush_kern_tlb_page(kaddr)
+
+/*
+ * Now select the calling method
+ */
+#ifdef MULTI_TLB
+
+extern struct cpu_tlb_fns cpu_tlb;
+
+#define __cpu_flush_kern_tlb_all	cpu_tlb.flush_kern_all
+#define __cpu_flush_user_tlb_mm		cpu_tlb.flush_user_mm
+#define __cpu_flush_user_tlb_range	cpu_tlb.flush_user_range
+#define __cpu_flush_user_tlb_page	cpu_tlb.flush_user_page
+#define __cpu_flush_kern_tlb_range	cpu_tlb.flush_kern_range
+#define __cpu_flush_kern_tlb_page	cpu_tlb.flush_kern_page
+
+#else
+
+#define __cpu_flush_kern_tlb_all	__glue(_TLB,_flush_kern_tlb_all)
+#define __cpu_flush_user_tlb_mm		__glue(_TLB,_flush_user_tlb_mm)
+#define __cpu_flush_user_tlb_range	__glue(_TLB,_flush_user_tlb_range)
+#define __cpu_flush_user_tlb_page	__glue(_TLB,_flush_user_tlb_page)
+#define __cpu_flush_kern_tlb_range	__glue(_TLB,_flush_kern_tlb_range)
+#define __cpu_flush_kern_tlb_page	__glue(_TLB,_flush_kern_tlb_page)
+
+extern void __cpu_flush_kern_tlb_all(void);
+extern void __cpu_flush_user_tlb_mm(struct mm_struct *);
+extern void __cpu_flush_user_tlb_range(unsigned long, unsigned long, struct vm_area_struct *);
+extern void __cpu_flush_user_tlb_page(unsigned long, struct vm_area_struct *);
+extern void __cpu_flush_kern_tlb_range(unsigned long, unsigned long);
+extern void __cpu_flush_kern_tlb_page(unsigned long);
+
+#endif
+
+/*
+ * if PG_dcache_dirty is set for the page, we need to ensure that any
+ * cache entries for the kernels virtual memory range are written
+ * back to the page.
+ */
+extern void update_mmu_cache(struct vm_area_struct *vma, unsigned long addr, pte_t pte);
+
+/*
+ * ARM processors do not cache TLB tables in RAM.
+ */
+#define flush_tlb_pgtables(mm,start,end)	do { } while (0)
+
+/*
+ * Old ARM MEMC stuff.  This supports the reversed mapping handling that
+ * we have on the older 26-bit machines.  We don't have a MEMC chip, so...
+ */
+#define memc_update_all()		do { } while (0)
+#define memc_update_mm(mm)		do { } while (0)
+#define memc_update_addr(mm,pte,log)	do { } while (0)
+#define memc_clear(mm,physaddr)		do { } while (0)
+
diff -Nru a/include/asm-arm/thread_info.h b/include/asm-arm/thread_info.h
--- a/include/asm-arm/thread_info.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-arm/thread_info.h	Mon Apr 22 15:32:19 2002
@@ -102,6 +102,8 @@
 
 #endif
 
+#define PREEMPT_ACTIVE	0x04000000
+
 /*
  * thread information flags:
  *  TIF_SYSCALL_TRACE	- syscall trace active
diff -Nru a/include/asm-arm/tlbflush.h b/include/asm-arm/tlbflush.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/include/asm-arm/tlbflush.h	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,15 @@
+/*
+ *  linux/include/asm-arm/tlbflush.h
+ *
+ *  Copyright (C) 2000-2002 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef _ASMARM_TLBFLUSH_H
+#define _ASMARM_TLBFLUSH_H
+
+#include <asm-arm/proc/tlbflush.h>
+
+#endif
diff -Nru a/include/asm-i386/bitops.h b/include/asm-i386/bitops.h
--- a/include/asm-i386/bitops.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-i386/bitops.h	Mon Apr 22 15:32:19 2002
@@ -34,7 +34,7 @@
  * Note that @nr may be almost arbitrarily large; this function is not
  * restricted to acting on a single-word quantity.
  */
-static __inline__ void set_bit(int nr, volatile void * addr)
+static __inline__ void set_bit(int nr, volatile unsigned long * addr)
 {
 	__asm__ __volatile__( LOCK_PREFIX
 		"btsl %1,%0"
@@ -51,7 +51,7 @@
  * If it's called on the same region of memory simultaneously, the effect
  * may be that only one operation succeeds.
  */
-static __inline__ void __set_bit(int nr, volatile void * addr)
+static __inline__ void __set_bit(int nr, volatile unsigned long * addr)
 {
 	__asm__(
 		"btsl %1,%0"
@@ -69,7 +69,7 @@
  * you should call smp_mb__before_clear_bit() and/or smp_mb__after_clear_bit()
  * in order to ensure changes are visible on other processors.
  */
-static __inline__ void clear_bit(int nr, volatile void * addr)
+static __inline__ void clear_bit(int nr, volatile unsigned long * addr)
 {
 	__asm__ __volatile__( LOCK_PREFIX
 		"btrl %1,%0"
@@ -77,7 +77,7 @@
 		:"Ir" (nr));
 }
 
-static __inline__ void __clear_bit(int nr, volatile void * addr)
+static __inline__ void __clear_bit(int nr, volatile unsigned long * addr)
 {
 	__asm__ __volatile__(
 		"btrl %1,%0"
@@ -96,7 +96,7 @@
  * If it's called on the same region of memory simultaneously, the effect
  * may be that only one operation succeeds.
  */
-static __inline__ void __change_bit(int nr, volatile void * addr)
+static __inline__ void __change_bit(int nr, volatile unsigned long * addr)
 {
 	__asm__ __volatile__(
 		"btcl %1,%0"
@@ -113,7 +113,7 @@
  * Note that @nr may be almost arbitrarily large; this function is not
  * restricted to acting on a single-word quantity.
  */
-static __inline__ void change_bit(int nr, volatile void * addr)
+static __inline__ void change_bit(int nr, volatile unsigned long * addr)
 {
 	__asm__ __volatile__( LOCK_PREFIX
 		"btcl %1,%0"
@@ -129,7 +129,7 @@
  * This operation is atomic and cannot be reordered.  
  * It also implies a memory barrier.
  */
-static __inline__ int test_and_set_bit(int nr, volatile void * addr)
+static __inline__ int test_and_set_bit(int nr, volatile unsigned long * addr)
 {
 	int oldbit;
 
@@ -149,7 +149,7 @@
  * If two examples of this operation race, one can appear to succeed
  * but actually fail.  You must protect multiple accesses with a lock.
  */
-static __inline__ int __test_and_set_bit(int nr, volatile void * addr)
+static __inline__ int __test_and_set_bit(int nr, volatile unsigned long * addr)
 {
 	int oldbit;
 
@@ -168,7 +168,7 @@
  * This operation is atomic and cannot be reordered.  
  * It also implies a memory barrier.
  */
-static __inline__ int test_and_clear_bit(int nr, volatile void * addr)
+static __inline__ int test_and_clear_bit(int nr, volatile unsigned long * addr)
 {
 	int oldbit;
 
@@ -188,7 +188,7 @@
  * If two examples of this operation race, one can appear to succeed
  * but actually fail.  You must protect multiple accesses with a lock.
  */
-static __inline__ int __test_and_clear_bit(int nr, volatile void * addr)
+static __inline__ int __test_and_clear_bit(int nr, volatile unsigned long *addr)
 {
 	int oldbit;
 
@@ -200,7 +200,7 @@
 }
 
 /* WARNING: non atomic and it can be reordered! */
-static __inline__ int __test_and_change_bit(int nr, volatile void * addr)
+static __inline__ int __test_and_change_bit(int nr, volatile unsigned long *addr)
 {
 	int oldbit;
 
@@ -219,7 +219,7 @@
  * This operation is atomic and cannot be reordered.  
  * It also implies a memory barrier.
  */
-static __inline__ int test_and_change_bit(int nr, volatile void * addr)
+static __inline__ int test_and_change_bit(int nr, volatile unsigned long* addr)
 {
 	int oldbit;
 
@@ -239,12 +239,12 @@
 static int test_bit(int nr, const volatile void * addr);
 #endif
 
-static __inline__ int constant_test_bit(int nr, const volatile void * addr)
+static __inline__ int constant_test_bit(int nr, const volatile unsigned long * addr)
 {
 	return ((1UL << (nr & 31)) & (((const volatile unsigned int *) addr)[nr >> 5])) != 0;
 }
 
-static __inline__ int variable_test_bit(int nr, volatile void * addr)
+static __inline__ int variable_test_bit(int nr, volatile unsigned long * addr)
 {
 	int oldbit;
 
@@ -268,7 +268,7 @@
  * Returns the bit-number of the first zero bit, not the number of the byte
  * containing a bit.
  */
-static __inline__ int find_first_zero_bit(void * addr, unsigned size)
+static __inline__ int find_first_zero_bit(unsigned long * addr, unsigned size)
 {
 	int d0, d1, d2;
 	int res;
@@ -300,7 +300,7 @@
  * Returns the bit-number of the first set bit, not the number of the byte
  * containing a bit.
  */
-static __inline__ int find_first_bit(void * addr, unsigned size)
+static __inline__ int find_first_bit(unsigned long * addr, unsigned size)
 {
 	int d0, d1;
 	int res;
@@ -326,7 +326,7 @@
  * @offset: The bitnumber to start searching at
  * @size: The maximum size to search
  */
-static __inline__ int find_next_zero_bit (void * addr, int size, int offset)
+static __inline__ int find_next_zero_bit(unsigned long * addr, int size, int offset)
 {
 	unsigned long * p = ((unsigned long *) addr) + (offset >> 5);
 	int set = 0, bit = offset & 31, res;
@@ -359,9 +359,9 @@
  * @offset: The bitnumber to start searching at
  * @size: The maximum size to search
  */
-static __inline__ int find_next_bit(void * addr, int size, int offset)
+static __inline__ int find_next_bit(unsigned long *addr, int size, int offset)
 {
-	unsigned long * p = ((unsigned long *) addr) + (offset >> 5);
+	unsigned long * p = addr + (offset >> 5);
 	int set = 0, bit = offset & 31, res;
 
 	if (bit) {
@@ -382,7 +382,7 @@
 	/*
 	 * No set bit yet, search remaining full words for a bit
 	 */
-	res = find_first_bit (p, size - 32 * (p - (unsigned long *) addr));
+	res = find_first_bit (p, size - 32 * (p - addr));
 	return (offset + set + res);
 }
 
diff -Nru a/include/asm-i386/bugs.h b/include/asm-i386/bugs.h
--- a/include/asm-i386/bugs.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-i386/bugs.h	Mon Apr 22 15:32:19 2002
@@ -179,14 +179,6 @@
 #endif
 
 /*
- * If we configured ourselves for PGE, we'd better have it.
- */
-#ifdef CONFIG_X86_PGE
-	if (!cpu_has_pge)
-		panic("Kernel compiled for PPro+, requires PGE feature!");
-#endif
-
-/*
  * If we were told we had a good local APIC, check for buggy Pentia,
  * i.e. all B steppings and the C2 stepping of P54C when using their
  * integrated APIC (see 11AP erratum in "Pentium Processor
diff -Nru a/include/asm-i386/errno.h b/include/asm-i386/errno.h
--- a/include/asm-i386/errno.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-i386/errno.h	Mon Apr 22 15:32:19 2002
@@ -7,7 +7,7 @@
 #define	EINTR		 4	/* Interrupted system call */
 #define	EIO		 5	/* I/O error */
 #define	ENXIO		 6	/* No such device or address */
-#define	E2BIG		 7	/* Arg list too long */
+#define	E2BIG		 7	/* Argument list too long */
 #define	ENOEXEC		 8	/* Exec format error */
 #define	EBADF		 9	/* Bad file number */
 #define	ECHILD		10	/* No child processes */
diff -Nru a/include/asm-i386/hardirq.h b/include/asm-i386/hardirq.h
--- a/include/asm-i386/hardirq.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-i386/hardirq.h	Mon Apr 22 15:32:19 2002
@@ -12,6 +12,7 @@
 	unsigned int __local_bh_count;
 	unsigned int __syscall_count;
 	struct task_struct * __ksoftirqd_task; /* waitqueue is too large */
+	unsigned long idle_timestamp;
 	unsigned int __nmi_count;	/* arch dependent */
 } ____cacheline_aligned irq_cpustat_t;
 
diff -Nru a/include/asm-i386/i387.h b/include/asm-i386/i387.h
--- a/include/asm-i386/i387.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-i386/i387.h	Mon Apr 22 15:32:19 2002
@@ -17,7 +17,7 @@
 #include <asm/sigcontext.h>
 #include <asm/user.h>
 
-extern void init_fpu(void);
+extern void init_fpu(struct task_struct *);
 /*
  * FPU lazy state save handling...
  */
diff -Nru a/include/asm-i386/ioctls.h b/include/asm-i386/ioctls.h
--- a/include/asm-i386/ioctls.h	Mon Apr 22 15:32:18 2002
+++ b/include/asm-i386/ioctls.h	Mon Apr 22 15:32:18 2002
@@ -6,7 +6,7 @@
 /* 0x54 is just a magic number to make these relatively unique ('T') */
 
 #define TCGETS		0x5401
-#define TCSETS		0x5402
+#define TCSETS		0x5402 /* Clashes with SNDCTL_TMR_START sound ioctl */
 #define TCSETSW		0x5403
 #define TCSETSF		0x5404
 #define TCGETA		0x5405
diff -Nru a/include/asm-i386/pgtable.h b/include/asm-i386/pgtable.h
--- a/include/asm-i386/pgtable.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-i386/pgtable.h	Mon Apr 22 15:32:19 2002
@@ -139,9 +139,6 @@
 #define __PAGE_KERNEL_RO \
 	(_PAGE_PRESENT | _PAGE_DIRTY | _PAGE_ACCESSED)
 
-#ifdef CONFIG_X86_PGE
-# define MAKE_GLOBAL(x) __pgprot((x) | _PAGE_GLOBAL)
-#else
 # define MAKE_GLOBAL(x)						\
 	({							\
 		pgprot_t __ret;					\
@@ -152,7 +149,6 @@
 			__ret = __pgprot(x);			\
 		__ret;						\
 	})
-#endif
 
 #define PAGE_KERNEL MAKE_GLOBAL(__PAGE_KERNEL)
 #define PAGE_KERNEL_RO MAKE_GLOBAL(__PAGE_KERNEL_RO)
diff -Nru a/include/asm-i386/tlbflush.h b/include/asm-i386/tlbflush.h
--- a/include/asm-i386/tlbflush.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-i386/tlbflush.h	Mon Apr 22 15:32:19 2002
@@ -37,12 +37,6 @@
 
 extern unsigned long pgkern_mask;
 
-/*
- * Do not check the PGE bit unnecesserily if this is a PPro+ kernel.
- */
-#ifdef CONFIG_X86_PGE
-# define __flush_tlb_all() __flush_tlb_global()
-#else
 # define __flush_tlb_all()						\
 	do {								\
 		if (cpu_has_pge)					\
@@ -50,7 +44,6 @@
 		else							\
 			__flush_tlb();					\
 	} while (0)
-#endif
 
 #ifndef CONFIG_X86_INVLPG
 #define __flush_tlb_one(addr) __flush_tlb()
diff -Nru a/include/asm-ia64/acpi-ext.h b/include/asm-ia64/acpi-ext.h
--- a/include/asm-ia64/acpi-ext.h	Mon Apr 22 15:32:18 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,323 +0,0 @@
-#ifndef _ASM_IA64_ACPI_EXT_H
-#define _ASM_IA64_ACPI_EXT_H
-
-/*
- * Advanced Configuration and Power Infterface
- * Based on 'ACPI Specification 1.0b' Febryary 2, 1999
- * and 'IA-64 Extensions to the ACPI Specification' Rev 0.6
- *
- * Copyright (C) 1999 VA Linux Systems
- * Copyright (C) 1999 Walt Drummond <drummond@valinux.com>
- * Copyright (C) 2000 Intel Corp.
- * Copyright (C) 2000,2001 J.I. Lee <jung-ik.lee@intel.com>
- *	ACPI 2.0 specification
- */
-
-#include <linux/config.h>
-#include <linux/types.h>
-#include <linux/mm.h>
-
-#pragma	pack(1)
-#define ACPI_RSDP_SIG "RSD PTR " /* Trailing space required */
-#define ACPI_RSDP_SIG_LEN 8
-typedef struct {
-	char signature[8];
-	u8 checksum;
-	char oem_id[6];
-	u8 revision;
-	u32 rsdt;
-	u32 length;
-	struct acpi_xsdt *xsdt;
-	u8 ext_checksum;
-	u8 reserved[3];
-} acpi20_rsdp_t;
-
-typedef struct {
-	char signature[4];
-	u32 length;
-	u8 revision;
-	u8 checksum;
-	char oem_id[6];
-	char oem_table_id[8];
-	u32 oem_revision;
-	u32 creator_id;
-	u32 creator_revision;
-} acpi_desc_table_hdr_t;
-
-#define ACPI_RSDT_SIG "RSDT"
-#define ACPI_RSDT_SIG_LEN 4
-typedef struct {
-	acpi_desc_table_hdr_t header;
-	u8 reserved[4];
-	u32 entry_ptrs[1];	/* Not really . . . */
-} acpi20_rsdt_t;
-
-#define ACPI_XSDT_SIG "XSDT"
-#define ACPI_XSDT_SIG_LEN 4
-typedef struct acpi_xsdt {
-	acpi_desc_table_hdr_t header;
-	unsigned long entry_ptrs[1];	/* Not really . . . */
-} acpi_xsdt_t;
-
-/* Common structures for ACPI 2.0 and 0.71 */
-
-typedef struct acpi_entry_iosapic {
-	u8 type;
-	u8 length;
-	u8 id;
-	u8 reserved;
-	u32 irq_base;	/* start of IRQ's this IOSAPIC is responsible for. */
-	unsigned long address;	/* Address of this IOSAPIC */
-} acpi_entry_iosapic_t;
-
-/* Local SAPIC flags */
-#define LSAPIC_ENABLED                (1<<0)
-#define LSAPIC_PERFORMANCE_RESTRICTED (1<<1)
-#define LSAPIC_PRESENT                (1<<2)
-
-/* Defines legacy IRQ->pin mapping */
-typedef struct {
-	u8 type;
-	u8 length;
-	u8 bus;		/* Constant 0 == ISA */
-	u8 isa_irq;	/* ISA IRQ # */
-	u32 pin;		/* called vector in spec; really IOSAPIC pin number */
-	u16 flags;	/* Edge/Level trigger & High/Low active */
-} acpi_entry_int_override_t;
-
-#define INT_OVERRIDE_ACTIVE_LOW    0x03
-#define INT_OVERRIDE_LEVEL_TRIGGER 0x0d
-
-/* IA64 ext 0.71 */
-
-typedef struct {
-	char signature[8];
-	u8 checksum;
-	char oem_id[6];
-	char reserved;		/* Must be 0 */
-	struct acpi_rsdt *rsdt;
-} acpi_rsdp_t;
-
-typedef struct acpi_rsdt {
-	acpi_desc_table_hdr_t header;
-	u8 reserved[4];
-	unsigned long entry_ptrs[1];	/* Not really . . . */
-} acpi_rsdt_t;
-
-#define ACPI_SAPIC_SIG "SPIC"
-#define ACPI_SAPIC_SIG_LEN 4
-typedef struct {
-	acpi_desc_table_hdr_t header;
-	u8 reserved[4];
-	unsigned long interrupt_block;
-} acpi_sapic_t;
-
-/* SAPIC structure types */
-#define ACPI_ENTRY_LOCAL_SAPIC         0
-#define ACPI_ENTRY_IO_SAPIC            1
-#define ACPI_ENTRY_INT_SRC_OVERRIDE    2
-#define ACPI_ENTRY_PLATFORM_INT_SOURCE 3	/* Unimplemented */
-
-typedef struct acpi_entry_lsapic {
-	u8 type;
-	u8 length;
-	u16 acpi_processor_id;
-	u16 flags;
-	u8 id;
-	u8 eid;
-} acpi_entry_lsapic_t;
-
-typedef struct {
-	u8 type;
-	u8 length;
-	u16 flags;
-	u8 int_type;
-	u8 id;
-	u8 eid;
-	u8 iosapic_vector;
-	u8 reserved[4];
-	u32 global_vector;
-} acpi_entry_platform_src_t;
-
-/* ACPI 2.0 with 1.3 errata specific structures */
-
-#define ACPI_MADT_SIG "APIC"
-#define ACPI_MADT_SIG_LEN 4
-typedef struct {
-	acpi_desc_table_hdr_t header;
-	u32 lapic_address;
-	u32 flags;
-} acpi_madt_t;
-
-/* acpi 2.0 MADT flags */
-#define MADT_PCAT_COMPAT	(1<<0)
-
-/* acpi 2.0 MADT structure types */
-#define ACPI20_ENTRY_LOCAL_APIC			0
-#define ACPI20_ENTRY_IO_APIC			1
-#define ACPI20_ENTRY_INT_SRC_OVERRIDE		2
-#define ACPI20_ENTRY_NMI_SOURCE			3
-#define ACPI20_ENTRY_LOCAL_APIC_NMI		4
-#define ACPI20_ENTRY_LOCAL_APIC_ADDR_OVERRIDE	5
-#define ACPI20_ENTRY_IO_SAPIC			6
-#define ACPI20_ENTRY_LOCAL_SAPIC		7
-#define ACPI20_ENTRY_PLATFORM_INT_SOURCE	8
-
-typedef struct acpi20_entry_lsapic {
-	u8 type;
-	u8 length;
-	u8 acpi_processor_id;
-	u8 id;
-	u8 eid;
-	u8 reserved[3];
-	u32 flags;
-} acpi20_entry_lsapic_t;
-
-typedef struct acpi20_entry_lapic_addr_override {
-	u8 type;
-	u8 length;
-	u8 reserved[2];
-	unsigned long lapic_address;
-} acpi20_entry_lapic_addr_override_t;
-
-typedef struct {
-	u8 type;
-	u8 length;
-	u16 flags;
-	u8 int_type;
-	u8 id;
-	u8 eid;
-	u8 iosapic_vector;
-	u32 global_vector;
-} acpi20_entry_platform_src_t;
-
-/* constants for interrupt routing API for device drivers */
-#define ACPI20_ENTRY_PIS_PMI	1
-#define ACPI20_ENTRY_PIS_INIT	2
-#define ACPI20_ENTRY_PIS_CPEI	3
-#define ACPI_MAX_PLATFORM_IRQS	4
-
-#define ACPI_SPCRT_SIG	"SPCR"
-#define ACPI_SPCRT_SIG_LEN 4
-
-#define ACPI_DBGPT_SIG	"DBGP"
-#define ACPI_DBGPT_SIG_LEN 4
-
-extern int acpi20_parse(acpi20_rsdp_t *);
-extern int acpi20_early_parse(acpi20_rsdp_t *);
-extern int acpi_parse(acpi_rsdp_t *);
-extern const char *acpi_get_sysname (void);
-extern int acpi_request_vector(u32 int_type);
-extern void (*acpi_idle) (void);	/* power-management idle function, if any */
-#ifdef CONFIG_NUMA
-extern cnodeid_t paddr_to_nid(unsigned long paddr);
-#endif
-
-/*
- * ACPI 2.0 SRAT Table
- * http://www.microsoft.com/HWDEV/design/SRAT.htm
- */
-
-typedef struct acpi_srat {
-	acpi_desc_table_hdr_t header;
-	u32 table_revision;
-	u64 reserved;
-} acpi_srat_t;
-
-typedef struct srat_cpu_affinity {
-	u8 type;
-	u8 length;
-	u8 proximity_domain;
-	u8 apic_id;
-	u32 flags;
-	u8 local_sapic_eid;
-	u8 reserved[7];
-} srat_cpu_affinity_t;
-
-typedef struct srat_memory_affinity {
-	u8 type;
-	u8 length;
-	u8 proximity_domain;
-	u8 reserved[5];
-	u32 base_addr_lo;
-	u32 base_addr_hi;
-	u32 length_lo;
-	u32 length_hi;
-	u32 memory_type;
-	u32 flags;
-	u64 reserved2;
-} srat_memory_affinity_t;
-
-/* ACPI 2.0 SRAT structure */
-#define ACPI_SRAT_SIG "SRAT"
-#define ACPI_SRAT_SIG_LEN 4
-#define ACPI_SRAT_REVISION 1
-
-#define SRAT_CPU_STRUCTURE		0
-#define SRAT_MEMORY_STRUCTURE		1
-
-/* Only 1 flag for cpu affinity structure! */
-#define SRAT_CPU_FLAGS_ENABLED			0x00000001
-
-#define SRAT_MEMORY_FLAGS_ENABLED		0x00000001
-#define SRAT_MEMORY_FLAGS_HOTREMOVABLE		0x00000002
-
-/* ACPI 2.0 address range types */
-#define ACPI_ADDRESS_RANGE_MEMORY	1
-#define ACPI_ADDRESS_RANGE_RESERVED	2
-#define ACPI_ADDRESS_RANGE_ACPI		3
-#define ACPI_ADDRESS_RANGE_NVS		4
-
-#define NODE_ARRAY_INDEX(x)	((x) / 8)	/* 8 bits/char */
-#define NODE_ARRAY_OFFSET(x)	((x) % 8)	/* 8 bits/char */
-#define MAX_PXM_DOMAINS		(256)
-
-#ifdef CONFIG_DISCONTIGMEM
-/*
- * List of node memory chunks. Filled when parsing SRAT table to
- * obtain information about memory nodes.
-*/
-
-struct node_memory_chunk_s {
-	unsigned long start_paddr;
-	unsigned long size;
-	int pxm;			// proximity domain of node
-	int nid;			// which cnode contains this chunk?
-	int bank;			// which mem bank on this node
-};
-
-extern struct node_memory_chunk_s node_memory_chunk[PLAT_MAXCLUMPS];	// temporary?
-
-struct node_cpuid_s {
-	u16	phys_id;		/* id << 8 | eid */
-	int	pxm;			// proximity domain of cpu
-	int	nid;
-};
-extern struct node_cpuid_s node_cpuid[NR_CPUS];
-
-extern int pxm_to_nid_map[MAX_PXM_DOMAINS]; /* _PXM to logical node ID map */
-extern int nid_to_pxm_map[PLAT_MAX_COMPACT_NODES]; /* logical node ID to _PXM map */
-extern int numnodes;			/* total number of nodes in system */
-extern int num_memory_chunks;		/* total number of memory chunks */
-
-/*
- * ACPI 2.0 SLIT Table
- * http://devresource.hp.com/devresource/Docs/TechPapers/IA64/slit.pdf
- */
-
-typedef struct acpi_slit {
-	acpi_desc_table_hdr_t header;
-	u64 localities;
-	u8 entries[1];			/* dummy, real size = locality^2 */
-} acpi_slit_t;
-
-extern u8 acpi20_slit[PLAT_MAX_COMPACT_NODES * PLAT_MAX_COMPACT_NODES];
-
-#define ACPI_SLIT_SIG "SLIT"
-#define ACPI_SLIT_SIG_LEN 4
-#define ACPI_SLIT_REVISION 1
-#define ACPI_SLIT_LOCAL 10
-#endif /* CONFIG_DISCONTIGMEM */
-
-#pragma	pack()
-#endif /* _ASM_IA64_ACPI_EXT_H */
diff -Nru a/include/asm-ia64/acpi.h b/include/asm-ia64/acpi.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/include/asm-ia64/acpi.h	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,49 @@
+/*
+ *  asm-ia64/acpi.h
+ *
+ *  Copyright (C) 1999 VA Linux Systems
+ *  Copyright (C) 1999 Walt Drummond <drummond@valinux.com>
+ *  Copyright (C) 2000,2001 J.I. Lee <jung-ik.lee@intel.com>
+ *  Copyright (C) 2001,2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#ifndef _ASM_ACPI_H
+#define _ASM_ACPI_H
+
+#ifdef __KERNEL__
+
+#define __acpi_map_table(phys_addr, size) __va(phys_addr)
+
+int acpi_boot_init (char *cdline);
+int acpi_find_rsdp (unsigned long *phys_addr);
+int acpi_request_vector (u32 int_type);
+int acpi_get_prt (struct pci_vector_struct **vectors, int *count);
+int acpi_get_interrupt_model(int *type);
+
+#ifdef CONFIG_DISCONTIGMEM
+#define NODE_ARRAY_INDEX(x)	((x) / 8)	/* 8 bits/char */
+#define NODE_ARRAY_OFFSET(x)	((x) % 8)	/* 8 bits/char */
+#define MAX_PXM_DOMAINS		(256)
+#endif /* CONFIG_DISCONTIGMEM */
+
+#endif /*__KERNEL__*/
+
+#endif /*_ASM_ACPI_H*/
diff -Nru a/include/asm-ia64/acpikcfg.h b/include/asm-ia64/acpikcfg.h
--- a/include/asm-ia64/acpikcfg.h	Mon Apr 22 15:32:19 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,30 +0,0 @@
-#ifndef _ASM_IA64_ACPIKCFG_H
-#define _ASM_IA64_ACPIKCFG_H
-
-/*
- *  acpikcfg.h - ACPI based Kernel Configuration Manager External Interfaces
- *
- *  Copyright (C) 2000 Intel Corp.
- *  Copyright (C) 2000 J.I. Lee  <jung-ik.lee@intel.com>
- */
-
-
-u32	__init acpi_cf_init (void * rsdp);
-u32	__init acpi_cf_terminate (void );
-
-u32	__init
-acpi_cf_get_pci_vectors (
-	struct pci_vector_struct	**vectors,
-	int				*num_pci_vectors
-	);
-
-
-#ifdef	CONFIG_ACPI_KERNEL_CONFIG_DEBUG
-void	__init
-acpi_cf_print_pci_vectors (
-	struct pci_vector_struct	*vectors,
-	int				num_pci_vectors
-	);
-#endif
-
-#endif /* _ASM_IA64_ACPIKCFG_H */
diff -Nru a/include/asm-ia64/cacheflush.h b/include/asm-ia64/cacheflush.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/include/asm-ia64/cacheflush.h	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,37 @@
+#ifndef _ASM_IA64_CACHEFLUSH_H
+#define _ASM_IA64_CACHEFLUSH_H
+
+/*
+ * Copyright (C) 2002 Hewlett-Packard Co
+ *	David Mosberger-Tang <davidm@hpl.hp.com>
+ */
+
+#include <asm/bitops.h>
+#include <asm/page.h>
+
+/*
+ * Cache flushing routines.  This is the kind of stuff that can be very expensive, so try
+ * to avoid them whenever possible.
+ */
+
+#define flush_cache_all()			do { } while (0)
+#define flush_cache_mm(mm)			do { } while (0)
+#define flush_cache_range(vma, start, end)	do { } while (0)
+#define flush_cache_page(vma, vmaddr)		do { } while (0)
+#define flush_page_to_ram(page)			do { } while (0)
+#define flush_icache_page(vma,page)		do { } while (0)
+
+#define flush_dcache_page(page)			\
+do {						\
+	clear_bit(PG_arch_1, &page->flags);	\
+} while (0)
+
+extern void flush_icache_range (unsigned long start, unsigned long end);
+
+#define flush_icache_user_range(vma, page, user_addr, len)					\
+do {												\
+	unsigned long _addr = (unsigned long) page_address(page) + ((user_addr) & ~PAGE_MASK);	\
+	flush_icache_range(_addr, _addr + (len));						\
+} while (0)
+
+#endif /* _ASM_IA64_CACHEFLUSH_H */
diff -Nru a/include/asm-ia64/efi.h b/include/asm-ia64/efi.h
--- a/include/asm-ia64/efi.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-ia64/efi.h	Mon Apr 22 15:32:19 2002
@@ -87,6 +87,8 @@
 #define EFI_MEMORY_RUNTIME	0x8000000000000000	/* range requires runtime mapping */
 #define EFI_MEMORY_DESCRIPTOR_VERSION	1
 
+#define EFI_PAGE_SHIFT		12
+
 typedef struct {
 	u32 type;
 	u32 pad;
@@ -257,6 +259,7 @@
 extern void efi_gettimeofday (struct timeval *tv);
 extern void efi_enter_virtual_mode (void);	/* switch EFI to virtual mode, if possible */
 extern u64  efi_get_iobase (void);
+extern u32  efi_mem_type (u64 phys_addr);
 
 /*
  * Variable Attributes
diff -Nru a/include/asm-ia64/hw_irq.h b/include/asm-ia64/hw_irq.h
--- a/include/asm-ia64/hw_irq.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-ia64/hw_irq.h	Mon Apr 22 15:32:19 2002
@@ -88,6 +88,7 @@
 
 extern struct irq_desc _irq_desc[NR_IRQS];
 
+#ifndef CONFIG_IA64_GENERIC
 static inline struct irq_desc *
 __ia64_irq_desc (unsigned int irq)
 {
@@ -105,6 +106,7 @@
 {
 	return (unsigned int) vec;
 }
+#endif
 
 /*
  * Next follows the irq descriptor interface.  On IA-64, each CPU supports 256 interrupt
diff -Nru a/include/asm-ia64/ide.h b/include/asm-ia64/ide.h
--- a/include/asm-ia64/ide.h	Mon Apr 22 15:32:18 2002
+++ b/include/asm-ia64/ide.h	Mon Apr 22 15:32:18 2002
@@ -31,14 +31,14 @@
 ide_default_irq (ide_ioreg_t base)
 {
 	switch (base) {
-		case 0x1f0: return isa_irq_to_vector(14);
-		case 0x170: return isa_irq_to_vector(15);
-		case 0x1e8: return isa_irq_to_vector(11);
-		case 0x168: return isa_irq_to_vector(10);
-		case 0x1e0: return isa_irq_to_vector(8);
-		case 0x160: return isa_irq_to_vector(12);
-		default:
-			return 0;
+	      case 0x1f0: return isa_irq_to_vector(14);
+	      case 0x170: return isa_irq_to_vector(15);
+	      case 0x1e8: return isa_irq_to_vector(11);
+	      case 0x168: return isa_irq_to_vector(10);
+	      case 0x1e0: return isa_irq_to_vector(8);
+	      case 0x160: return isa_irq_to_vector(12);
+	      default:
+		return 0;
 	}
 }
 
@@ -46,14 +46,14 @@
 ide_default_io_base (int index)
 {
 	switch (index) {
-		case 0:	return 0x1f0;
-		case 1:	return 0x170;
-		case 2: return 0x1e8;
-		case 3: return 0x168;
-		case 4: return 0x1e0;
-		case 5: return 0x160;
-		default:
-			return 0;
+	      case 0: return 0x1f0;
+	      case 1: return 0x170;
+	      case 2: return 0x1e8;
+	      case 3: return 0x168;
+	      case 4: return 0x1e0;
+	      case 5: return 0x160;
+	      default:
+		return 0;
 	}
 }
 
diff -Nru a/include/asm-ia64/machvec.h b/include/asm-ia64/machvec.h
--- a/include/asm-ia64/machvec.h	Mon Apr 22 15:32:18 2002
+++ b/include/asm-ia64/machvec.h	Mon Apr 22 15:32:18 2002
@@ -68,6 +68,8 @@
 #  include <asm/machvec_hpsim.h>
 # elif defined (CONFIG_IA64_DIG)
 #  include <asm/machvec_dig.h>
+# elif defined (CONFIG_IA64_HP_ZX1)
+#  include <asm/machvec_hpzx1.h>
 # elif defined (CONFIG_IA64_SGI_SN1)
 #  include <asm/machvec_sn1.h>
 # elif defined (CONFIG_IA64_SGI_SN2)
@@ -123,6 +125,7 @@
 	ia64_mv_cmci_handler_t *cmci_handler;
 	ia64_mv_log_print_t *log_print;
 	ia64_mv_send_ipi_t *send_ipi;
+	ia64_mv_global_tlb_purge_t *global_tlb_purge;
 	ia64_mv_pci_dma_init *dma_init;
 	ia64_mv_pci_alloc_consistent *alloc_consistent;
 	ia64_mv_pci_free_consistent *free_consistent;
@@ -149,6 +152,7 @@
 {						\
 	#name,					\
 	platform_setup,				\
+	platform_cpu_init,			\
 	platform_irq_init,			\
 	platform_pci_fixup,			\
 	platform_map_nr,			\
diff -Nru a/include/asm-ia64/machvec_hpzx1.h b/include/asm-ia64/machvec_hpzx1.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/include/asm-ia64/machvec_hpzx1.h	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,74 @@
+#ifndef _ASM_IA64_MACHVEC_HPZX1_h
+#define _ASM_IA64_MACHVEC_HPZX1_h
+
+extern ia64_mv_setup_t dig_setup;
+extern ia64_mv_pci_fixup_t hpzx1_pci_fixup;
+extern ia64_mv_map_nr_t map_nr_dense;
+extern ia64_mv_pci_alloc_consistent sba_alloc_consistent;
+extern ia64_mv_pci_free_consistent sba_free_consistent;
+extern ia64_mv_pci_map_single sba_map_single;
+extern ia64_mv_pci_unmap_single sba_unmap_single;
+extern ia64_mv_pci_map_sg sba_map_sg;
+extern ia64_mv_pci_unmap_sg sba_unmap_sg;
+extern ia64_mv_pci_dma_address sba_dma_address;
+
+/*
+ * This stuff has dual use!
+ *
+ * For a generic kernel, the macros are used to initialize the
+ * platform's machvec structure.  When compiling a non-generic kernel,
+ * the macros are used directly.
+ */
+#define platform_name			"hpzx1"
+#define platform_setup			dig_setup
+#define platform_pci_fixup		hpzx1_pci_fixup
+#define platform_map_nr			map_nr_dense
+#define platform_pci_dma_init		((ia64_mv_pci_dma_init *) machvec_noop)
+#define platform_pci_alloc_consistent	sba_alloc_consistent
+#define platform_pci_free_consistent	sba_free_consistent
+#define platform_pci_map_single		sba_map_single
+#define platform_pci_unmap_single	sba_unmap_single
+#define platform_pci_map_sg		sba_map_sg
+#define platform_pci_unmap_sg		sba_unmap_sg
+#define platform_pci_dma_sync_single	((ia64_mv_pci_dma_sync_single *) machvec_noop)
+#define platform_pci_dma_sync_sg	((ia64_mv_pci_dma_sync_sg *) machvec_noop)
+#define platform_pci_dma_address	sba_dma_address
+
+#endif /* _ASM_IA64_MACHVEC_HPZX1_h */
+#ifndef _ASM_IA64_MACHVEC_HPZX1_h
+#define _ASM_IA64_MACHVEC_HPZX1_h
+
+extern ia64_mv_setup_t dig_setup;
+extern ia64_mv_pci_fixup_t hpzx1_pci_fixup;
+extern ia64_mv_map_nr_t map_nr_dense;
+extern ia64_mv_pci_alloc_consistent sba_alloc_consistent;
+extern ia64_mv_pci_free_consistent sba_free_consistent;
+extern ia64_mv_pci_map_single sba_map_single;
+extern ia64_mv_pci_unmap_single sba_unmap_single;
+extern ia64_mv_pci_map_sg sba_map_sg;
+extern ia64_mv_pci_unmap_sg sba_unmap_sg;
+extern ia64_mv_pci_dma_address sba_dma_address;
+
+/*
+ * This stuff has dual use!
+ *
+ * For a generic kernel, the macros are used to initialize the
+ * platform's machvec structure.  When compiling a non-generic kernel,
+ * the macros are used directly.
+ */
+#define platform_name			"hpzx1"
+#define platform_setup			dig_setup
+#define platform_pci_fixup		hpzx1_pci_fixup
+#define platform_map_nr			map_nr_dense
+#define platform_pci_dma_init		((ia64_mv_pci_dma_init *) machvec_noop)
+#define platform_pci_alloc_consistent	sba_alloc_consistent
+#define platform_pci_free_consistent	sba_free_consistent
+#define platform_pci_map_single		sba_map_single
+#define platform_pci_unmap_single	sba_unmap_single
+#define platform_pci_map_sg		sba_map_sg
+#define platform_pci_unmap_sg		sba_unmap_sg
+#define platform_pci_dma_sync_single	((ia64_mv_pci_dma_sync_single *) machvec_noop)
+#define platform_pci_dma_sync_sg	((ia64_mv_pci_dma_sync_sg *) machvec_noop)
+#define platform_pci_dma_address	sba_dma_address
+
+#endif /* _ASM_IA64_MACHVEC_HPZX1_h */
diff -Nru a/include/asm-ia64/machvec_init.h b/include/asm-ia64/machvec_init.h
--- a/include/asm-ia64/machvec_init.h	Mon Apr 22 15:32:18 2002
+++ b/include/asm-ia64/machvec_init.h	Mon Apr 22 15:32:18 2002
@@ -5,6 +5,11 @@
 #include <asm/machvec.h>
 
 extern ia64_mv_send_ipi_t ia64_send_ipi;
+extern ia64_mv_global_tlb_purge_t ia64_global_tlb_purge;
+extern ia64_mv_irq_desc __ia64_irq_desc;
+extern ia64_mv_irq_to_vector __ia64_irq_to_vector;
+extern ia64_mv_local_vector_to_irq __ia64_local_vector_to_irq;
+
 extern ia64_mv_inb_t __ia64_inb;
 extern ia64_mv_inw_t __ia64_inw;
 extern ia64_mv_inl_t __ia64_inl;
diff -Nru a/include/asm-ia64/module.h b/include/asm-ia64/module.h
--- a/include/asm-ia64/module.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-ia64/module.h	Mon Apr 22 15:32:19 2002
@@ -51,6 +51,9 @@
 		return 0;
 	archdata = (struct archdata *)(mod->archdata_start);
 
+	if (archdata->unw_start == 0)
+		return 0;
+
 	/*
 	 * Make sure the unwind pointers are sane.
 	 */
diff -Nru a/include/asm-ia64/offsets.h b/include/asm-ia64/offsets.h
--- a/include/asm-ia64/offsets.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-ia64/offsets.h	Mon Apr 22 15:32:19 2002
@@ -6,8 +6,8 @@
  * This file was generated by arch/ia64/tools/print_offsets.awk.
  *
  */
-#define IA64_TASK_SIZE			3936	/* 0xf60 */
-#define IA64_THREAD_INFO_SIZE		24	/* 0x18 */
+#define IA64_TASK_SIZE			3952	/* 0xf70 */
+#define IA64_THREAD_INFO_SIZE		32	/* 0x20 */
 #define IA64_PT_REGS_SIZE		400	/* 0x190 */
 #define IA64_SWITCH_STACK_SIZE		560	/* 0x230 */
 #define IA64_SIGINFO_SIZE		128	/* 0x80 */
@@ -15,7 +15,7 @@
 #define SIGFRAME_SIZE			2816	/* 0xb00 */
 #define UNW_FRAME_INFO_SIZE		448	/* 0x1c0 */
 
-#define IA64_TASK_THREAD_KSP_OFFSET	1480	/* 0x5c8 */
+#define IA64_TASK_THREAD_KSP_OFFSET	1496	/* 0x5d8 */
 #define IA64_PT_REGS_CR_IPSR_OFFSET	0	/* 0x0 */
 #define IA64_PT_REGS_CR_IIP_OFFSET	8	/* 0x8 */
 #define IA64_PT_REGS_CR_IFS_OFFSET	16	/* 0x10 */
diff -Nru a/include/asm-ia64/pci.h b/include/asm-ia64/pci.h
--- a/include/asm-ia64/pci.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-ia64/pci.h	Mon Apr 22 15:32:19 2002
@@ -20,6 +20,11 @@
 #define PCIBIOS_MIN_IO		0x1000
 #define PCIBIOS_MIN_MEM		0x10000000
 
+void pcibios_config_init(void);
+struct pci_bus * pcibios_scan_root(int seg, int bus);
+extern int (*pci_config_read)(int seg, int bus, int dev, int fn, int reg, int len, u32 *value);
+extern int (*pci_config_write)(int seg, int bus, int dev, int fn, int reg, int len, u32 value);
+
 struct pci_dev;
 
 /*
diff -Nru a/include/asm-ia64/percpu.h b/include/asm-ia64/percpu.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/include/asm-ia64/percpu.h	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,24 @@
+#ifndef _ASM_IA64_PERCPU_H
+#define _ASM_IA64_PERCPU_H
+
+/*
+ * Copyright (C) 2002 Hewlett-Packard Co
+ *	David Mosberger-Tang <davidm@hpl.hp.com>
+ */
+
+#ifdef __ASSEMBLY__
+
+#define THIS_CPU(var)	(var)	/* use this to mark accesses to per-CPU variables... */
+
+#else /* !__ASSEMBLY__ */
+
+#include <linux/threads.h>
+
+extern unsigned long __per_cpu_offset[NR_CPUS];
+
+#define per_cpu(var, cpu)	(*(__typeof__(&(var))) ((void *) &(var) + __per_cpu_offset[cpu]))
+#define this_cpu(var)		(var)
+
+#endif /* !__ASSEMBLY__ */
+
+#endif /* _ASM_IA64_PERCPU_H */
diff -Nru a/include/asm-ia64/pgalloc.h b/include/asm-ia64/pgalloc.h
--- a/include/asm-ia64/pgalloc.h	Mon Apr 22 15:32:18 2002
+++ b/include/asm-ia64/pgalloc.h	Mon Apr 22 15:32:18 2002
@@ -150,7 +150,7 @@
 	free_page((unsigned long) pte);
 }
 
-extern int do_check_pgt_cache (int, int);
+extern void check_pgt_cache (void);
 
 /*
  * IA-64 doesn't have any external MMU info: the page tables contain all the necessary
@@ -175,71 +175,6 @@
 
 	flush_icache_range(addr, addr + PAGE_SIZE);
 	set_bit(PG_arch_1, &page->flags);	/* mark page as clean */
-}
-
-/*
- * Now for some TLB flushing routines.  This is the kind of stuff that
- * can be very expensive, so try to avoid them whenever possible.
- */
-
-/*
- * Flush everything (kernel mapping may also have changed due to
- * vmalloc/vfree).
- */
-extern void __flush_tlb_all (void);
-
-#ifdef CONFIG_SMP
-  extern void smp_flush_tlb_all (void);
-# define flush_tlb_all()	smp_flush_tlb_all()
-#else
-# define flush_tlb_all()	__flush_tlb_all()
-#endif
-
-/*
- * Flush a specified user mapping
- */
-static inline void
-flush_tlb_mm (struct mm_struct *mm)
-{
-	if (mm) {
-		mm->context = 0;
-		if (mm == current->active_mm) {
-			/* This is called, e.g., as a result of exec().  */
-			get_new_mmu_context(mm);
-			reload_context(mm);
-		}
-	}
-}
-
-extern void flush_tlb_range (struct vm_area_struct *vma, unsigned long start, unsigned long end);
-
-/*
- * Page-granular tlb flush.
- */
-static inline void
-flush_tlb_page (struct vm_area_struct *vma, unsigned long addr)
-{
-#ifdef CONFIG_SMP
-	flush_tlb_range(vma, (addr & PAGE_MASK), (addr & PAGE_MASK) + PAGE_SIZE);
-#else
-	if (vma->vm_mm == current->active_mm)
-		asm volatile ("ptc.l %0,%1" :: "r"(addr), "r"(PAGE_SHIFT << 2) : "memory");
-#endif
-}
-
-/*
- * Flush the TLB entries mapping the virtually mapped linear page
- * table corresponding to address range [START-END).
- */
-static inline void
-flush_tlb_pgtables (struct mm_struct *mm, unsigned long start, unsigned long end)
-{
-	struct vm_area_struct vma;
-
-	if (rgn_index(start) != rgn_index(end))
-		printk("flush_tlb_pgtables: can't flush across regions!!\n");
-	vma.vm_mm = mm;
-	flush_tlb_range(&vma, ia64_thash(start), ia64_thash(end));
 }
 
 #endif /* _ASM_IA64_PGALLOC_H */
diff -Nru a/include/asm-ia64/pgtable.h b/include/asm-ia64/pgtable.h
--- a/include/asm-ia64/pgtable.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-ia64/pgtable.h	Mon Apr 22 15:32:19 2002
@@ -121,6 +121,7 @@
 # ifndef __ASSEMBLY__
 
 #include <asm/bitops.h>
+#include <asm/cacheflush.h>
 #include <asm/mmu_context.h>
 #include <asm/processor.h>
 
@@ -290,30 +291,6 @@
 # define pgprot_writecombine(prot)	__pgprot((pgprot_val(prot) & ~_PAGE_MA_MASK) | _PAGE_MA_WC)
 #endif
 
-/*
- * Return the region index for virtual address ADDRESS.
- */
-static inline unsigned long
-rgn_index (unsigned long address)
-{
-	ia64_va a;
-
-	a.l = address;
-	return a.f.reg;
-}
-
-/*
- * Return the region offset for virtual address ADDRESS.
- */
-static inline unsigned long
-rgn_offset (unsigned long address)
-{
-	ia64_va a;
-
-	a.l = address;
-	return a.f.off;
-}
-
 static inline unsigned long
 pgd_index (unsigned long address)
 {
@@ -439,33 +416,6 @@
 #define PageSkip(page)		(0)
 
 #define io_remap_page_range remap_page_range	/* XXX is this right? */
-
-
-/*
- * Now for some cache flushing routines.  This is the kind of stuff that can be very
- * expensive, so try to avoid them whenever possible.
- */
-
-/* Caches aren't brain-dead on the IA-64. */
-#define flush_cache_all()			do { } while (0)
-#define flush_cache_mm(mm)			do { } while (0)
-#define flush_cache_range(vma, start, end)	do { } while (0)
-#define flush_cache_page(vma, vmaddr)		do { } while (0)
-#define flush_page_to_ram(page)			do { } while (0)
-#define flush_icache_page(vma,page)		do { } while (0)
-
-#define flush_dcache_page(page)			\
-do {						\
-	clear_bit(PG_arch_1, &page->flags);	\
-} while (0)
-
-extern void flush_icache_range (unsigned long start, unsigned long end);
-
-#define flush_icache_user_range(vma, page, user_addr, len)			\
-do {										\
-	unsigned long _addr = page_address(page) + ((user_addr) & ~PAGE_MASK);	\
-	flush_icache_range(_addr, _addr + (len));				\
-} while (0)
 
 /*
  * ZERO_PAGE is a global shared page that is always zero: used
diff -Nru a/include/asm-ia64/processor.h b/include/asm-ia64/processor.h
--- a/include/asm-ia64/processor.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-ia64/processor.h	Mon Apr 22 15:32:19 2002
@@ -15,7 +15,7 @@
 
 #include <linux/config.h>
 
-#include <linux/compiler.h>
+#include <linux/percpu.h>
 
 #include <asm/ptrace.h>
 #include <asm/kregs.h>
@@ -186,10 +186,6 @@
  */
 #define IA64_USEC_PER_CYC_SHIFT	41
 
-#define __HAVE_ARCH_PER_CPU
-
-#define THIS_CPU(var)	(var)
-
 #ifndef __ASSEMBLY__
 
 #include <linux/threads.h>
@@ -201,11 +197,6 @@
 #include <asm/rse.h>
 #include <asm/unwind.h>
 #include <asm/atomic.h>
-
-extern unsigned long __per_cpu_offset[NR_CPUS];
-
-#define per_cpu(var, cpu)	(*(__typeof__(&(var))) ((void *) &(var) + __per_cpu_offset[cpu]))
-#define this_cpu(var)		(var)
 
 /* like above but expressed as bitfields for more efficient access: */
 struct ia64_psr {
diff -Nru a/include/asm-ia64/string.h b/include/asm-ia64/string.h
--- a/include/asm-ia64/string.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-ia64/string.h	Mon Apr 22 15:32:19 2002
@@ -5,8 +5,8 @@
  * Here is where we want to put optimized versions of the string
  * routines.
  *
- * Copyright (C) 1998-2000 Hewlett-Packard Co
- * Copyright (C) 1998-2000 David Mosberger-Tang <davidm@hpl.hp.com>
+ * Copyright (C) 1998-2000, 2002 Hewlett-Packard Co
+ *	David Mosberger-Tang <davidm@hpl.hp.com>
  */
 
 #include <linux/config.h>	/* remove this once we remove the A-step workaround... */
@@ -17,7 +17,21 @@
 #define __HAVE_ARCH_BCOPY	1 /* see arch/ia64/lib/memcpy.S */
 
 extern __kernel_size_t strlen (const char *);
-extern void *memset (void *, int, __kernel_size_t);
 extern void *memcpy (void *, const void *, __kernel_size_t);
+
+extern void *__memset_generic (void *, int, __kernel_size_t);
+extern void __bzero (void *, __kernel_size_t);
+
+#define memset(s, c, count)				\
+({							\
+	void *_s = (s);					\
+	int _c = (c);					\
+	__kernel_size_t _count = (count);		\
+							\
+	if (__builtin_constant_p(_c) && _c == 0)	\
+		__bzero(_s, _count);			\
+	else						\
+		__memset_generic(_s, _c, _count);	\
+})
 
 #endif /* _ASM_IA64_STRING_H */
diff -Nru a/include/asm-ia64/system.h b/include/asm-ia64/system.h
--- a/include/asm-ia64/system.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-ia64/system.h	Mon Apr 22 15:32:19 2002
@@ -18,14 +18,6 @@
 
 #define KERNEL_START		(PAGE_OFFSET + 68*1024*1024)
 
-/*
- * The following #defines must match with vmlinux.lds.S:
- */
-#define IVT_ADDR		(KERNEL_START)
-#define IVT_END_ADDR		(KERNEL_START + 0x8000)
-#define ZERO_PAGE_ADDR		PAGE_ALIGN(IVT_END_ADDR)
-#define SWAPPER_PGD_ADDR	(ZERO_PAGE_ADDR + 1*PAGE_SIZE)
-
 #define GATE_ADDR		(0xa000000000000000 + PAGE_SIZE)
 #define PERCPU_ADDR		(0xa000000000000000 + 2*PAGE_SIZE)
 
diff -Nru a/include/asm-ia64/thread_info.h b/include/asm-ia64/thread_info.h
--- a/include/asm-ia64/thread_info.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-ia64/thread_info.h	Mon Apr 22 15:32:19 2002
@@ -12,7 +12,10 @@
 #define TI_EXEC_DOMAIN	0x00
 #define TI_FLAGS	0x08
 #define TI_CPU		0x0c
-#define TI_ADDR_LIMI	0x10
+#define TI_ADDR_LIMIT	0x10
+#define TI_PRE_COUNT	0x18
+
+#define PREEMPT_ACTIVE	0x4000000
 
 #ifndef __ASSEMBLY__
 
@@ -26,6 +29,7 @@
 	__u32 flags;			/* thread_info flags (see TIF_*) */
 	__u32 cpu;			/* current CPU */
 	mm_segment_t addr_limit;	/* user-level address space limit */
+	__s32 preempt_count;		/* 0=premptable, <0=BUG; will also serve as bh-counter */
 };
 
 #define INIT_THREAD_SIZE		/* tell sched.h not to declare the thread_union */
@@ -37,6 +41,7 @@
 	flags:		0,			\
 	cpu:		0,			\
 	addr_limit:	KERNEL_DS,		\
+	preempt_count:	0,			\
 }
 
 /* how to get the thread information struct from C */
diff -Nru a/include/asm-ia64/tlbflush.h b/include/asm-ia64/tlbflush.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/include/asm-ia64/tlbflush.h	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,83 @@
+#ifndef _ASM_IA64_TLBFLUSH_H
+#define _ASM_IA64_TLBFLUSH_H
+
+/*
+ * Copyright (C) 2002 Hewlett-Packard Co
+ *	David Mosberger-Tang <davidm@hpl.hp.com>
+ */
+
+#include <linux/config.h>
+
+#include <linux/mm.h>
+
+#include <asm/mmu_context.h>
+#include <asm/page.h>
+
+/*
+ * Now for some TLB flushing routines.  This is the kind of stuff that
+ * can be very expensive, so try to avoid them whenever possible.
+ */
+
+/*
+ * Flush everything (kernel mapping may also have changed due to
+ * vmalloc/vfree).
+ */
+extern void __flush_tlb_all (void);
+
+#ifdef CONFIG_SMP
+  extern void smp_flush_tlb_all (void);
+# define flush_tlb_all()	smp_flush_tlb_all()
+#else
+# define flush_tlb_all()	__flush_tlb_all()
+#endif
+
+/*
+ * Flush a specified user mapping
+ */
+static inline void
+flush_tlb_mm (struct mm_struct *mm)
+{
+	if (mm) {
+		mm->context = 0;
+		if (mm == current->active_mm) {
+			/* This is called, e.g., as a result of exec().  */
+			get_new_mmu_context(mm);
+			reload_context(mm);
+		}
+	}
+}
+
+extern void flush_tlb_range (struct vm_area_struct *vma, unsigned long start, unsigned long end);
+
+/*
+ * Page-granular tlb flush.
+ */
+static inline void
+flush_tlb_page (struct vm_area_struct *vma, unsigned long addr)
+{
+#ifdef CONFIG_SMP
+	flush_tlb_range(vma, (addr & PAGE_MASK), (addr & PAGE_MASK) + PAGE_SIZE);
+#else
+	if (vma->vm_mm == current->active_mm)
+		asm volatile ("ptc.l %0,%1" :: "r"(addr), "r"(PAGE_SHIFT << 2) : "memory");
+#endif
+}
+
+/*
+ * Flush the TLB entries mapping the virtually mapped linear page
+ * table corresponding to address range [START-END).
+ */
+static inline void
+flush_tlb_pgtables (struct mm_struct *mm, unsigned long start, unsigned long end)
+{
+	struct vm_area_struct vma;
+
+	if (REGION_NUMBER(start) != REGION_NUMBER(end))
+		printk("flush_tlb_pgtables: can't flush across regions!!\n");
+	vma.vm_mm = mm;
+	flush_tlb_range(&vma, ia64_thash(start), ia64_thash(end));
+}
+
+#define flush_tlb_kernel_range(start, end)	flush_tlb_all()	/* XXX fix me */
+
+#endif /* _ASM_IA64_TLBFLUSH_H */
diff -Nru a/include/asm-ia64/uaccess.h b/include/asm-ia64/uaccess.h
--- a/include/asm-ia64/uaccess.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-ia64/uaccess.h	Mon Apr 22 15:32:19 2002
@@ -56,8 +56,9 @@
  * address TASK_SIZE is never valid.  We also need to make sure that the address doesn't
  * point inside the virtually mapped linear page table.
  */
-#define __access_ok(addr,size,segment)	(((unsigned long) (addr)) <= (segment).seg		\
-	 && ((segment).seg == KERNEL_DS.seg || rgn_offset((unsigned long) (addr)) < RGN_MAP_LIMIT))
+#define __access_ok(addr,size,segment)	(((unsigned long) (addr)) <= (segment).seg	\
+	 && ((segment).seg == KERNEL_DS.seg						\
+	     || REGION_OFFSET((unsigned long) (addr)) < RGN_MAP_LIMIT))
 #define access_ok(type,addr,size)	__access_ok((addr),(size),get_fs())
 
 static inline int
diff -Nru a/include/asm-ia64/unistd.h b/include/asm-ia64/unistd.h
--- a/include/asm-ia64/unistd.h	Mon Apr 22 15:32:18 2002
+++ b/include/asm-ia64/unistd.h	Mon Apr 22 15:32:18 2002
@@ -219,6 +219,9 @@
 #define __NR_lremovexattr		1227
 #define __NR_fremovexattr		1228
 #define __NR_tkill			1229
+#define __NR_futex			1230
+#define __NR_sched_setaffinity		1231
+#define __NR_sched_getaffinity		1232
 
 #if !defined(__ASSEMBLY__) && !defined(ASSEMBLER)
 
diff -Nru a/include/asm-ppc/bitops.h b/include/asm-ppc/bitops.h
--- a/include/asm-ppc/bitops.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-ppc/bitops.h	Mon Apr 22 15:32:19 2002
@@ -30,7 +30,7 @@
  * These used to be if'd out here because using : "cc" as a constraint
  * resulted in errors from egcs.  Things appear to be OK with gcc-2.95.
  */
-static __inline__ void set_bit(int nr, volatile void * addr)
+static __inline__ void set_bit(int nr, volatile unsigned long * addr)
 {
 	unsigned long old;
 	unsigned long mask = 1 << (nr & 0x1f);
@@ -50,7 +50,7 @@
 /*
  * non-atomic version
  */
-static __inline__ void __set_bit(int nr, volatile void *addr)
+static __inline__ void __set_bit(int nr, volatile unsigned long *addr)
 {
 	unsigned long mask = 1 << (nr & 0x1f);
 	unsigned long *p = ((unsigned long *)addr) + (nr >> 5);
@@ -64,7 +64,7 @@
 #define smp_mb__before_clear_bit()	smp_mb()
 #define smp_mb__after_clear_bit()	smp_mb()
 
-static __inline__ void clear_bit(int nr, volatile void *addr)
+static __inline__ void clear_bit(int nr, volatile unsigned long *addr)
 {
 	unsigned long old;
 	unsigned long mask = 1 << (nr & 0x1f);
@@ -84,7 +84,7 @@
 /*
  * non-atomic version
  */
-static __inline__ void __clear_bit(int nr, volatile void *addr)
+static __inline__ void __clear_bit(int nr, volatile unsigned long *addr)
 {
 	unsigned long mask = 1 << (nr & 0x1f);
 	unsigned long *p = ((unsigned long *)addr) + (nr >> 5);
@@ -92,7 +92,7 @@
 	*p &= ~mask;
 }
 
-static __inline__ void change_bit(int nr, volatile void *addr)
+static __inline__ void change_bit(int nr, volatile unsigned long *addr)
 {
 	unsigned long old;
 	unsigned long mask = 1 << (nr & 0x1f);
@@ -112,7 +112,7 @@
 /*
  * non-atomic version
  */
-static __inline__ void __change_bit(int nr, volatile void *addr)
+static __inline__ void __change_bit(int nr, volatile unsigned long *addr)
 {
 	unsigned long mask = 1 << (nr & 0x1f);
 	unsigned long *p = ((unsigned long *)addr) + (nr >> 5);
@@ -123,7 +123,7 @@
 /*
  * test_and_*_bit do imply a memory barrier (?)
  */
-static __inline__ int test_and_set_bit(int nr, volatile void *addr)
+static __inline__ int test_and_set_bit(int nr, volatile unsigned long *addr)
 {
 	unsigned int old, t;
 	unsigned int mask = 1 << (nr & 0x1f);
@@ -146,7 +146,7 @@
 /*
  * non-atomic version
  */
-static __inline__ int __test_and_set_bit(int nr, volatile void *addr)
+static __inline__ int __test_and_set_bit(int nr, volatile unsigned long *addr)
 {
 	unsigned long mask = 1 << (nr & 0x1f);
 	unsigned long *p = ((unsigned long *)addr) + (nr >> 5);
@@ -156,7 +156,7 @@
 	return (old & mask) != 0;
 }
 
-static __inline__ int test_and_clear_bit(int nr, volatile void *addr)
+static __inline__ int test_and_clear_bit(int nr, volatile unsigned long *addr)
 {
 	unsigned int old, t;
 	unsigned int mask = 1 << (nr & 0x1f);
@@ -179,7 +179,7 @@
 /*
  * non-atomic version
  */
-static __inline__ int __test_and_clear_bit(int nr, volatile void *addr)
+static __inline__ int __test_and_clear_bit(int nr, volatile unsigned long *addr)
 {
 	unsigned long mask = 1 << (nr & 0x1f);
 	unsigned long *p = ((unsigned long *)addr) + (nr >> 5);
@@ -189,7 +189,7 @@
 	return (old & mask) != 0;
 }
 
-static __inline__ int test_and_change_bit(int nr, volatile void *addr)
+static __inline__ int test_and_change_bit(int nr, volatile unsigned long *addr)
 {
 	unsigned int old, t;
 	unsigned int mask = 1 << (nr & 0x1f);
@@ -212,7 +212,7 @@
 /*
  * non-atomic version
  */
-static __inline__ int __test_and_change_bit(int nr, volatile void *addr)
+static __inline__ int __test_and_change_bit(int nr, volatile unsigned long *addr)
 {
 	unsigned long mask = 1 << (nr & 0x1f);
 	unsigned long *p = ((unsigned long *)addr) + (nr >> 5);
@@ -222,7 +222,7 @@
 	return (old & mask) != 0;
 }
 
-static __inline__ int test_bit(int nr, __const__ volatile void *addr)
+static __inline__ int test_bit(int nr, __const__ volatile unsigned long *addr)
 {
 	__const__ unsigned int *p = (__const__ unsigned int *) addr;
 
@@ -230,7 +230,7 @@
 }
 
 /* Return the bit position of the most significant 1 bit in a word */
-static __inline__ int __ilog2(unsigned int x)
+static __inline__ int __ilog2(unsigned long x)
 {
 	int lz;
 
@@ -238,7 +238,7 @@
 	return 31 - lz;
 }
 
-static __inline__ int ffz(unsigned int x)
+static __inline__ int ffz(unsigned long x)
 {
 	if ((x = ~x) == 0)
 		return 32;
@@ -296,7 +296,7 @@
  * @offset: The bitnumber to start searching at
  * @size: The maximum size to search
  */
-static __inline__ unsigned long find_next_bit(void *addr,
+static __inline__ unsigned long find_next_bit(unsigned long *addr,
 	unsigned long size, unsigned long offset)
 {
 	unsigned int *p = ((unsigned int *) addr) + (offset >> 5);
@@ -353,7 +353,7 @@
 #define find_first_zero_bit(addr, size) \
 	find_next_zero_bit((addr), (size), 0)
 
-static __inline__ unsigned long find_next_zero_bit(void * addr,
+static __inline__ unsigned long find_next_zero_bit(unsigned long * addr,
 	unsigned long size, unsigned long offset)
 {
 	unsigned int * p = ((unsigned int *) addr) + (offset >> 5);
diff -Nru a/include/asm-x86_64/apic.h b/include/asm-x86_64/apic.h
--- a/include/asm-x86_64/apic.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-x86_64/apic.h	Mon Apr 22 15:32:19 2002
@@ -3,6 +3,7 @@
 
 #include <linux/config.h>
 #include <linux/pm.h>
+#include <asm/fixmap.h>
 #include <asm/apicdef.h>
 #include <asm/system.h>
 
@@ -20,24 +21,24 @@
  * Basic functions accessing APICs.
  */
 
-static __inline void apic_write(unsigned long reg, unsigned long v)
+static __inline void apic_write(unsigned long reg, unsigned int v)
 {
-	*((volatile unsigned long *)(APIC_BASE+reg)) = v;
+	*((volatile unsigned int *)(APIC_BASE+reg)) = v;
 }
 
-static __inline void apic_write_atomic(unsigned long reg, unsigned long v)
+static __inline void apic_write_atomic(unsigned long reg, unsigned int v)
 {
-	xchg((volatile unsigned long *)(APIC_BASE+reg), v);
+	xchg((volatile unsigned int *)(APIC_BASE+reg), v);
 }
 
-static __inline unsigned long apic_read(unsigned long reg)
+static __inline unsigned int apic_read(unsigned long reg)
 {
-	return *((volatile unsigned long *)(APIC_BASE+reg));
+	return *((volatile unsigned int *)(APIC_BASE+reg));
 }
 
 static __inline__ void apic_wait_icr_idle(void)
 {
-	do { } while ( apic_read( APIC_ICR ) & APIC_ICR_BUSY );
+	while ( apic_read( APIC_ICR ) & APIC_ICR_BUSY );
 }
 
 #ifdef CONFIG_X86_GOOD_APIC
@@ -63,8 +64,8 @@
 	apic_write_around(APIC_EOI, 0);
 }
 
-extern int get_maxlvt(void);
-extern void clear_local_APIC(void);
+extern int get_maxlvt (void);
+extern void clear_local_APIC (void);
 extern void connect_bsp_APIC (void);
 extern void disconnect_bsp_APIC (void);
 extern void disable_local_APIC (void);
diff -Nru a/include/asm-x86_64/apicdef.h b/include/asm-x86_64/apicdef.h
--- a/include/asm-x86_64/apicdef.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-x86_64/apicdef.h	Mon Apr 22 15:32:19 2002
@@ -71,6 +71,7 @@
 #define			GET_APIC_DEST_FIELD(x)	(((x)>>24)&0xFF)
 #define			SET_APIC_DEST_FIELD(x)	((x)<<24)
 #define		APIC_LVTT	0x320
+#define		APIC_LVTTHMR	0x330
 #define		APIC_LVTPC	0x340
 #define		APIC_LVT0	0x350
 #define			APIC_LVT_TIMER_BASE_MASK	(0x3<<18)
@@ -280,7 +281,16 @@
 		u32 __reserved_4[3];
 	} lvt_timer;
 
-/*330*/	struct { u32 __reserved[4]; } __reserved_15;
+/*330*/	struct { /* LVT - Thermal Sensor */
+		u32  vector		:  8,
+			delivery_mode	:  3,
+			__reserved_1	:  1,
+			delivery_status	:  1,
+			__reserved_2	:  3,
+			mask		:  1,
+			__reserved_3	: 15;
+		u32 __reserved_4[3];
+	} lvt_thermal;
 
 /*340*/	struct { /* LVT - Performance Counter */
 		u32   vector		:  8,
diff -Nru a/include/asm-x86_64/bitops.h b/include/asm-x86_64/bitops.h
--- a/include/asm-x86_64/bitops.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-x86_64/bitops.h	Mon Apr 22 15:32:19 2002
@@ -30,13 +30,15 @@
  *
  * This function is atomic and may not be reordered.  See __set_bit()
  * if you do not require the atomic guarantees.
+ * Note that @nr may be almost arbitrarily large; this function is not
+ * restricted to acting on a single-word quantity.
  */
-static __inline__ void set_bit(int nr, volatile void * addr)
+static __inline__ void set_bit(long nr, volatile void * addr)
 {
 	__asm__ __volatile__( LOCK_PREFIX
-		"btsl %1,%0"
+		"btsq %1,%0"
 		:"=m" (ADDR)
-		:"dIr" (nr));
+		:"dIr" (nr) : "memory");
 }
 
 /**
@@ -50,10 +52,10 @@
  */
 static __inline__ void __set_bit(int nr, volatile void * addr)
 {
-	__asm__(
+	__asm__ volatile(
 		"btsl %1,%0"
 		:"=m" (ADDR)
-		:"dIr" (nr));
+		:"dIr" (nr) : "memory");
 }
 
 /**
@@ -79,8 +81,9 @@
 	__asm__ __volatile__(
 		"btrl %1,%0"
 		:"=m" (ADDR)
-		:"Ir" (nr));
+		:"dIr" (nr));
 }
+
 #define smp_mb__before_clear_bit()	barrier()
 #define smp_mb__after_clear_bit()	barrier()
 
@@ -274,7 +277,7 @@
 		return 0;
 	__asm__ __volatile__(
 		"movl $-1,%%eax\n\t"
-		"xorq %%rdx,%%rdx\n\t"
+		"xorl %%edx,%%edx\n\t"
 		"repe; scasl\n\t"
 		"je 1f\n\t"
 		"xorl -4(%%rdi),%%eax\n\t"
@@ -289,6 +292,39 @@
 }
 
 /**
+ * find_next_zero_bit - find the first zero bit in a memory region
+ * @addr: The address to base the search on
+ * @offset: The bitnumber to start searching at
+ * @size: The maximum size to search
+ */
+static __inline__ int find_next_zero_bit (void * addr, int size, int offset)
+{
+	unsigned long * p = ((unsigned long *) addr) + (offset >> 6);
+	unsigned long set = 0;
+	long res, bit = offset&63;
+	
+	if (bit) {
+		/*
+		 * Look for zero in first word
+		 */
+		__asm__("bsfq %1,%0\n\t"
+			"cmoveq %2,%0"
+			: "=r" (set)
+			: "r" (~(*p >> bit)), "r"(64L));
+		if (set < (64 - bit))
+			return set + offset;
+		set = 64 - bit;
+		p++;
+	}
+	/*
+	 * No zero yet, search remaining full words for a zero
+	 */
+	res = find_first_zero_bit (p, size - 64 * (p - (unsigned long *) addr));
+	return (offset + set + res);
+}
+
+
+/**
  * find_first_bit - find the first set bit in a memory region
  * @addr: The address to start the search at
  * @size: The maximum size to search
@@ -302,84 +338,49 @@
 	int res;
 
 	/* This looks at memory. Mark it volatile to tell gcc not to move it around */
-	/* Work in 32bit for now */ 
 	__asm__ __volatile__(
 		"xorl %%eax,%%eax\n\t"
 		"repe; scasl\n\t"
 		"jz 1f\n\t"
 		"leaq -4(%%rdi),%%rdi\n\t"
-		"bsfl (%%rdi),%%eax\n"
-		"1:\tsubq %%rbx,%%rdi\n\t"
-		"shlq $3,%%rdi\n\t"
-		"addq %%rdi,%%rax"
+		"bsfq (%%rdi),%%rax\n"
+		"1:\tsubl %%ebx,%%edi\n\t"
+		"shll $3,%%edi\n\t"
+		"addl %%edi,%%eax"
 		:"=a" (res), "=&c" (d0), "=&D" (d1)
 		:"1" ((size + 31) >> 5), "2" (addr), "b" (addr));
 	return res;
 }
 
+
 /**
- * find_next_zero_bit - find the first zero bit in a memory region
+ * find_next_bit - find the first set bit in a memory region
  * @addr: The address to base the search on
  * @offset: The bitnumber to start searching at
  * @size: The maximum size to search
  */
-static __inline__ int find_next_zero_bit (void * addr, int size, int offset)
+static __inline__ int find_next_bit(void * addr, int size, int offset)
 {
 	unsigned int * p = ((unsigned int *) addr) + (offset >> 5);
 	int set = 0, bit = offset & 31, res;
 	
 	if (bit) {
 		/*
-		 * Look for zero in the first 32 bits.
+		 * Look for nonzero in the first 32 bits:
 		 */
 		__asm__("bsfl %1,%0\n\t"
-			"jne 1f\n\t"
-			"movl $32, %0\n"
-			"1:"
+			"cmovel %2,%0\n\t"
 			: "=r" (set)
-			: "r" (~(*p >> bit)));
+			: "r" (*p >> bit), "r" (32));
 		if (set < (32 - bit))
 			return set + offset;
 		set = 32 - bit;
 		p++;
 	}
 	/*
-	 * No zero yet, search remaining full bytes for a zero
-	 */
-	res = find_first_zero_bit (p, size - 32 * (p - (unsigned int *) addr));
-	return (offset + set + res);
-}
-
-/**
- * find_next_bit - find the first set bit in a memory region
- * @addr: The address to base the search on
- * @offset: The bitnumber to start searching at
- * @size: The maximum size to search
- */
-static __inline__ int find_next_bit (void * addr, int size, int offset)
-{
-	unsigned long * p = ((unsigned long *) addr) + (offset >> 5);
-	unsigned long set = 0, bit = offset & 63, res;
-
-	if (bit) {
-		/*
-		 * Look for nonzero in the first 64 bits:
-		 */
-		__asm__("bsfq %1,%0\n\t"
-			"jne 1f\n\t"
-			"movq $64, %0\n"
-			"1:"
-			: "=r" (set)
-			: "r" (*p >> bit));
-		if (set < (64 - bit))
-			return set + offset;
-		set = 64 - bit;
-		p++;
-	}
-	/*
 	 * No set bit yet, search remaining full words for a bit
 	 */
-	res = find_first_bit (p, size - 64 * (p - (unsigned long *) addr));
+	res = find_first_bit (p, size - 32 * (p - (unsigned int *) addr));
 	return (offset + set + res);
 }
 
@@ -436,9 +437,8 @@
 	int r;
 
 	__asm__("bsfl %1,%0\n\t"
-		"jnz 1f\n\t"
-		"movl $-1,%0\n"
-		"1:" : "=r" (r) : "g" (x));
+		"cmovzl %2,%0" 
+		: "=r" (r) : "g" (x), "r" (32));
 	return r+1;
 }
 
@@ -457,18 +457,23 @@
 
 #ifdef __KERNEL__
 
-#define ext2_set_bit                 __test_and_set_bit
-#define ext2_clear_bit               __test_and_clear_bit
-#define ext2_test_bit                test_bit
-#define ext2_find_first_zero_bit     find_first_zero_bit
-#define ext2_find_next_zero_bit      find_next_zero_bit
+#define ext2_set_bit(nr,addr) \
+	__test_and_set_bit((nr),(unsigned long*)addr)
+#define ext2_clear_bit(nr, addr) \
+	__test_and_clear_bit((nr),(unsigned long*)addr)
+#define ext2_test_bit(nr, addr)      test_bit((nr),(unsigned long*)addr)
+#define ext2_find_first_zero_bit(addr, size) \
+	find_first_zero_bit((unsigned long*)addr, size)
+#define ext2_find_next_zero_bit(addr, size, off) \
+	find_next_zero_bit((unsigned long*)addr, size, off)
 
 /* Bitmap functions for the minix filesystem.  */
-#define minix_test_and_set_bit(nr,addr) __test_and_set_bit(nr,addr)
-#define minix_set_bit(nr,addr) __set_bit(nr,addr)
-#define minix_test_and_clear_bit(nr,addr) __test_and_clear_bit(nr,addr)
-#define minix_test_bit(nr,addr) test_bit(nr,addr)
-#define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size)
+#define minix_test_and_set_bit(nr,addr) __test_and_set_bit(nr,(void*)addr)
+#define minix_set_bit(nr,addr) __set_bit(nr,(void*)addr)
+#define minix_test_and_clear_bit(nr,addr) __test_and_clear_bit(nr,(void*)addr)
+#define minix_test_bit(nr,addr) test_bit(nr,(void*)addr)
+#define minix_find_first_zero_bit(addr,size) \
+	find_first_zero_bit((void*)addr,size)
 
 #endif /* __KERNEL__ */
 
diff -Nru a/include/asm-x86_64/bugs.h b/include/asm-x86_64/bugs.h
--- a/include/asm-x86_64/bugs.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-x86_64/bugs.h	Mon Apr 22 15:32:19 2002
@@ -13,28 +13,12 @@
 #include <linux/config.h>
 #include <asm/processor.h>
 #include <asm/i387.h>
-
-static inline void check_fpu(void)
-{
-	extern void __bad_fxsave_alignment(void);
-	if (offsetof(struct task_struct, thread.i387.fxsave) & 15)
-		__bad_fxsave_alignment();
-	printk(KERN_INFO "Enabling fast FPU save and restore... ");
-	set_in_cr4(X86_CR4_OSFXSR);
-	printk("done.\n");
-	printk(KERN_INFO "Enabling unmasked SIMD FPU exception support... ");
-	set_in_cr4(X86_CR4_OSXMMEXCPT);
-	printk("done.\n");
-}
-
-/*
- * If we configured ourselves for FXSR, we'd better have it.
- */
+#include <asm/msr.h>
+#include <asm/pda.h>
 
 static void __init check_bugs(void)
 {
 	identify_cpu(&boot_cpu_data);
-	check_fpu();
 #if !defined(CONFIG_SMP)
 	printk("CPU: ");
 	print_cpu_info(&boot_cpu_data);
diff -Nru a/include/asm-x86_64/cacheflush.h b/include/asm-x86_64/cacheflush.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/include/asm-x86_64/cacheflush.h	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,18 @@
+#ifndef _I386_CACHEFLUSH_H
+#define _I386_CACHEFLUSH_H
+
+/* Keep includes the same across arches.  */
+#include <linux/mm.h>
+
+/* Caches aren't brain-dead on the intel. */
+#define flush_cache_all()			do { } while (0)
+#define flush_cache_mm(mm)			do { } while (0)
+#define flush_cache_range(vma, start, end)	do { } while (0)
+#define flush_cache_page(vma, vmaddr)		do { } while (0)
+#define flush_page_to_ram(page)			do { } while (0)
+#define flush_dcache_page(page)			do { } while (0)
+#define flush_icache_range(start, end)		do { } while (0)
+#define flush_icache_page(vma,pg)		do { } while (0)
+#define flush_icache_user_range(vma,pg,adr,len)	do { } while (0)
+
+#endif /* _I386_CACHEFLUSH_H */
diff -Nru a/include/asm-x86_64/calling.h b/include/asm-x86_64/calling.h
--- a/include/asm-x86_64/calling.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-x86_64/calling.h	Mon Apr 22 15:32:19 2002
@@ -1,27 +1,71 @@
-/* Some macros to handle stack frames */ 
-
-	.macro SAVE_ARGS	
-	pushq %rdi
-	pushq %rsi
-	pushq %rdx
-	pushq %rcx
-	pushq %rax
-	pushq %r8
-	pushq %r9
-	pushq %r10
-	pushq %r11
-	.endm
-
-	.macro RESTORE_ARGS
-	popq %r11
-	popq %r10
-	popq %r9
-	popq %r8
-	popq %rax
-	popq %rcx	
-	popq %rdx	
-	popq %rsi	
-	popq %rdi	
+/* 
+ * Some macros to handle stack frames in assembly.
+ */ 
+
+#include <linux/config.h>
+
+#define R15 0
+#define R14 8
+#define R13 16
+#define R12 24
+#define RBP 36
+#define RBX 40
+/* arguments: interrupts/non tracing syscalls only save upto here*/
+#define R11 48
+#define R10 56	
+#define R9 64
+#define R8 72
+#define RAX 80
+#define RCX 88
+#define RDX 96
+#define RSI 104
+#define RDI 112
+#define ORIG_RAX 120       /* + error_code */ 
+/* end of arguments */ 	
+/* cpu exception frame or undefined in case of fast syscall. */
+#define RIP 128
+#define CS 136
+#define EFLAGS 144
+#define RSP 152
+#define SS 160
+#define ARGOFFSET R11
+
+	.macro SAVE_ARGS addskip=0,norcx=0 	
+	subq  $9*8+\addskip,%rsp
+	movq  %rdi,8*8(%rsp) 
+	movq  %rsi,7*8(%rsp) 
+	movq  %rdx,6*8(%rsp)
+	.if \norcx
+	.else
+	movq  %rcx,5*8(%rsp)
+	.endif
+	movq  %rax,4*8(%rsp) 
+	movq  %r8,3*8(%rsp) 
+	movq  %r9,2*8(%rsp) 
+	movq  %r10,1*8(%rsp) 
+	movq  %r11,(%rsp) 
+	.endm
+
+#define ARG_SKIP 9*8
+	.macro RESTORE_ARGS skiprax=0,addskip=0,skiprcx=0
+	movq (%rsp),%r11
+	movq 1*8(%rsp),%r10
+	movq 2*8(%rsp),%r9
+	movq 3*8(%rsp),%r8
+	.if \skiprax
+	.else
+	movq 4*8(%rsp),%rax
+	.endif
+	.if \skiprcx
+	.else
+	movq 5*8(%rsp),%rcx
+	.endif
+	movq 6*8(%rsp),%rdx
+	movq 7*8(%rsp),%rsi
+	movq 8*8(%rsp),%rdi
+	.if ARG_SKIP+\addskip > 0
+	addq $ARG_SKIP+\addskip,%rsp
+	.endif
 	.endm	
 
 	.macro LOAD_ARGS offset
@@ -37,21 +81,24 @@
 	.endm
 			
 	.macro SAVE_REST
-	pushq %rbx
-	pushq %rbp
-	pushq %r12
-	pushq %r13
-	pushq %r14
-	pushq %r15
+	subq $6*8,%rsp
+	movq %rbx,5*8(%rsp) 
+	movq %rbp,4*8(%rsp) 
+	movq %r12,3*8(%rsp) 
+	movq %r13,2*8(%rsp) 
+	movq %r14,1*8(%rsp) 
+	movq %r15,(%rsp) 
 	.endm		
 
+#define REST_SKIP 6*8
 	.macro RESTORE_REST
-	popq %r15
-	popq %r14
-	popq %r13
-	popq %r12
-	popq %rbp
-	popq %rbx
+	movq (%rsp),%r15
+	movq 1*8(%rsp),%r14
+	movq 2*8(%rsp),%r13
+	movq 3*8(%rsp),%r12
+	movq 4*8(%rsp),%rbp
+	movq 5*8(%rsp),%rbx
+	addq $REST_SKIP,%rsp
 	.endm
 		
 	.macro SAVE_ALL
@@ -59,42 +106,35 @@
 	SAVE_REST
 	.endm
 		
-	.macro RESTORE_ALL
+	.macro RESTORE_ALL addskip=0
 	RESTORE_REST
-	RESTORE_ARGS
+	RESTORE_ARGS 0,\addskip
 	.endm
 
+	/* push in order ss, rsp, eflags, cs, rip */
+	.macro FAKE_STACK_FRAME child_rip
+	xorl %eax,%eax
+	subq $6*8,%rsp
+	movq %rax,5*8(%rsp)  /* ss */
+	movq %rax,4*8(%rsp)  /* rsp */
+	movq %rax,3*8(%rsp)  /* eflags */
+	movq $__KERNEL_CS,2*8(%rsp) /* cs */
+	movq \child_rip,1*8(%rsp)  /* rip */ 
+	movq %rax,(%rsp)   /* orig_rax */ 
+	.endm
 
-R15 = 0
-R14 = 8
-R13 = 16
-R12 = 24
-RBP = 36
-RBX = 40
-/* arguments: interrupts/non tracing syscalls only save upto here*/
-R11 = 48
-R10 = 56	
-R9 = 64
-R8 = 72
-RAX = 80
-RCX = 88
-RDX = 96
-RSI = 104
-RDI = 112
-ORIG_RAX = 120       /* = ERROR */ 
-/* end of arguments */ 	
-/* cpu exception frame or undefined in case of fast syscall. */
-RIP = 128
-CS = 136
-EFLAGS = 144
-RSP = 152
-SS = 160
-ARGOFFSET = R11
-
-	.macro SYSRET32
-	.byte 0x0f,0x07
+	.macro UNFAKE_STACK_FRAME
+	addq $8*6, %rsp
 	.endm
 
-	.macro SYSRET64
-	.byte 0x48,0x0f,0x07
+	.macro icebp
+	.byte 0xf1
 	.endm
+
+#ifdef CONFIG_FRAME_POINTER
+#define ENTER enter
+#define LEAVE leave
+#else
+#define ENTER
+#define LEAVE
+#endif
diff -Nru a/include/asm-x86_64/checksum.h b/include/asm-x86_64/checksum.h
--- a/include/asm-x86_64/checksum.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-x86_64/checksum.h	Mon Apr 22 15:32:19 2002
@@ -1,6 +1,7 @@
 #ifndef _X86_64_CHECKSUM_H
 #define _X86_64_CHECKSUM_H
 
+#include <linux/in6.h>
 
 /*
  *	This is a version of ip_compute_csum() optimized for IP headers,
diff -Nru a/include/asm-x86_64/current.h b/include/asm-x86_64/current.h
--- a/include/asm-x86_64/current.h	Mon Apr 22 15:32:18 2002
+++ b/include/asm-x86_64/current.h	Mon Apr 22 15:32:18 2002
@@ -13,6 +13,13 @@
 } 
 
 
+#define stack_current() \
+({								\
+	struct thread_info *ti;					\
+	__asm__("andq %%rsp,%0; ":"=r" (ti) : "0" (~8191UL));	\
+	ti->task;					\
+})
+
 
 #define current get_current()
 
diff -Nru a/include/asm-x86_64/desc.h b/include/asm-x86_64/desc.h
--- a/include/asm-x86_64/desc.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-x86_64/desc.h	Mon Apr 22 15:32:19 2002
@@ -8,15 +8,14 @@
 #ifndef __ASSEMBLY__
 
 /* Keep this syncronized with kernel/head.S */
-#define TSS_START (7 * 8)
-#define LDT_START (TSS_START + NR_CPUS*16) 
+#define TSS_START (8 * 8)
+#define LDT_START (TSS_START + 16) 
 
-#define __TSS(n)  (TSS_START + (n)*16)
-#define __LDT(n)  (LDT_START + (n)*16)
+#define __TSS(n)  (TSS_START + (n)*64)
+#define __LDT(n)  (LDT_START + (n)*64)
 
 extern __u8 tss_start[]; 
 extern __u8 gdt_table[];
-extern __u8 ldt_start[];
 extern __u8 gdt_end[];
 
 enum { 
@@ -50,7 +49,6 @@
 enum { 
 	DESC_TSS = 0x9,
 	DESC_LDT = 0x2,
-	TSSLIMIT = 0x67,	
 }; 
 
 // LDT or TSS descriptor in the GDT. 16 bytes.
@@ -68,9 +66,9 @@
 	unsigned long address;
 } __attribute__((packed)) ;
 
-/* FIXME: these should use more generic register classes */
-#define load_TR(n) asm volatile("ltr %%ax"::"a" (__TSS(n)))
-#define __load_LDT(n) asm volatile("lldt %%ax"::"a" (__LDT(n)))
+#define load_TR(n) asm volatile("ltr %w0"::"r" (__TSS(n)))
+#define __load_LDT(n) asm volatile("lldt %w0"::"r" (__LDT(n)))
+#define clear_LDT(n)  asm volatile("lldt %w0"::"r" (0))
 
 /*
  * This is the ldt that every process will get unless we need
@@ -111,21 +109,6 @@
 	_set_gate(&idt_table[nr], GATE_INTERRUPT, (unsigned long) func, 3, 0); 
 } 
 
-static inline void set_trap_gate(int nr, void *func) 
-{ 
-	_set_gate(&idt_table[nr], GATE_TRAP, (unsigned long) func, 0, 0); 
-} 
-
-static inline void set_call_gate(void *adr, void *func) 
-{ 
-	_set_gate(adr, GATE_CALL, (unsigned long) func, 3, 0); 
-} 
-
-static inline void set_priv_gate(int nr, void *func) 
-{ 
-	_set_gate(&idt_table[nr], GATE_TRAP, (unsigned long) func, 0, 0); 	
-} 
-
 static inline void set_tssldt_descriptor(void *ptr, unsigned long tss, unsigned type, 
 					 unsigned size) 
 { 
@@ -144,25 +127,17 @@
 
 static inline void set_tss_desc(unsigned n, void *addr)
 { 
-	set_tssldt_descriptor((__u8*)gdt_table + __TSS(n), (unsigned long)addr, DESC_TSS, 
-			      TSSLIMIT); 
+	set_tssldt_descriptor((__u8*)gdt_table + __TSS(n), (unsigned long)addr, 
+			      DESC_TSS,
+			      sizeof(struct tss_struct)); 
 } 
 
 static inline void set_ldt_desc(unsigned n, void *addr, int size)
 { 
-	set_tssldt_descriptor((__u8*)gdt_table + __LDT(n), (unsigned long)addr, DESC_LDT, size); 
-} 
-
-
-#ifndef MINIKERNEL
-extern inline void clear_LDT(void)
-{
-	int cpu = smp_processor_id();
-	set_ldt_desc(cpu, &default_ldt[0], 5);
-	__load_LDT(cpu);
+	set_tssldt_descriptor((__u8*)gdt_table + __LDT(n), (unsigned long)addr, 
+			      DESC_LDT, size); 
 }
 
-
 /*
  * load one particular LDT into the current CPU
  */
@@ -170,17 +145,15 @@
 {
 	int cpu = smp_processor_id();
 	void *segments = mm->context.segments;
-	int count = LDT_ENTRIES;
 
 	if (!segments) {
-		segments = &default_ldt[0];
-		count = 5;
+		clear_LDT(cpu);
+		return;
 	}
 		
-	set_ldt_desc(cpu, segments, count);
+	set_ldt_desc(cpu, segments, LDT_ENTRIES);
 	__load_LDT(cpu);
 }
-#endif
 
 #endif /* !__ASSEMBLY__ */
 
diff -Nru a/include/asm-x86_64/elf.h b/include/asm-x86_64/elf.h
--- a/include/asm-x86_64/elf.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-x86_64/elf.h	Mon Apr 22 15:32:19 2002
@@ -36,7 +36,7 @@
 
    A value of 0 tells we have no such handler. 
 
-   We might as well make sure everything else is cleared too (except for %rsp),
+   We might as well make sure everything else is cleared too (except for %esp),
    just to make things more deterministic.
  */
 #define ELF_PLAT_INIT(_r)	do { \
@@ -55,6 +55,7 @@
         cur->thread.fs = 0; cur->thread.gs = 0; \
 	cur->thread.fsindex = 0; cur->thread.gsindex = 0; \
         cur->thread.ds = 0; cur->thread.es = 0;  \
+	clear_thread_flag(TIF_IA32); \
 } while (0)
 
 #define USE_ELF_CORE_DUMP
@@ -68,9 +69,11 @@
 #define ELF_ET_DYN_BASE         (2 * TASK_SIZE / 3)
 
 /* regs is struct pt_regs, pr_reg is elf_gregset_t (which is
-   now struct_user_regs, they are different) */
+   now struct_user_regs, they are different). Assumes current is the process
+   getting dumped. */
 
-#define ELF_CORE_COPY_REGS(pr_reg, regs)		\
+#define ELF_CORE_COPY_REGS(pr_reg, regs)  do { \
+	unsigned v;						\
 	(pr_reg)[0] = (regs)->r15;				\
 	(pr_reg)[1] = (regs)->r14;				\
 	(pr_reg)[2] = (regs)->r13;				\
@@ -92,8 +95,13 @@
 	(pr_reg)[18] = (regs)->eflags;			\
 	(pr_reg)[19] = (regs)->rsp;			\
 	(pr_reg)[20] = (regs)->ss;			\
-	rdmsrl(MSR_FS_BASE, (pr_reg)[21]);		\
-	rdmsrl(MSR_KERNEL_GS_BASE, (pr_reg)[22]);
+	(pr_reg)[21] = current->thread.fs;			\
+	(pr_reg)[22] = current->thread.gs;			\
+	asm("movl %%ds,%0" : "=r" (v)); (pr_reg)[23] = v;	\
+	asm("movl %%es,%0" : "=r" (v)); (pr_reg)[24] = v;	\
+	asm("movl %%fs,%0" : "=r" (v)); (pr_reg)[25] = v;	\
+	asm("movl %%gs,%0" : "=r" (v)); (pr_reg)[26] = v;	\
+} while(0);
 
 /* This yields a mask that user programs can use to figure out what
    instruction set this CPU supports.  This could be done in user space,
diff -Nru a/include/asm-x86_64/fixmap.h b/include/asm-x86_64/fixmap.h
--- a/include/asm-x86_64/fixmap.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-x86_64/fixmap.h	Mon Apr 22 15:32:19 2002
@@ -21,11 +21,7 @@
  * Here we define all the compile-time 'special' virtual
  * addresses. The point is to have a constant address at
  * compile time, but to set the physical address only
- * in the boot process. We allocate these special  addresses
- * from the end of virtual memory (0xfffff000) backwards.
- * Also this lets us do fail-safe vmalloc(), we
- * can guarantee that these special addresses and
- * vmalloc()-ed addresses never overlap.
+ * in the boot process.
  *
  * these 'compile-time allocated' memory buffers are
  * fixed-size 4k pages. (or larger if used with an increment
@@ -36,12 +32,6 @@
  * task switches.
  */
 
-/*
- * on UP currently we will have no trace of the fixmap mechanizm,
- * no page table allocations, etc. This might change in the
- * future, say framebuffers for the console driver(s) could be
- * fix-mapped?
- */
 enum fixed_addresses {
 	VSYSCALL_LAST_PAGE,
 	VSYSCALL_FIRST_PAGE = VSYSCALL_LAST_PAGE + ((VSYSCALL_END-VSYSCALL_START) >> PAGE_SHIFT) - 1,
@@ -65,13 +55,7 @@
  */
 #define set_fixmap_nocache(idx, phys) \
 		__set_fixmap(idx, phys, PAGE_KERNEL_NOCACHE)
-/*
- * used by vmalloc.c.
- *
- * Leave one empty page between vmalloc'ed areas and
- * the start of the fixmap, and leave one page empty
- * at the top of mem..
- */
+
 #define FIXADDR_TOP	(VSYSCALL_END-PAGE_SIZE)
 #define FIXADDR_SIZE	(__end_of_fixed_addresses << PAGE_SHIFT)
 #define FIXADDR_START	(FIXADDR_TOP - FIXADDR_SIZE)
diff -Nru a/include/asm-x86_64/fpu32.h b/include/asm-x86_64/fpu32.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/include/asm-x86_64/fpu32.h	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,10 @@
+#ifndef _FPU32_H
+#define _FPU32_H 1
+
+struct _fpstate_ia32;
+
+int restore_i387_ia32(struct task_struct *tsk, struct _fpstate_ia32 *buf, int fsave);
+int save_i387_ia32(struct task_struct *tsk, struct _fpstate_ia32 *buf, 
+		   struct pt_regs *regs, int fsave);
+
+#endif
diff -Nru a/include/asm-x86_64/hw_irq.h b/include/asm-x86_64/hw_irq.h
--- a/include/asm-x86_64/hw_irq.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-x86_64/hw_irq.h	Mon Apr 22 15:32:19 2002
@@ -16,9 +16,11 @@
  *  $Id: hw_irq.h,v 1.24 2001/09/14 20:55:03 vojtech Exp $
  */
 
+#ifndef __ASSEMBLY__
 #include <linux/config.h>
 #include <asm/atomic.h>
 #include <asm/irq.h>
+#endif
 
 /*
  * IDT vectors usable for external interrupt sources start
@@ -48,6 +50,10 @@
 #define RESCHEDULE_VECTOR	0xfc
 #define TASK_MIGRATION_VECTOR	0xfb
 #define CALL_FUNCTION_VECTOR	0xfa
+#define KDB_VECTOR	0xf9
+
+#define THERMAL_APIC_VECTOR	0xf0
+
 
 /*
  * Local APIC timer IRQ vector is on a different priority level,
@@ -64,6 +70,8 @@
 #define FIRST_DEVICE_VECTOR	0x31
 #define FIRST_SYSTEM_VECTOR	0xef
 
+
+#ifndef __ASSEMBLY__
 extern int irq_vector[NR_IRQS];
 #define IO_APIC_VECTOR(irq)	irq_vector[irq]
 
@@ -103,36 +111,6 @@
 
 #include <asm/ptrace.h>
 
-#ifdef CONFIG_PREEMPT
-#define PREEMPT_LOCK \
-"	movq %rsp,%rdx ;"				\
-"	andq $-8192,%rdx ;"				\
-"	incl " __STR(threadinfo_preempt_count)"(%rdx) ;"
-#else
-#define PREEMPT_LOCK
-#endif
-
-/* IF:off, stack contains irq number on origrax */ 
-#define IRQ_ENTER								\
-"	cld ;"									\
-"	pushq %rdi ;"								\
-"	pushq %rsi ;"								\
-"	pushq %rdx ;"								\
-"	pushq %rcx ;"								\
-"	pushq %rax ;"								\
-"	pushq %r8 ;"								\
-"	pushq %r9 ;"								\
-"	pushq %r10 ;"								\
-"	pushq %r11 ;"								\
-	PREEMPT_LOCK								\
-"	leaq -48(%rsp),%rdi	# arg1 for handler ;"				\
-"	cmpq $ " __STR(__KERNEL_CS) ",88(%rsp)	# CS - ARGOFFSET ;"		\
-"	je 1f ;"								\
-"	swapgs ;"								\
-"1:	addl $1,%gs: " __STR(pda_irqcount) ";"					\
-"	movq %gs: " __STR(pda_irqstackptr) ",%rax ;"				\
-"	cmoveq %rax,%rsp ;"	
-
 #define IRQ_NAME2(nr) nr##_interrupt(void)
 #define IRQ_NAME(nr) IRQ_NAME2(IRQ##nr)
 
@@ -140,28 +118,11 @@
  *	SMP has a few special interrupts for IPI messages
  */
 
-	/* there is a second layer of macro just to get the symbolic
-	   name for the vector evaluated. This change is for RTLinux */
-#define BUILD_SMP_INTERRUPT(x,v) XBUILD_SMP_INTERRUPT(x,v)
-#define XBUILD_SMP_INTERRUPT(x,v)\
-asmlinkage void x(void); \
-asmlinkage void call_##x(void); \
-__asm__( \
-"\n"__ALIGN_STR"\n" \
-SYMBOL_NAME_STR(x) ":\n\t" \
-	"push $" #v "-256;" \
-	IRQ_ENTER \
-	"pushq %rdi ; " \
-	"call " SYMBOL_NAME_STR(smp_##x) " ; " \
-	"jmp ret_from_intr")
-
-#define BUILD_COMMON_IRQ()
-
 #define BUILD_IRQ(nr) \
 asmlinkage void IRQ_NAME(nr); \
 __asm__( \
-"\n"__ALIGN_STR "\n" \
-SYMBOL_NAME_STR(IRQ) #nr "_interrupt:\n\t" \
+"\n.p2align\n" \
+"IRQ" #nr "_interrupt:\n\t" \
 	"push $" #nr "-256 ; " \
 	"jmp common_interrupt");
 
@@ -205,6 +166,8 @@
 }
 #else
 static inline void hw_resend_irq(struct hw_interrupt_type *h, unsigned int i) {}
+#endif
+
 #endif
 
 #endif /* _ASM_HW_IRQ_H */
diff -Nru a/include/asm-x86_64/i387.h b/include/asm-x86_64/i387.h
--- a/include/asm-x86_64/i387.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-x86_64/i387.h	Mon Apr 22 15:32:19 2002
@@ -6,82 +6,146 @@
  * Pentium III FXSR, SSE support
  * General FPU state handling cleanups
  *	Gareth Hughes <gareth@valinux.com>, May 2000
+ * x86-64 work by Andi Kleen 2002
  */
 
 #ifndef __ASM_X86_64_I387_H
 #define __ASM_X86_64_I387_H
 
 #include <linux/sched.h>
-#include <linux/spinlock.h>
 #include <asm/processor.h>
 #include <asm/sigcontext.h>
 #include <asm/user.h>
 
+extern void fpu_init(void);
 extern void init_fpu(void);
+int save_i387(struct _fpstate *buf);
 
 /*
  * FPU lazy state save handling...
  */
-extern void save_fpu( struct task_struct *tsk );
-extern void save_init_fpu( struct task_struct *tsk );
-extern void restore_fpu( struct task_struct *tsk );
 
-extern void kernel_fpu_begin(void);
-#define kernel_fpu_end() do { stts(); preempt_enable(); } while(0)
+#define kernel_fpu_end() stts()
 
-
-#define unlazy_fpu( tsk ) do { \
+#define unlazy_fpu(tsk) do { \
 	if (test_tsk_thread_flag(tsk, TIF_USEDFPU)) \
-		save_init_fpu( tsk ); \
+		save_init_fpu(tsk); \
 } while (0)
 
-#define clear_fpu( tsk )					\
-do {								\
+#define clear_fpu(tsk) do { \
 	if (test_tsk_thread_flag(tsk, TIF_USEDFPU)) {		\
 		asm volatile("fwait");				\
-		clear_tsk_thread_flag(tsk, TIF_USEDFPU);	\
+		clear_tsk_thread_flag(tsk,TIF_USEDFPU); 	\
 		stts();						\
 	}							\
 } while (0)
 
-/*
- * FPU state interaction...
- */
-extern unsigned short get_fpu_cwd( struct task_struct *tsk );
-extern unsigned short get_fpu_swd( struct task_struct *tsk );
-extern unsigned short get_fpu_twd( struct task_struct *tsk );
-extern unsigned short get_fpu_mxcsr( struct task_struct *tsk );
-
-extern void set_fpu_cwd( struct task_struct *tsk, unsigned short cwd );
-extern void set_fpu_swd( struct task_struct *tsk, unsigned short swd );
-extern void set_fpu_twd( struct task_struct *tsk, unsigned short twd );
-extern void set_fpu_mxcsr( struct task_struct *tsk, unsigned short mxcsr );
-
-#define load_mxcsr( val ) do { \
+#define load_mxcsr(val) do { \
 		unsigned long __mxcsr = ((unsigned long)(val) & 0xffbf); \
-		asm volatile( "ldmxcsr %0" : : "m" (__mxcsr) ); \
+		asm volatile("ldmxcsr %0" : : "m" (__mxcsr)); \
 } while (0)
 
 /*
- * Signal frame handlers...
+ * ptrace request handers...
  */
-extern int save_i387( struct _fpstate *buf );
-extern int restore_i387( struct _fpstate *buf );
+extern int get_fpregs(struct user_i387_struct *buf,
+		      struct task_struct *tsk);
+extern int set_fpregs(struct task_struct *tsk,
+		      struct user_i387_struct *buf);
 
 /*
- * ptrace request handers...
+ * FPU state for core dumps...
  */
-extern int get_fpregs( struct user_i387_struct *buf,
-		       struct task_struct *tsk );
-extern int set_fpregs( struct task_struct *tsk,
-		       struct user_i387_struct *buf );
+extern int dump_fpu(struct pt_regs *regs,
+		    struct user_i387_struct *fpu);
 
 /*
- * FPU state for core dumps...
+ * i387 state interaction
  */
-extern int dump_fpu( struct pt_regs *regs,
-		     struct user_i387_struct *fpu );
-extern int dump_extended_fpu( struct pt_regs *regs,
-			      struct user_i387_struct *fpu );
+#define get_fpu_mxcsr(t) ((t)->thread.i387.fxsave.mxcsr)
+#define get_fpu_cwd(t) ((t)->thread.i387.fxsave.cwd)
+#define get_fpu_fxsr_twd(t) ((t)->thread.i387.fxsave.twd)
+#define get_fpu_swd(t) ((t)->thread.i387.fxsave.swd)
+#define set_fpu_cwd(t,val) ((t)->thread.i387.fxsave.cwd = (val))
+#define set_fpu_swd(t,val) ((t)->thread.i387.fxsave.swd = (val))
+#define set_fpu_fxsr_twd(t,val) ((t)->thread.i387.fxsave.twd = (val))
+#define set_fpu_mxcsr(t,val) ((t)->thread.i387.fxsave.mxcsr = (val)&0xffbf)
+
+static inline int restore_fpu_checking(struct i387_fxsave_struct *fx) 
+{ 
+	int err;
+	asm volatile("1:  rex64 ; fxrstor (%[fx])\n\t"
+		     "2:\n"
+		     ".section .fixup,\"ax\"\n"
+		     "3:  movl $-1,%[err]\n"
+		     "    jmp  2b\n"
+		     ".previous\n"
+		     ".section __ex_table,\"a\"\n"
+		     "   .align 8\n"
+		     "   .quad  1b,3b\n"
+		     ".previous"
+		     : [err] "=r" (err)
+		     : [fx] "r" (fx), "0" (0)); 
+	return err;
+} 
+
+static inline int save_i387_checking(struct i387_fxsave_struct *fx) 
+{ 
+	int err;
+	asm volatile("1:  rex64 ; fxsave (%[fx])\n\t"
+		     "2:\n"
+		     ".section .fixup,\"ax\"\n"
+		     "3:  movl $-1,%[err]\n"
+		     "    jmp  2b\n"
+		     ".previous\n"
+		     ".section __ex_table,\"a\"\n"
+		     "   .align 8\n"
+		     "   .quad  1b,3b\n"
+		     ".previous"
+		     : [err] "=r" (err)
+		     : [fx] "r" (fx), "0" (0)); 
+	return err;
+} 
+
+static inline void kernel_fpu_begin(void)
+{
+	struct task_struct *me = current;
+	if (test_tsk_thread_flag(me,TIF_USEDFPU)) {
+		asm volatile("fxsave %0 ; fnclex"
+			      : "=m" (me->thread.i387.fxsave));
+		clear_tsk_thread_flag(me, TIF_USEDFPU);
+		return;
+	}
+	clts();
+}
+
+static inline void save_init_fpu( struct task_struct *tsk )
+{
+	asm volatile( "fxsave %0 ; fnclex"
+		      : "=m" (tsk->thread.i387.fxsave));
+	clear_tsk_thread_flag(tsk, TIF_USEDFPU);
+	stts();
+}
+
+/* 
+ * This restores directly out of user space. Exceptions are handled.
+ */
+static inline int restore_i387(struct _fpstate *buf)
+{
+	return restore_fpu_checking((struct i387_fxsave_struct *)buf);
+}
+
+
+static inline void empty_fpu(struct task_struct *child)
+{
+	if (!child->used_math) {
+		/* Simulate an empty FPU. */
+		child->thread.i387.fxsave.cwd = 0x037f; 
+		child->thread.i387.fxsave.swd = 0;
+		child->thread.i387.fxsave.twd = 0; 
+		child->thread.i387.fxsave.mxcsr = 0x1f80;
+	}
+	child->used_math = 1; 
+}		
 
 #endif /* __ASM_X86_64_I387_H */
diff -Nru a/include/asm-x86_64/ia32.h b/include/asm-x86_64/ia32.h
--- a/include/asm-x86_64/ia32.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-x86_64/ia32.h	Mon Apr 22 15:32:19 2002
@@ -53,38 +53,7 @@
 #define F_SETLK64	13
 #define F_SETLKW64	14
 
-
-
-/* sigcontext.h */
-/* The x86-64 port uses FXSAVE without prefix; thus a 32bit compatible
-   FXSAVE layout. The additional XMM registers are added, but they're 
-   in currently unused space. Hopefully nobody else will use them*/ 
-#define _fpstate_ia32 _fpstate
-
-struct sigcontext_ia32 {
-       unsigned short gs, __gsh;
-       unsigned short fs, __fsh;
-       unsigned short es, __esh;
-       unsigned short ds, __dsh;
-       unsigned int edi;
-       unsigned int esi;
-       unsigned int ebp;
-       unsigned int esp;
-       unsigned int ebx;
-       unsigned int edx;
-       unsigned int ecx;
-       unsigned int eax;
-       unsigned int trapno;
-       unsigned int err;
-       unsigned int eip;
-       unsigned short cs, __csh;
-       unsigned int eflags;
-       unsigned int esp_at_signal;
-       unsigned short ss, __ssh;
-       unsigned int fpstate;		/* really (struct _fpstate_ia32 *) */
-       unsigned int oldmask;
-       unsigned int cr2;
-};
+#include <asm/sigcontext32.h>
 
 /* signal.h */
 #define _IA32_NSIG	       64
diff -Nru a/include/asm-x86_64/ia32_unistd.h b/include/asm-x86_64/ia32_unistd.h
--- a/include/asm-x86_64/ia32_unistd.h	Mon Apr 22 15:32:18 2002
+++ b/include/asm-x86_64/ia32_unistd.h	Mon Apr 22 15:32:18 2002
@@ -245,7 +245,11 @@
 #define __NR_ia32_lremovexattr	236
 #define __NR_ia32_fremovexattr	237
 #define __NR_ia32_tkill		238
+#define __NR_ia32_sendfile64		239
+#define __NR_ia32_futex		240
+#define __NR_ia32_sched_setaffinity	241
+#define __NR_ia32_sched_getaffinity	242
 
-#define IA32_NR_syscalls 240	/* must be > than biggest syscall! */	
+#define IA32_NR_syscalls 243	/* must be > than biggest syscall! */	
 
 #endif /* _ASM_X86_64_IA32_UNISTD_H_ */
diff -Nru a/include/asm-x86_64/io.h b/include/asm-x86_64/io.h
--- a/include/asm-x86_64/io.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-x86_64/io.h	Mon Apr 22 15:32:19 2002
@@ -166,11 +166,11 @@
 /*
  * However PCI ones are not necessarily 1:1 and therefore these interfaces
  * are forbidden in portable PCI drivers.
+ *
+ * Allow them on x86 for legacy drivers, though.
  */
-extern unsigned long virt_to_bus_not_defined_use_pci_map(volatile void *addr);
-#define virt_to_bus virt_to_bus_not_defined_use_pci_map
-extern unsigned long bus_to_virt_not_defined_use_pci_map(volatile void *addr);
-#define bus_to_virt bus_to_virt_not_defined_use_pci_map
+#define virt_to_bus virt_to_phys
+#define bus_to_virt phys_to_virt
 
 /*
  * readX/writeX() are used to access memory mapped devices. On some
@@ -225,6 +225,17 @@
 #define eth_io_copy_and_sum(a,b,c,d)		eth_copy_and_sum((a),__io_virt(b),(c),(d))
 #define isa_eth_io_copy_and_sum(a,b,c,d)	eth_copy_and_sum((a),__io_virt(__ISA_IO_base + (b)),(c),(d))
 
+/**
+ *	check_signature		-	find BIOS signatures
+ *	@io_addr: mmio address to check 
+ *	@signature:  signature block
+ *	@length: length of signature
+ *
+ *	Perform a signature comparison with the mmio address io_addr. This
+ *	address should have been obtained by ioremap.
+ *	Returns 1 on a match.
+ */
+ 
 static inline int check_signature(unsigned long io_addr,
 	const unsigned char *signature, int length)
 {
@@ -240,6 +251,20 @@
 out:
 	return retval;
 }
+
+/**
+ *	isa_check_signature		-	find BIOS signatures
+ *	@io_addr: mmio address to check 
+ *	@signature:  signature block
+ *	@length: length of signature
+ *
+ *	Perform a signature comparison with the ISA mmio address io_addr.
+ *	Returns 1 on a match.
+ *
+ *	This function is deprecated. New drivers should use ioremap and
+ *	check_signature.
+ */
+ 
 
 static inline int isa_check_signature(unsigned long io_addr,
 	const unsigned char *signature, int length)
diff -Nru a/include/asm-x86_64/io_apic.h b/include/asm-x86_64/io_apic.h
--- a/include/asm-x86_64/io_apic.h	Mon Apr 22 15:32:18 2002
+++ b/include/asm-x86_64/io_apic.h	Mon Apr 22 15:32:18 2002
@@ -3,7 +3,6 @@
 
 #include <linux/config.h>
 #include <asm/types.h>
-#include <asm/mpspec.h>
 
 /*
  * Intel IO-APIC support for SMP and UP systems.
@@ -16,7 +15,8 @@
 #define APIC_MISMATCH_DEBUG
 
 #define IO_APIC_BASE(idx) \
-		((volatile int *)__fix_to_virt(FIX_IO_APIC_BASE_0 + idx))
+		((volatile int *)(__fix_to_virt(FIX_IO_APIC_BASE_0 + idx) \
+		+ (mp_ioapics[idx].mpc_apicaddr & ~PAGE_MASK)))
 
 /*
  * The structure of the IO-APIC:
diff -Nru a/include/asm-x86_64/ipc.h b/include/asm-x86_64/ipc.h
--- a/include/asm-x86_64/ipc.h	Mon Apr 22 15:32:18 2002
+++ b/include/asm-x86_64/ipc.h	Mon Apr 22 15:32:18 2002
@@ -5,6 +5,8 @@
  * These are used to wrap system calls on x86.
  *
  * See arch/i386/kernel/sys_i386.c for ugly details..
+ *
+ * (on x86-64 only used for 32bit emulation) 
  */
 
 struct ipc_kludge {
diff -Nru a/include/asm-x86_64/kdebug.h b/include/asm-x86_64/kdebug.h
--- a/include/asm-x86_64/kdebug.h	Mon Apr 22 15:32:18 2002
+++ b/include/asm-x86_64/kdebug.h	Mon Apr 22 15:32:18 2002
@@ -13,11 +13,26 @@
 
 extern struct notifier_block *die_chain;
 
-enum { 
-	DIE_DIE = 1,
+/* Grossly misnamed. */
+enum die_val { 
+	DIE_OOPS = 1,
 	DIE_INT3,
 	DIE_DEBUG,
 	DIE_PANIC,
+	DIE_NMI,
+	DIE_DIE,
+	DIE_CALL,
+	DIE_CPUINIT,	/* not really a die, but .. */
+	DIE_TRAPINIT,	/* not really a die, but .. */
+	DIE_STOP, 
 }; 
 	
+static inline int notify_die(enum die_val val,char *str,struct pt_regs *regs,long err)
+{ 
+	struct die_args args = { regs: regs, str: str, err: err }; 
+	return notifier_call_chain(&die_chain, val, &args); 
+} 
+
+int printk_address(unsigned long address);
+
 #endif
diff -Nru a/include/asm-x86_64/ldt.h b/include/asm-x86_64/ldt.h
--- a/include/asm-x86_64/ldt.h	Mon Apr 22 15:32:18 2002
+++ b/include/asm-x86_64/ldt.h	Mon Apr 22 15:32:18 2002
@@ -6,20 +6,18 @@
 #ifndef _LINUX_LDT_H
 #define _LINUX_LDT_H
 
-/* Is this to allow userland manipulate LDTs? It looks so. We should
-   consider disallowing LDT manipulations altogether: in long mode
-   there's no possibility of v86 mode, so something will have to
-   break, anyway.					--pavel */
-
 /* Maximum number of LDT entries supported. */
 #define LDT_ENTRIES	8192
 /* The size of each LDT entry. */
 #define LDT_ENTRY_SIZE	8
 
 #ifndef __ASSEMBLY__
+/* Note on 64bit base and limit is ignored and you cannot set
+   DS/ES/CS not to the default values if you still want to do syscalls. This
+   call is more for 32bit mode therefore. */
 struct modify_ldt_ldt_s {
 	unsigned int  entry_number;
-	unsigned long base_addr;
+	unsigned int  base_addr;
 	unsigned int  limit;
 	unsigned int  seg_32bit:1;
 	unsigned int  contents:2;
@@ -27,6 +25,7 @@
 	unsigned int  limit_in_pages:1;
 	unsigned int  seg_not_present:1;
 	unsigned int  useable:1;
+	unsigned int  lm:1;
 };
 
 #define MODIFY_LDT_CONTENTS_DATA	0
diff -Nru a/include/asm-x86_64/mman.h b/include/asm-x86_64/mman.h
--- a/include/asm-x86_64/mman.h	Mon Apr 22 15:32:18 2002
+++ b/include/asm-x86_64/mman.h	Mon Apr 22 15:32:18 2002
@@ -5,6 +5,7 @@
 #define PROT_WRITE	0x2		/* page can be written */
 #define PROT_EXEC	0x4		/* page can be executed */
 #define PROT_NONE	0x0		/* page can not be accessed */
+#define PROT_SEM	0x0
 
 #define MAP_SHARED	0x01		/* Share changes */
 #define MAP_PRIVATE	0x02		/* Changes are private */
diff -Nru a/include/asm-x86_64/mmu_context.h b/include/asm-x86_64/mmu_context.h
--- a/include/asm-x86_64/mmu_context.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-x86_64/mmu_context.h	Mon Apr 22 15:32:19 2002
@@ -5,6 +5,9 @@
 #include <asm/desc.h>
 #include <asm/atomic.h>
 #include <asm/pgalloc.h>
+#include <asm/pda.h>
+#include <asm/pgtable.h>
+#include <asm/tlbflush.h>
 
 /*
  * possibly do the LDT unload here?
@@ -25,7 +28,8 @@
 }
 #endif
 
-static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, struct task_struct *tsk, unsigned cpu)
+static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, 
+			     struct task_struct *tsk, unsigned cpu)
 {
 	if (likely(prev != next)) {
 		/* stop flush ipis for the previous mm */
@@ -42,14 +46,14 @@
 		set_bit(cpu, &next->cpu_vm_mask);
 		set_bit(cpu, &next->context.cpuvalid);
 		/* Re-load page tables */
-		asm volatile("movq %0,level4_pgt": :"r" (__pa(next->pgd) | 7));
+		*read_pda(level4_pgt) = __pa(next->pgd) | _PAGE_TABLE;
 		__flush_tlb();
 	}
 #ifdef CONFIG_SMP
 	else {
 		cpu_tlbstate[cpu].state = TLBSTATE_OK;
 		if(cpu_tlbstate[cpu].active_mm != next)
-			BUG();
+			out_of_line_bug();
 		if(!test_and_set_bit(cpu, &next->cpu_vm_mask)) {
 			/* We were in lazy tlb mode and leave_mm disabled 
 			 * tlb flush IPI delivery. We must flush our tlb.
@@ -64,5 +68,6 @@
 
 #define activate_mm(prev, next) \
 	switch_mm((prev),(next),NULL,smp_processor_id())
+
 
 #endif
diff -Nru a/include/asm-x86_64/module.h b/include/asm-x86_64/module.h
--- a/include/asm-x86_64/module.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-x86_64/module.h	Mon Apr 22 15:32:19 2002
@@ -1,11 +1,14 @@
 #ifndef _ASM_X8664_MODULE_H
 #define _ASM_X8664_MODULE_H
+
 /*
  * This file contains the x8664 architecture specific module code.
+ * Modules need to be mapped near the kernel code to allow 32bit relocations.
  */
 
-#define module_map(x)		vmalloc(x)
-#define module_unmap(x)		vfree(x)
+extern void *module_map(unsigned long);
+extern void module_unmap(void *);
+
 #define module_arch_init(x)	(0)
 #define arch_init_modules(x)	do { } while (0)
 
diff -Nru a/include/asm-x86_64/mpspec.h b/include/asm-x86_64/mpspec.h
--- a/include/asm-x86_64/mpspec.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-x86_64/mpspec.h	Mon Apr 22 15:32:19 2002
@@ -165,6 +165,9 @@
 	MP_BUS_MCA
 };
 extern int mp_bus_id_to_type [MAX_MP_BUSSES];
+extern int mp_bus_id_to_node [MAX_MP_BUSSES];
+extern int mp_bus_id_to_local [MAX_MP_BUSSES];
+extern int quad_local_to_mp_bus_id [NR_CPUS/4][4];
 extern int mp_bus_id_to_pci_bus [MAX_MP_BUSSES];
 
 extern unsigned int boot_cpu_physical_apicid;
diff -Nru a/include/asm-x86_64/msr.h b/include/asm-x86_64/msr.h
--- a/include/asm-x86_64/msr.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-x86_64/msr.h	Mon Apr 22 15:32:19 2002
@@ -1,5 +1,7 @@
 #ifndef X86_64_MSR_H
 #define X86_64_MSR_H 1
+
+#ifndef __ASSEMBLY__
 /*
  * Access to machine-specific registers (available on 586 and better only)
  * Note: the rd* operations modify the parameters directly (without using
@@ -38,7 +40,7 @@
 		     "   .quad 	2b,3b\n\t"						\
 		     ".previous"							\
 		     : "=a" (ret__)							\
-		     : "c" (msr), "0" ((__u32)val), "d" ((val)>>32), "i" (-EFAULT)); 	\
+		     : "c" (msr), "0" ((__u32)val), "d" ((val)>>32), "i" (-EFAULT));\
 	ret__; })
 
 #define rdtsc(low,high) \
@@ -47,8 +49,16 @@
 #define rdtscl(low) \
      __asm__ __volatile__ ("rdtsc" : "=a" (low) : : "edx")
 
-#define rdtscll(val) \
-     __asm__ __volatile__ ("rdtsc" : "=A" (val))
+#define rdtscll(val) do { \
+     unsigned int a,d; \
+     asm volatile("rdtsc" : "=a" (a), "=d" (d)); \
+     (val) = ((unsigned long)a) | (((unsigned long)d)<<32); \
+} while(0)
+
+#define rdpmc(counter,low,high) \
+     __asm__ __volatile__("rdpmc" \
+			  : "=a" (low), "=d" (high) \
+			  : "c" (counter))
 
 #define write_tsc(val1,val2) wrmsr(0x10, val1, val2)
 
@@ -57,6 +67,7 @@
 			  : "=a" (low), "=d" (high) \
 			  : "c" (counter))
 
+#endif
 
 /* AMD/K8 specific MSRs */ 
 #define MSR_EFER 0xc0000080		/* extended feature register */
@@ -66,8 +77,17 @@
 #define MSR_SYSCALL_MASK 0xc0000084	/* EFLAGS mask for syscall */
 #define MSR_FS_BASE 0xc0000100		/* 64bit GS base */
 #define MSR_GS_BASE 0xc0000101		/* 64bit FS base */
-#define MSR_KERNEL_GS_BASE  0xc0000102	/* SwapGS GS shadow (or USER_GS from kernel view) */ 
-
+#define MSR_KERNEL_GS_BASE  0xc0000102	/* SwapGS GS shadow (or USER_GS from kernel) */ 
+/* EFER bits: */ 
+#define _EFER_SCE 0  /* SYSCALL/SYSRET */
+#define _EFER_LME 8  /* Long mode enable */
+#define _EFER_LMA 10 /* Long mode active (read-only) */
+#define _EFER_NX 11  /* No execute enable */
+
+#define EFER_SCE (1<<_EFER_SCE)
+#define EFER_LME (1<<EFER_LME)
+#define EFER_LMA (1<<EFER_LMA)
+#define EFER_NX (1<<_EFER_NX)
 
 /* Intel MSRs. Some also available on other CPUs */
 #define MSR_IA32_PLATFORM_ID	0x17
@@ -95,9 +115,23 @@
 #define MSR_IA32_MC0_ADDR      0x402
 #define MSR_IA32_MC0_MISC      0x403
 
-/* K7 MSRs */
+#define MSR_P6_PERFCTR0			0xc1
+#define MSR_P6_PERFCTR1			0xc2
+#define MSR_P6_EVNTSEL0			0x186
+#define MSR_P6_EVNTSEL1			0x187
+
+/* K7/K8 MSRs. Not complete. See the architecture manual for a more complete list. */
 #define MSR_K7_EVNTSEL0            0xC0010000
 #define MSR_K7_PERFCTR0            0xC0010004
+#define MSR_K7_EVNTSEL1            0xC0010001
+#define MSR_K7_PERFCTR1            0xC0010005
+#define MSR_K7_EVNTSEL2            0xC0010002
+#define MSR_K7_PERFCTR2            0xC0010006
+#define MSR_K7_EVNTSEL3            0xC0010003
+#define MSR_K7_PERFCTR3            0xC0010007
+#define MSR_K8_TOP_MEM1		   0xC001001A
+#define MSR_K8_TOP_MEM2		   0xC001001D
+#define MSR_K8_SYSCFG		   0xC0000010	
 
 /* K6 MSRs */
 #define MSR_K6_EFER			0xC0000080
@@ -136,5 +170,12 @@
 #define MSR_IA32_APICBASE_BSP           (1<<8)
 #define MSR_IA32_APICBASE_ENABLE        (1<<11)
 #define MSR_IA32_APICBASE_BASE          (0xfffff<<12)
+
+
+#define MSR_IA32_THERM_CONTROL		0x19a
+#define MSR_IA32_THERM_INTERRUPT	0x19b
+#define MSR_IA32_THERM_STATUS		0x19c
+#define MSR_IA32_MISC_ENABLE		0x1a0
+
 
 #endif
diff -Nru a/include/asm-x86_64/page.h b/include/asm-x86_64/page.h
--- a/include/asm-x86_64/page.h	Mon Apr 22 15:32:18 2002
+++ b/include/asm-x86_64/page.h	Mon Apr 22 15:32:18 2002
@@ -15,26 +15,13 @@
 #ifdef __KERNEL__
 #ifndef __ASSEMBLY__
 
-#include <linux/config.h>
-
-#ifdef CONFIG_X86_USE_3DNOW
-
+#if 0 
 #include <asm/mmx.h>
-
 #define clear_page(page)	mmx_clear_page((void *)(page))
 #define copy_page(to,from)	mmx_copy_page(to,from)
-
 #else
-
-/*
- *	On older X86 processors its not a win to use MMX here it seems.
- *	Maybe the K6-III ?
- */
-
 #define clear_page(page)	memset((void *)(page), 0, PAGE_SIZE)
-
 #define copy_page(to,from)	memcpy((void *)(to), (void *)(from), PAGE_SIZE)
-
 #endif
 
 #define clear_user_page(page, vaddr)	clear_page(page)
@@ -46,7 +33,7 @@
 typedef struct { unsigned long pte; } pte_t;
 typedef struct { unsigned long pmd; } pmd_t;
 typedef struct { unsigned long pgd; } pgd_t;
-typedef struct { unsigned long level4; } level4_t;
+typedef struct { unsigned long pml4; } pml4_t;
 #define PTE_MASK	PAGE_MASK
 
 typedef struct { unsigned long pgprot; } pgprot_t;
@@ -54,7 +41,7 @@
 #define pte_val(x)	((x).pte)
 #define pmd_val(x)	((x).pmd)
 #define pgd_val(x)	((x).pgd)
-#define level4_val(x)	((x).level4)
+#define pml4_val(x)	((x).pml4)
 #define pgprot_val(x)	((x).pgprot)
 
 #define __pte(x) ((pte_t) { (x) } )
@@ -68,25 +55,24 @@
 /* to align the pointer to the (next) page boundary */
 #define PAGE_ALIGN(addr)	(((addr)+PAGE_SIZE-1)&PAGE_MASK)
 
-
+/* See Documentation/x86_64/mm.txt for a description of the layout. */
 #define __START_KERNEL		0xffffffff80100000
 #define __START_KERNEL_map	0xffffffff80000000
-#define __PAGE_OFFSET           0xffff800000000000
+#define __PAGE_OFFSET           0x0000010000000000
 
 #ifndef __ASSEMBLY__
 
 /*
- * Tell the user there is some problem.
+ * Tell the user there is some problem.  The exception handler decodes this frame.
  */ 
-
 struct bug_frame { 
-	unsigned short ud2;          
+       unsigned char ud2[2];          
 	char *filename;    /* should use 32bit offset instead, but the assembler doesn't like it */ 
 	unsigned short line; 
 } __attribute__((packed)); 
-
 #define BUG() asm volatile("ud2 ; .quad %c1 ; .short %c0" :: "i"(__LINE__), "i" (__FILE__))
-#define PAGE_BUG(page) BUG(); 
+#define PAGE_BUG(page) BUG()
+void out_of_line_bug(void);
 
 /* Pure 2^n version of get_order */
 extern __inline__ int get_order(unsigned long size)
@@ -102,15 +88,24 @@
 	return order;
 }
 
-static unsigned long start_kernel_map __attribute__((unused)) = __START_KERNEL_map; /* FIXME: workaround gcc bug */
-
 #endif /* __ASSEMBLY__ */
 
 #define PAGE_OFFSET		((unsigned long)__PAGE_OFFSET)
-#define __pa(x)			(((unsigned long)(x)>=start_kernel_map)?(unsigned long)(x) - (unsigned long)start_kernel_map:(unsigned long)(x) - PAGE_OFFSET)
+
+/* Note: __pa(&symbol_visible_to_c) should be always replaced with __pa_symbol.
+   Otherwise you risk miscompilation. */ 
+#define __pa(x)			(((unsigned long)(x)>=__START_KERNEL_map)?(unsigned long)(x) - (unsigned long)__START_KERNEL_map:(unsigned long)(x) - PAGE_OFFSET)
+/* __pa_symbol should be used for C visible symbols.
+   This seems to be the official gcc blessed way to do such arithmetic. */ 
+#define __pa_symbol(x)		\
+	({unsigned long v;  \
+	  asm("" : "=r" (v) : "0" (x)); \
+	  __pa(v); })
+
 #define __va(x)			((void *)((unsigned long)(x)+PAGE_OFFSET))
 #define virt_to_page(kaddr)	(mem_map + (__pa(kaddr) >> PAGE_SHIFT))
 #define VALID_PAGE(page)	((page - mem_map) < max_mapnr)
+
 
 #define VM_DATA_DEFAULT_FLAGS  (VM_READ | VM_WRITE | VM_EXEC | \
                                 VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
diff -Nru a/include/asm-x86_64/pda.h b/include/asm-x86_64/pda.h
--- a/include/asm-x86_64/pda.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-x86_64/pda.h	Mon Apr 22 15:32:19 2002
@@ -7,8 +7,6 @@
 #endif
 #include <linux/cache.h>
 
-struct task_struct; 
-
 /* Per processor datastructure. %gs points to it while the kernel runs */ 
 /* To use a new field with the *_pda macros it needs to be added to tools/offset.c */
 struct x8664_pda {
@@ -19,17 +17,20 @@
 	struct task_struct *pcurrent;	/* Current process */
         int irqcount;		    /* Irq nesting counter. Starts with -1 */  	
 	int cpunumber;		    /* Logical CPU number */
-	char *irqstackptr;	  
+	char *irqstackptr;	/* top of irqstack */
+	unsigned long volatile *level4_pgt; 
 	unsigned int __softirq_pending;
 	unsigned int __local_irq_count;
 	unsigned int __local_bh_count;
 	unsigned int __nmi_count;	/* arch dependent */
 	struct task_struct * __ksoftirqd_task; /* waitqueue is too large */
-	char irqstack[16 * 1024];   /* Stack used by interrupts */     
 } ____cacheline_aligned;
 
 #define PDA_STACKOFFSET (5*8)
 
+#define IRQSTACK_ORDER 2
+#define IRQSTACKSIZE (PAGE_SIZE << IRQSTACK_ORDER) 
+
 extern struct x8664_pda cpu_pda[];
 
 /* 
@@ -58,9 +59,9 @@
 #define pda_from_op(op,field) ({ \
        typedef typeof_field(struct x8664_pda, field) T__; T__ ret__; \
        switch (sizeof_field(struct x8664_pda, field)) { 		\
-       case 2: asm volatile (op "w %%gs:" __STR2(pda_ ## field) ",%0":"=r" (ret__)::"memory"); break;	\
-       case 4: asm volatile (op "l %%gs:" __STR2(pda_ ## field) ",%0":"=r" (ret__)::"memory"); break;	\
-       case 8: asm volatile (op "q %%gs:" __STR2(pda_ ## field) ",%0":"=r" (ret__)::"memory"); break;	\
+       case 2: asm volatile(op "w %%gs:" __STR2(pda_ ## field) ",%0":"=r" (ret__)::"memory"); break;	\
+       case 4: asm volatile(op "l %%gs:" __STR2(pda_ ## field) ",%0":"=r" (ret__)::"memory"); break;	\
+       case 8: asm volatile(op "q %%gs:" __STR2(pda_ ## field) ",%0":"=r" (ret__)::"memory"); break;	\
        default: __bad_pda_field(); 					\
        } \
        ret__; })
diff -Nru a/include/asm-x86_64/percpu.h b/include/asm-x86_64/percpu.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/include/asm-x86_64/percpu.h	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,6 @@
+#ifndef __ARCH_I386_PERCPU__
+#define __ARCH_I386_PERCPU__
+
+#include <asm-generic/percpu.h>
+
+#endif /* __ARCH_I386_PERCPU__ */
diff -Nru a/include/asm-x86_64/pgalloc.h b/include/asm-x86_64/pgalloc.h
--- a/include/asm-x86_64/pgalloc.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-x86_64/pgalloc.h	Mon Apr 22 15:32:19 2002
@@ -1,7 +1,6 @@
 #ifndef _X86_64_PGALLOC_H
 #define _X86_64_PGALLOC_H
 
-#include <linux/config.h>
 #include <asm/processor.h>
 #include <asm/fixmap.h>
 #include <asm/pda.h>
@@ -76,84 +75,5 @@
 	__free_page(pte);
 } 
 
-
-/*
- * TLB flushing:
- *
- *  - flush_tlb() flushes the current mm struct TLBs
- *  - flush_tlb_all() flushes all processes TLBs
- *  - flush_tlb_mm(mm) flushes the specified mm context TLB's
- *  - flush_tlb_page(vma, vmaddr) flushes one page
- *  - flush_tlb_range(vma, start, end) flushes a range of pages
- *  - flush_tlb_pgtables(mm, start, end) flushes a range of page tables
- *
- * ..but the i386 has somewhat limited tlb flushing capabilities,
- * and page-granular flushes are available only on i486 and up.
- */
-
-#ifndef CONFIG_SMP
-
-#define flush_tlb() __flush_tlb()
-#define flush_tlb_all() __flush_tlb_all()
-#define local_flush_tlb() __flush_tlb()
-
-static inline void flush_tlb_mm(struct mm_struct *mm)
-{
-	if (mm == current->active_mm)
-		__flush_tlb();
-}
-
-static inline void flush_tlb_page(struct vm_area_struct *vma,
-	unsigned long addr)
-{
-	if (vma->vm_mm == current->active_mm)
-		__flush_tlb_one(addr);
-}
-
-static inline void flush_tlb_range(struct vm_area_struct *vma,
-	unsigned long start, unsigned long end)
-{
-	if (vma->vm_mm == current->active_mm)
-		__flush_tlb();
-}
-
-#else
-
-#include <asm/smp.h>
-
-#define local_flush_tlb() \
-	__flush_tlb()
-
-extern void flush_tlb_all(void);
-extern void flush_tlb_current_task(void);
-extern void flush_tlb_mm(struct mm_struct *);
-extern void flush_tlb_page(struct vm_area_struct *, unsigned long);
-
-#define flush_tlb()	flush_tlb_current_task()
-
-static inline void flush_tlb_range(struct vm_area_struct * vma, unsigned long start, unsigned long end)
-{
-	flush_tlb_mm(vma->vm_mm);
-}
-
-#define TLBSTATE_OK	1
-#define TLBSTATE_LAZY	2
-
-struct tlb_state
-{
-	struct mm_struct *active_mm;
-	int state;
-	char __cacheline_padding[24];
-};
-extern struct tlb_state cpu_tlbstate[NR_CPUS];
-
-
-#endif
-
-extern inline void flush_tlb_pgtables(struct mm_struct *mm,
-				      unsigned long start, unsigned long end)
-{
-	/* i386 does not keep any page table caches in TLB */
-}
 
 #endif /* _X86_64_PGALLOC_H */
diff -Nru a/include/asm-x86_64/pgtable.h b/include/asm-x86_64/pgtable.h
--- a/include/asm-x86_64/pgtable.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-x86_64/pgtable.h	Mon Apr 22 15:32:19 2002
@@ -1,8 +1,6 @@
 #ifndef _X86_64_PGTABLE_H
 #define _X86_64_PGTABLE_H
 
-#include <linux/config.h>
-
 /*
  * This file contains the functions and defines necessary to modify and use
  * the x86-64 page table tree.
@@ -13,82 +11,34 @@
  * three level page setup on the beginning and some kernel mappings at 
  * the end. For more details see Documentation/x86_64/mm.txt
  */
-#ifndef __ASSEMBLY__
 #include <asm/processor.h>
 #include <asm/fixmap.h>
 #include <asm/bitops.h>
 #include <linux/threads.h>
+#include <asm/pda.h>
 
-extern level4_t level4_pgt[512];
 extern pgd_t level3_kernel_pgt[512];
 extern pgd_t level3_physmem_pgt[512];
-extern pgd_t level3_ident_pgt[512], swapper_pg_dir[512];
+extern pgd_t level3_ident_pgt[512];
 extern pmd_t level2_kernel_pgt[512];
-extern void paging_init(void);
+extern pml4_t init_level4_pgt[];
+extern pgd_t boot_vmalloc_pgt[];
 
-/* Caches aren't brain-dead. */
-#define flush_cache_all()			do { } while (0)
-#define flush_cache_mm(mm)			do { } while (0)
-#define flush_cache_range(vma, start, end)	do { } while (0)
-#define flush_cache_page(vma, vmaddr)		do { } while (0)
-#define flush_page_to_ram(page)			do { } while (0)
-#define flush_dcache_page(page)			do { } while (0)
-#define flush_icache_range(start, end)		do { } while (0)
-#define flush_icache_page(vma,pg)		do { } while (0)
-#define flush_icache_user_range(vma,pg,adr,len)       do { } while (0)
-
-#define __flush_tlb()							\
-	do {								\
-		unsigned long tmpreg;					\
-									\
-		__asm__ __volatile__(					\
-			"movq %%cr3, %0;  # flush TLB \n"		\
-			"movq %0, %%cr3;              \n"		\
-			: "=r" (tmpreg)					\
-			:: "memory");					\
-	} while (0)
+#define swapper_pg_dir NULL
 
-/*
- * Global pages have to be flushed a bit differently. Not a real
- * performance problem because this does not happen often.
- */
-#define __flush_tlb_global()						\
-	do {								\
-		unsigned long tmpreg;					\
-									\
-		__asm__ __volatile__(					\
-			"movq %1, %%cr4;  # turn off PGE     \n"	\
-			"movq %%cr3, %0;  # flush TLB        \n"	\
-			"movq %0, %%cr3;                     \n"	\
-			"movq %2, %%cr4;  # turn PGE back on \n"	\
-			: "=&r" (tmpreg)				\
-			: "r" (mmu_cr4_features & ~X86_CR4_PGE),	\
-			  "r" (mmu_cr4_features)			\
-			: "memory");					\
-	} while (0)
+extern void paging_init(void);
 
 extern unsigned long pgkern_mask;
 
 /*
- * Do not check the PGE bit unnecesserily if this is a PPro+ kernel.
- * FIXME: This should be cleaned up
- */
-
-# define __flush_tlb_all() __flush_tlb_global()
-
-#define __flush_tlb_one(addr) __asm__ __volatile__("invlpg %0": :"m" (*(char *) addr))
-
-/*
  * ZERO_PAGE is a global shared page that is always zero: used
  * for zero-mapped memory areas etc..
  */
 extern unsigned long empty_zero_page[1024];
 #define ZERO_PAGE(vaddr) (virt_to_page(empty_zero_page))
 
-#endif /* !__ASSEMBLY__ */
-
-#define LEVEL4_SHIFT	39
-#define PTRS_PER_LEVEL4	512
+#define PML4_SHIFT	39
+#define PTRS_PER_PML4	512
 
 /*
  * PGDIR_SHIFT determines what a top-level page table entry can map
@@ -115,37 +65,34 @@
 #define pgd_ERROR(e) \
 	printk("%s:%d: bad pgd %p(%016lx).\n", __FILE__, __LINE__, &(e), pgd_val(e))
 
-#define level4_none(x)	(!level4_val(x))
+#define pml4_none(x)	(!pml4_val(x))
 #define pgd_none(x)	(!pgd_val(x))
 
-#define pgd_bad(x) ((pgd_val(x) & (~PAGE_MASK & ~_PAGE_USER)) != _KERNPG_TABLE )
-
 extern inline int pgd_present(pgd_t pgd)	{ return !pgd_none(pgd); }
 
 static inline void set_pte(pte_t *dst, pte_t val)
 {
-	*((unsigned long *)dst) = pte_val(val); 
+	pte_val(*dst) = pte_val(val);
 } 
 
 static inline void set_pmd(pmd_t *dst, pmd_t val)
 {
-	*((unsigned long *)dst) = pmd_val(val); 
+        pmd_val(*dst) = pmd_val(val); 
 } 
 
 static inline void set_pgd(pgd_t *dst, pgd_t val)
 {
-	*((unsigned long *)dst) = pgd_val(val); 
+	pgd_val(*dst) = pgd_val(val); 
 } 
 
-extern inline void __pgd_clear (pgd_t * pgd)
+extern inline void pgd_clear (pgd_t * pgd)
 {
 	set_pgd(pgd, __pgd(0));
 }
 
-extern inline void pgd_clear (pgd_t * pgd)
+static inline void set_pml4(pml4_t *dst, pml4_t val)
 {
-	__pgd_clear(pgd);
-	__flush_tlb();
+	pml4_val(*dst) = pml4_val(val); 
 }
 
 #define pgd_page(pgd) \
@@ -177,18 +124,12 @@
 
 
 #ifndef __ASSEMBLY__
-/* Just any arbitrary offset to the start of the vmalloc VM area: the
- * current 8MB value just means that there will be a 8MB "hole" after the
- * physical memory until the kernel virtual memory starts.  That means that
- * any out-of-bounds memory accesses will hopefully be caught.
- * The vmalloc() routines leaves a hole of 4kB between each vmalloced
- * area for the same reason. ;)
- */
-#define VMALLOC_OFFSET	(8*1024*1024)
-#define VMALLOC_START	(((unsigned long) high_memory + 2*VMALLOC_OFFSET-1) & \
-						~(VMALLOC_OFFSET-1))
+#define VMALLOC_START    0xffffff0000000000
+#define VMALLOC_END      0xffffff7fffffffff
 #define VMALLOC_VMADDR(x) ((unsigned long)(x))
-#define VMALLOC_END	(__START_KERNEL_map-PAGE_SIZE)
+#define MODULES_VADDR    0xffffffffa0000000
+#define MODULES_END      0xffffffffafffffff
+#define MODULES_LEN   (MODULES_END - MODULES_VADDR)
 
 #define _PAGE_BIT_PRESENT	0
 #define _PAGE_BIT_RW		1
@@ -197,8 +138,9 @@
 #define _PAGE_BIT_PCD		4
 #define _PAGE_BIT_ACCESSED	5
 #define _PAGE_BIT_DIRTY		6
-#define _PAGE_BIT_PSE		7	/* 4 MB (or 2MB) page, Pentium+, if present.. */
+#define _PAGE_BIT_PSE		7	/* 4 MB (or 2MB) page */
 #define _PAGE_BIT_GLOBAL	8	/* Global TLB entry PPro+ */
+#define _PAGE_BIT_NX           63       /* No execute: only valid after cpuid check */
 
 #define _PAGE_PRESENT	0x001
 #define _PAGE_RW	0x002
@@ -208,9 +150,10 @@
 #define _PAGE_ACCESSED	0x020
 #define _PAGE_DIRTY	0x040
 #define _PAGE_PSE	0x080	/* 2MB page */
-#define _PAGE_GLOBAL	0x100	/* Global TLB entry PPro+ */
+#define _PAGE_GLOBAL	0x100	/* Global TLB entry */
 
 #define _PAGE_PROTNONE	0x080	/* If not present */
+#define _PAGE_NX        (1UL<<_PAGE_BIT_NX)
 
 #define _PAGE_TABLE	(_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | _PAGE_ACCESSED | _PAGE_DIRTY)
 #define _KERNPG_TABLE	(_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY)
@@ -238,11 +181,6 @@
 #define PAGE_KERNEL_NOCACHE MAKE_GLOBAL(__PAGE_KERNEL_NOCACHE)
 #define PAGE_KERNEL_VSYSCALL MAKE_GLOBAL(__PAGE_KERNEL_VSYSCALL)
 
-/*
- * The i386 can't do page protection for execute, and considers that
- * the same are read. Also, write permissions imply read permissions.
- * This is the closest we can get..
- */
 #define __P000	PAGE_NONE
 #define __P001	PAGE_READONLY
 #define __P010	PAGE_COPY
@@ -261,21 +199,13 @@
 #define __S110	PAGE_SHARED
 #define __S111	PAGE_SHARED
 
-/*
- * Define this if things work differently on an i386 and an i486:
- * it will (on an i486) warn about kernel memory accesses that are
- * done without a 'verify_area(VERIFY_WRITE,..)'
- */
-#undef TEST_VERIFY_AREA
-
-/* page table for 0-4MB for everybody */
-extern unsigned long pg0[1024];
-
-/*
- * Handling allocation failures during page table setup.
- */
-extern void __handle_bad_pmd(pmd_t * pmd);
-extern void __handle_bad_pmd_kernel(pmd_t * pmd);
+static inline unsigned long pgd_bad(pgd_t pgd) 
+{ 
+       unsigned long val = pgd_val(pgd);
+       val &= ~PAGE_MASK; 
+       val &= ~(_PAGE_USER | _PAGE_DIRTY); 
+       return val & ~(_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED);      
+} 
 
 #define pte_none(x)	(!pte_val(x))
 #define pte_present(x)	(pte_val(x) & (_PAGE_PRESENT | _PAGE_PROTNONE))
@@ -286,10 +216,6 @@
 #define pmd_clear(xp)	do { set_pmd(xp, __pmd(0)); } while (0)
 #define	pmd_bad(x)	((pmd_val(x) & (~PAGE_MASK & ~_PAGE_USER)) != _KERNPG_TABLE )
 
-/*
- * Permanent address of a page. Obviously must never be
- * called on a highmem page.
- */
 #define pages_to_mb(x) ((x) >> (20-PAGE_SHIFT))	/* FIXME: is this
 						   right? */
 #define pte_page(x) (mem_map+((unsigned long)((pte_val(x) >> PAGE_SHIFT))))
@@ -324,64 +250,82 @@
  * and a page entry and page directory to the page they refer to.
  */
 
-#define mk_pte(page,pgprot) \
-({									\
-	pte_t __pte;							\
-									\
-	set_pte(&__pte, __pte(((page)-mem_map) * 			\
-		(unsigned long long)PAGE_SIZE + pgprot_val(pgprot)));	\
-	__pte;								\
-})
-
-/* This takes a physical page address that is used by the remapping functions */
-#define mk_pte_phys(physpage, pgprot) \
-({ pte_t __pte; set_pte(&__pte, __pte(physpage + pgprot_val(pgprot))); __pte; })
-
-extern inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
-{ set_pte(&pte, __pte((pte_val(pte) & _PAGE_CHG_MASK) | pgprot_val(newprot))); return pte; }
-
 #define page_pte(page) page_pte_prot(page, __pgprot(0))
 
-#define pmd_page_kernel(pmd) \
-((unsigned long) __va(pmd_val(pmd) & PAGE_MASK))
-#define pmd_page(pmd) \
-       (mem_map + (pmd_val(pmd) >> PAGE_SHIFT))
+/*
+ * Level 4 access.
+ * Never use these in the common code.
+ */
+#define pml4_page(pml4) ((unsigned long) __va(pml4_val(pml4) & PAGE_MASK))
+#define pml4_index(address) ((address >> PML4_SHIFT) & (PTRS_PER_PML4-1))
+#define pml4_offset_k(address) (init_level4_pgt + pml4_index(address))
+#define mk_kernel_pml4(address) ((pml4_t){ (address) | _KERNPG_TABLE })
+#define level3_offset_k(dir, address) ((pgd_t *) pml4_page(*(dir)) + pgd_index(address))
 
+/* PGD - Level3 access */
+
+#define __pgd_offset_k(pgd, address) ((pgd) + pgd_index(address))
 /* to find an entry in a page-table-directory. */
 #define pgd_index(address) ((address >> PGDIR_SHIFT) & (PTRS_PER_PGD-1))
+#define current_pgd_offset_k(address) \
+       __pgd_offset_k((pgd_t *)read_pda(level4_pgt), address)
 
-#define __pgd_offset(address) pgd_index(address)
+/* This accesses the reference page table of the boot cpu. 
+   Other CPUs get synced lazily via the page fault handler. */
+static inline pgd_t *pgd_offset_k(unsigned long address)
+{
+	pml4_t pml4;
+
+	pml4 = init_level4_pgt[pml4_index(address)];
+	return __pgd_offset_k(__va(pml4_val(pml4) & PAGE_MASK), address);
+}
 
+#define __pgd_offset(address) pgd_index(address)
 #define pgd_offset(mm, address) ((mm)->pgd+pgd_index(address))
 
-/* to find an entry in a kernel page-table-directory */
-#define pgd_offset_k(address) pgd_offset(&init_mm, address)
+/* PMD  - Level 2 access */
+#define pmd_page_kernel(pmd) ((unsigned long) __va(pmd_val(pmd) & PAGE_MASK))
+#define pmd_page(pmd)        (mem_map + (pmd_val(pmd) >> PAGE_SHIFT))
 
 #define __pmd_offset(address) \
 		(((address) >> PMD_SHIFT) & (PTRS_PER_PMD-1))
 
-/* Find an entry in the third-level page table.. */
+/* PTE - Level 1 access. */
+
+#define mk_pte(page,pgprot) \
+({                                                                     \
+       pte_t __pte;                                                    \
+                                                                       \
+       set_pte(&__pte, __pte(((page)-mem_map) *                        \
+               (unsigned long long)PAGE_SIZE + pgprot_val(pgprot)));   \
+       __pte;                                                          \
+})
+ 
+/* This takes a physical page address that is used by the remapping functions */
+static inline pte_t mk_pte_phys(unsigned long physpage, pgprot_t pgprot)
+{ 
+       pte_t __pte; 
+       set_pte(&__pte, __pte(physpage + pgprot_val(pgprot))); 
+       return __pte;
+}
+ 
+extern inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
+{ 
+       set_pte(&pte, __pte((pte_val(pte) & _PAGE_CHG_MASK) | pgprot_val(newprot))); 
+       return pte; 
+}
+
 #define __pte_offset(address) \
 		((address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))
 #define pte_offset_kernel(dir, address) ((pte_t *) pmd_page_kernel(*(dir)) + \
 			__pte_offset(address))
 
+/* x86-64 always has all page tables mapped. */
 #define pte_offset_map(dir,address) pte_offset_kernel(dir,address)
 #define pte_offset_map_nested(dir,address) pte_offset_kernel(dir,address)
 #define pte_unmap(pte) /* NOP */
 #define pte_unmap_nested(pte) /* NOP */ 
 
-
-/* never use these in the common code */
-#define level4_page(level4) ((unsigned long) __va(level4_val(level4) & PAGE_MASK))
-#define level4_index(address) ((address >> LEVEL4_SHIFT) & (PTRS_PER_LEVEL4-1))
-#define level4_offset_k(address) (level4_pgt + level4_index(address))
-#define level3_offset_k(dir, address) ((pgd_t *) level4_page(*(dir)) + pgd_index(address))
-
-/*
- * The i386 doesn't have any external MMU info: the kernel page
- * tables contain all the necessary information.
- */
 #define update_mmu_cache(vma,address,pte) do { } while (0)
 
 /* Encode and de-code a swap entry */
@@ -402,6 +346,6 @@
 #define HAVE_ARCH_UNMAPPED_AREA
 
 #define pgtable_cache_init()   do { } while (0)
-
+#define check_pgt_cache()      do { } while (0)
 
 #endif /* _X86_64_PGTABLE_H */
diff -Nru a/include/asm-x86_64/processor.h b/include/asm-x86_64/processor.h
--- a/include/asm-x86_64/processor.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-x86_64/processor.h	Mon Apr 22 15:32:19 2002
@@ -28,6 +28,10 @@
 #define VIP_MASK	0x00100000	/* virtual interrupt pending */
 #define ID_MASK		0x00200000
 
+/*
+ * Default implementation of macro that returns current
+ * instruction pointer ("program counter").
+ */
 #define current_text_addr() ({ void *pc; asm volatile("leaq 1f(%%rip),%0\n1:":"=r"(pc)); pc; })
 
 /*
@@ -218,6 +222,10 @@
 		:"ax");
 }
 
+#if 0
+/*
+ *      Cyrix CPU configuration register indexes
+ */
 #define CX86_CCR0 0xc0
 #define CX86_CCR1 0xc1
 #define CX86_CCR2 0xc2
@@ -242,6 +250,7 @@
 	outb((data), 0x23); \
 } while (0)
 
+#endif
 
 /*
  * Bus types
@@ -262,7 +271,7 @@
 #define TASK_UNMAPPED_32 0x40000000
 #define TASK_UNMAPPED_64 (TASK_SIZE/3) 
 #define TASK_UNMAPPED_BASE	\
-	((current->thread.flags & THREAD_IA32) ? TASK_UNMAPPED_32 : TASK_UNMAPPED_64)  
+	(test_thread_flags(TIF_IA32) ? TASK_UNMAPPED_32 : TASK_UNMAPPED_64)  
 
 /*
  * Size of io_bitmap in longwords: 32 is ports 0-0x3ff.
@@ -271,54 +280,22 @@
 #define IO_BITMAP_OFFSET offsetof(struct tss_struct,io_bitmap)
 #define INVALID_IO_BITMAP_OFFSET 0x8000
 
-/* We'll have to decide which format to use for floating stores, and
-   kill all others... */
-struct i387_fsave_struct {
-	u32	cwd;
-	u32	swd;
-	u32	twd;
-	u32	fip;
-	u32	fcs;
-	u32	foo;
-	u32	fos;
-	u32	st_space[20];	/* 8*10 bytes for each FP-reg = 80 bytes */
-	u32	status;		/* software status information */
-};
-
 struct i387_fxsave_struct {
 	u16	cwd;
 	u16	swd;
 	u16	twd;
 	u16	fop;
-	u32	fip;
-	u32	fcs;
-	u32	foo;
-	u32	fos;
+	u64	rip;
+	u64	rdp; 
 	u32	mxcsr;
-	u32	reserved;
+	u32	mxcsr_mask;
 	u32	st_space[32];	/* 8*16 bytes for each FP-reg = 128 bytes */
-	u32	xmm_space[32];	/* 8*16 bytes for each XMM-reg = 128 bytes */
-	u32	padding[56];
+	u32	xmm_space[64];	/* 16*16 bytes for each XMM-reg = 128 bytes */
+	u32	padding[24];
 } __attribute__ ((aligned (16)));
 
-struct i387_soft_struct {
-	u32	cwd;
-	u32	swd;
-	u32	twd;
-	u32	fip;
-	u32	fcs;
-	u32	foo;
-	u32	fos;
-	u32	st_space[20];	/* 8*10 bytes for each FP-reg = 80 bytes */
-	unsigned char	ftop, changed, lookahead, no_update, rm, alimit;
-	struct info	*info;
-	unsigned long	entry_eip;
-};
-
 union i387_union {
-	struct i387_fsave_struct	fsave;
 	struct i387_fxsave_struct	fxsave;
-	struct i387_soft_struct soft;
 };
 
 typedef struct {
@@ -365,26 +342,11 @@
 #define INIT_MMAP \
 { &init_mm, 0, 0, NULL, PAGE_SHARED, VM_READ | VM_WRITE | VM_EXEC, 1, NULL, NULL }
 
-
-#ifndef CONFIG_SMP
-extern char stackfault_stack[]; 
-#define STACKDESC rsp2: (unsigned long)stackfault_stack,
-#define STACKFAULT_STACK 2
-#else
-#define STACKFAULT_STACK 0
-#define STACKDESC
-#endif
-
-/* Doublefault currently shares the same stack on all CPUs. Hopefully
-   only one gets into this unfortunate condition at a time. Cannot do
-   the same for SF because that can be easily triggered by user
-   space. */
-#define INIT_TSS  {						\
-	rsp1: (unsigned long)doublefault_stack, 		\
-	STACKDESC \
-}
-
-extern char doublefault_stack[];
+#define STACKFAULT_STACK 1
+#define DOUBLEFAULT_STACK 2 
+#define NMI_STACK 3 
+#define N_EXCEPTION_STACKS 3  /* hw limit: 7 */
+#define EXCEPTION_STKSZ 1024
 
 #define start_thread(regs,new_rip,new_rsp) do { \
 	__asm__("movl %0,%%fs; movl %0,%%es; movl %0,%%ds": :"r" (0));		 \
@@ -414,22 +376,14 @@
 
 /*
  * Return saved PC of a blocked thread.
+ * What is this good for? it will be always the scheduler or ret_from_fork.
  */
-extern inline unsigned long thread_saved_pc(struct task_struct *t)
-{
-	return -1;  /* FIXME */
-}
-
-unsigned long get_wchan(struct task_struct *p);
-
+#define thread_saved_pc(t) (*(unsigned long *)((t)->thread.rsp - 8))
 
-/* FIXME: this is incorrect when the task is sleeping in a syscall entered
-   through SYSCALL. */ 
-#define __kstk_regs(tsk)  \
-	((struct pt_regs *)\
-	(((char *)(tsk)->thread_info) + THREAD_SIZE - sizeof(struct pt_regs)))
-#define KSTK_EIP(tsk) (__kstk_regs(tsk)->rip)
-#define KSTK_ESP(tsk) (__kstk_regs(tsk)->rsp)
+extern unsigned long get_wchan(struct task_struct *p);
+#define KSTK_EIP(tsk) \
+	(((struct pt_regs *)(tsk->thread.rsp0 - sizeof(struct pt_regs)))->rip)
+#define KSTK_ESP(tsk) -1 /* sorry. doesn't work for syscall. */
 
 /* REP NOP (PAUSE) is a good thing to insert into busy-wait loops. */
 extern inline void rep_nop(void)
@@ -439,23 +393,12 @@
 
 #define cpu_has_fpu 1
 
-/* 3d now! prefetch instructions. Could also use the SSE flavours; not sure
-   if it makes a difference. gcc 3.1 has __builtin_prefetch too, but I am
-   not sure it makes sense to use them. */ 
-
 #define ARCH_HAS_PREFETCH
 #define ARCH_HAS_PREFETCHW
 #define ARCH_HAS_SPINLOCK_PREFETCH
 
-extern inline void prefetch(const void *x)
-{
-    __asm__ __volatile__ ("prefetch (%0)" : : "r"(x));
-}
-
-extern inline void prefetchw(const void *x)
-{
-    __asm__ __volatile__ ("prefetchw (%0)" : : "r"(x));
-}
+#define prefetch(x) __builtin_prefetch((x),0)
+#define prefetchw(x) __builtin_prefetch((x),1)
 #define spin_lock_prefetch(x)  prefetchw(x)
 #define cpu_relax()   rep_nop()
 
diff -Nru a/include/asm-x86_64/ptrace.h b/include/asm-x86_64/ptrace.h
--- a/include/asm-x86_64/ptrace.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-x86_64/ptrace.h	Mon Apr 22 15:32:19 2002
@@ -1,7 +1,7 @@
 #ifndef _X86_64_PTRACE_H
 #define _X86_64_PTRACE_H
 
-#ifdef __ASSEMBLY__
+#if defined(__ASSEMBLY__) || defined(__FRAME_OFFSETS) 
 #define R15 0
 #define R14 8
 #define R13 16
@@ -82,8 +82,8 @@
 #define PTRACE_SETFPXREGS         19
 
 #if defined(__KERNEL__) && !defined(__ASSEMBLY__) 
-#define user_mode(regs) ((regs)->rsp <= PAGE_OFFSET)
-#define instruction_pointer(regs) ((regs)->eip)
+#define user_mode(regs) (!!((regs)->cs & 3))
+#define instruction_pointer(regs) ((regs)->rip)
 extern void show_regs(struct pt_regs *);
 
 enum {
diff -Nru a/include/asm-x86_64/rwlock.h b/include/asm-x86_64/rwlock.h
--- a/include/asm-x86_64/rwlock.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-x86_64/rwlock.h	Mon Apr 22 15:32:19 2002
@@ -6,7 +6,7 @@
  *	spinlock.h Copyright 1996 Linus Torvalds.
  *
  *	Copyright 1999 Red Hat, Inc.
- *	Copyright 2001 SuSE labs 
+ *	Copyright 2001,2002 SuSE labs 
  *
  *	Written by Benjamin LaHaise.
  *
@@ -18,6 +18,8 @@
 #ifndef _ASM_X86_64_RWLOCK_H
 #define _ASM_X86_64_RWLOCK_H
 
+#include <linux/stringify.h>
+
 #define RW_LOCK_BIAS		 0x01000000
 #define RW_LOCK_BIAS_STR	"0x01000000"
 
@@ -25,23 +27,23 @@
 	asm volatile(LOCK "subl $1,(%0)\n\t" \
 		     "js 2f\n" \
 		     "1:\n" \
-		     ".section .text.lock,\"ax\"\n" \
+		    LOCK_SECTION_START("") \
 		     "2:\tcall " helper "\n\t" \
 		     "jmp 1b\n" \
-		     ".previous" \
-		     ::"d" (rw) : "memory")
+		    LOCK_SECTION_END \
+		     ::"a" (rw) : "memory")
 
 #define __build_read_lock_const(rw, helper)   \
 	asm volatile(LOCK "subl $1,%0\n\t" \
 		     "js 2f\n" \
 		     "1:\n" \
-		     ".section .text.lock,\"ax\"\n" \
+		    LOCK_SECTION_START("") \
 		     "2:\tpushq %%rax\n\t" \
-		     "leal %0,%%eax\n\t" \
+		     "leaq %0,%%rax\n\t" \
 		     "call " helper "\n\t" \
 		     "popq %%rax\n\t" \
 		     "jmp 1b\n" \
-		     ".previous" \
+		    LOCK_SECTION_END \
 		     :"=m" (*((volatile int *)rw))::"memory")
 
 #define __build_read_lock(rw, helper)	do { \
@@ -55,23 +57,23 @@
 	asm volatile(LOCK "subl $" RW_LOCK_BIAS_STR ",(%0)\n\t" \
 		     "jnz 2f\n" \
 		     "1:\n" \
-		     ".section .text.lock,\"ax\"\n" \
+		     LOCK_SECTION_START("") \
 		     "2:\tcall " helper "\n\t" \
 		     "jmp 1b\n" \
-		     ".previous" \
-		     ::"d" (rw) : "memory")
+		     LOCK_SECTION_END \
+		     ::"a" (rw) : "memory")
 
 #define __build_write_lock_const(rw, helper) \
 	asm volatile(LOCK "subl $" RW_LOCK_BIAS_STR ",(%0)\n\t" \
 		     "jnz 2f\n" \
 		     "1:\n" \
-		     ".section .text.lock,\"ax\"\n" \
+		    LOCK_SECTION_START("") \
 		     "2:\tpushq %%rax\n\t" \
 		     "leaq %0,%%rax\n\t" \
 		     "call " helper "\n\t" \
 		     "popq %%rax\n\t" \
 		     "jmp 1b\n" \
-		     ".previous" \
+		    LOCK_SECTION_END \
 		     :"=m" (*((volatile long *)rw))::"memory")
 
 #define __build_write_lock(rw, helper)	do { \
diff -Nru a/include/asm-x86_64/rwsem.h b/include/asm-x86_64/rwsem.h
--- a/include/asm-x86_64/rwsem.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-x86_64/rwsem.h	Mon Apr 22 15:32:19 2002
@@ -52,7 +52,7 @@
  * the semaphore definition
  */
 struct rw_semaphore {
-	signed long		count;
+	signed int		count;
 #define RWSEM_UNLOCKED_VALUE		0x00000000
 #define RWSEM_ACTIVE_BIAS		0x00000001
 #define RWSEM_ACTIVE_MASK		0x0000ffff
@@ -99,17 +99,17 @@
 {
 	__asm__ __volatile__(
 		"# beginning down_read\n\t"
-LOCK_PREFIX	"  incl      (%%rax)\n\t" /* adds 0x00000001, returns the old value */
+LOCK_PREFIX	"  incl      (%%rdi)\n\t" /* adds 0x00000001, returns the old value */
 		"  js        2f\n\t" /* jump if we weren't granted the lock */
 		"1:\n\t"
-		".section .text.lock,\"ax\"\n"
+		LOCK_SECTION_START("") \
 		"2:\n\t"
 		"  call      rwsem_down_read_failed_thunk\n\t"
 		"  jmp       1b\n"
-		".previous"
+		LOCK_SECTION_END \
 		"# ending down_read\n\t"
 		: "+m"(sem->count)
-		: "a"(sem)
+		: "D"(sem)
 		: "memory", "cc");
 }
 
@@ -123,18 +123,18 @@
 	tmp = RWSEM_ACTIVE_WRITE_BIAS;
 	__asm__ __volatile__(
 		"# beginning down_write\n\t"
-LOCK_PREFIX	"  xadd      %0,(%%rax)\n\t" /* subtract 0x0000ffff, returns the old value */
+LOCK_PREFIX	"  xaddl      %0,(%%rdi)\n\t" /* subtract 0x0000ffff, returns the old value */
 		"  testl     %0,%0\n\t" /* was the count 0 before? */
 		"  jnz       2f\n\t" /* jump if we weren't granted the lock */
 		"1:\n\t"
-		".section .text.lock,\"ax\"\n"
+		LOCK_SECTION_START("")
 		"2:\n\t"
 		"  call      rwsem_down_write_failed_thunk\n\t"
 		"  jmp       1b\n"
-		".previous\n"
+		LOCK_SECTION_END
 		"# ending down_write"
-		: "=r" (tmp) 
-		: "0"(tmp), "a"(sem)
+		: "=&r" (tmp) 
+		: "0"(tmp), "D"(sem)
 		: "memory", "cc");
 }
 
@@ -146,20 +146,20 @@
 	__s32 tmp = -RWSEM_ACTIVE_READ_BIAS;
 	__asm__ __volatile__(
 		"# beginning __up_read\n\t"
-LOCK_PREFIX	"  xadd      %%edx,(%%rax)\n\t" /* subtracts 1, returns the old value */
+LOCK_PREFIX	"  xaddl      %%edx,(%%rdi)\n\t" /* subtracts 1, returns the old value */
 		"  js        2f\n\t" /* jump if the lock is being waited upon */
 		"1:\n\t"
-		".section .text.lock,\"ax\"\n"
+		LOCK_SECTION_START("")
 		"2:\n\t"
 		"  decw      %%dx\n\t" /* do nothing if still outstanding active readers */
 		"  jnz       1b\n\t"
 		"  call      rwsem_wake_thunk\n\t"
 		"  jmp       1b\n"
-		".previous\n"
+		LOCK_SECTION_END
 		"# ending __up_read\n"
-		: "+d"(tmp)
-		: "a"(sem)
-		: "memory");
+		: "+m"(sem->count), "+d"(tmp)
+		: "D"(sem)
+		: "memory", "cc");
 }
 
 /*
@@ -169,21 +169,21 @@
 {
 	__asm__ __volatile__(
 		"# beginning __up_write\n\t"
-		"  movl      %1,%%edx\n\t"
-LOCK_PREFIX	"  xaddl     %%edx,(%%rax)\n\t" /* tries to transition 0xffff0001 -> 0x00000000 */
+		"  movl      %2,%%edx\n\t"
+LOCK_PREFIX	"  xaddl     %%edx,(%%rdi)\n\t" /* tries to transition 0xffff0001 -> 0x00000000 */
 		"  jnz       2f\n\t" /* jump if the lock is being waited upon */
 		"1:\n\t"
-		".section .text.lock,\"ax\"\n"
+		LOCK_SECTION_START("")
 		"2:\n\t"
 		"  decw      %%dx\n\t" /* did the active count reduce to 0? */
 		"  jnz       1b\n\t" /* jump back if not */
 		"  call      rwsem_wake_thunk\n\t"
 		"  jmp       1b\n"
-		".previous\n"
+		LOCK_SECTION_END
 		"# ending __up_write\n"
-		: 
-		: "a"(sem), "i"(-RWSEM_ACTIVE_WRITE_BIAS)
-		: "memory", "cc", "edx");
+		: "+m"(sem->count)
+		: "D"(sem), "i"(-RWSEM_ACTIVE_WRITE_BIAS)
+		: "memory", "cc", "rdx");
 }
 
 /*
@@ -205,7 +205,7 @@
 	int tmp = delta;
 
 	__asm__ __volatile__(
-LOCK_PREFIX	"xadd %0,(%2)"
+LOCK_PREFIX	"xaddl %0,(%2)"
 		: "=r"(tmp), "=m"(sem->count)
 		: "r"(sem), "m"(sem->count), "0" (tmp)
 		: "memory");
@@ -214,4 +214,4 @@
 }
 
 #endif /* __KERNEL__ */
-#endif /* _I386_RWSEM_H */
+#endif /* _X8664_RWSEM_H */
diff -Nru a/include/asm-x86_64/segment.h b/include/asm-x86_64/segment.h
--- a/include/asm-x86_64/segment.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-x86_64/segment.h	Mon Apr 22 15:32:19 2002
@@ -4,6 +4,8 @@
 #define __KERNEL_CS	0x10
 #define __KERNEL_DS	0x18
 
+#define __KERNEL32_CS   0x38
+
 /* 
  * we cannot use the same code segment descriptor for user and kernel
  * even not in the long flat model, because of different DPL /kkeil 
diff -Nru a/include/asm-x86_64/semaphore.h b/include/asm-x86_64/semaphore.h
--- a/include/asm-x86_64/semaphore.h	Mon Apr 22 15:32:18 2002
+++ b/include/asm-x86_64/semaphore.h	Mon Apr 22 15:32:18 2002
@@ -41,6 +41,7 @@
 #include <asm/rwlock.h>
 #include <linux/wait.h>
 #include <linux/rwsem.h>
+#include <linux/stringify.h>
 
 struct semaphore {
 	atomic_t count;
@@ -123,10 +124,10 @@
 		LOCK "decl %0\n\t"     /* --sem->count */
 		"js 2f\n"
 		"1:\n"
-		".section .text.lock,\"ax\"\n"
+		LOCK_SECTION_START("")
 		"2:\tcall __down_failed\n\t"
 		"jmp 1b\n"
-		".previous"
+		LOCK_SECTION_END
 		:"=m" (sem->count)
 		:"D" (sem)
 		:"memory");
@@ -150,10 +151,10 @@
 		"js 2f\n\t"
 		"xorl %0,%0\n"
 		"1:\n"
-		".section .text.lock,\"ax\"\n"
+		LOCK_SECTION_START("")
 		"2:\tcall __down_failed_interruptible\n\t"
 		"jmp 1b\n"
-		".previous"
+		LOCK_SECTION_END
 		:"=a" (result), "=m" (sem->count)
 		:"D" (sem)
 		:"memory");
@@ -178,13 +179,13 @@
 		"js 2f\n\t"
 		"xorl %0,%0\n"
 		"1:\n"
-		".section .text.lock,\"ax\"\n"
+		LOCK_SECTION_START("")
 		"2:\tcall __down_failed_trylock\n\t"
 		"jmp 1b\n"
-		".previous"
+		LOCK_SECTION_END
 		:"=a" (result), "=m" (sem->count)
 		:"D" (sem)
-		:"memory");
+		:"memory","cc");
 	return result;
 }
 
@@ -204,10 +205,10 @@
 		LOCK "incl %0\n\t"     /* ++sem->count */
 		"jle 2f\n"
 		"1:\n"
-		".section .text.lock,\"ax\"\n"
+		LOCK_SECTION_START("")
 		"2:\tcall __up_wakeup\n\t"
 		"jmp 1b\n"
-		".previous"
+		LOCK_SECTION_END
 		:"=m" (sem->count)
 		:"D" (sem)
 		:"memory");
diff -Nru a/include/asm-x86_64/sigcontext.h b/include/asm-x86_64/sigcontext.h
--- a/include/asm-x86_64/sigcontext.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-x86_64/sigcontext.h	Mon Apr 22 15:32:19 2002
@@ -3,71 +3,28 @@
 
 #include <asm/types.h>
 
-/*
- * The first part of "struct _fpstate" is just the normal i387
- * hardware setup, the extra "status" word is used to save the
- * coprocessor status word before entering the handler.
- *
- * Pentium III FXSR, SSE support
- *	Gareth Hughes <gareth@valinux.com>, May 2000
- *
- * The FPU state data structure has had to grow to accomodate the
- * extended FPU state required by the Streaming SIMD Extensions.
- * There is no documented standard to accomplish this at the moment.
- */
-struct _fpreg {
-	unsigned short significand[4];
-	unsigned short exponent;
-};
-
-struct _fpxreg {
-	unsigned short significand[4];
-	unsigned short exponent;
-	unsigned short padding[3];
-};
-
-struct _xmmreg {
-	__u32	element[4];
-};
-
-
-/* This is FXSAVE layout without 64bit prefix thus 32bit compatible. 
-   This means that the IP and DPs are only 32bit and are not useful
-   in 64bit space.
-   If someone used them we would need to switch to 64bit FXSAVE.   
-*/ 
+/* FXSAVE frame */
+/* Note: reserved1/2 may someday contain valuable data. Always save/restore
+   them when you change signal frames. */
 struct _fpstate {
-	/* Regular FPU environment */
-	__u32 	cw;
-	__u32	sw;
-	__u32	tag;
-	__u32	ipoff;
-	__u32	cssel;
-	__u32	dataoff;
-	__u32	datasel;
-	struct _fpreg	_st[8];
-	unsigned short	status;
-	unsigned short	magic;		/* 0xffff = regular FPU data only */
-
-	/* FXSR FPU environment */
-	__u32	_fxsr_env[6];
+	__u16	cwd;
+	__u16	swd;
+	__u16	twd;	/* Note this is not the same as the 32bit/x87/FSAVE twd */
+	__u16	fop;
+	__u64	rip;
+	__u64	rdp; 
 	__u32	mxcsr;
-	__u32	reserved;
-	struct _fpxreg	_fxsr_st[8];
-	struct _xmmreg	_xmm[8];	/* It's actually 16 */ 
-	__u32	padding[56];
+	__u32	mxcsr_mask;
+	__u32	st_space[32];	/* 8*16 bytes for each FP-reg */
+	__u32	xmm_space[64];	/* 16*16 bytes for each XMM-reg  */
+	__u32	reserved2[24];
 };
 
-#define X86_FXSR_MAGIC		0x0000
-
 struct sigcontext { 
-	unsigned short gs, __gsh;
-	unsigned short fs, __fsh;
-	unsigned short es, __esh;
-	unsigned short ds, __dsh;
 	unsigned long r8;
 	unsigned long r9;
 	unsigned long r10;
+	unsigned long r11;
 	unsigned long r12;
 	unsigned long r13;
 	unsigned long r14;
@@ -78,20 +35,20 @@
 	unsigned long rbx;
 	unsigned long rdx;
 	unsigned long rax;
-	unsigned long trapno;
-	unsigned long err;
+	unsigned long rcx;
+	unsigned long rsp;
 	unsigned long rip;
-	unsigned short cs, __csh;
-	unsigned int __pad0;
-	unsigned long eflags;
-	unsigned long rsp_at_signal;
-	struct _fpstate * fpstate;
+	unsigned long eflags;		/* RFLAGS */
+	unsigned short cs;
+	unsigned short gs;
+	unsigned short fs;
+	unsigned short __pad0; 
+	unsigned long err;
+	unsigned long trapno;
 	unsigned long oldmask;
 	unsigned long cr2;
-	unsigned long r11;
-	unsigned long rcx;
-	unsigned long rsp;
+	struct _fpstate *fpstate;	/* zero when no FPU context */
+	unsigned long reserved1[8];
 };
-
 
 #endif
diff -Nru a/include/asm-x86_64/sigcontext32.h b/include/asm-x86_64/sigcontext32.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/include/asm-x86_64/sigcontext32.h	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,71 @@
+#ifndef _SIGCONTEXT32_H
+#define _SIGCONTEXT32_H 1
+
+/* signal context for 32bit programs. */
+
+#define X86_FXSR_MAGIC		0x0000
+
+struct _fpreg {
+	unsigned short significand[4];
+	unsigned short exponent;
+};
+
+struct _fpxreg {
+	unsigned short significand[4];
+	unsigned short exponent;
+	unsigned short padding[3];
+};
+
+struct _xmmreg {
+	__u32	element[4];
+};
+
+/* FSAVE frame with extensions */
+struct _fpstate_ia32 {
+	/* Regular FPU environment */
+	__u32 	cw;
+	__u32	sw;
+	__u32	tag;	/* not compatible to 64bit twd */
+	__u32	ipoff;			
+	__u32	cssel;
+	__u32	dataoff;
+	__u32	datasel;
+	struct _fpreg	_st[8];
+	unsigned short	status;
+	unsigned short	magic;		/* 0xffff = regular FPU data only */
+
+	/* FXSR FPU environment */
+	__u32	_fxsr_env[6];
+	__u32	mxcsr;
+	__u32	reserved;
+	struct _fpxreg	_fxsr_st[8];
+	struct _xmmreg	_xmm[8];	/* It's actually 16 */ 
+	__u32	padding[56];
+};
+
+struct sigcontext_ia32 {
+       unsigned short gs, __gsh;
+       unsigned short fs, __fsh;
+       unsigned short es, __esh;
+       unsigned short ds, __dsh;
+       unsigned int edi;
+       unsigned int esi;
+       unsigned int ebp;
+       unsigned int esp;
+       unsigned int ebx;
+       unsigned int edx;
+       unsigned int ecx;
+       unsigned int eax;
+       unsigned int trapno;
+       unsigned int err;
+       unsigned int eip;
+       unsigned short cs, __csh;
+       unsigned int eflags;
+       unsigned int esp_at_signal;
+       unsigned short ss, __ssh;
+       unsigned int fpstate;		/* really (struct _fpstate_ia32 *) */
+       unsigned int oldmask;
+       unsigned int cr2;
+};
+
+#endif
diff -Nru a/include/asm-x86_64/siginfo.h b/include/asm-x86_64/siginfo.h
--- a/include/asm-x86_64/siginfo.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-x86_64/siginfo.h	Mon Apr 22 15:32:19 2002
@@ -107,6 +107,7 @@
 #define SI_ASYNCIO	-4		/* sent by AIO completion */
 #define SI_SIGIO	-5		/* sent by queued SIGIO */
 #define SI_TKILL	-6		/* sent by tkill system call */
+#define SI_DETHREAD    -7              /* sent by execve() killing subsidiary thread */
 
 #define SI_FROMUSER(siptr)	((siptr)->si_code <= 0)
 #define SI_FROMKERNEL(siptr)	((siptr)->si_code > 0)
diff -Nru a/include/asm-x86_64/smp.h b/include/asm-x86_64/smp.h
--- a/include/asm-x86_64/smp.h	Mon Apr 22 15:32:18 2002
+++ b/include/asm-x86_64/smp.h	Mon Apr 22 15:32:18 2002
@@ -19,6 +19,7 @@
 #include <asm/io_apic.h>
 #endif
 #include <asm/apic.h>
+#include <asm/thread_info.h>
 #endif
 #endif
 
@@ -90,9 +91,22 @@
 
 #define NO_PROC_ID		0xFF		/* No processor magic marker */
 
-
-
 #endif
 #define INT_DELIVERY_MODE 1     /* logical delivery */
 #define TARGET_CPUS 1
+
+
+#ifndef CONFIG_SMP
+#define stack_smp_processor_id() 0
+#else
+#include <asm/thread_info.h>
+#define stack_smp_processor_id() \
+({ 								\
+	struct thread_info *ti;					\
+	__asm__("andq %%rsp,%0; ":"=r" (ti) : "0" (~8191UL));	\
+	ti->cpu;						\
+})
 #endif
+
+#endif
+
diff -Nru a/include/asm-x86_64/system.h b/include/asm-x86_64/system.h
--- a/include/asm-x86_64/system.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-x86_64/system.h	Mon Apr 22 15:32:19 2002
@@ -13,49 +13,57 @@
 #define LOCK_PREFIX ""
 #endif
 
-struct task_struct;	/* one of the stranger aspects of C forward declarations.. */
-extern void __switch_to(struct task_struct *prev, struct task_struct *next);
+#define prepare_to_switch() do {} while(0)
 
-#define prepare_to_switch()	do { } while(0)
+#define __STR(x) #x
+#define STR(x) __STR(x)
 
-#define switch_to(prev,next) do {					\
-	asm volatile("pushq %%rbp\n\t"					\
-		     "pushq %%rbx\n\t"					\
-		     "pushq %%r8\n\t"					\
-		     "pushq %%r9\n\t"					\
-		     "pushq %%r10\n\t"					\
-		     "pushq %%r11\n\t"					\
-		     "pushq %%r12\n\t"					\
-		     "pushq %%r13\n\t"					\
-		     "pushq %%r14\n\t"					\
-		     "pushq %%r15\n\t"					\
-		     "movq %%rsp,%0\n\t"	/* save RSP */		\
-		     "movq %2,%%rsp\n\t"	/* restore RSP */	\
-		     "leaq 1f(%%rip),%%rbp\n\t"				\
-		     "movq %%rbp,%1\n\t"	/* save RIP */		\
-		     "pushq %3\n\t"		/* setup new RIP */	\
+#define __PUSH(x) "pushq %%" __STR(x) "\n\t"
+#define __POP(x)  "popq  %%" __STR(x) "\n\t"
+
+/* frame pointer must be last for get_wchan */
+
+/* It would be more efficient to let the compiler clobber most of these registers.
+   Clobbering all is not possible because that lets reload freak out. Even just 
+   clobbering six generates wrong code with gcc 3.1 for me so do it this way for now.
+   rbp needs to be always explicitely saved because gcc cannot clobber the
+   frame pointer and the scheduler is compiled with frame pointers. -AK */
+#define SAVE_CONTEXT \
+	__PUSH(r8) __PUSH(r9) __PUSH(r10) __PUSH(r11) __PUSH(r12) __PUSH(r13) \
+	__PUSH(r14) __PUSH(r15) __PUSH(rax) \
+	__PUSH(rdi) __PUSH(rsi) \
+	__PUSH(rdx) __PUSH(rcx) \
+	__PUSH(rbx) __PUSH(rbp) 
+#define RESTORE_CONTEXT \
+	__POP(rbp) __POP(rbx) \
+	__POP(rcx) __POP(rdx) \
+	__POP(rsi) __POP(rdi) \
+	__POP(rax) __POP(r15) __POP(r14) __POP(r13) __POP(r12) __POP(r11) __POP(r10) \
+	__POP(r9) __POP(r8)
+
+#define switch_to(prev,next) \
+	asm volatile(SAVE_CONTEXT						    \
+		     "movq %%rsp,%[prevrsp]\n\t"				    \
+		     "movq %[nextrsp],%%rsp\n\t"				    \
+		     "movq $1f,%[prevrip]\n\t"					    \
+		     "pushq %[nextrip]\n\t"					    \
 		     "jmp __switch_to\n\t"		\
-		     "1:\t"		\
-		     "popq %%r15\n\t"				\
-		     "popq %%r14\n\t"					\
-		     "popq %%r13\n\t"					\
-		     "popq %%r12\n\t"					\
-		     "popq %%r11\n\t"					\
-		     "popq %%r10\n\t"					\
-		     "popq %%r9\n\t"					\
-		     "popq %%r8\n\t"					\
-		     "popq %%rbx\n\t"					\
-		     "popq %%rbp\n\t"					\
-		     :"=m" (prev->thread.rsp),"=m" (prev->thread.rip)	\
-		     :"m" (next->thread.rsp),"m" (next->thread.rip),	\
-		      "b" (prev), "S" (next), "D" (prev));		\
-} while (0)
+		     "1:\n\t"							    \
+		     RESTORE_CONTEXT						    \
+		     :[prevrsp] "=m" (prev->thread.rsp), 			    \
+		      [prevrip] "=m" (prev->thread.rip)			    	    \
+		     :[nextrsp] "m" (next->thread.rsp), 			    \
+		      [nextrip]"m" (next->thread.rip),				    \
+		      [next] "S" (next), [prev] "D" (prev)  			    \
+	             :"memory")
+    
+extern void load_gs_index(unsigned); 
 
 /*
  * Load a segment. Fall back on loading the zero
  * segment if something goes wrong..
  */
-#define loadsegment(seg,value)	do { int v = value;	\
+#define loadsegment(seg,value)	\
 	asm volatile("\n"			\
 		"1:\t"				\
 		"movl %0,%%" #seg "\n"		\
@@ -69,7 +77,7 @@
 		".align 4\n\t"			\
 		".quad 1b,3b\n"			\
 		".previous"			\
-		: :"r" (v)); } while(0)
+		: :"r" ((int)(value)))
 
 #define set_debug(value,register) \
                 __asm__("movq %0,%%db" #register  \
@@ -219,24 +227,17 @@
  * Force strict CPU ordering.
  * And yes, this is required on UP too when we're talking
  * to devices.
- *
- * For now, "wmb()" doesn't actually do anything, as all
- * Intel CPU's follow what Intel calls a *Processor Order*,
- * in which all writes are seen in the program order even
- * outside the CPU.
- *
- * I expect future Intel CPU's to have a weaker ordering,
- * but I'd also expect them to finally get their act together
- * and add some real memory barriers if so.
  */
-#define mb() 	__asm__ __volatile__ ("lock; addl $0,0(%%rsp)": : :"memory")
-#define rmb()	mb()
-#define wmb()	__asm__ __volatile__ ("": : :"memory")
+#define mb() 	asm volatile("mfence":::"memory")
+#define rmb()	asm volatile("lfence":::"memory")
+#define wmb()	asm volatile("sfence":::"memory")
 #define set_mb(var, value) do { xchg(&var, value); } while (0)
 #define set_wmb(var, value) do { var = value; wmb(); } while (0)
 
+#define warn_if_not_ulong(x) do { unsigned long foo; (void) (&(x) == &foo); } while (0)
+
 /* interrupt control.. */
-#define __save_flags(x)		__asm__ __volatile__("# save_flags \n\t pushfq ; popq %q0":"=g" (x): /* no input */ :"memory")
+#define __save_flags(x)		do { warn_if_not_ulong(x); __asm__ __volatile__("# save_flags \n\t pushfq ; popq %q0":"=g" (x): /* no input */ :"memory"); } while (0)
 #define __restore_flags(x) 	__asm__ __volatile__("# restore_flags \n\t pushq %0 ; popfq": /* no output */ :"g" (x):"memory", "cc")
 #define __cli() 		__asm__ __volatile__("cli": : :"memory")
 #define __sti()			__asm__ __volatile__("sti": : :"memory")
@@ -244,10 +245,10 @@
 #define safe_halt()		__asm__ __volatile__("sti; hlt": : :"memory")
 
 /* For spinlocks etc */
-#define local_irq_save(x)	__asm__ __volatile__("# local_irq_save \n\t pushfq ; popq %0 ; cli":"=g" (x): /* no input */ :"memory")
+#define local_irq_save(x) 	do { warn_if_not_ulong(x); __asm__ __volatile__("# local_irq_save \n\t pushfq ; popq %0 ; cli":"=g" (x): /* no input */ :"memory"); } while (0)
 #define local_irq_restore(x)	__asm__ __volatile__("# local_irq_restore \n\t pushq %0 ; popfq": /* no output */ :"g" (x):"memory")
-#define local_irq_disable()	__asm__ __volatile__("cli": : :"memory")
-#define local_irq_enable()	__asm__ __volatile__("sti": : :"memory")
+#define local_irq_disable()	__cli()
+#define local_irq_enable()	__sti()
 
 #ifdef CONFIG_SMP
 
@@ -269,8 +270,8 @@
 
 #endif
 
-#define icebp() asm volatile("xchg %bx,%bx")
-
+/* Default simics "magic" breakpoint */
+#define icebp() asm volatile("xchg %%bx,%%bx" ::: "ebx")
 
 /*
  * disable hlt during certain critical i/o operations
diff -Nru a/include/asm-x86_64/thread_info.h b/include/asm-x86_64/thread_info.h
--- a/include/asm-x86_64/thread_info.h	Mon Apr 22 15:32:18 2002
+++ b/include/asm-x86_64/thread_info.h	Mon Apr 22 15:32:19 2002
@@ -51,23 +51,19 @@
 
 /* how to get the thread information struct from C */
 
-#ifdef CONFIG_PREEMPT 
-/* Preemptive kernels need to access this from interrupt context too. */ 
 static inline struct thread_info *current_thread_info(void)
 { 
 	struct thread_info *ti;
 	ti = (void *)read_pda(kernelstack) + PDA_STACKOFFSET - THREAD_SIZE;
 	return ti; 
 } 
-#else
-/* On others go for a minimally cheaper way. */
-static inline struct thread_info *current_thread_info(void)
+
+static inline struct thread_info *stack_thread_info(void)
 {
 	struct thread_info *ti;
 	__asm__("andq %%rsp,%0; ":"=r" (ti) : "0" (~8191UL));
 	return ti;
 }
-#endif
 
 /* thread information allocation */
 #define THREAD_SIZE (2*PAGE_SIZE)
@@ -96,7 +92,7 @@
 #define TIF_NOTIFY_RESUME	1	/* resumption notification requested */
 #define TIF_SIGPENDING		2	/* signal pending */
 #define TIF_NEED_RESCHED	3	/* rescheduling necessary */
-#define TIF_USEDFPU		16	/* FPU was used by this task this quantum (SMP) */
+#define TIF_USEDFPU		16	/* FPU was used by this task this quantum */
 #define TIF_POLLING_NRFLAG	17	/* true if poll_idle() is polling TIF_NEED_RESCHED */
 #define TIF_IA32		18	/* 32bit process */ 
 
@@ -110,6 +106,8 @@
 
 #define _TIF_WORK_MASK		0x0000FFFE	/* work to do on interrupt/exception return */
 #define _TIF_ALLWORK_MASK	0x0000FFFF	/* work to do on any return to u-space */
+
+#define PREEMPT_ACTIVE     0x4000000
 
 #endif /* __KERNEL__ */
 
diff -Nru a/include/asm-x86_64/timex.h b/include/asm-x86_64/timex.h
--- a/include/asm-x86_64/timex.h	Mon Apr 22 15:32:18 2002
+++ b/include/asm-x86_64/timex.h	Mon Apr 22 15:32:18 2002
@@ -12,7 +12,7 @@
 
 #define CLOCK_TICK_RATE	1193180 /* Underlying HZ */
 #define CLOCK_TICK_FACTOR	20	/* Factor of both 1000000 and CLOCK_TICK_RATE */
-#define FINETUNE ((((((long)LATCH * HZ - CLOCK_TICK_RATE) << SHIFT_HZ) * \
+#define FINETUNE ((((((int)LATCH * HZ - CLOCK_TICK_RATE) << SHIFT_HZ) * \
 	(1000000/CLOCK_TICK_FACTOR) / (CLOCK_TICK_RATE/CLOCK_TICK_FACTOR)) \
 		<< (SHIFT_SCALE-SHIFT_HZ)) / HZ)
 
@@ -46,6 +46,6 @@
 #endif
 }
 
-extern unsigned long cpu_khz;
+extern unsigned int cpu_khz;
 
 #endif
diff -Nru a/include/asm-x86_64/tlbflush.h b/include/asm-x86_64/tlbflush.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/include/asm-x86_64/tlbflush.h	Mon Apr 22 15:32:19 2002
@@ -0,0 +1,128 @@
+#ifndef _X8664_TLBFLUSH_H
+#define _X8664_TLBFLUSH_H
+
+#include <linux/config.h>
+#include <linux/mm.h>
+#include <asm/processor.h>
+
+#define __flush_tlb()							\
+	do {								\
+		unsigned long tmpreg;					\
+									\
+		__asm__ __volatile__(					\
+			"movq %%cr3, %0;  # flush TLB \n"		\
+			"movq %0, %%cr3;              \n"		\
+			: "=r" (tmpreg)					\
+			:: "memory");					\
+	} while (0)
+
+/*
+ * Global pages have to be flushed a bit differently. Not a real
+ * performance problem because this does not happen often.
+ */
+#define __flush_tlb_global()						\
+	do {								\
+		unsigned long tmpreg;					\
+									\
+		__asm__ __volatile__(					\
+			"movq %1, %%cr4;  # turn off PGE     \n"	\
+			"movq %%cr3, %0;  # flush TLB        \n"	\
+			"movq %0, %%cr3;                     \n"	\
+			"movq %2, %%cr4;  # turn PGE back on \n"	\
+			: "=&r" (tmpreg)				\
+			: "r" (mmu_cr4_features & ~X86_CR4_PGE),	\
+			  "r" (mmu_cr4_features)			\
+			: "memory");					\
+	} while (0)
+
+extern unsigned long pgkern_mask;
+
+#define __flush_tlb_all() __flush_tlb_global()
+
+#define __flush_tlb_one(addr) \
+	__asm__ __volatile__("invlpg %0": :"m" (*(char *) addr))
+
+
+/*
+ * TLB flushing:
+ *
+ *  - flush_tlb() flushes the current mm struct TLBs
+ *  - flush_tlb_all() flushes all processes TLBs
+ *  - flush_tlb_mm(mm) flushes the specified mm context TLB's
+ *  - flush_tlb_page(vma, vmaddr) flushes one page
+ *  - flush_tlb_range(vma, start, end) flushes a range of pages
+ *  - flush_tlb_kernel_range(start, end) flushes a range of kernel pages
+ *  - flush_tlb_pgtables(mm, start, end) flushes a range of page tables
+ *
+ * ..but the x86_64 has somewhat limited tlb flushing capabilities,
+ * and page-granular flushes are available only on i486 and up.
+ */
+
+#ifndef CONFIG_SMP
+
+#define flush_tlb() __flush_tlb()
+#define flush_tlb_all() __flush_tlb_all()
+#define local_flush_tlb() __flush_tlb()
+
+static inline void flush_tlb_mm(struct mm_struct *mm)
+{
+	if (mm == current->active_mm)
+		__flush_tlb();
+}
+
+static inline void flush_tlb_page(struct vm_area_struct *vma,
+	unsigned long addr)
+{
+	if (vma->vm_mm == current->active_mm)
+		__flush_tlb_one(addr);
+}
+
+static inline void flush_tlb_range(struct vm_area_struct *vma,
+	unsigned long start, unsigned long end)
+{
+	if (vma->vm_mm == current->active_mm)
+		__flush_tlb();
+}
+
+#else
+
+#include <asm/smp.h>
+
+#define local_flush_tlb() \
+	__flush_tlb()
+
+extern void flush_tlb_all(void);
+extern void flush_tlb_current_task(void);
+extern void flush_tlb_mm(struct mm_struct *);
+extern void flush_tlb_page(struct vm_area_struct *, unsigned long);
+
+#define flush_tlb()	flush_tlb_current_task()
+
+static inline void flush_tlb_range(struct vm_area_struct * vma, unsigned long start, unsigned long end)
+{
+	flush_tlb_mm(vma->vm_mm);
+}
+
+#define TLBSTATE_OK	1
+#define TLBSTATE_LAZY	2
+
+struct tlb_state
+{
+	struct mm_struct *active_mm;
+	int state;
+	char __cacheline_padding[24];
+};
+extern struct tlb_state cpu_tlbstate[NR_CPUS];
+
+
+#endif
+
+#define flush_tlb_kernel_range(start, end) flush_tlb_all()
+
+static inline void flush_tlb_pgtables(struct mm_struct *mm,
+				      unsigned long start, unsigned long end)
+{
+	/* x86_64 does not keep any page table caches in TLB */
+}
+
+#endif /* _X8664_TLBFLUSH_H */
diff -Nru a/include/asm-x86_64/types.h b/include/asm-x86_64/types.h
--- a/include/asm-x86_64/types.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-x86_64/types.h	Mon Apr 22 15:32:19 2002
@@ -39,7 +39,7 @@
 
 #define BITS_PER_LONG 64
 
-typedef u32 dma64_addr_t;
+typedef u64 dma64_addr_t;
 typedef u64 dma_addr_t;
 
 #endif /* __KERNEL__ */
diff -Nru a/include/asm-x86_64/unistd.h b/include/asm-x86_64/unistd.h
--- a/include/asm-x86_64/unistd.h	Mon Apr 22 15:32:18 2002
+++ b/include/asm-x86_64/unistd.h	Mon Apr 22 15:32:18 2002
@@ -76,7 +76,7 @@
 #define __NR_shmget                             29
 __SYSCALL(__NR_shmget, sys_shmget)
 #define __NR_shmat                              30
-__SYSCALL(__NR_shmat, sys_shmat)
+__SYSCALL(__NR_shmat, wrap_sys_shmat)
 #define __NR_shmctl                             31
 __SYSCALL(__NR_shmctl, sys_shmctl)
 
@@ -153,7 +153,7 @@
 #define __NR_semop                              65
 __SYSCALL(__NR_semop, sys_semop)
 #define __NR_semctl                             66
-__SYSCALL(__NR_semctl, sys_semctl)
+__SYSCALL(__NR_semctl, wrap_sys_semctl)
 #define __NR_shmdt                              67
 __SYSCALL(__NR_shmdt, sys_shmdt)
 #define __NR_msgget                             68
@@ -460,12 +460,21 @@
 __SYSCALL(__NR_fremovexattr, sys_fremovexattr) 
 #define __NR_tkill	200
 __SYSCALL(__NR_tkill, sys_tkill) 
+#define __NR_time      201
+__SYSCALL(__NR_time, sys_time)
+#define __NR_futex     202
+__SYSCALL(__NR_futex, sys_futex)
+#define __NR_sched_setaffinity    203
+__SYSCALL(__NR_sched_setaffinity, sys_sched_setaffinity)
+#define __NR_sched_getaffinity     204
+__SYSCALL(__NR_sched_getaffinity, sys_sched_getaffinity)
 
-#define __NR_syscall_max __NR_tkill
+
+#define __NR_syscall_max __NR_sched_getaffinity
 
 #ifndef __NO_STUBS
 
-/* user-visible error numbers are in the range -1 - -124: see <asm-i386/errno.h> */
+/* user-visible error numbers are in the range -1 - -4095 */
 
 #define __syscall_clobber "r11","rcx","memory" 
 
diff -Nru a/include/asm-x86_64/user.h b/include/asm-x86_64/user.h
--- a/include/asm-x86_64/user.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-x86_64/user.h	Mon Apr 22 15:32:19 2002
@@ -5,13 +5,14 @@
 #include <asm/page.h>
 #include <linux/ptrace.h>
 /* Core file format: The core file is written in such a way that gdb
-   can understand it and provide useful information to the user (under
-   linux we use the 'trad-core' bfd).  There are quite a number of
-   obstacles to being able to view the contents of the floating point
-   registers, and until these are solved you will not be able to view the
-   contents of them.  Actually, you can read in the core file and look at
-   the contents of the user struct to find out what the floating point
-   registers contain.
+   can understand it and provide useful information to the user.
+   There are quite a number of obstacles to being able to view the
+   contents of the floating point registers, and until these are
+   solved you will not be able to view the contents of them.
+   Actually, you can read in the core file and look at the contents of
+   the user struct to find out what the floating point registers
+   contain.
+
    The actual file contents are as follows:
    UPAGE: 1 page consisting of a user struct that tells gdb what is present
    in the file.  Directly after this is a copy of the task_struct, which
@@ -28,11 +29,7 @@
    backtrace.  We need to write the data from (esp) to
    current->start_stack, so we round each of these off in order to be able
    to write an integer number of pages.
-   The minimum core file size is 3 pages, or 12288 bytes.
-*/
-
-/* This is not neccessary in first phase. It will have to be
-   synchronized with gdb later. */ 
+   The minimum core file size is 3 pages, or 12288 bytes.  */
 
 /*
  * Pentium III FXSR, SSE support
@@ -44,36 +41,37 @@
  * and both the standard and SIMD floating point data can be accessed via
  * the new ptrace requests.  In either case, changes to the FPU environment
  * will be reflected in the task's state as expected.
+ * 
+ * x86-64 support by Andi Kleen.
  */
 
+/* This matches the 64bit FXSAVE format as defined by AMD. It is the same
+   as the 32bit format defined by Intel, except that the selector:offset pairs for
+   data and eip are replaced with flat 64bit pointers. */ 
 struct user_i387_struct {
 	unsigned short	cwd;
 	unsigned short	swd;
-	unsigned short	twd;
+	unsigned short	twd; /* Note this is not the same as the 32bit/x87/FSAVE twd */
 	unsigned short	fop;
-	u32	fip;
-	u32	fcs;
-	u32	foo;
-	u32	fos;
+	u64	rip;
+	u64	rdp;
 	u32	mxcsr;
-	u32	reserved;
+	u32	mxcsr_mask;
 	u32	st_space[32];	/* 8*16 bytes for each FP-reg = 128 bytes */
-	u32	xmm_space[32];	/* 8*16 bytes for each XMM-reg = 128 bytes */
-	u32	padding[56];
+	u32	xmm_space[64];	/* 16*16 bytes for each XMM-reg = 256 bytes */
+	u32	padding[24];
 };
 
 /*
- * This is copy of the layout of "struct pt_regs", and
- * is still the layout used by user mode (the new
- * pt_regs doesn't have all registers as the kernel
- * doesn't use the extra segment registers)
+ * Segment register layout in coredumps.
  */
 struct user_regs_struct {
 	unsigned long r15,r14,r13,r12,rbp,rbx,r11,r10;
 	unsigned long r9,r8,rax,rcx,rdx,rsi,rdi,orig_rax;
 	unsigned long rip,cs,eflags;
 	unsigned long rsp,ss;
-  	unsigned long fs_base, kernel_gs_base;
+  	unsigned long fs_base, gs_base;
+	unsigned long ds,es,fs,gs; 
 }; 
 
 /* When the kernel dumps core, it starts by dumping the user struct -
@@ -103,7 +101,7 @@
   struct user_i387_struct* u_fpstate;	/* Math Co-processor pointer. */
   unsigned long magic;		/* To uniquely identify a core file */
   char u_comm[32];		/* User command that was responsible */
-  int u_debugreg[8];
+  unsigned long u_debugreg[8];
 };
 #define NBPG PAGE_SIZE
 #define UPAGES 1
diff -Nru a/include/asm-x86_64/user32.h b/include/asm-x86_64/user32.h
--- a/include/asm-x86_64/user32.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-x86_64/user32.h	Mon Apr 22 15:32:19 2002
@@ -14,12 +14,23 @@
 	u32	st_space[20];   /* 8*10 bytes for each FP-reg = 80 bytes */
 };
 
-/*
- * This is the old layout of "struct pt_regs", and
- * is still the layout used by user mode (the new
- * pt_regs doesn't have all registers as the kernel
- * doesn't use the extra segment registers)
- */
+/* FSAVE frame with extensions */
+struct user32_fxsr_struct {
+	unsigned short	cwd;
+	unsigned short	swd;
+	unsigned short	twd;	/* not compatible to 64bit twd */
+	unsigned short	fop;
+	int	fip;
+	int	fcs;
+	int	foo;
+	int	fos;
+	int	mxcsr;
+	int	reserved;
+	int	st_space[32];	/* 8*16 bytes for each FP-reg = 128 bytes */
+	int	xmm_space[32];	/* 8*16 bytes for each XMM-reg = 128 bytes */
+	int	padding[56];
+};
+
 struct user_regs_struct32 {
 	__u32 ebx, ecx, edx, esi, edi, ebp, eax;
 	unsigned short ds, __ds, es, __es;
diff -Nru a/include/asm-x86_64/vsyscall.h b/include/asm-x86_64/vsyscall.h
--- a/include/asm-x86_64/vsyscall.h	Mon Apr 22 15:32:19 2002
+++ b/include/asm-x86_64/vsyscall.h	Mon Apr 22 15:32:19 2002
@@ -25,8 +25,8 @@
 /* vsyscall space (readonly) */
 extern long __vxtime_sequence[2];
 extern int __delay_at_last_interrupt;
-extern unsigned long __last_tsc_low;
-extern unsigned long __fast_gettimeoffset_quotient;
+extern unsigned int __last_tsc_low;
+extern unsigned int __fast_gettimeoffset_quotient;
 extern struct timeval __xtime;
 extern volatile unsigned long __jiffies;
 extern unsigned long __wall_jiffies;
@@ -35,7 +35,7 @@
 /* kernel space (writeable) */
 extern unsigned long last_tsc_low;
 extern int delay_at_last_interrupt;
-extern unsigned long fast_gettimeoffset_quotient;
+extern unsigned int fast_gettimeoffset_quotient;
 extern unsigned long wall_jiffies;
 extern struct timezone sys_tz;
 extern long vxtime_sequence[2];
diff -Nru a/include/linux/acpi_serial.h b/include/linux/acpi_serial.h
--- a/include/linux/acpi_serial.h	Mon Apr 22 15:32:19 2002
+++ b/include/linux/acpi_serial.h	Mon Apr 22 15:32:19 2002
@@ -11,6 +11,8 @@
 
 extern void setup_serial_acpi(void *);
 
+#define ACPI_SIG_LEN		4
+
 /* ACPI table signatures */
 #define ACPI_SPCRT_SIGNATURE	"SPCR"
 #define ACPI_DBGPT_SIGNATURE	"DBGP"
diff -Nru a/include/linux/auto_fs.h b/include/linux/auto_fs.h
--- a/include/linux/auto_fs.h	Mon Apr 22 15:32:18 2002
+++ b/include/linux/auto_fs.h	Mon Apr 22 15:32:18 2002
@@ -45,7 +45,7 @@
  * If so, 32-bit user-space code should be backwards compatible.
  */
 
-#if defined(__sparc__) || defined(__mips__)
+#if defined(__sparc__) || defined(__mips__) || defined(__x86_64__)
 typedef unsigned int autofs_wqt_t;
 #else
 typedef unsigned long autofs_wqt_t;
diff -Nru a/include/linux/bfs_fs.h b/include/linux/bfs_fs.h
--- a/include/linux/bfs_fs.h	Mon Apr 22 15:32:19 2002
+++ b/include/linux/bfs_fs.h	Mon Apr 22 15:32:19 2002
@@ -7,6 +7,7 @@
 #define _LINUX_BFS_FS_H
 
 #include <linux/bfs_fs_i.h>
+#include <linux/bfs_fs_sb.h>
 
 #define BFS_BSIZE_BITS		9
 #define BFS_BSIZE		(1<<BFS_BSIZE_BITS)
@@ -88,6 +89,11 @@
 /* dir.c */
 extern struct inode_operations bfs_dir_inops;
 extern struct file_operations bfs_dir_operations;
+
+static inline struct bfs_sb_info *BFS_SB(struct super_block *sb)
+{
+	return sb->u.generic_sbp;
+}
 
 static inline struct bfs_inode_info *BFS_I(struct inode *inode)
 {
diff -Nru a/include/linux/capability.h b/include/linux/capability.h
--- a/include/linux/capability.h	Mon Apr 22 15:32:19 2002
+++ b/include/linux/capability.h	Mon Apr 22 15:32:19 2002
@@ -242,6 +242,7 @@
 /* Allow use of FIFO and round-robin (realtime) scheduling on own
    processes and setting the scheduling algorithm used by another
    process. */
+/* Allow setting cpu affinity on other processes */
 
 #define CAP_SYS_NICE         23
 
diff -Nru a/include/linux/dcache.h b/include/linux/dcache.h
--- a/include/linux/dcache.h	Mon Apr 22 15:32:19 2002
+++ b/include/linux/dcache.h	Mon Apr 22 15:32:19 2002
@@ -117,12 +117,18 @@
 					 * renamed" and has to be
 					 * deleted on the last dput()
 					 */
-#define	DCACHE_NFSD_DISCONNECTED 0x0004	/* This dentry is not currently connected to the
-					 * dcache tree. Its parent will either be itself,
-					 * or will have this flag as well.
-					 * If this dentry points to a directory, then
-					 * s_nfsd_free_path semaphore will be down
-					 */
+#define	DCACHE_DISCONNECTED 0x0004
+     /* This dentry is possibly not currently connected to the dcache tree,
+      * in which case its parent will either be itself, or will have this
+      * flag as well.  nfsd will not use a dentry with this bit set, but will
+      * first endeavour to clear the bit either by discovering that it is
+      * connected, or by performing lookup operations.   Any filesystem which
+      * supports nfsd_operations MUST have a lookup function which, if it finds
+      * a directory inode with a DCACHE_DISCONNECTED dentry, will d_move
+      * that dentry into place and return that dentry rather than the passed one,
+      * typically using d_splice_alias.
+      */
+
 #define DCACHE_REFERENCED	0x0008  /* Recently used, don't discard. */
 
 extern spinlock_t dcache_lock;
@@ -166,8 +172,11 @@
 
 /* allocate/de-allocate */
 extern struct dentry * d_alloc(struct dentry *, const struct qstr *);
+extern struct dentry * d_alloc_anon(struct inode *);
+extern struct dentry * d_splice_alias(struct inode *, struct dentry *);
 extern void shrink_dcache_sb(struct super_block *);
 extern void shrink_dcache_parent(struct dentry *);
+extern void shrink_dcache_anon(struct list_head *);
 extern int d_invalidate(struct dentry *);
 
 #define shrink_dcache() prune_dcache(0)
diff -Nru a/include/linux/fb.h b/include/linux/fb.h
--- a/include/linux/fb.h	Mon Apr 22 15:32:19 2002
+++ b/include/linux/fb.h	Mon Apr 22 15:32:19 2002
@@ -253,19 +253,52 @@
 	__u32 reserved[4];		/* reserved for future compatibility */
 };
 
+/* Internal HW accel */
+#define ROP_COPY 0
+#define ROP_XOR  1
+
+struct fb_copyarea {
+	__u32 sx;	/* screen-relative */
+	__u32 sy;
+	__u32 width;
+	__u32 height;
+	__u32 dx;
+	__u32 dy;
+};
+
+struct fb_fillrect {
+	__u32 dx;	/* screen-relative */
+	__u32 dy;
+	__u32 width;
+	__u32 height;
+	__u32 color;
+	__u32 rop;
+};
+
+struct fb_image {
+	__u32 width;	/* Size of image */
+	__u32 height;
+	__u16 dx;	/* Where to place image */
+	__u16 dy;
+	__u32 fg_color;	/* Only used when a mono bitmap */
+	__u32 bg_color;
+	__u8  depth;	/* Dpeth of the image */
+	char  *data;	/* Pointer to image data */
+};
+
 #ifdef __KERNEL__
 
 #if 1 /* to go away in 2.5.0 */
 extern int GET_FB_IDX(kdev_t rdev);
 #else
-#define GET_FB_IDX(node)	(MINOR(node))
+#define GET_FB_IDX(node)	(minor(node))
 #endif
 
 #include <linux/fs.h>
+#include <linux/poll.h>
 #include <linux/init.h>
 #include <linux/devfs_fs_kernel.h>
 
-
 struct fb_info;
 struct fb_info_gen;
 struct vm_area_struct;
@@ -295,9 +328,25 @@
     /* set colormap */
     int (*fb_set_cmap)(struct fb_cmap *cmap, int kspc, int con,
 		       struct fb_info *info);
-    /* pan display (optional) */
-    int (*fb_pan_display)(struct fb_var_screeninfo *var, int con,
-			  struct fb_info *info);
+    /* checks var and creates a par based on it */
+    int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);
+    /* set the video mode according to par */
+    int (*fb_set_par)(struct fb_info *info);
+    /* set color register */
+    int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,
+                        unsigned blue, unsigned transp, struct fb_info *info);
+    /* blank display */
+    int (*fb_blank)(int blank, struct fb_info *info);
+    /* pan display */
+    int (*fb_pan_display)(struct fb_var_screeninfo *var, int con, struct fb_info *info);
+    /* draws a rectangle */
+    void (*fb_fillrect)(struct fb_info *info, struct fb_fillrect *rect); 
+    /* Copy data from area to another */
+    void (*fb_copyarea)(struct fb_info *info, struct fb_copyarea *region); 
+    /* Draws a image to the display */
+    void (*fb_imageblit)(struct fb_info *info, struct fb_image *image);
+    /* perform polling on fb device */
+    int (*fb_poll)(struct fb_info *info, poll_table *wait);
     /* perform fb specific ioctl (optional) */
     int (*fb_ioctl)(struct inode *inode, struct file *file, unsigned int cmd,
 		    unsigned long arg, int con, struct fb_info *info);
@@ -321,6 +370,7 @@
    char *screen_base;                   /* Virtual address */
    struct display *disp;		/* initial display variable */
    struct vc_data *display_fg;		/* Console visible on this display */
+   int currcon;				/* Current VC. */	
    char fontname[40];			/* default font name */
    devfs_handle_t devfs_handle;         /* Devfs handle for new name         */
    devfs_handle_t devfs_lhandle;        /* Devfs handle for compat. symlink  */
@@ -389,16 +439,29 @@
 
 extern int fbgen_get_fix(struct fb_fix_screeninfo *fix, int con,
 			 struct fb_info *info);
+extern int gen_get_fix(struct fb_fix_screeninfo *fix, int con,
+		       struct fb_info *info);
 extern int fbgen_get_var(struct fb_var_screeninfo *var, int con,
 			 struct fb_info *info);
+extern int gen_get_var(struct fb_var_screeninfo *var, int con,
+		       struct fb_info *info);
 extern int fbgen_set_var(struct fb_var_screeninfo *var, int con,
 			 struct fb_info *info);
+extern int gen_set_var(struct fb_var_screeninfo *var, int con,
+		       struct fb_info *info);
 extern int fbgen_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			  struct fb_info *info);
+extern int gen_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+			struct fb_info *info);
 extern int fbgen_set_cmap(struct fb_cmap *cmap, int kspc, int con,
 			  struct fb_info *info);
+extern int gen_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+			struct fb_info *info);
 extern int fbgen_pan_display(struct fb_var_screeninfo *var, int con,
 			     struct fb_info *info);
+extern void cfb_fillrect(struct fb_info *info, struct fb_fillrect *rect); 
+extern void cfb_copyarea(struct fb_info *info, struct fb_copyarea *region); 
+extern void cfb_imageblit(struct fb_info *info, struct fb_image *image);
 
     /*
      *  Helper functions
@@ -409,9 +472,12 @@
 extern void fbgen_set_disp(int con, struct fb_info_gen *info);
 extern void fbgen_install_cmap(int con, struct fb_info_gen *info);
 extern int fbgen_update_var(int con, struct fb_info *info);
+extern int gen_update_var(int con, struct fb_info *info);
 extern int fbgen_switch(int con, struct fb_info *info);
 extern void fbgen_blank(int blank, struct fb_info *info);
+extern int gen_switch(int con, struct fb_info *info);
 
+extern void gen_set_disp(int con, struct fb_info *info);
 
 /* drivers/video/fbmem.c */
 extern int register_framebuffer(struct fb_info *fb_info);
diff -Nru a/include/linux/fs.h b/include/linux/fs.h
--- a/include/linux/fs.h	Mon Apr 22 15:32:19 2002
+++ b/include/linux/fs.h	Mon Apr 22 15:32:19 2002
@@ -22,9 +22,9 @@
 #include <linux/stddef.h>
 #include <linux/string.h>
 #include <linux/radix-tree.h>
+#include <linux/bitops.h>
 
 #include <asm/atomic.h>
-#include <asm/bitops.h>
 
 struct poll_table_struct;
 
@@ -684,7 +684,6 @@
 #include <linux/ufs_fs_sb.h>
 #include <linux/romfs_fs_sb.h>
 #include <linux/adfs_fs_sb.h>
-#include <linux/bfs_fs_sb.h>
 
 extern struct list_head super_blocks;
 extern spinlock_t sb_lock;
@@ -702,6 +701,7 @@
 	struct file_system_type	*s_type;
 	struct super_operations	*s_op;
 	struct dquot_operations	*dq_op;
+	struct export_operations *s_export_op;
 	unsigned long		s_flags;
 	unsigned long		s_magic;
 	struct dentry		*s_root;
@@ -712,6 +712,7 @@
 
 	struct list_head	s_dirty;	/* dirty inodes */
 	struct list_head	s_locked_inodes;/* inodes being synced */
+	struct list_head	s_anon;		/* anonymous dentries for (nfs) exporting */
 	struct list_head	s_files;
 
 	struct block_device	*s_bdev;
@@ -728,7 +729,6 @@
 		struct ufs_sb_info	ufs_sb;
 		struct romfs_sb_info	romfs_sb;
 		struct adfs_sb_info	adfs_sb;
-		struct bfs_sb_info	bfs_sb;
 		void			*generic_sbp;
 	} u;
 	/*
@@ -881,7 +881,7 @@
 	 *   and must return a dentry for the referenced object or, if "parent" is
 	 *   set, a dentry for the parent of the object.
 	 *   If a dentry cannot be found, a "root" dentry should be created and
-	 *   flaged as DCACHE_NFSD_DISCONNECTED. nfsd_iget is an example implementation.
+	 *   flaged as DCACHE_DISCONNECTED. nfsd_iget is an example implementation.
 	 *
 	 * dentry_to_fh is given a dentry and must generate the filesys specific
 	 *   part of the file handle.  Available length is passed in *lenp and used
@@ -936,6 +936,110 @@
 	void (*free_inode) (const struct inode *, unsigned long);
 	int (*transfer) (struct inode *, struct iattr *);
 };
+
+
+/**
+ * &export_operations - for nfsd to communicate with file systems
+ * decode_fh:      decode a file handle fragment and return a &struct dentry
+ * encode_fh:      encode a file handle fragment from a dentry
+ * get_name:       find the name for a given inode in a given directory
+ * get_parent:     find the parent of a given directory
+ * get_dentry:     find a dentry for the inode given a file handle sub-fragment
+ *
+ * Description:
+ *    The export_operations structure provides a means for nfsd to communicate
+ *    with a particular exported file system  - particularly enabling nfsd and
+ *    the filesystem to co-operate when dealing with file handles.
+ *
+ *    export_operations contains two basic operation for dealing with file handles,
+ *    decode_fh() and encode_fh(), and allows for some other operations to be defined
+ *    which standard helper routines use to get specific information from the
+ *    filesystem.
+ *
+ *    nfsd encodes information use to determine which filesystem a filehandle
+ *    applies to in the initial part of the file handle.  The remainder, termed a
+ *    file handle fragment, is controlled completely by the filesystem.
+ *    The standard helper routines assume that this fragment will contain one or two
+ *    sub-fragments, one which identifies the file, and one which may be used to
+ *    identify the (a) directory containing the file.
+ *
+ *    In some situations, nfsd needs to get a dentry which is connected into a
+ *    specific part of the file tree.  To allow for this, it passes the function
+ *    acceptable() together with a @context which can be used to see if the dentry
+ *    is acceptable.  As there can be multiple dentrys for a given file, the filesystem
+ *    should check each one for acceptability before looking for the next.  As soon
+ *    as an acceptable one is found, it should be returned.
+ *
+ * decode_fh:
+ *    @decode_fh is given a &struct super_block (@sb), a file handle fragment (@fh, @fh_len)
+ *    and an acceptability testing function (@acceptable, @context).  It should return
+ *    a &struct dentry which refers to the same file that the file handle fragment refers
+ *    to,  and which passes the acceptability test.  If it cannot, it should return
+ *    a %NULL pointer if the file was found but no acceptable &dentries were available, or
+ *    a %ERR_PTR error code indicating why it couldn't be found (e.g. %ENOENT or %ENOMEM).
+ *
+ * encode_fh:
+ *    @encode_fh should store in the file handle fragment @fh (using at most @max_len bytes)
+ *    information that can be used by @decode_fh to recover the file refered to by the
+ *    &struct dentry @de.  If the @connectable flag is set, the encode_fh() should store
+ *    sufficient information so that a good attempt can be made to find not only
+ *    the file but also it's place in the filesystem.   This typically means storing
+ *    a reference to de->d_parent in the filehandle fragment.
+ *    encode_fh() should return the number of bytes stored or a negative error code
+ *    such as %-ENOSPC
+ *
+ * get_name:
+ *    @get_name should find a name for the given @child in the given @parent directory.
+ *    The name should be stored in the @name (with the understanding that it is already
+ *    pointing to a a %NAME_MAX+1 sized buffer.   get_name() should return %0 on success,
+ *    a negative error code or error.
+ *    @get_name will be called without @parent->i_sem held.
+ *
+ * get_parent:
+ *    @get_parent should find the parent directory for the given @child which is also
+ *    a directory.  In the event that it cannot be found, or storage space cannot be
+ *    allocated, a %ERR_PTR should be returned.
+ *
+ * get_dentry:
+ *    Given a &super_block (@sb) and a pointer to a file-system specific inode identifier,
+ *    possibly an inode number, (@inump) get_dentry() should find the identified inode and
+ *    return a dentry for that inode.
+ *    Any suitable dentry can be returned including, if necessary, a new dentry created
+ *    with d_alloc_root.  The caller can then find any other extant dentrys by following the
+ *    d_alias links.  If a new dentry was created using d_alloc_root, DCACHE_NFSD_DISCONNECTED
+ *    should be set, and the dentry should be d_rehash()ed.
+ *
+ *    If the inode cannot be found, either a %NULL pointer or an %ERR_PTR code can be returned.
+ *    The @inump will be whatever was passed to nfsd_find_fh_dentry() in either the
+ *    @obj or @parent parameters.
+ *
+ * Locking rules:
+ *  get_parent is called with child->d_inode->i_sem down
+ *  get_name is not (which is possibly inconsistent)
+ */
+
+struct export_operations {
+	struct dentry *(*decode_fh)(struct super_block *sb, __u32 *fh, int fh_len, int fh_type,
+			 int (*acceptable)(void *context, struct dentry *de),
+			 void *context);
+	int (*encode_fh)(struct dentry *de, __u32 *fh, int *max_len,
+			 int connectable);
+
+	/* the following are only called from the filesystem itself */
+	int (*get_name)(struct dentry *parent, char *name,
+			struct dentry *child);
+	struct dentry * (*get_parent)(struct dentry *child);
+	struct dentry * (*get_dentry)(struct super_block *sb, void *inump);
+
+	/* This is set by the exporting module to a standard helper */
+	struct dentry * (*find_exported_dentry)(
+		struct super_block *sb, void *obj, void *parent,
+		int (*acceptable)(void *context, struct dentry *de),
+		void *context);
+
+
+};
+
 
 struct file_system_type {
 	const char *name;
diff -Nru a/include/linux/hdreg.h b/include/linux/hdreg.h
--- a/include/linux/hdreg.h	Mon Apr 22 15:32:19 2002
+++ b/include/linux/hdreg.h	Mon Apr 22 15:32:19 2002
@@ -296,7 +296,6 @@
 #define HDIO_GET_MULTCOUNT	0x0304	/* get current IDE blockmode setting */
 #define HDIO_GET_QDMA		0x0305	/* get use-qdma flag */
 #define HDIO_OBSOLETE_IDENTITY	0x0307	/* OBSOLETE, DO NOT USE: returns 142 bytes */
-#define HDIO_GET_KEEPSETTINGS	0x0308	/* get keep-settings-on-reset flag */
 #define HDIO_GET_32BIT		0x0309	/* get current io_32bit setting */
 #define HDIO_GET_NOWERR		0x030a	/* get ignore-write-error flag */
 #define HDIO_GET_DMA		0x030b	/* get use-dma flag */
@@ -316,7 +315,6 @@
 /* hd/ide ctl's that pass (arg) non-ptr values are numbered 0x032n/0x033n */
 #define HDIO_SET_MULTCOUNT	0x0321	/* change IDE blockmode */
 #define HDIO_SET_UNMASKINTR	0x0322	/* permit other irqs during I/O */
-#define HDIO_SET_KEEPSETTINGS	0x0323	/* keep ioctl settings on reset */
 #define HDIO_SET_32BIT		0x0324	/* change io_32bit flags */
 #define HDIO_SET_NOWERR		0x0325	/* change ignore-write-error flag */
 #define HDIO_SET_DMA		0x0326	/* change use-dma flag */
diff -Nru a/include/linux/ide.h b/include/linux/ide.h
--- a/include/linux/ide.h	Mon Apr 22 15:32:19 2002
+++ b/include/linux/ide.h	Mon Apr 22 15:32:19 2002
@@ -14,8 +14,8 @@
 #include <linux/device.h>
 #include <linux/devfs_fs_kernel.h>
 #include <linux/interrupt.h>
+#include <linux/bitops.h>
 #include <asm/hdreg.h>
-#include <asm/bitops.h>
 
 /*
  * This is the multiple IDE interface driver, as evolved from hd.c.
@@ -273,19 +273,22 @@
 	} b;
 } special_t;
 
-#define IDE_MAX_TAG	32		/* spec says 32 max */
+#define IDE_MAX_TAG		(32)		/* spec says 32 max */
+#define IDE_INACTIVE_TAG	(-1)
 
 struct ata_request;
 typedef struct ide_tag_info_s {
 	unsigned long tag_mask;			/* next tag bit mask */
 	struct ata_request *ar[IDE_MAX_TAG];	/* in-progress requests */
 	int active_tag;				/* current active tag */
+
 	int queued;				/* current depth */
 
 	/*
 	 * stats ->
 	 */
 	int max_depth;				/* max depth ever */
+
 	int max_last_depth;			/* max since last check */
 
 	/*
@@ -295,19 +298,26 @@
 	 */
 	int immed_rel;
 	int immed_comp;
+	unsigned long oldest_command;
 } ide_tag_info_t;
 
 #define IDE_GET_AR(drive, tag)	((drive)->tcq->ar[(tag)])
 #define IDE_CUR_TAG(drive)	(IDE_GET_AR((drive), (drive)->tcq->active_tag))
-#define IDE_SET_CUR_TAG(drive, tag)	((drive)->tcq->active_tag = (tag))
+#define IDE_SET_CUR_TAG(drive, tag)	\
+	do {						\
+		((drive)->tcq->active_tag = (tag));	\
+		if ((tag) == IDE_INACTIVE_TAG)		\
+			HWGROUP((drive))->rq = NULL;	\
+	} while (0);
 
-#define IDE_CUR_AR(drive)	\
-	((drive)->using_tcq ? IDE_CUR_TAG((drive)) : HWGROUP((drive))->rq->special)
+#define IDE_CUR_AR(drive)	(HWGROUP((drive))->rq->special)
 
 struct ide_settings_s;
-
-typedef struct ide_drive_s {
-	struct ata_channel *channel;	/* parent pointer to the channel we are attached to  */
+/* structure describing an ATA/ATAPI device */
+typedef
+struct ata_device {
+	struct ata_channel *	channel;
+	char			name[6];	/* device name */
 
 	unsigned int usage;		/* current "open()" count for drive */
 	char type; /* distingiush different devices: disk, cdrom, tape, floppy, ... */
@@ -316,11 +326,11 @@
 	 * could move this to the channel and many sync problems would
 	 * magically just go away.
 	 */
-	request_queue_t	queue;	/* per device request queue */
+	request_queue_t	queue;		/* per device request queue */
 
-	struct list_head free_req; /* free ata requests */
+	struct list_head free_req;	/* free ata requests */
 
-	struct ide_drive_s	*next;	/* circular list of hwgroup drives */
+	struct ata_device *next;	/* circular list of hwgroup drives */
 
 	/* Those are directly injected jiffie values. They should go away and
 	 * we should use generic timers instead!!!
@@ -332,14 +342,10 @@
 	unsigned long PADAM_timeout;		/* max time to wait for irq */
 
 	special_t	special;	/* special action flags */
-	byte     keep_settings;		/* restore settings after drive reset */
 	byte     using_dma;		/* disk is using dma for read/write */
 	byte	 using_tcq;		/* disk is using queued dma operations*/
 	byte	 retry_pio;		/* retrying dma capable host in pio */
 	byte	 state;			/* retry state */
-	byte     unmask;		/* flag: okay to unmask other irqs */
-	byte     slow;			/* flag: slow data port */
-	byte     bswap;			/* flag: byte swap data */
 	byte     dsc_overlap;		/* flag: DSC overlap */
 
 	unsigned waiting_for_dma: 1;	/* dma currently in progress */
@@ -350,8 +356,6 @@
 	unsigned noprobe	: 1;	/* from:  hdx=noprobe */
 	unsigned removable	: 1;	/* 1 if need to do check_media_change */
 	unsigned forced_geom	: 1;	/* 1 if hdx=c,h,s was given at boot */
-	unsigned no_unmask	: 1;	/* disallow setting unmask bit */
-	unsigned no_io_32bit	: 1;	/* disallow enabling 32bit I/O */
 	unsigned nobios		: 1;	/* flag: do not probe bios for drive */
 	unsigned revalidate	: 1;	/* request revalidation */
 	unsigned atapi_overlap	: 1;	/* flag: ATAPI overlap (not supported) */
@@ -367,7 +371,6 @@
 	byte		mult_count;	/* current multiple sector setting */
 	byte		mult_req;	/* requested multiple sector setting */
 	byte		tune_req;	/* requested drive tuning setting */
-	byte		io_32bit;	/* 0=16-bit, 1=32-bit, 2/3=32bit+sync */
 	byte		bad_wstat;	/* used for ignoring WRERR_STAT */
 	byte		nowerr;		/* used for ignoring WRERR_STAT */
 	byte		sect0;		/* offset of first sector for DM6:DDO */
@@ -386,7 +389,6 @@
 	struct hd_driveid *id;		/* drive model identification info */
 	struct hd_struct  *part;	/* drive partition table */
 
-	char		name[6];	/* drive name, such as "hda" */
 	struct ata_operations *driver;
 
 	void		*driver_data;	/* extra driver data */
@@ -438,47 +440,6 @@
 
 typedef int (ide_dmaproc_t)(ide_dma_action_t, ide_drive_t *);
 
-/*
- * An ide_ideproc_t() performs CPU-polled transfers to/from a drive.
- * Arguments are: the drive, the buffer pointer, and the length (in bytes or
- * words depending on if it's an IDE or ATAPI call).
- *
- * If it is not defined for a controller, standard-code is used from ide.c.
- *
- * Controllers which are not memory-mapped in the standard way need to
- * override that mechanism using this function to work.
- *
- */
-typedef enum { ideproc_ide_input_data,    ideproc_ide_output_data,
-	       ideproc_atapi_input_bytes, ideproc_atapi_output_bytes
-} ide_ide_action_t;
-
-typedef void (ide_ideproc_t)(ide_ide_action_t, ide_drive_t *, void *, unsigned int);
-
-/*
- * An ide_tuneproc_t() is used to set the speed of an IDE interface
- * to a particular PIO mode.  The "byte" parameter is used
- * to select the PIO mode by number (0,1,2,3,4,5), and a value of 255
- * indicates that the interface driver should "auto-tune" the PIO mode
- * according to the drive capabilities in drive->id;
- *
- * Not all interface types support tuning, and not all of those
- * support all possible PIO settings.  They may silently ignore
- * or round values as they see fit.
- */
-typedef void (ide_tuneproc_t) (ide_drive_t *, byte);
-typedef int (ide_speedproc_t) (ide_drive_t *, byte);
-
-/*
- * This is used to provide support for strange interfaces
- */
-typedef void (ide_selectproc_t) (ide_drive_t *);
-typedef void (ide_resetproc_t) (ide_drive_t *);
-typedef int (ide_quirkproc_t) (ide_drive_t *);
-typedef void (ide_intrproc_t) (ide_drive_t *);
-typedef void (ide_maskproc_t) (ide_drive_t *, int);
-typedef void (ide_rw_proc_t) (ide_drive_t *, ide_dma_action_t);
-
 enum {
 	ATA_PRIMARY	= 0,
 	ATA_SECONDARY	= 1
@@ -498,15 +459,40 @@
 #endif
 	ide_drive_t	drives[MAX_DRIVES];	/* drive info */
 	struct gendisk	*gd;		/* gendisk structure */
-	ide_tuneproc_t	*tuneproc;	/* routine to tune PIO mode for drives */
-	ide_speedproc_t	*speedproc;	/* routine to retune DMA modes for drives */
-	ide_selectproc_t *selectproc;	/* tweaks hardware to select drive */
-	ide_resetproc_t	*resetproc;	/* routine to reset controller after a disk reset */
-	ide_intrproc_t	*intrproc;	/* special interrupt handling for shared pci interrupts */
-	ide_maskproc_t	*maskproc;	/* special host masking for drive selection */
-	ide_quirkproc_t	*quirkproc;	/* check host's drive quirk list */
-	ide_rw_proc_t	*rwproc;	/* adjust timing based upon rq->cmd direction */
-	ide_ideproc_t   *ideproc;       /* CPU-polled transfer routine */
+
+	/*
+	 * Routines to tune PIO and DMA mode for drives.
+	 *
+	 * A value of 255 indicates that the function should choose the optimal
+	 * mode itself.
+	 */
+	void (*tuneproc) (ide_drive_t *, byte pio);
+	int (*speedproc) (ide_drive_t *, byte pio);
+
+	/* tweaks hardware to select drive */
+	void (*selectproc) (ide_drive_t *);
+
+	/* routine to reset controller after a disk reset */
+	void (*resetproc) (ide_drive_t *);
+
+	/* special interrupt handling for shared pci interrupts */
+	void (*intrproc) (ide_drive_t *);
+
+	/* special host masking for drive selection */
+	void (*maskproc) (ide_drive_t *, int);
+
+	/* adjust timing based upon rq->cmd direction */
+	void (*rwproc) (ide_drive_t *, ide_dma_action_t);
+
+	/* check host's drive quirk list */
+	int (*quirkproc) (ide_drive_t *);
+
+	/* CPU-polled transfer routines */
+	void (*ata_read)(ide_drive_t *, void *, unsigned int);
+	void (*ata_write)(ide_drive_t *, void *, unsigned int);
+	void (*atapi_read)(ide_drive_t *, void *, unsigned int);
+	void (*atapi_write)(ide_drive_t *, void *, unsigned int);
+
 	ide_dmaproc_t	*dmaproc;	/* dma read/write/abort routine */
 	unsigned long	dma_base;	/* base addr for dma ports */
 	unsigned	dma_extra;	/* extra addr for dma ports */
@@ -526,6 +512,12 @@
 	unsigned	autodma    : 1;	/* automatically try to enable DMA at boot */
 	unsigned	udma_four  : 1;	/* 1=ATA-66 capable, 0=default */
 	unsigned	highmem	   : 1; /* can do full 32-bit dma */
+	byte		slow;		/* flag: slow data port */
+	unsigned no_io_32bit	   : 1;	/* disallow enabling 32bit I/O */
+	byte		io_32bit;	/* 0=16-bit, 1=32-bit, 2/3=32bit+sync */
+	unsigned no_unmask	   : 1;	/* disallow setting unmask bit */
+	byte		unmask;		/* flag: okay to unmask other irqs */
+	unsigned	auto_poll  : 1; /* supports nop auto-poll */
 #if (DISK_RECOVERY_TIME > 0)
 	unsigned long	last_time;	/* time when previous rq was done */
 #endif
@@ -546,7 +538,7 @@
 typedef enum {
 	ide_stopped,	/* no drive operation was started */
 	ide_started,	/* a drive operation was started, and a handler was set */
-	ide_released	/* started and released bus */
+	ide_released,	/* started, handler set, bus released */
 } ide_startstop_t;
 
 /*
@@ -820,7 +812,7 @@
  */
 struct ata_request {
 	struct request		*ar_rq;		/* real request */
-	struct ide_drive_s	*ar_drive;	/* associated drive */
+	struct ata_device	*ar_drive;	/* associated drive */
 	unsigned long		ar_flags;	/* ATA_AR_* flags */
 	int			ar_tag;		/* tag number, if any */
 	struct list_head	ar_queue;	/* pending list */
@@ -839,12 +831,11 @@
 
 #define AR_TASK_CMD(ar)	((ar)->ar_task.taskfile.command)
 
-void ata_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount);
-void ata_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount);
-void atapi_input_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount);
-void atapi_output_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount);
-void taskfile_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount);
-void taskfile_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount);
+extern void ata_read(ide_drive_t *drive, void *buffer, unsigned int wcount);
+extern void ata_write(ide_drive_t *drive, void *buffer, unsigned int wcount);
+
+extern void atapi_read(ide_drive_t *drive, void *buffer, unsigned int bytecount);
+extern void atapi_write(ide_drive_t *drive, void *buffer, unsigned int bytecount);
 
 extern ide_startstop_t ata_taskfile(ide_drive_t *drive,
 		struct ata_taskfile *args, struct request *rq);
@@ -976,9 +967,14 @@
 /*
  * ata_request flag bits
  */
-#define ATA_AR_QUEUED	1
-#define ATA_AR_SETUP	2
-#define ATA_AR_RETURN	4
+#define ATA_AR_QUEUED	1	/* was queued */
+#define ATA_AR_SETUP	2	/* dma table mapped */
+#define ATA_AR_POOL	4	/* originated from drive pool */
+
+/*
+ * if turn-around time is longer than this, halve queue depth
+ */
+#define ATA_AR_MAX_TURNAROUND	(3 * HZ)
 
 #define list_ata_entry(entry) list_entry((entry), struct ata_request, ar_queue)
 
@@ -1005,8 +1001,10 @@
 
 	if (!list_empty(&drive->free_req)) {
 		ar = list_ata_entry(drive->free_req.next);
+
 		list_del(&ar->ar_queue);
 		ata_ar_init(drive, ar);
+		ar->ar_flags |= ATA_AR_POOL;
 	}
 
 	return ar;
@@ -1014,7 +1012,8 @@
 
 static inline void ata_ar_put(ide_drive_t *drive, struct ata_request *ar)
 {
-	list_add(&ar->ar_queue, &drive->free_req);
+	if (ar->ar_flags & ATA_AR_POOL)
+		list_add(&ar->ar_queue, &drive->free_req);
 
 	if (ar->ar_flags & ATA_AR_QUEUED) {
 		/* clear the tag */
@@ -1026,7 +1025,7 @@
 	ar->ar_rq = NULL;
 }
 
-extern inline int ide_get_tag(ide_drive_t *drive)
+static inline int ide_get_tag(ide_drive_t *drive)
 {
 	int tag = ffz(drive->tcq->tag_mask);
 
@@ -1043,12 +1042,28 @@
 }
 
 #ifdef CONFIG_BLK_DEV_IDE_TCQ
-# define ide_pending_commands(drive)	((drive)->using_tcq && (drive)->tcq->queued)
+static inline int ide_pending_commands(ide_drive_t *drive)
+{
+	if (!drive->tcq)
+		return 0;
+
+	return drive->tcq->queued;
+}
+
+static inline int ide_can_queue(ide_drive_t *drive)
+{
+	if (!drive->tcq)
+		return 1;
+
+	return drive->tcq->queued < drive->queue_depth;
+}
 #else
-# define ide_pending_commands(drive)	0
+#define ide_pending_commands(drive)	(0)
+#define ide_can_queue(drive)		(1)
 #endif
 
 int ide_build_commandlist(ide_drive_t *);
+int ide_init_commandlist(ide_drive_t *);
 void ide_teardown_commandlist(ide_drive_t *);
 int ide_tcq_dmaproc(ide_dma_action_t, ide_drive_t *);
 ide_startstop_t ide_start_tag(ide_dma_action_t, ide_drive_t *, struct ata_request *);
diff -Nru a/include/linux/interrupt.h b/include/linux/interrupt.h
--- a/include/linux/interrupt.h	Mon Apr 22 15:32:18 2002
+++ b/include/linux/interrupt.h	Mon Apr 22 15:32:18 2002
@@ -7,8 +7,8 @@
 #include <linux/kernel.h>
 #include <linux/smp.h>
 #include <linux/cache.h>
+#include <linux/bitops.h>
 
-#include <asm/bitops.h>
 #include <asm/atomic.h>
 #include <asm/system.h>
 #include <asm/ptrace.h>
diff -Nru a/include/linux/nls.h b/include/linux/nls.h
--- a/include/linux/nls.h	Mon Apr 22 15:32:19 2002
+++ b/include/linux/nls.h	Mon Apr 22 15:32:19 2002
@@ -18,7 +18,7 @@
 };
 
 /* this value hold the maximum octet of charset */
-#define NLS_MAX_CHARSET_SIZE 3
+#define NLS_MAX_CHARSET_SIZE 6 /* for UTF-8 */
 
 /* nls.c */
 extern int register_nls(struct nls_table *);
diff -Nru a/include/linux/pci_ids.h b/include/linux/pci_ids.h
--- a/include/linux/pci_ids.h	Mon Apr 22 15:32:19 2002
+++ b/include/linux/pci_ids.h	Mon Apr 22 15:32:19 2002
@@ -510,6 +510,9 @@
 #define PCI_DEVICE_ID_HP_DIVA1		0x1049
 #define PCI_DEVICE_ID_HP_DIVA2		0x104A
 #define PCI_DEVICE_ID_HP_SP2_0		0x104B
+#define PCI_DEVICE_ID_HP_ZX1_SBA	0x1229
+#define PCI_DEVICE_ID_HP_ZX1_IOC	0x122a
+#define PCI_DEVICE_ID_HP_ZX1_LBA	0x122e
 
 #define PCI_VENDOR_ID_PCTECH		0x1042
 #define PCI_DEVICE_ID_PCTECH_RZ1000	0x1000
diff -Nru a/include/linux/prctl.h b/include/linux/prctl.h
--- a/include/linux/prctl.h	Mon Apr 22 15:32:19 2002
+++ b/include/linux/prctl.h	Mon Apr 22 15:32:19 2002
@@ -20,4 +20,10 @@
 #define PR_GET_KEEPCAPS   7
 #define PR_SET_KEEPCAPS   8
 
+/* Get/set floating-point emulation control bits (if meaningful) */
+#define PR_GET_FPEMU  9
+#define PR_SET_FPEMU 10
+# define PR_FPEMU_NOPRINT	1	/* silently emulate fp operations accesses */
+# define PR_FPEMU_SIGFPE	2	/* don't emulate fp operations, send SIGFPE instead */
+
 #endif /* _LINUX_PRCTL_H */
diff -Nru a/include/linux/proc_fs.h b/include/linux/proc_fs.h
--- a/include/linux/proc_fs.h	Mon Apr 22 15:32:18 2002
+++ b/include/linux/proc_fs.h	Mon Apr 22 15:32:18 2002
@@ -212,7 +212,6 @@
 		int (*proc_get_link)(struct inode *, struct dentry **, struct vfsmount **);
 		int (*proc_read)(struct task_struct *task, char *page);
 	} op;
-	struct file *file;
 	struct proc_dir_entry *pde;
 	struct inode vfs_inode;
 };
diff -Nru a/include/linux/raid/md.h b/include/linux/raid/md.h
--- a/include/linux/raid/md.h	Mon Apr 22 15:32:18 2002
+++ b/include/linux/raid/md.h	Mon Apr 22 15:32:18 2002
@@ -23,7 +23,7 @@
 #include <linux/major.h>
 #include <linux/ioctl.h>
 #include <linux/types.h>
-#include <asm/bitops.h>
+#include <linux/bitops.h>
 #include <linux/module.h>
 #include <linux/hdreg.h>
 #include <linux/proc_fs.h>
diff -Nru a/include/linux/sched.h b/include/linux/sched.h
--- a/include/linux/sched.h	Mon Apr 22 15:32:18 2002
+++ b/include/linux/sched.h	Mon Apr 22 15:32:18 2002
@@ -346,6 +346,7 @@
 
 /* journalling filesystem info */
 	void *journal_info;
+	struct dentry *proc_dentry;
 };
 
 extern void __put_task_struct(struct task_struct *tsk);
@@ -769,15 +770,7 @@
 
 #define thread_group_leader(p)	(p->pid == p->tgid)
 
-static inline void unhash_process(struct task_struct *p)
-{
-	write_lock_irq(&tasklist_lock);
-	nr_threads--;
-	unhash_pid(p);
-	REMOVE_LINKS(p);
-	list_del(&p->thread_group);
-	write_unlock_irq(&tasklist_lock);
-}
+extern void unhash_process(struct task_struct *p);
 
 /* Protects ->fs, ->files, ->mm, and synchronises with wait4().  Nests inside tasklist_lock */
 static inline void task_lock(struct task_struct *p)
diff -Nru a/include/linux/signal.h b/include/linux/signal.h
--- a/include/linux/signal.h	Mon Apr 22 15:32:19 2002
+++ b/include/linux/signal.h	Mon Apr 22 15:32:19 2002
@@ -24,9 +24,9 @@
  */
 
 #ifndef __HAVE_ARCH_SIG_BITOPS
-#include <asm/bitops.h>
+#include <linux/bitops.h>
 
-/* We don't use <asm/bitops.h> for these because there is no need to
+/* We don't use <linux/bitops.h> for these because there is no need to
    be atomic.  */
 static inline void sigaddset(sigset_t *set, int _sig)
 {
diff -Nru a/include/linux/smp.h b/include/linux/smp.h
--- a/include/linux/smp.h	Mon Apr 22 15:32:19 2002
+++ b/include/linux/smp.h	Mon Apr 22 15:32:19 2002
@@ -12,6 +12,7 @@
 
 #include <linux/kernel.h>
 #include <linux/compiler.h>
+#include <linux/threads.h>
 #include <asm/smp.h>
 
 /*
diff -Nru a/include/linux/thread_info.h b/include/linux/thread_info.h
--- a/include/linux/thread_info.h	Mon Apr 22 15:32:19 2002
+++ b/include/linux/thread_info.h	Mon Apr 22 15:32:19 2002
@@ -7,8 +7,8 @@
 #ifndef _LINUX_THREAD_INFO_H
 #define _LINUX_THREAD_INFO_H
 
+#include <linux/bitops.h>
 #include <asm/thread_info.h>
-#include <asm/bitops.h>
 
 #ifdef __KERNEL__
 
diff -Nru a/include/linux/tqueue.h b/include/linux/tqueue.h
--- a/include/linux/tqueue.h	Mon Apr 22 15:32:18 2002
+++ b/include/linux/tqueue.h	Mon Apr 22 15:32:18 2002
@@ -15,7 +15,7 @@
 
 #include <linux/spinlock.h>
 #include <linux/list.h>
-#include <asm/bitops.h>
+#include <linux/bitops.h>
 #include <asm/system.h>
 
 /*
diff -Nru a/include/linux/usb.h b/include/linux/usb.h
--- a/include/linux/usb.h	Mon Apr 22 15:32:19 2002
+++ b/include/linux/usb.h	Mon Apr 22 15:32:19 2002
@@ -890,7 +890,6 @@
 	char *buf, size_t size);
 extern int usb_set_configuration(struct usb_device *dev, int configuration);
 extern int usb_set_interface(struct usb_device *dev, int ifnum, int alternate);
-extern int usb_make_path(struct usb_device *dev, char *buf, size_t size);
 
 /*
  * timeouts, in seconds, used for sending/receiving control messages
@@ -917,6 +916,7 @@
  */
 struct usb_bus {
 	int busnum;			/* Bus number (in order of reg) */
+	char *bus_name;			/* stable id (PCI slot_name etc) */
 
 #ifdef DEVNUM_ROUND_ROBIN
 	int devnum_next;                /* Next open device number in round-robin allocation */
@@ -1086,6 +1086,37 @@
 const struct usb_device_id *usb_match_id(struct usb_device *dev,
 					 struct usb_interface *interface,
 					 const struct usb_device_id *id);
+
+/**
+ * usb_make_path - returns stable device path in the usb tree
+ * @dev: the device whose path is being constructed
+ * @buf: where to put the string
+ * @size: how big is "buf"?
+ *
+ * Returns length of the string (> 0) or negative if size was too small.
+ *
+ * This identifier is intended to be "stable", reflecting physical paths in
+ * hardware such as physical bus addresses for host controllers or ports on
+ * USB hubs.  That makes it stay the same until systems are physically
+ * reconfigured, by re-cabling a tree of USB devices or by moving USB host
+ * controllers.  Adding and removing devices, including virtual root hubs
+ * in host controller driver modules, does not change these path identifers;
+ * neither does rebooting or re-enumerating.  These are more useful identifiers
+ * than changeable ("unstable") ones like bus numbers or device addresses.
+ * 
+ * With a partial exception for devices connected to USB 2.0 root hubs, these
+ * identifiers are also predictable:  so long as the device tree isn't changed,
+ * plugging any USB device into a given hub port always gives it the same path.
+ * Because of the use of "companion" controllers, devices connected to ports on
+ * USB 2.0 root hubs (EHCI host controllers) will get one path ID if they are
+ * high speed, and a different one if they are full or low speed.
+ */
+static inline int usb_make_path (struct usb_device *dev, char *buf, size_t size)
+{
+	int actual;
+	actual = snprintf (buf, size, "usb-%s-%s", dev->bus->bus_name, dev->devpath);
+	return (actual >= size) ? -1 : actual;
+}
 
 /* -------------------------------------------------------------------------- */
 
diff -Nru a/include/sound/asound.h b/include/sound/asound.h
--- a/include/sound/asound.h	Mon Apr 22 15:32:19 2002
+++ b/include/sound/asound.h	Mon Apr 22 15:32:19 2002
@@ -30,6 +30,7 @@
 #ifdef __KERNEL__
 
 #include <linux/types.h>
+#include <linux/time.h>
 #include <asm/byteorder.h>
 
 #if  __LITTLE_ENDIAN == 1234
diff -Nru a/include/sound/core.h b/include/sound/core.h
--- a/include/sound/core.h	Mon Apr 22 15:32:19 2002
+++ b/include/sound/core.h	Mon Apr 22 15:32:19 2002
@@ -252,6 +252,7 @@
 #define snd_free_isa_pages(size, ptr, dma_addr) snd_free_pages(ptr, size)
 #endif
 #ifdef CONFIG_PCI
+struct pci_dev;
 void *snd_malloc_pci_pages(struct pci_dev *pci, unsigned long size, dma_addr_t *dma_addr);
 void *snd_malloc_pci_pages_fallback(struct pci_dev *pci, unsigned long size, dma_addr_t *dma_addr, unsigned long *res_size);
 void snd_free_pci_pages(struct pci_dev *pci, unsigned long size, void *ptr, dma_addr_t dma_addr);
diff -Nru a/init/main.c b/init/main.c
--- a/init/main.c	Mon Apr 22 15:32:19 2002
+++ b/init/main.c	Mon Apr 22 15:32:19 2002
@@ -271,6 +271,13 @@
 #else
 #define smp_init()	do { } while (0)
 #endif
+static inline void setup_per_cpu_areas(void)
+{
+}
+
+static inline void setup_per_cpu_areas(void)
+{
+}
 
 #else
 
@@ -295,10 +302,6 @@
 		__per_cpu_offset[i] = ptr - __per_cpu_start;
 		memcpy(ptr, __per_cpu_start, size);
 	}
-}
-#else
-static inline void setup_per_cpu_areas(void)
-{
 }
 #endif /* !__GENERIC_PER_CPU */
 
diff -Nru a/kernel/exit.c b/kernel/exit.c
--- a/kernel/exit.c	Mon Apr 22 15:32:19 2002
+++ b/kernel/exit.c	Mon Apr 22 15:32:19 2002
@@ -27,6 +27,32 @@
 
 int getrusage(struct task_struct *, int, struct rusage *);
 
+static inline void __unhash_process(struct task_struct *p)
+{
+	struct dentry *proc_dentry;
+	write_lock_irq(&tasklist_lock);
+	nr_threads--;
+	unhash_pid(p);
+	REMOVE_LINKS(p);
+	list_del(&p->thread_group);
+	p->pid = 0;
+	proc_dentry = p->proc_dentry;
+	if (unlikely(proc_dentry)) {
+		spin_lock(&dcache_lock);
+		if (!list_empty(&proc_dentry->d_hash)) {
+			dget_locked(proc_dentry);
+			list_del_init(&proc_dentry->d_hash);
+		} else
+			proc_dentry = NULL;
+		spin_unlock(&dcache_lock);
+	}
+	write_unlock_irq(&tasklist_lock);
+	if (unlikely(proc_dentry)) {
+		shrink_dcache_parent(proc_dentry);
+		dput(proc_dentry);
+	}
+}
+
 static void release_task(struct task_struct * p)
 {
 	if (p == current)
@@ -43,8 +69,14 @@
 	current->cmaj_flt += p->maj_flt + p->cmaj_flt;
 	current->cnswap += p->nswap + p->cnswap;
 	sched_exit(p);
-	p->pid = 0;
 	put_task_struct(p);
+}
+
+/* we are using it only for SMP init */
+
+void unhash_process(struct task_struct *p)
+{
+	return __unhash_process(p);
 }
 
 /*
diff -Nru a/kernel/fork.c b/kernel/fork.c
--- a/kernel/fork.c	Mon Apr 22 15:32:18 2002
+++ b/kernel/fork.c	Mon Apr 22 15:32:18 2002
@@ -665,6 +665,7 @@
 
 	copy_flags(clone_flags, p);
 	p->pid = get_pid(clone_flags);
+	p->proc_dentry = NULL;
 
 	INIT_LIST_HEAD(&p->run_list);
 
diff -Nru a/kernel/ksyms.c b/kernel/ksyms.c
--- a/kernel/ksyms.c	Mon Apr 22 15:32:18 2002
+++ b/kernel/ksyms.c	Mon Apr 22 15:32:18 2002
@@ -162,6 +162,8 @@
 EXPORT_SYMBOL(d_move);
 EXPORT_SYMBOL(d_instantiate);
 EXPORT_SYMBOL(d_alloc);
+EXPORT_SYMBOL(d_alloc_anon);
+EXPORT_SYMBOL(d_splice_alias);
 EXPORT_SYMBOL(d_lookup);
 EXPORT_SYMBOL(__d_path);
 EXPORT_SYMBOL(mark_buffer_dirty);
@@ -209,6 +211,7 @@
 EXPORT_SYMBOL(__wait_on_buffer);
 EXPORT_SYMBOL(___wait_on_page);
 EXPORT_SYMBOL(generic_direct_IO);
+EXPORT_SYMBOL(discard_bh_page);
 EXPORT_SYMBOL(block_write_full_page);
 EXPORT_SYMBOL(block_read_full_page);
 EXPORT_SYMBOL(block_prepare_write);
@@ -240,6 +243,7 @@
 EXPORT_SYMBOL(prune_dcache);
 EXPORT_SYMBOL(shrink_dcache_sb);
 EXPORT_SYMBOL(shrink_dcache_parent);
+EXPORT_SYMBOL(shrink_dcache_anon);
 EXPORT_SYMBOL(find_inode_number);
 EXPORT_SYMBOL(is_subdir);
 EXPORT_SYMBOL(get_unused_fd);
diff -Nru a/kernel/printk.c b/kernel/printk.c
--- a/kernel/printk.c	Mon Apr 22 15:32:19 2002
+++ b/kernel/printk.c	Mon Apr 22 15:32:19 2002
@@ -39,6 +39,10 @@
 
 #define LOG_BUF_MASK	(LOG_BUF_LEN-1)
 
+#ifndef arch_consoles_callable
+#define arch_consoles_callable() (1)
+#endif
+
 /* printk's without a loglevel use this.. */
 #define DEFAULT_MESSAGE_LOGLEVEL 4 /* KERN_WARNING */
 
@@ -439,6 +443,14 @@
 			log_level_unknown = 1;
 	}
 
+	if (!arch_consoles_callable()) {
+		/*
+		 * On some architectures, the consoles are not usable
+		 * on secondary CPUs early in the boot process.
+		 */
+		spin_unlock_irqrestore(&logbuf_lock, flags);
+		goto out;
+	}
 	if (!down_trylock(&console_sem)) {
 		/*
 		 * We own the drivers.  We can drop the spinlock and let
@@ -455,6 +467,7 @@
 		 */
 		spin_unlock_irqrestore(&logbuf_lock, flags);
 	}
+out:
 	return printed_len;
 }
 EXPORT_SYMBOL(printk);
diff -Nru a/kernel/sched.c b/kernel/sched.c
--- a/kernel/sched.c	Mon Apr 22 15:32:19 2002
+++ b/kernel/sched.c	Mon Apr 22 15:32:19 2002
@@ -1237,11 +1237,11 @@
 /**
  * sys_sched_setaffinity - set the cpu affinity of a process
  * @pid: pid of the process
- * @len: length of new_mask
- * @new_mask: user-space pointer to the new cpu mask
+ * @len: length of the bitmask pointed to by user_mask_ptr
+ * @user_mask_ptr: user-space pointer to the new cpu mask
  */
 asmlinkage int sys_sched_setaffinity(pid_t pid, unsigned int len,
-				      unsigned long *new_mask_ptr)
+				      unsigned long *user_mask_ptr)
 {
 	unsigned long new_mask;
 	task_t *p;
@@ -1250,7 +1250,7 @@
 	if (len < sizeof(new_mask))
 		return -EINVAL;
 
-	if (copy_from_user(&new_mask, new_mask_ptr, sizeof(new_mask)))
+	if (copy_from_user(&new_mask, user_mask_ptr, sizeof(new_mask)))
 		return -EFAULT;
 
 	new_mask &= cpu_online_map;
@@ -1289,8 +1289,8 @@
 /**
  * sys_sched_getaffinity - get the cpu affinity of a process
  * @pid: pid of the process
- * @len: length of the new mask
- * @user_mask_ptr: userspace pointer to the mask
+ * @len: length of the bitmask pointed to by user_mask_ptr
+ * @user_mask_ptr: user-space pointer to hold the current cpu mask
  */
 asmlinkage int sys_sched_getaffinity(pid_t pid, unsigned int len,
 				      unsigned long *user_mask_ptr)
@@ -1649,6 +1649,7 @@
 	if (!new_mask)
 		BUG();
 
+	preempt_disable();
 	rq = task_rq_lock(p, &flags);
 	p->cpus_allowed = new_mask;
 	/*
@@ -1657,7 +1658,7 @@
 	 */
 	if (new_mask & (1UL << p->thread_info->cpu)) {
 		task_rq_unlock(rq, &flags);
-		return;
+		goto out;
 	}
 
 	init_MUTEX_LOCKED(&req.sem);
@@ -1667,12 +1668,13 @@
 	wake_up_process(rq->migration_thread);
 
 	down(&req.sem);
+out:
+	preempt_enable();
 }
 
-static volatile unsigned long migration_mask;
-
-static int migration_thread(void * unused)
+static int migration_thread(void * bind_cpu)
 {
+	int cpu = cpu_logical_map((int) (long) bind_cpu);
 	struct sched_param param = { sched_priority: 99 };
 	runqueue_t *rq;
 	int ret;
@@ -1680,36 +1682,20 @@
 	daemonize();
 	sigfillset(&current->blocked);
 	set_fs(KERNEL_DS);
-	ret = setscheduler(0, SCHED_FIFO, &param);
-
 	/*
-	 * We have to migrate manually - there is no migration thread
-	 * to do this for us yet :-)
-	 *
-	 * We use the following property of the Linux scheduler. At
-	 * this point no other task is running, so by keeping all
-	 * migration threads running, the load-balancer will distribute
-	 * them between all CPUs equally. At that point every migration
-	 * task binds itself to the current CPU.
+	 * The first migration thread is started on CPU #0. This one can migrate
+	 * the other migration threads to their destination CPUs.
 	 */
-
-	/* wait for all migration threads to start up. */
-	while (!migration_mask)
-		yield();
-
-	for (;;) {
-		preempt_disable();
-		if (test_and_clear_bit(smp_processor_id(), &migration_mask))
-			current->cpus_allowed = 1 << smp_processor_id();
-		if (test_thread_flag(TIF_NEED_RESCHED))
-			schedule();
-		if (!migration_mask)
-			break;
-		preempt_enable();
+	if (cpu != 0) {
+		while (!cpu_rq(cpu_logical_map(0))->migration_thread)
+			yield();
+		set_cpus_allowed(current, 1UL << cpu);
 	}
+	printk("migration_task %d on cpu=%d\n",cpu,smp_processor_id());
+	ret = setscheduler(0, SCHED_FIFO, &param);
+
 	rq = this_rq();
 	rq->migration_thread = current;
-	preempt_enable();
 
 	sprintf(current->comm, "migration_CPU%d", smp_processor_id());
 
@@ -1740,9 +1726,11 @@
 		cpu_src = p->thread_info->cpu;
 		rq_src = cpu_rq(cpu_src);
 
+		local_irq_save(flags);
 		double_rq_lock(rq_src, rq_dest);
 		if (p->thread_info->cpu != cpu_src) {
 			double_rq_unlock(rq_src, rq_dest);
+			local_irq_restore(flags);
 			goto repeat;
 		}
 		if (rq_src == rq) {
@@ -1753,6 +1741,7 @@
 			}
 		}
 		double_rq_unlock(rq_src, rq_dest);
+		local_irq_restore(flags);
 
 		up(&req->sem);
 	}
@@ -1760,33 +1749,18 @@
 
 void __init migration_init(void)
 {
-	unsigned long tmp, orig_cache_decay_ticks;
 	int cpu;
 
-	tmp = 0;
+	current->cpus_allowed = 1UL << cpu_logical_map(0);
 	for (cpu = 0; cpu < smp_num_cpus; cpu++) {
-		if (kernel_thread(migration_thread, NULL,
+		if (kernel_thread(migration_thread, (void *) (long) cpu,
 				CLONE_FS | CLONE_FILES | CLONE_SIGNAL) < 0)
 			BUG();
-		tmp |= (1UL << cpu_logical_map(cpu));
 	}
+	current->cpus_allowed = -1L;
 
-	migration_mask = tmp;
-
-	orig_cache_decay_ticks = cache_decay_ticks;
-	cache_decay_ticks = 0;
-
-	for (cpu = 0; cpu < smp_num_cpus; cpu++) {
-		int logical = cpu_logical_map(cpu);
-
-		while (!cpu_rq(logical)->migration_thread) {
-			set_current_state(TASK_INTERRUPTIBLE);
+	for (cpu = 0; cpu < smp_num_cpus; cpu++)
+		while (!cpu_rq(cpu)->migration_thread)
 			schedule_timeout(2);
-		}
-	}
-	if (migration_mask)
-		BUG();
-
-	cache_decay_ticks = orig_cache_decay_ticks;
 }
 #endif
diff -Nru a/kernel/sys.c b/kernel/sys.c
--- a/kernel/sys.c	Mon Apr 22 15:32:18 2002
+++ b/kernel/sys.c	Mon Apr 22 15:32:18 2002
@@ -21,6 +21,19 @@
 #include <asm/io.h>
 #include <asm/unistd.h>
 
+#ifndef SET_UNALIGN_CTL
+# define SET_UNALIGN_CTL(a,b)	(-EINVAL)
+#endif
+#ifndef GET_UNALIGN_CTL
+# define GET_UNALIGN_CTL(a,b)	(-EINVAL)
+#endif
+#ifndef SET_FPEMU_CTL
+# define SET_FPEMU_CTL(a,b)	(-EINVAL)
+#endif
+#ifndef GET_FPEMU_CTL
+# define GET_FPEMU_CTL(a,b)	(-EINVAL)
+#endif
+
 /*
  * this is where the system-wide overflow UID and GID are defined, for
  * architectures that now have 32-bit UID/GID but didn't in the past
@@ -1240,20 +1253,18 @@
 			}
 			current->mm->dumpable = arg2;
 			break;
-	        case PR_SET_UNALIGN:
-#ifdef SET_UNALIGN_CTL
+
+		case PR_SET_UNALIGN:
 			error = SET_UNALIGN_CTL(current, arg2);
-#else
-			error = -EINVAL;
-#endif
 			break;
-
-	        case PR_GET_UNALIGN:
-#ifdef GET_UNALIGN_CTL
+		case PR_GET_UNALIGN:
 			error = GET_UNALIGN_CTL(current, arg2);
-#else
-			error = -EINVAL;
-#endif
+			break;
+		case PR_SET_FPEMU:
+			error = SET_FPEMU_CTL(current, arg2);
+			break;
+		case PR_GET_FPEMU:
+			error = GET_FPEMU_CTL(current, arg2);
 			break;
 
 		case PR_GET_KEEPCAPS:
diff -Nru a/kernel/time.c b/kernel/time.c
--- a/kernel/time.c	Mon Apr 22 15:32:19 2002
+++ b/kernel/time.c	Mon Apr 22 15:32:19 2002
@@ -216,6 +216,11 @@
 		
 	/* Now we validate the data before disabling interrupts */
 
+	if ((txc->modes & ADJ_OFFSET_SINGLESHOT) == ADJ_OFFSET_SINGLESHOT)
+	  /* singleshot must not be used with any other mode bits */
+		if (txc->modes != ADJ_OFFSET_SINGLESHOT)
+			return -EINVAL;
+
 	if (txc->modes != ADJ_OFFSET_SINGLESHOT && (txc->modes & ADJ_OFFSET))
 	  /* adjustment Offset limited to +- .512 seconds */
 		if (txc->offset <= - MAXPHASE || txc->offset >= MAXPHASE )
diff -Nru a/lib/radix-tree.c b/lib/radix-tree.c
--- a/lib/radix-tree.c	Mon Apr 22 15:32:19 2002
+++ b/lib/radix-tree.c	Mon Apr 22 15:32:19 2002
@@ -24,6 +24,7 @@
 #include <linux/module.h>
 #include <linux/radix-tree.h>
 #include <linux/slab.h>
+#include <linux/string.h>
 
 /*
  * Radix tree node definition.
@@ -49,11 +50,27 @@
 static kmem_cache_t *radix_tree_node_cachep;
 static mempool_t *radix_tree_node_pool;
 
-#define radix_tree_node_alloc(root) \
-	mempool_alloc(radix_tree_node_pool, (root)->gfp_mask)
-#define radix_tree_node_free(node) \
-	mempool_free((node), radix_tree_node_pool);
-
+/*
+ * mempool scribbles on the first eight bytes of the managed
+ * memory.  Here we implement a temp workaround for that.
+ */
+#include <linux/list.h>
+static inline struct radix_tree_node *
+radix_tree_node_alloc(struct radix_tree_root *root)
+{
+	struct radix_tree_node *ret;
+
+	ret = mempool_alloc(radix_tree_node_pool, root->gfp_mask);
+	if (ret)
+		memset(ret, 0, sizeof(struct list_head));
+	return ret;
+}
+
+static inline void
+radix_tree_node_free(struct radix_tree_node *node)
+{
+	mempool_free(node, radix_tree_node_pool);
+}
 
 /*
  *	Return the maximum key which can be store into a
diff -Nru a/mm/filemap.c b/mm/filemap.c
--- a/mm/filemap.c	Mon Apr 22 15:32:19 2002
+++ b/mm/filemap.c	Mon Apr 22 15:32:19 2002
@@ -797,9 +797,9 @@
 }
 
 /*
- * Must be called with the pagecache lock held,
- * will return with it held (but it may be dropped
- * during blocking operations..
+ * Must be called with the mapping lock held for writing.
+ * Will return with it held for writing, but it may be dropped
+ * while locking the page.
  */
 static struct page *__find_lock_page(struct address_space *mapping,
 					unsigned long offset)
@@ -815,11 +815,11 @@
 	if (page) {
 		page_cache_get(page);
 		if (TryLockPage(page)) {
-			read_unlock(&mapping->page_lock);
+			write_unlock(&mapping->page_lock);
 			lock_page(page);
-			read_lock(&mapping->page_lock);
+			write_lock(&mapping->page_lock);
 
-			/* Has the page been re-allocated while we slept? */
+			/* Has the page been truncated while we slept? */
 			if (page->mapping != mapping || page->index != offset) {
 				UnlockPage(page);
 				page_cache_release(page);
@@ -830,25 +830,53 @@
 	return page;
 }
 
+/**
+ * find_lock_page - locate, pin and lock a pagecache page
+ *
+ * @mapping - the address_space to search
+ * @offset - the page index
+ *
+ * Locates the desired pagecache page, locks it, increments its reference
+ * count and returns its address.
+ *
+ * Returns zero if the page was not present. find_lock_page() may sleep.
+ */
+
 /*
- * Same as the above, but lock the page too, verifying that
- * it's still valid once we own it.
+ * The write_lock is unfortunate, but __find_lock_page() requires that on
+ * behalf of find_or_create_page().  We could just clone __find_lock_page() -
+ * one for find_lock_page(), one for find_or_create_page()...
  */
-struct page * find_lock_page(struct address_space *mapping, unsigned long offset)
+struct page *find_lock_page(struct address_space *mapping,
+				unsigned long offset)
 {
 	struct page *page;
 
-	read_lock(&mapping->page_lock);
+	write_lock(&mapping->page_lock);
 	page = __find_lock_page(mapping, offset);
-	read_unlock(&mapping->page_lock);
-
+	write_unlock(&mapping->page_lock);
 	return page;
 }
 
-/*
- * Same as above, but create the page if required..
+/**
+ * find_or_create_page - locate or add a pagecache page
+ *
+ * @mapping - the page's address_space
+ * @index - the page's index into the mapping
+ * @gfp_mask - page allocation mode
+ *
+ * Locates a page in the pagecache.  If the page is not present, a new page
+ * is allocated using @gfp_mask and is added to the pagecache and to the VM's
+ * LRU list.  The returned page is locked and has its reference count
+ * incremented.
+ *
+ * find_or_create_page() may sleep, even if @gfp_flags specifies an atomic
+ * allocation!
+ *
+ * find_or_create_page() returns the desired page's address, or zero on
+ * memory exhaustion.
  */
-struct page * find_or_create_page(struct address_space *mapping,
+struct page *find_or_create_page(struct address_space *mapping,
 		unsigned long index, unsigned int gfp_mask)
 {
 	struct page *page;
diff -Nru a/mm/page_alloc.c b/mm/page_alloc.c
--- a/mm/page_alloc.c	Mon Apr 22 15:32:18 2002
+++ b/mm/page_alloc.c	Mon Apr 22 15:32:18 2002
@@ -340,6 +340,8 @@
 
 	zone = zonelist->zones;
 	classzone = *zone;
+	if (classzone == NULL)
+		return NULL;
 	min = 1UL << order;
 	for (;;) {
 		zone_t *z = *(zone++);
diff -Nru a/mm/swapfile.c b/mm/swapfile.c
--- a/mm/swapfile.c	Mon Apr 22 15:32:19 2002
+++ b/mm/swapfile.c	Mon Apr 22 15:32:19 2002
@@ -1095,7 +1095,8 @@
 	swap_list_lock();
 	for (i = 0; i < nr_swapfiles; i++) {
 		unsigned int j;
-		if (!(swap_info[i].flags & SWP_USED))
+		if (!(swap_info[i].flags & SWP_USED) ||
+		     (swap_info[i].flags & SWP_WRITEOK))
 			continue;
 		for (j = 0; j < swap_info[i].max; ++j) {
 			switch (swap_info[i].swap_map[j]) {
diff -Nru a/mm/vmalloc.c b/mm/vmalloc.c
--- a/mm/vmalloc.c	Mon Apr 22 15:32:18 2002
+++ b/mm/vmalloc.c	Mon Apr 22 15:32:18 2002
@@ -78,11 +78,11 @@
 	} while (address < end);
 }
 
-void vmfree_area_pages(unsigned long address, unsigned long size)
+void vmfree_area_pages(unsigned long start, unsigned long size)
 {
 	pgd_t * dir;
-	unsigned long start = address;
-	unsigned long end = address + size;
+	unsigned long address = start;
+	unsigned long end = start + size;
 
 	dir = pgd_offset_k(address);
 	flush_cache_all();
diff -Nru a/mm/vmscan.c b/mm/vmscan.c
--- a/mm/vmscan.c	Mon Apr 22 15:32:18 2002
+++ b/mm/vmscan.c	Mon Apr 22 15:32:18 2002
@@ -35,6 +35,29 @@
 #define DEF_PRIORITY (6)
 
 /*
+ * On the swap_out path, the radix-tree node allocations are performing
+ * GFP_ATOMIC allocations under PF_MEMALLOC.  They can completely
+ * exhaust the page allocator.  This is bad; some pages should be left
+ * available for the I/O system to start sending the swapcache contents
+ * to disk.
+ *
+ * So PF_MEMALLOC is dropped here.  This causes the slab allocations to fail
+ * earlier, so radix-tree nodes will then be allocated from the mempool
+ * reserves.
+ */
+static inline int
+swap_out_add_to_swap_cache(struct page *page, swp_entry_t entry)
+{
+	int flags = current->flags;
+	int ret;
+
+	current->flags &= ~PF_MEMALLOC;
+	ret = add_to_swap_cache(page, entry);
+	current->flags = flags;
+	return ret;
+}
+
+/*
  * The swap-out function returns 1 if it successfully
  * scanned all the pages it was asked to (`count').
  * It returns zero if it couldn't do anything,
@@ -139,7 +162,7 @@
 		 * (adding to the page cache will clear the dirty
 		 * and uptodate bits, so we need to do it again)
 		 */
-		switch (add_to_swap_cache(page, entry)) {
+		switch (swap_out_add_to_swap_cache(page, entry)) {
 		case 0:				/* Success */
 			SetPageUptodate(page);
 			set_page_dirty(page);
diff -Nru a/net/atm/Makefile b/net/atm/Makefile
--- a/net/atm/Makefile	Mon Apr 22 15:32:18 2002
+++ b/net/atm/Makefile	Mon Apr 22 15:32:18 2002
@@ -1,17 +1,11 @@
 #
 # Makefile for the ATM Protocol Families.
 #
-# Note! Dependencies are done automagically by 'make dep', which also
-# removes any old dependencies. DON'T put your own dependencies here
-# unless it's something special (ie not a .c file).
-#
-# Note 2! The CFLAGS definition is now in the main makefile...
 
 O_TARGET	:= atm.o
 
 export-objs 	:= common.o atm_misc.o raw.o resources.o ipcommon.o proc.o
 
-list-multi	:= mpoa.o
 mpoa-objs	:= mpc.o mpoa_caches.o mpoa_proc.o
 
 obj-$(CONFIG_ATM) := addr.o pvc.o signaling.o svc.o common.o atm_misc.o raw.o resources.o
@@ -36,6 +30,3 @@
 obj-$(CONFIG_PPPOATM) += pppoatm.o
 
 include $(TOPDIR)/Rules.make
-
-mpoa.o: $(mpoa-objs)
-	$(LD) -r -o mpoa.o $(mpoa-objs)
diff -Nru a/net/bluetooth/Makefile b/net/bluetooth/Makefile
--- a/net/bluetooth/Makefile	Mon Apr 22 15:32:19 2002
+++ b/net/bluetooth/Makefile	Mon Apr 22 15:32:19 2002
@@ -1,9 +1,9 @@
 #
 # Makefile for the Bluetooth subsystem
 #
+
 O_TARGET	:= bluetooth.o
 
-list-multi	:= hci.o l2cap.o
 export-objs	:= syms.o
 hci-objs	:= af_bluetooth.o hci_core.o hci_sock.o lib.o syms.o
 l2cap-objs	:= l2cap_core.o l2cap_proc.o
@@ -12,9 +12,3 @@
 obj-$(CONFIG_BLUEZ_L2CAP) += l2cap.o
 
 include $(TOPDIR)/Rules.make
-
-hci.o: $(hci-objs)
-	$(LD) -r -o $@ $(hci-objs)
-
-l2cap.o: $(l2cap-objs)
-	$(LD) -r -o $@ $(l2cap-objs)
diff -Nru a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile
--- a/net/ipv4/netfilter/Makefile	Mon Apr 22 15:32:19 2002
+++ b/net/ipv4/netfilter/Makefile	Mon Apr 22 15:32:19 2002
@@ -1,19 +1,11 @@
 #
 # Makefile for the netfilter modules on top of IPv4.
 #
-# Note! Dependencies are done automagically by 'make dep', which also
-# removes any old dependencies. DON'T put your own dependencies here
-# unless it's something special (ie not a .c file).
-#
-# Note 2! The CFLAGS definition is now in the main makefile...
 
 O_TARGET := netfilter.o
 
 export-objs = ip_conntrack_standalone.o ip_fw_compat.o ip_nat_standalone.o ip_tables.o arp_tables.o
 
-# Multipart objects.
-list-multi		:= ip_conntrack.o iptable_nat.o ipfwadm.o ipchains.o
-
 # objects for the conntrack and NAT core (used by standalone and backw. compat)
 ip_nf_conntrack-objs	:= ip_conntrack_core.o ip_conntrack_proto_generic.o ip_conntrack_proto_tcp.o ip_conntrack_proto_udp.o ip_conntrack_proto_icmp.o
 ip_nf_nat-objs		:= ip_nat_core.o ip_nat_helper.o ip_nat_proto_unknown.o ip_nat_proto_tcp.o ip_nat_proto_udp.o ip_nat_proto_icmp.o
@@ -95,15 +87,3 @@
 obj-$(CONFIG_IP_NF_QUEUE) += ip_queue.o
 
 include $(TOPDIR)/Rules.make
-
-ip_conntrack.o: $(ip_conntrack-objs)
-	$(LD) -r -o $@ $(ip_conntrack-objs)
-
-iptable_nat.o: $(iptable_nat-objs)
-	$(LD) -r -o $@ $(iptable_nat-objs)
-
-ipfwadm.o: $(ipfwadm-objs)
-	$(LD) -r -o $@ $(ipfwadm-objs)
-
-ipchains.o: $(ipchains-objs)
-	$(LD) -r -o $@ $(ipchains-objs)
diff -Nru a/net/irda/ircomm/Makefile b/net/irda/ircomm/Makefile
--- a/net/irda/ircomm/Makefile	Mon Apr 22 15:32:18 2002
+++ b/net/irda/ircomm/Makefile	Mon Apr 22 15:32:18 2002
@@ -1,25 +1,13 @@
 #
 # Makefile for the Linux IrDA IrCOMM protocol layer.
 #
-# Note! Dependencies are done automagically by 'make dep', which also
-# removes any old dependencies. DON'T put your own dependencies here
-# unless it's something special (ie not a .c file).
-#
-# Note 2! The CFLAGS definition is now in the main makefile...
 
 O_TARGET := ircomm_and_tty.o
 
-list-multi := ircomm.o ircomm-tty.o
 ircomm-objs := ircomm_core.o ircomm_event.o ircomm_lmp.o ircomm_ttp.o
 ircomm-tty-objs := ircomm_tty.o ircomm_tty_attach.o ircomm_tty_ioctl.o ircomm_param.o
 
 obj-$(CONFIG_IRCOMM) += ircomm.o ircomm-tty.o
 
 include $(TOPDIR)/Rules.make
-
-ircomm.o:  $(ircomm-objs)
-	$(LD) $(LD_RFLAG) -r -o $@ $(ircomm-objs)
-
-ircomm-tty.o: $(ircomm-tty-objs)
-	$(LD) $(LD_RFLAG) -r -o $@ $(ircomm-tty-objs)
 
diff -Nru a/scripts/Configure b/scripts/Configure
--- a/scripts/Configure	Mon Apr 22 15:32:19 2002
+++ b/scripts/Configure	Mon Apr 22 15:32:19 2002
@@ -537,9 +537,12 @@
 	CONFIG_IN=$1
 fi
 
-DEFAULTS=arch/$ARCH/defconfig
-if [ -f .config ]; then
-  DEFAULTS=.config
+DEFAULTS=.config
+if [ ! -f .config ]; then
+  DEFAULTS=/etc/kernel-config
+  if [ ! -f $DEFAULTS ]; then
+    DEFAULTS=arch/$ARCH/defconfig
+  fi
 fi
 
 if [ -f $DEFAULTS ]; then
diff -Nru a/sound/core/device.c b/sound/core/device.c
--- a/sound/core/device.c	Mon Apr 22 15:32:18 2002
+++ b/sound/core/device.c	Mon Apr 22 15:32:18 2002
@@ -24,6 +24,7 @@
 #include <linux/slab.h>
 #include <linux/time.h>
 #include <sound/core.h>
+#include <linux/errno.h>
 
 int snd_device_new(snd_card_t *card, snd_device_type_t type,
 		   void *device_data, snd_device_ops_t *ops)
diff -Nru a/sound/core/memory.c b/sound/core/memory.c
--- a/sound/core/memory.c	Mon Apr 22 15:32:18 2002
+++ b/sound/core/memory.c	Mon Apr 22 15:32:18 2002
@@ -26,6 +26,7 @@
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/time.h>
+#include <linux/pci.h>
 #include <sound/core.h>
 #include <sound/info.h>
 
diff -Nru a/sound/core/seq/seq_dummy.c b/sound/core/seq/seq_dummy.c
--- a/sound/core/seq/seq_dummy.c	Mon Apr 22 15:32:19 2002
+++ b/sound/core/seq/seq_dummy.c	Mon Apr 22 15:32:19 2002
@@ -20,6 +20,7 @@
 
 #include <sound/driver.h>
 #include <linux/init.h>
+#include <linux/slab.h>
 #include <sound/core.h>
 #include "seq_clientmgr.h"
 #include <sound/initval.h>
diff -Nru a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c
--- a/sound/core/seq/seq_midi.c	Mon Apr 22 15:32:19 2002
+++ b/sound/core/seq/seq_midi.c	Mon Apr 22 15:32:19 2002
@@ -29,6 +29,8 @@
 #include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/slab.h>
+#include <linux/errno.h>
+#include <asm/semaphore.h>
 #include <sound/core.h>
 #include <sound/rawmidi.h>
 #include <sound/seq_kernel.h>
diff -Nru a/sound/core/seq/seq_midi_event.c b/sound/core/seq/seq_midi_event.c
--- a/sound/core/seq/seq_midi_event.c	Mon Apr 22 15:32:18 2002
+++ b/sound/core/seq/seq_midi_event.c	Mon Apr 22 15:32:18 2002
@@ -21,6 +21,7 @@
 
 #include <sound/driver.h>
 #include <linux/slab.h>
+#include <linux/errno.h>
 #include <sound/core.h>
 #include <sound/seq_kernel.h>
 #include <sound/seq_midi_event.h>
diff -Nru a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c
--- a/sound/core/seq/seq_queue.c	Mon Apr 22 15:32:19 2002
+++ b/sound/core/seq/seq_queue.c	Mon Apr 22 15:32:19 2002
@@ -544,10 +544,10 @@
 		return -EINVAL;
 	down(&queue->timer_mutex);
 	if (use) {
-		if (!test_and_set_bit(client, &queue->clients_bitmap))
+		if (!test_and_set_bit(client, queue->clients_bitmap))
 			queue->clients++;
 	} else {
-		if (test_and_clear_bit(client, &queue->clients_bitmap))
+		if (test_and_clear_bit(client, queue->clients_bitmap))
 			queue->clients--;
 	}
 	if (queue->clients) {
@@ -575,7 +575,7 @@
 	q = queueptr(queueid);
 	if (q == NULL)
 		return -EINVAL; /* invalid queue */
-	result = test_bit(client, &q->clients_bitmap) ? 1 : 0;
+	result = test_bit(client, q->clients_bitmap) ? 1 : 0;
 	queuefree(q);
 	return result;
 }
diff -Nru a/sound/core/seq/seq_queue.h b/sound/core/seq/seq_queue.h
--- a/sound/core/seq/seq_queue.h	Mon Apr 22 15:32:19 2002
+++ b/sound/core/seq/seq_queue.h	Mon Apr 22 15:32:19 2002
@@ -51,7 +51,7 @@
 	spinlock_t check_lock;
 
 	/* clients which uses this queue (bitmap) */
-	unsigned int clients_bitmap[SNDRV_SEQ_MAX_CLIENTS/sizeof(unsigned int)];
+	unsigned long clients_bitmap[SNDRV_SEQ_MAX_CLIENTS/sizeof(unsigned long)];
 	unsigned int clients;	/* users of this queue */
 	struct semaphore timer_mutex;
 
diff -Nru a/sound/drivers/mpu401/mpu401_uart.c b/sound/drivers/mpu401/mpu401_uart.c
--- a/sound/drivers/mpu401/mpu401_uart.c	Mon Apr 22 15:32:19 2002
+++ b/sound/drivers/mpu401/mpu401_uart.c	Mon Apr 22 15:32:19 2002
@@ -29,6 +29,8 @@
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
 #include <sound/core.h>
 #include <sound/mpu401.h>
 
