diff -Nru a/CREDITS b/CREDITS
--- a/CREDITS	Sun Apr 28 20:10:32 2002
+++ b/CREDITS	Sun Apr 28 20:10:32 2002
@@ -785,9 +785,8 @@
 S: USA
 
 N: Randy Dunlap
-E: rddunlap@osdlab.org
-W: http://home.att.net/~randy.dunlap/
-W: http://home.att.net/~rddunlap/
+E: rddunlap@osdl.org
+W: http://www.xenotime.net/linux/linux.html
 W: http://www.linux-usb.org
 D: Linux-USB subsystem, USB core/UHCI/printer/storage drivers
 D: x86 SMP, ACPI, bootflag hacking
diff -Nru a/Documentation/DocBook/Makefile b/Documentation/DocBook/Makefile
--- a/Documentation/DocBook/Makefile	Sun Apr 28 20:10:36 2002
+++ b/Documentation/DocBook/Makefile	Sun Apr 28 20:10:36 2002
@@ -2,7 +2,7 @@
 	   kernel-api.sgml parportbook.sgml kernel-hacking.sgml \
 	   kernel-locking.sgml via-audio.sgml mousedrivers.sgml sis900.sgml \
 	   deviceiobook.sgml procfs-guide.sgml tulip-user.sgml \
-	   writing_usb_driver.sgml
+	   writing_usb_driver.sgml scsidrivers.sgml
 
 PS	:=	$(patsubst %.sgml, %.ps, $(BOOKS))
 PDF	:=	$(patsubst %.sgml, %.pdf, $(BOOKS))
@@ -67,6 +67,9 @@
 	$(TOPDIR)/scripts/docgen <$< >$@
 
 writing_usb_driver.sgml: writing_usb_driver.tmpl
+	$(TOPDIR)/scripts/docgen <$< >$@
+
+scsidrivers.sgml : scsidrivers.tmpl
 	$(TOPDIR)/scripts/docgen <$< >$@
 
 sis900.sgml: sis900.tmpl $(TOPDIR)/drivers/net/sis900.c
diff -Nru a/Documentation/DocBook/scsidrivers.tmpl b/Documentation/DocBook/scsidrivers.tmpl
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/Documentation/DocBook/scsidrivers.tmpl	Sun Apr 28 20:10:39 2002
@@ -0,0 +1,172 @@
+<!-- -*- sgml -*- -->
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V4.1//EN"[]>
+
+<book id="scsidrivers">
+ <bookinfo>
+  <title>SCSI Subsystem Interfaces</title>
+  
+  <authorgroup>
+   <author>
+    <firstname>Douglas</firstname>
+    <surname>Gilbert</surname>
+    <affiliation>
+     <address>
+      <email>dgilbert@interlog.com</email>
+     </address>
+    </affiliation>
+   </author>
+  </authorgroup>
+  <pubdate>2002-04-27</pubdate>
+
+  <copyright>
+   <year>2002</year>
+   <holder>Douglas Gilbert</holder>
+  </copyright>
+  <legalnotice>
+   <para>
+    Permission is granted to copy, distribute and/or modify this
+    document under the terms of the GNU Free Documentation License,
+    Version 1.1 or any later version published by the Free Software
+    Foundation; with no Invariant Sections, with no Front-Cover Texts,
+    and with no Back-Cover Texts.  A copy of the license is included
+    in the section entitled "GNU Free Documentation License".
+   </para>
+  </legalnotice>
+
+ </bookinfo>
+
+<toc></toc>
+
+  <chapter id="intro">
+      <title>Introduction</title>
+  <para>
+This document outlines the interface between the Linux scsi mid level
+and lower level drivers. Lower level drivers are variously called HBA
+(host bus adapter) drivers, host drivers (HD) or pseudo adapter drivers.
+The latter alludes to the fact that a lower level driver may be a
+bridge to another IO subsystem (and the "ide-scsi" driver is an example
+of this). There can be many lower level drivers active in a running
+system, but only one per hardware type. For example, the aic7xxx driver
+controls adaptec controllers based on the 7xxx chip series. Most lower
+level drivers can control one or more scsi hosts (a.k.a. scsi initiators).
+  </para>
+<para>
+This document can been found in an ASCII text file in the linux kernel 
+source: <filename>drivers/scsi/scsi_mid_low_api.txt</filename> .
+It currently hold a little more information than this document. The
+<filename>drivers/scsi/hosts.h</filename> and <filename>
+drivers/scsi/scsi.h</filename> headers contain descriptions of members
+of important structures for the scsi subsystem.
+</para>
+  </chapter>
+
+  <chapter id="driver_struct">
+      <title>Driver structure</title>
+  <para>
+Traditionally a lower level driver for the scsi subsystem has been
+at least two files in the drivers/scsi directory. For example, a
+driver called "xyz" has a header file "xyz.h" and a source file
+"xyz.c". [Actually there is no good reason why this couldn't all
+be in one file.] Some drivers that have been ported to several operating
+systems (e.g. aic7xxx which has separate  files for generic and
+OS-specific code) have more than two files. Such drivers tend to have
+their own directory under the drivers/scsi directory.
+  </para>
+  <para>
+scsi_module.c is normally included at the end of a lower
+level driver. For it to work a declaration like this is needed before
+it is included:
+<programlisting>
+    static Scsi_Host_Template driver_template = DRIVER_TEMPLATE;
+    /* DRIVER_TEMPLATE should contain pointers to supported interface
+       functions. Scsi_Host_Template is defined hosts.h */
+    #include "scsi_module.c"
+</programlisting>
+  </para>
+  <para>
+The scsi_module.c assumes the name "driver_template" is appropriately
+defined. It contains 2 functions:
+<orderedlist>
+<listitem><para>
+     init_this_scsi_driver() called during builtin and module driver
+     initialization: invokes mid level's scsi_register_host()
+</para></listitem>
+<listitem><para>
+     exit_this_scsi_driver() called during closedown: invokes
+     mid level's scsi_unregister_host()
+</para></listitem>
+</orderedlist>
+  </para>
+<para>
+When a new, lower level driver is being added to Linux, the following 
+files (all found in the drivers/scsi directory) will need some attention: 
+Makefile, Config.help and Config.in . It is probably best to look at what 
+an existing lower level driver does in this regard.
+</para>
+  </chapter>
+
+  <chapter id="intfunctions">
+     <title>Interface Functions</title>
+!Edrivers/scsi/scsi_mid_low_api.txt
+  </chapter>
+
+  <chapter id="locks">
+     <title>Locks</title>
+<para>
+Each Scsi_Host instance has a spin_lock called Scsi_Host::default_lock
+which is initialized in scsi_register() [found in hosts.c]. Within the
+same function the Scsi_Host::host_lock pointer is initialized to point
+at default_lock with the scsi_assign_lock() function. Thereafter
+lock and unlock operations performed by the mid level use the
+Scsi_Host::host_lock pointer.
+</para>
+<para>
+Lower level drivers can override the use of Scsi_Host::default_lock by
+using scsi_assign_lock(). The earliest opportunity to do this would
+be in the detect() function after it has invoked scsi_register(). It
+could be replaced by a coarser grain lock (e.g. per driver) or a
+lock of equal granularity (i.e. per host). Using finer grain locks
+(e.g. per scsi device) may be possible by juggling locks in
+queuecommand().
+</para>
+  </chapter>
+
+  <chapter id="changes">
+     <title>Changes since lk 2.4 series</title>
+<para>
+io_request_lock has been replaced by several finer grained locks. The lock
+relevant to lower level drivers is Scsi_Host::host_lock and there is one
+per scsi host.
+</para>
+<para>
+The older error handling mechanism has been removed. This means the
+lower level interface functions abort() and reset() have been removed.
+</para>
+<para>
+In the 2.4 series the scsi subsystem configuration descriptions were
+aggregated with the configuration descriptions from all other Linux
+subsystems in the Documentation/Configure.help file. In the 2.5 series,
+the scsi subsystem now has its own (much smaller) drivers/scsi/Config.help
+file.
+</para>
+  </chapter>
+
+  <chapter id="credits">
+     <title>Credits</title>
+<para>
+The following people have contributed to this document:
+<orderedlist>
+<listitem><para>
+Mike Anderson <email>andmike@us.ibm.com</email>
+</para></listitem>
+<listitem><para>
+James Bottomley <email>James.Bottomley@steeleye.com</email>
+</para></listitem>
+<listitem><para>
+Patrick Mansfield <email>patmans@us.ibm.com</email>
+</para></listitem>
+</orderedlist>
+</para>
+  </chapter>
+
+</book>
diff -Nru a/Documentation/cachetlb.txt b/Documentation/cachetlb.txt
--- a/Documentation/cachetlb.txt	Sun Apr 28 20:10:35 2002
+++ b/Documentation/cachetlb.txt	Sun Apr 28 20:10:35 2002
@@ -49,6 +49,9 @@
 	page table operations such as what happens during
 	fork, and exec.
 
+	Platform developers note that generic code will always
+	invoke this interface without mm->page_table_lock held.
+
 3) void flush_tlb_range(struct vm_area_struct *vma,
 			unsigned long start, unsigned long end)
 
@@ -69,6 +72,9 @@
 	call flush_tlb_page (see below) for each entry which may be
 	modified.
 
+	Platform developers note that generic code will always
+	invoke this interface with mm->page_table_lock held.
+
 4) void flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
 
 	This time we need to remove the PAGE_SIZE sized translation
@@ -86,6 +92,9 @@
 	'vma->vm_mm' for virtual address 'page'.
 
 	This is used primarily during fault processing.
+
+	Platform developers note that generic code will always
+	invoke this interface with mm->page_table_lock held.
 
 5) void flush_tlb_pgtables(struct mm_struct *mm,
 			   unsigned long start, unsigned long end)
diff -Nru a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking
--- a/Documentation/filesystems/Locking	Sun Apr 28 20:10:34 2002
+++ b/Documentation/filesystems/Locking	Sun Apr 28 20:10:34 2002
@@ -69,10 +69,10 @@
 permission:	yes	no
 getattr:				(see below)
 revalidate:	no			(see below)
-setxattr:	yes	no
-getxattr:	yes	no
-listxattr:	yes	no
-removexattr:	yes	no
+setxattr:	no	yes
+getxattr:	no	yes
+listxattr:	no	yes
+removexattr:	no	yes
 	Additionally, ->rmdir(), ->unlink() and ->rename() have ->i_sem on
 victim.
 	cross-directory ->rename() has (per-superblock) ->s_vfs_rename_sem.
diff -Nru a/Documentation/filesystems/ntfs.txt b/Documentation/filesystems/ntfs.txt
--- a/Documentation/filesystems/ntfs.txt	Sun Apr 28 20:10:36 2002
+++ b/Documentation/filesystems/ntfs.txt	Sun Apr 28 20:10:39 2002
@@ -1,68 +1,140 @@
-NTFS Overview
-=============
+The Linux NTFS filesystem driver
+================================
 
-Legato Systems, Inc. (http://www.legato.com) have sponsored Anton Altaparmakov
-to develop NTFS on Linux since June 2001.
 
-To mount an NTFS volume, use the filesystem type 'ntfs'. The driver
-currently works only in read-only mode, with no fault-tolerance supported.
+Table of contents
+=================
+
+- Overview
+- Supported mount options
+- Features
+- Known bugs and (mis-)features
+- Using Software RAID with NTFS
+- Limitiations when using the MD driver
+- ChangeLog
+
+
+Overview
+========
+
+To mount an NTFS 1.2/3.x (Windows NT4/2000/XP) volume, use the filesystem
+type 'ntfs'. The driver currently works only in read-only mode, with no
+fault-tolerance or journalling supported.
+
+For fault tolerance and raid support (i.e. volume and stripe sets), you can use
+the kernel's Software RAID / MD driver. See section "Using Software RAID with
+NTFS" for details.
 
-If you enable the dangerous(!) write support, make sure you can recover
-from a complete loss of data. Also, download the Linux-NTFS project
-distribution from Sourceforge at http://sourceforge.net/projects/linux-ntfs/
-and always run the included ntfsfix utility after performing a write to an
-NTFS partition from Linux to fix some of the damage done by the Linux NTFS
-driver and to schedule an automatic chkdsk when Windows reboots. You should
-run ntfsfix _after_ unmounting the partition in Linux but _before_ rebooting
-into Windows. During the next reboot into Windows, chkdsk will be run
-automatically fixing the remaining damage. If no errors are found it is a
-good indication that the driver + ntfsfix together worked to full
-satisfaction. (-;
-
-Please note that the experimental write support is limited to Windows NT4 and
-earlier versions at the moment.
-
-If you think you have discovered a bug please have look at the "Known bugs"
-section below to see whether it isn't known already.
-
-For ftdisk support, limited success was reported with volume sets on top of
-the md driver, although mirror and stripe sets should work as well - if the
-md driver can be talked into using the same layout as Windows NT. However,
-using the md driver will fail if any of your NTFS partitions have an odd
-number of sectors.
 
 Supported mount options
 =======================
 
-iocharset=name		Character set to use when returning file names.
+In addition to the generic mount options described by the manual page for the
+mount command (man 8 mount, also see man 5 fstab), the NTFS driver supports the
+following mount options:
+
+iocharset=name		Deprecated option. Still supported but please use
+			nls=name in the future. See description for nls=name.
+
+nls=name		Character set to use when returning file names.
 			Unlike VFAT, NTFS suppresses names that contain
 			unconvertible characters. Note that most character
 			sets contain insufficient characters to represent all
 			possible Unicode characters that can exist on NTFS. To
 			be sure you are not missing any files, you are advised
-			to use the iocharset=utf8 which should be capable of
-			representing all Unicode characters.
+			to use nls=utf8 which is capable of representing all
+			Unicode characters.
 
-utf8=<bool>		Use UTF-8 for converting file names. - It is preferable
-			to use iocharset=utf8 instead, but if the utf8 NLS is
-			not available, you can use this utf8 option, which
-			enables the driver's builtin utf8 conversion functions.
+utf8=<bool>		Option no longer supported. Currently mapped to
+			nls=utf8 but please use nls=utf8 in the future and
+			make sure utf8 is compiled either as module or into
+			the kernel. See description for nls=name.
 
 uid=
 gid=
-umask=			These options work as documented in mount(8).
-			By default, the files are owned by root and
-			not readable by anyone else.
-
-posix=<bool>		If enabled, the file system distinguishes between
-			upper and lower case. The 8.3 alias names are presented
-			as hard links instead of being suppressed.
-
-show_sys_files=<bool>	If enabled, show all system files as normal files. Note
-			that $MFT does not appear unless specifically
-			requested. For example in bash, use: "ls -l \$MFT".
-			Be careful not to write anything to them or you could
-			crash the kernel and/or corrupt your file system!
+umask=			Provide default owner, group, and access mode mask.
+			These options work as documented in mount(8). By
+			default, the files/directories are owned by root and
+			he/she has read and write permissions, as well as
+			browse permission for directories. No one else has any
+			access permissions. I.e. the mode on all files is by
+			default rw------- and for directories rwx------, a
+			consequence of the default fmask=0177 and dmask=0077.
+			Using a umask of zero will grant all permissions to
+			everyone, i.e. all files and directories will have mode
+			rwxrwxrwx.
+
+fmask=
+dmask=			Instead of specifying umask which applies both to
+			files and directories, fmask applies only to files and
+			dmask only to directories.
+
+sloppy=<BOOL>		If sloppy is specified, ignore unknown mount options.
+			Otherwise the default behaviour is to abort mount if
+			any unknown options are found.
+
+posix=<bool>		Deprecated option. Still supported but please use
+			show_inodes=posix in the future. See description for
+			show_inodes=opt.
+
+show_sys_files=<bool>	Deprecated option. Still supported but please use
+			show_inodes=system in the future. See description for
+			show_inodes=opt.
+
+show_inodes=opt		Allows choice of which types of inode names readdir()
+			returns, i.e. this affects what "ls" shows. Following
+			values can be used for "opt":
+			   system: show system files
+			   win32:  long file names (includes POSIX) [DEFAULT]
+			   long:   same as win32
+			   dos:    short file names only (excludes POSIX)
+			   short:  same as dos
+			   posix:  same as both win32 and dos
+			   all:    all file names
+			Note that the options are additive, i.e. specifying:
+			   show_inodes=system,show_inodes=win32,show_inodes=dos
+			is the same as specifying:
+			   show_inodes=all
+			Note that the "posix" and "all" options will show all
+			directory names, BUT the link count on each directory
+			inode entry is set to 1, due to Linux not supporting
+			directory hard links. This may well confuse some
+			user space applications, since the directory names will
+			have the same inode numbers. Thus it is NOT advisable
+			to use the "posix" and "all" options. We provide them
+			only for completeness sake.
+			Further, note that the "system" option will not show
+			"$MFT" due to bugs/mis-features in glibc. Even though
+			it does not show, you can specifically "ls" it:
+				ls -l \$MFT
+			And of course you can stat it, too.
+			Further, note that irrespective of what show_inodes
+			option(s) you use, all files are accessible when you
+			specify the correct name, even though they may not be
+			shown in a normal "ls", i.e. you can always access the
+			system files and both the short and long file names of
+			files and directories.
+			Finally, note that win32 and dos file names are not
+			case sensitive and can be accessed using any
+			combination of lower and upper case, while POSIX file
+			names are case sensitive and they can only be accessed
+			given the correct case.
+
+errors=opt		What to do when critical file system errors are found.
+			Following values can be used for "opt":
+			  continue: DEFAULT, try to clean-up as much as
+				    possible, e.g. marking a corrupt inode as
+				    bad so it is no longer accessed, and then
+				    continue.
+			  recover:  At present only supported is recovery of
+				    the boot sector from the backup copy. If a
+				    read-only mount, the recovery is done in
+				    memory only and not written to disk.
+			Note that the options are additive, i.e. specifying:
+			   errors=continue,errors=recover
+			This means the driver will attempt to recover and if
+			that fails it will clean-up as much as possible and
+			continue.
 
 mft_zone_multiplier=	Set the MFT zone multiplier for the volume (this
 			setting is not persistent across mounts and can be
@@ -82,173 +154,127 @@
 				2		25%
 				3		37.5%
 				4		50%
+			Note this option is irrelevant for read-only mounts.
+
+
+Features
+========
+
+- This is a complete rewrite of the NTFS driver that used to be in the kernel.
+  This new driver implements NTFS read support and is functionally equivalent
+  to the old ntfs driver.
+- The new driver has full support for sparse files on NTFS 3.x volumes which
+  the old driver isn't happy with.
+- The new driver supports execution of binaries due to mmap() now being
+  supported.
+- A comparison of the two drivers using:
+	time find . -type f -exec md5sum "{}" \;
+  run three times in sequence with each driver (after a reboot) on a 1.4GiB
+  NTFS partition, showed the new driver to be 20% faster in total time elapsed
+  (from 9:43 minutes on average down to 7:53). The time spent in user space
+  was unchanged but the time spent in the kernel was decreased by a factor of
+  2.5 (from 85 CPU seconds down to 33).
+
 
 Known bugs and (mis-)features
 =============================
 
-- Do not use the driver for writing as it corrupts the file system. If you do
-  use it, get the Linux-NTFS tools and use the ntfsfix utility after
-  dismounting a partition you wrote to.
-
-- Writing of extension records is not supported properly.
+- None
 
-Please send bug reports/comments/feed back/abuse to the Linux-NTFS development
+Please send bug reports/comments/feedback/abuse to the Linux-NTFS development
 list at sourceforge: linux-ntfs-dev@lists.sourceforge.net
 
+
+Using Software RAID with NTFS
+=============================
+
+For support of volume and stripe sets, use the kernel's Software RAID / MD
+driver and set up your /etc/raidtab appropriately (see man 5 raidtab).
+
+Linear volume sets, i.e. linear raid, as well as stripe sets, i.e. raid level 0,
+have been tested and work fine (though see section "Limitiations when using the
+MD driver with NTFS volumes" especially if you want to use linear raid). Even
+though untested, there is no reason why mirrors, i.e. raid level 1, and stripes
+with parity, i.e. raid level 5, should not work, too.
+
+You have to use the "persistent-superblock 0" option for each raid-disk in the
+NTFS volume/stripe you are configuring in /etc/raidtab as the persistent
+superblock used by the MD driver would damange the NTFS volume.
+
+Windows by default uses a stripe chunk size of 64k, so you probably want the
+"chunk-size 64k" option for each raid-disk, too.
+
+For example, if you have a stripe set consisting of two partitions /dev/hda5
+and /dev/hdb1 your /etc/raidtab would look like this:
+
+raiddev /dev/md0
+	raid-level	0
+	nr-raid-disks	2
+	nr-spare-disks	0
+	persistent-superblock	0
+	chunk-size	64k
+	device		/dev/hda5
+	raid-disk	0
+	device		/dev/hdb1
+	raid-disl	1
+
+For linear raid, just change the raid-level above to "raid-level linear", for
+mirrors, change it to "raid-level 1", and for stripe sets with parity, change
+it to "raid-level 5".
+
+Note for stripe sets with parity you will also need to tell the MD driver which
+parity algorithm to use by specifying the option "parity-algorithm which",
+where you need to replace "which" with the name of the algorithm to use (see
+man 5 raidtab for available algorithms) and you will have to try the different
+available algorithms until you find one that works. Make sure you are working
+read-only when playing with this as you may damage your data otherwise. If you
+find which algorithm works please let us know (email the linux-ntfs developers
+list linux-ntfs-dev@lists.sourceforge.net or drop in on IRC in channel #ntfs
+on the irc.openprojects.net network) so we can update this documentation.
+
+Once the raidtab is setup, run for example raid0run -a to start all devices or
+raid0run /dev/md0 to start a particular md device, in this case /dev/md0.
+
+Then just use the mount command as usual to mount the ntfs volume using for
+example:	mount -t ntfs -o ro /dev/md0 /mnt/myntfsvolume
+
+It is advisable to do the mount read-only to see if the md volume has been
+setup correctly to avoid the possibility of causing damage to the data on the
+ntfs volume.
+
+
+Limitiations when using the MD driver
+=====================================
+
+Using the md driver will not work properly if any of your NTFS partitions have
+an odd number of sectors. This is especially important for linear raid as all
+data after the first partition with an odd number of sectors will be offset by
+one or more sectors so if you mount such a partition with write support you
+will cause massive damage to the data on the volume which will only become
+apparent when you try to use the volume again under Windows.
+
+So when using linear raid, make sure that all your partitions have an even
+number of sectors BEFORE attempting to use it. You have been warned!
+
+
 ChangeLog
 =========
 
-NTFS 1.1.21:
-	- Fixed bug with reading $MFT where we try to read higher mft records
-	  before having read the $DATA attribute of $MFT. (Note this is only a
-	  partial solution which will only work in the case that the attribute
-	  list is resident or non-resident but $DATA is in the first 1024
-	  bytes. But this should be enough in the majority of cases. I am not
-	  going to bother fixing the general case until someone finds this to
-	  be a problem for them, which I doubt very much will ever happen...)
-	- Fixed bogus BUG() call in readdir().
-
-NTFS 1.1.20:
-	- Fixed two bugs in ntfs_readwrite_attr(). Thanks to Jan Kara for
-	  spotting the out of bounds one.
-	- Check return value of set_blocksize() in ntfs_read_super() and make
-	  use of get_hardsect_size() to determine the minimum block size.
-	- Fix return values of ntfs_vcn_to_lcn(). This should stop
-	  peoples start of partition being overwritten at random.
-
-NTFS 1.1.19:
-	- Fixed ntfs_getdir_unsorted(), ntfs_readdir() and ntfs_printcb() to
-	  cope with arbitrary cluster sizes. Very important for Win2k+. Also,
-	  make them detect directories which are too large and truncate the
-	  enumeration pretending end of directory was reached. Detect more
-	  error conditions and overflows. All this fixes the problem where the
-	  driver could end up in an infinite loop under certain circumstances.
-	- Fixed potential memory leaks in Unicode conversion functions and
-	  setup correct NULL return values.
-
-NTFS 1.1.18:
-
-	- Enhanced & bug fixed cluster deallocation (race fixes, etc.)
-	- Complete rewrite of cluster allocation, now race free.
-	- Fixed several bugs in the attribute modification codepaths.
-	- Hopefully fixed bug where the first sectors of some people's
-	  partitions would be overwritten by the mft. And in general fixed up
-	  mft extension code a bit (still incomplete though).
-	- Introduce splice_runlist() to allow generic splicing of two run
-	  lists into one.
-	- MFT zone is now implemented. [Stage 2 of 3; only lack dynamic
-	  growing of mft zone but that is AFAIK not even done by Windows, and
-	  the overhead would be so large that it is probably not worth doing
-	  at all, so Stage 3 might never happen...]
-	- Complete rewrite of $MFT extension and ntfs inode allocation code.
-	- Made the NTFS driver initialization string show the compile options
-	  used (i.e. whether read-only or read-write, whether a module, and
-	  whether with debug support).
-	- Modify ntfs_fill_mft_header() to set all fields and to accept more
-	  arguments.
-	- Get rid of superfluous add_mft_header().
-	- Get rid of some unused code.
-	- Fixed several bugs in and generally cleaned up ntfs_readdir,
-	  ntfs_getdir_unsorted(), and ntfs_printcb. Now they spew out huge
-	  amounts of debug output if debugging is enabled. This will be
-	  removed once I know that this works for everyone.
-	- ntfs_readdir now shows hidden files. The only files that are now
-	  hidden are the first 16 inodes (i.e. the hard coded system files),
-	  which is consistent with Windows NT4. Using the show_sys_files mount
-	  option, these files are then shown, too.
-	- Fixed the displaying of the "." and ".." directories. We still cannot
-	  cope with more than 65536 files in a directory index block which is
-	  not a problem and we now cannot cope with more than 32766 directory
-	  index blocks which should not be a problem unless you have a
-	  directory with an insanely large number of files in it. The exact
-	  number depends on the length of the file names of the directory
-	  entries and on the size of the dircetory index blocks.
-	- Fixed all problems with the last file in a directory (e.g. the last
-	  file should no longer disappear and tab completion should work). If
-	  there are still disappearing files or any other problems with the
-	  last file in a directory, please report them! Thanks.
-	- Rewrote ntfs_extend_attr() to use the new cluster allocator and the
-	  freshly introduced splice_runlists() function. This simplified
-	  ntfs_extend_attr() a lot which in turn seems to have removed one or
-	  more bugs from it.
-	- Probably other things I have forgotten... (-;
-	- Removed dollar signs from the names in the system file enumeration.
-	  Apparently gcc doesn't support dollar signs on PPC architecture.
-	  (Andrzej Krzysztofowicz)
-
-NTFS 1.1.17:
-
-	- Fixed system file handling. No longer need to use show_sys_files
-	  option for driver to work fine. System files are now always treated
-	  the same, but without the option, they are made invisible to
-	  directory listings. As a result system files can once again be opened
-	  even without the show_sys_files option. This is important for the
-	  statfs system call to work properly, for example.
-	- Implemented MFT zone including mount parameter to tune it (just like
-	  in Windows via the registry, only we make it per mount rather than
-	  global for the whole driver, so we are better but we have no way of
-	  storing the value as we don't have a registry so either specify on
-	  each mount or put it in /etc/fstab). [Stage 1 of 3, mount parameter
-	  handling.]
-	- Fixed fixup functions to handle corruption cases and to return error
-	  codes to the caller.
-	- Made fixup functions apply hotfixes where sensible. [Stage 1 of 2+,
-	  in memory only.]
-	- Fixed ommission of "NTFS: " string in ntfs_error() output.
-	- Fixed stupid if statement bug in unistr.c. Thanks to Yann E. Morin
-	  for spotting it. 
-	- Get rid of all uses of max and min macros. This actually allowed for
-	  optimizing the code in several places so it was a Good Thing(TM).
-	- Make ntfs use generic_file_open to enforce the O_LARGEFILE flag.
-	- Detect encrypted files and refuse to access them (return EACCES
-	  error code to user space).
-	- Fix handling of encrypted & compressed files so that an encrypted
-	  file no longer is considered to be compressed (this was causing
-	  kernel segmentation faults).
-
-NTFS 1.1.16:
-
-	- Removed non-functional uni_xlate mount options.
-	- Clarified the semantics of the utf8 and iocharset mount options.
-	- Threw out the non-functional mount options for using hard coded
-	  character set conversion. Only kept utf8 one.
-	- Fixed handling of mount options and proper handling of faulty mount
-	  options on remount.
-	- Cleaned up character conversion which basically became simplified a
-	  lot due to the removal of the above mentioned mount options.
-	- Made character conversion to be always consistent. Previously we
-	  could output to the VFS file names which we would then not accept
-	  back from the VFS so in effect we were generating ghost entries in
-	  the directory listings which could not be accessed by any means.
-	- Simplified time conversion functions drastically without sacrificing
-	  accuracy. (-8
-	- Fixed a use of a pointer before the check for the pointer being
-	  NULL, reported by the Stanford checker.
-	- Fixed several missing error checks, reported by the Stanford
-	  checker and fixed by Rasmus Andersen.
-
-NTFS 1.1.15 (changes since kernel 2.4.4's NTFS driver):
-
-	- New mount option show_sys_files=<bool> to show all system files as
-	  normal files.
-	- Support for files and in general any attributes up to the full 2TiB
-	  size supported by the NTFS filesystem. Note we only support up to
-	  32-bits worth of inodes/clusters at this point.
-	- Support for more than 128kiB sized runlists (using vmalloc_32()
-	  instead of kmalloc()).
-	- Fixed races in allocation of clusters and mft records.
-	- Fixed major bugs in attribute handling / searching / collation.
-	- Fixed major bugs in compressing a run list into a mapping pairs array.
-	- Fixed major bugs in inode allocation. Especially file create and
-	  mkdir.
-	- Fixed memory leaks.
-	- Fixed major bug in inode layout assignment of sequence numbers.
-	- Lots of other bug fixes I can't think of right now...
-	- Fixed NULL bug found by the Stanford checker in ntfs_dupuni2map().
-	- Convert large stack variable to dynamically allocated one in
-	  ntfs_get_free_cluster_count() (found by Stanford checker).
-
-Kernel 2.4.4:
+Note that a technical ChangeLog aimed at kernel hackers is in fs/ntfs/ChangeLog.
 
+2.0.4:
+	- Cleanups and updates for kernel 2.5.11.
+2.0.3:
+	- Small bug fixes, cleanups, and performance improvements.
+2.0.2:
+	- Use default fmask of 0177 so that files are no executable by default.
+	  If you want owner executable files, just use fmask=0077.
+	- Update for kernel 2.5.9 but preserve backwards compatibility with
+	  kernel 2.5.7.
+	- Minor bug fixes, cleanups, and updates.
+2.0.1:
+	- Minor updates, primarily set the executable bit by default on files
+	  so they can be executed.
+2.0.0:
 	- Started ChangeLog.
 
diff -Nru a/Documentation/filesystems/porting b/Documentation/filesystems/porting
--- a/Documentation/filesystems/porting	Sun Apr 28 20:10:34 2002
+++ b/Documentation/filesystems/porting	Sun Apr 28 20:10:34 2002
@@ -81,12 +81,19 @@
 [mandatory]
 
 ->lookup(), ->truncate(), ->create(), ->unlink(), ->mknod(), ->mkdir(),
-->rmdir(), ->link(), ->lseek(), ->symlink() and ->rename() are called
-without BKL now.  Grab it on the entry, drop upon return - that will
-guarantee the same locking you used to have.  If your method or its
+->rmdir(), ->link(), ->lseek(), ->symlink(), ->rename() and ->readdir() 
+are called without BKL now.  Grab it on the entry, drop upon return - that 
+will guarantee the same locking you used to have.  If your method or its
 parts do not need BKL - better yet, now you can shift lock_kernel() and
 unlock_kernel() so that they would protect exactly what needs to be
 protected.
+
+---
+[mandatory]
+
+BKL is also moved from around sb operations.  ->write_super() Is now called 
+without BKL held.  BKL should have been shifted into individual fs sb_op
+functions.  If you don't need it, remove it.  
 
 ---
 [informational]
diff -Nru a/Documentation/sonypi.txt b/Documentation/sonypi.txt
--- a/Documentation/sonypi.txt	Sun Apr 28 20:10:30 2002
+++ b/Documentation/sonypi.txt	Sun Apr 28 20:10:30 2002
@@ -43,7 +43,7 @@
 to /etc/modules.conf file, when the driver is compiled as a module or by
 adding the following to the kernel command line (in your bootloader):
 
-	sonypi=minor[[[[,camera],fnkeyinit],verbose],compat]
+	sonypi=minor[[[[[,camera],fnkeyinit],verbose],compat],nojogdial]
 
 where:
 
@@ -70,6 +70,9 @@
 			events. If the driver worked for you in the past
 			(prior to version 1.5) and does not work anymore,
 			add this option and report to the author.
+
+	nojogdial:	gives more accurate PKEY events on those Vaio models
+			which don't have a jogdial (like the FX series).
 
 Module use:
 -----------
diff -Nru a/Documentation/usb/ehci.txt b/Documentation/usb/ehci.txt
--- a/Documentation/usb/ehci.txt	Sun Apr 28 20:10:30 2002
+++ b/Documentation/usb/ehci.txt	Sun Apr 28 20:10:30 2002
@@ -1,4 +1,4 @@
-18-Dec-2001
+26-Apr-2002
 
 The EHCI driver is used to talk to high speed USB 2.0 devices using
 USB 2.0-capable host controller hardware.  The USB 2.0 standard is
@@ -19,6 +19,9 @@
 (TT) in the hub, which turns low or full speed transactions into
 high speed "split transactions" that don't waste transfer bandwidth.
 
+At this writing, this driver has been seen to work with implementations
+of EHCI from (in alphabetical order):  Intel, NEC, Philips, and VIA.
+
 At this writing, high speed devices are finally beginning to appear.
 While usb-storage devices have been available for some time (working
 quite speedily on the 2.4 version of this driver), hubs have only
@@ -36,7 +39,7 @@
 FUNCTIONALITY
 
 This driver is regularly tested on x86 hardware, and has also been
-used on PPC hardware so big/little endianneess issues should be gone.
+used on PPC hardware so big/little endianness issues should be gone.
 It's believed to do all the right PCI magic so that I/O works even on
 systems with interesting DMA mapping issues.
 
@@ -51,11 +54,13 @@
 connecting USB 1.1 hubs, keyboards, and mice to USB 2.0 hubs won't work.
 Connect them to USB 1.1 hubs, or to a root hub.
 
-Isochronous (ISO) transfer support is not yet working.  No production
+Isochronous (ISO) transfer support is also newly functional.  No production
 high speed devices are available which would need it (though high quality
 webcams are in the works!).  Note that split transaction support for ISO
 transfers can't share much code with the code for high speed ISO transfers,
-since EHCI represents these with a different data structure.
+since EHCI represents these with a different data structure.  So for now,
+most USB audio and video devices have the same restrictions as hubs, mice,
+and keyboards:  don't connect them using high speed USB hubs.
 
 The EHCI root hub code should hand off USB 1.1 devices to its companion
 controller.  This driver doesn't need to know anything about those
@@ -99,6 +104,8 @@
 but they may want to check for "usb_device->speed == USB_SPEED_HIGH".
 High speed devices can do things that full speed (or low speed) ones
 can't, such as "high bandwidth" periodic (interrupt or ISO) transfers.
+Also, some values in device descriptors (such as polling intervals for
+periodic transfers) use different encodings when operating at high speed.
 
 
 PERFORMANCE
diff -Nru a/Documentation/vm/locking b/Documentation/vm/locking
--- a/Documentation/vm/locking	Sun Apr 28 20:10:34 2002
+++ b/Documentation/vm/locking	Sun Apr 28 20:10:34 2002
@@ -77,6 +77,13 @@
 
 The page_table_lock is a spin lock.
 
+Note: PTL can also be used to guarantee that no new clones using the
+mm start up ... this is a loose form of stability on mm_users. For
+example, it is used in copy_mm to protect against a racing tlb_gather_mmu
+single address space optimization, so that the zap_page_range (from
+vmtruncate) does not loose sending ipi's to cloned threads that might 
+be spawned underneath it and go to user mode to drag in pte's into tlbs.
+
 swap_list_lock/swap_device_lock
 -------------------------------
 The swap devices are chained in priority order from the "swap_list" header. 
diff -Nru a/MAINTAINERS b/MAINTAINERS
--- a/MAINTAINERS	Sun Apr 28 20:10:37 2002
+++ b/MAINTAINERS	Sun Apr 28 20:10:37 2002
@@ -556,9 +556,9 @@
 S:	Maintained
 
 FARSYNC SYNCHRONOUS DRIVER
-P:	Bob Dunlop
-M:	rjd@xyzzy.clara.co.uk
-M:	bob.dunlop@farsite.co.uk
+P:	Kevin Curtis
+M:	kevin.curtis@farsite.co.uk
+M:	kevin.curtis@farsite.co.uk
 W:	http://www.farsite.co.uk/
 S:	Supported
 
@@ -737,16 +737,12 @@
 S:	Maintained
 
 IEEE 1394 SUBSYSTEM
-P:	Andreas Bombe
-M:	andreas.bombe@munich.netsurf.de
+P:	Ben Collins
+M:	bcollins@debian.org
 L:	linux1394-devel@lists.sourceforge.net
 W:	http://linux1394.sourceforge.net/
 S:	Maintained
 
-IEEE 1394 AIC5800 DRIVER
-L:	linux1394-devel@lists.sourceforge.net
-S:	Orphan
-
 IEEE 1394 OHCI DRIVER
 P:	Ben Collins
 M:	bcollins@debian.org
@@ -801,6 +797,12 @@
 L:	intermezzo-discuss@lists.sourceforge.net
 S:	Maintained
 
+IOC3 DRIVER
+P:	Ralf Baechle
+M:	ralf@oss.sgi.com
+L:	linux-mips@oss.sgi.com
+S:	Maintained
+
 IP MASQUERADING:
 P:	Juanjo Ciarlante
 M:	jjciarla@raiz.uncu.edu.ar
@@ -935,6 +937,13 @@
 L:	linuxppc-dev@lists.linuxppc.org
 S:	Maintained
 
+LINUX FOR 64BIT POWERPC
+P:	David Engebretsen
+M:	engebret@us.ibm.com
+W:	http://linuxppc64.org
+L:	linuxppc64-dev@lists.linuxppc.org
+S:	Supported
+
 LOGICAL DISK MANAGER SUPPORT (LDM, Windows 2000/XP Dynamic Disks)
 P:	Richard Russon (FlatCap)
 M:	ldm@flatcap.org
@@ -1198,8 +1207,10 @@
 S:	Maintained
 
 PCI ID DATABASE
-P:	Jens Maurer
-M:	jmaurer@cck.uni-kl.de
+P:	Martin Mares
+M:	mj@ucw.cz
+L:	pciids-devel@lists.sourceforge.net
+W:	http://pciids.sourceforge.net/
 S:	Maintained
 
 PCI SOUND DRIVERS (ES1370, ES1371 and SONICVIBES)
@@ -1387,11 +1398,11 @@
 S:	Maintained
 
 SIS 5513 IDE CONTROLLER DRIVER
-P:      Lionel Bouton
-M:      Lionel.Bouton@inet6.fr
-W:      http://inet6.dyn.dhs.org/sponsoring/sis5513/index.html
-W:      http://gyver.homeip.net/sis5513/index.html
-S:      Maintained
+P:	Lionel Bouton
+M:	Lionel.Bouton@inet6.fr
+W:	http://inet6.dyn.dhs.org/sponsoring/sis5513/index.html
+W:	http://gyver.homeip.net/sis5513/index.html
+S:	Maintained
 
 SIS 900/7016 FAST ETHERNET DRIVER
 P:	Ollie Lho
@@ -1508,26 +1519,24 @@
 S:	Maintained
 
 TI GRAPH LINK USB (SilverLink) CABLE DRIVER
-P:     Romain Lievin
-M:     roms@lpg.ticalc.org
-P:     Julien Blache
-M:     jb@technologeek.org
-S:     Maintained
+P:	Romain Lievin
+M:	roms@lpg.ticalc.org
+P:	Julien Blache
+M:	jb@technologeek.org
+S:	Maintained
 
 TIEMAN VOYAGER USB BRAILLE DISPLAY DRIVER
-P:      Stephane Dalton
-M:      sdalton@videotron.ca
-P:      Stéphane Doyon
-M:      s.doyon@videotron.ca
-S:      Maintained
+P:	Stephane Dalton
+M:	sdalton@videotron.ca
+P:	Stéphane Doyon
+M:	s.doyon@videotron.ca
+S:	Maintained
 
 TLAN NETWORK DRIVER
-P:	Torben Mathiasen
-M:	torben.mathiasen@compaq.com
-M:	torben@kernel.dk
-L:	tlan@vuser.vu.union.edu
-L:	linux-net@vger.kernel.org
-W:	http://tlan.kernel.dk
+P:	Samuel Chessman
+M:	chessman@tux.org
+L:	tlan-devel@lists.sourceforge.net
+W:	http://sourceforge.net/projects/tlan/
 S:	Maintained
 
 TOKEN-RING NETWORK DRIVER
@@ -1629,6 +1638,12 @@
 L:	linux-usb-devel@lists.sourceforge.net
 S:	Maintained
 
+USB EHCI DRIVER
+P:	David Brownell
+M:	dbrownell@users.sourceforge.net
+L:	linux-usb-devel@lists.sourceforge.net
+S:	Maintained
+
 USB HID/HIDBP/INPUT DRIVERS
 P:	Vojtech Pavlik
 M:	vojtech@suse.cz
@@ -1731,7 +1746,7 @@
 USB SERIAL CYBERJACK PINPAD/E-COM DRIVER
 L:	linux-usb-users@lists.sourceforge.net
 L:	linux-usb-devel@lists.sourceforge.net
-S:	Supported
+S:	Maintained
 
 USB AUERSWALD DRIVER
 P:	Wolfgang Muees
diff -Nru a/Makefile b/Makefile
--- a/Makefile	Sun Apr 28 20:10:31 2002
+++ b/Makefile	Sun Apr 28 20:10:31 2002
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 5
-SUBLEVEL = 10
+SUBLEVEL = 11
 EXTRAVERSION =
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
diff -Nru a/Rules.make b/Rules.make
--- a/Rules.make	Sun Apr 28 20:10:35 2002
+++ b/Rules.make	Sun Apr 28 20:10:35 2002
@@ -19,7 +19,6 @@
 unexport ALL_SUB_DIRS
 unexport MOD_SUB_DIRS
 unexport O_TARGET
-unexport ALL_MOBJS
 
 unexport obj-y
 unexport obj-m
@@ -34,6 +33,12 @@
 comma	:= ,
 
 #
+# When an object is listed to be built compiled-in and modular,
+# only build the compiled-in version
+#
+obj-m := $(filter-out $(obj-y),$(obj-m))
+
+#
 # Get things started.
 #
 first_rule: sub_dirs
@@ -182,8 +187,7 @@
 #
 # A rule to make modules
 #
-ALL_MOBJS = $(filter-out $(obj-y), $(obj-m))
-ifneq "$(strip $(ALL_MOBJS))" ""
+ifneq "$(strip $(obj-m))" ""
 MOD_DESTDIR := $(shell $(CONFIG_SHELL) $(TOPDIR)/scripts/pathdown.sh)
 endif
 
@@ -200,14 +204,14 @@
 endif
 
 .PHONY: modules
-modules: $(ALL_MOBJS) dummy \
+modules: $(obj-m) dummy \
 	 $(patsubst %,_modsubdir_%,$(MOD_DIRS))
 
 .PHONY: _modinst__
 _modinst__: dummy
-ifneq "$(strip $(ALL_MOBJS))" ""
+ifneq "$(strip $(obj-m))" ""
 	mkdir -p $(MODLIB)/kernel/$(MOD_DESTDIR)
-	cp $(ALL_MOBJS) $(MODLIB)/kernel/$(MOD_DESTDIR)$(MOD_TARGET)
+	cp $(obj-m) $(MODLIB)/kernel/$(MOD_DESTDIR)
 endif
 
 .PHONY: modules_install
@@ -304,7 +308,8 @@
 endif # CONFIG_MODVERSIONS
 
 ifneq "$(strip $(export-objs))" ""
-$(export-objs): $(export-objs:.o=.c) $(TOPDIR)/include/linux/modversions.h
+$(export-objs): $(TOPDIR)/include/linux/modversions.h
+$(export-objs): %.o: %.c
 	$(CC) $(CFLAGS) $(EXTRA_CFLAGS) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) $(CFLAGS_$@) -DEXPORT_SYMTAB -c $(@:.o=.c)
 	@ ( \
 	    echo 'ifeq ($(strip $(subst $(comma),:,$(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -DEXPORT_SYMTAB)),$$(strip $$(subst $$(comma),:,$$(CFLAGS) $$(EXTRA_CFLAGS) $$(CFLAGS_$@) -DEXPORT_SYMTAB)))' ; \
diff -Nru a/arch/alpha/defconfig b/arch/alpha/defconfig
--- a/arch/alpha/defconfig	Sun Apr 28 20:10:36 2002
+++ b/arch/alpha/defconfig	Sun Apr 28 20:10:36 2002
@@ -584,7 +584,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/alpha/kernel/ptrace.c b/arch/alpha/kernel/ptrace.c
--- a/arch/alpha/kernel/ptrace.c	Sun Apr 28 20:10:38 2002
+++ b/arch/alpha/kernel/ptrace.c	Sun Apr 28 20:10:38 2002
@@ -106,7 +106,7 @@
 		zero = 0;
 		addr = &zero;
 	} else {
-		addr = (long *)((long)task + regoff[regno]);
+		addr = (long *)((long)task->thread_info + regoff[regno]);
 	}
 	return addr;
 }
@@ -340,9 +340,9 @@
 		if ((unsigned long) data > _NSIG)
 			goto out;
 		if (request == PTRACE_SYSCALL)
-			set_thread_flag(TIF_SYSCALL_TRACE);
+			set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
 		else
-			clear_thread_flag(TIF_SYSCALL_TRACE);
+			clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
 		child->exit_code = data;
 		wake_up_process(child);
 		/* make sure single-step breakpoint is gone. */
@@ -371,7 +371,7 @@
 			goto out;
 		/* Mark single stepping.  */
 		child->thread_info->bpt_nsaved = -1;
-		clear_thread_flag(TIF_SYSCALL_TRACE);
+		clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
 		wake_up_process(child);
 		child->exit_code = data;
 		/* give it a chance to run. */
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	Sun Apr 28 20:10:33 2002
+++ b/arch/arm/boot/compressed/head-shark.S	Sun Apr 28 20:10:33 2002
@@ -38,7 +38,7 @@
 		adr	r1, __ofw_data
 		add	r2, r1, #4
 		mov	lr, pc
-		b	SYMBOL_NAME(ofw_init)
+		b	ofw_init
 		mov	r1, #0
 
 		adr	r2, __mmu_off			@ calculate physical address
@@ -109,7 +109,7 @@
 		add	sp, sp, #128
 		adr	r0, __ofw_data
 		mov	lr, pc
-		b	SYMBOL_NAME(create_params)
+		b	create_params
 	
 		mov	r8, #0
 		mov	r7, #15
diff -Nru a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S
--- a/arch/arm/boot/compressed/head.S	Sun Apr 28 20:10:38 2002
+++ b/arch/arm/boot/compressed/head.S	Sun Apr 28 20:10:38 2002
@@ -185,7 +185,7 @@
 		mov	r5, r2			@ decompress after malloc space
 		mov	r0, r5
 		mov	r3, r7
-		bl	SYMBOL_NAME(decompress_kernel)
+		bl	decompress_kernel
 
 		add	r0, r0, #127
 		bic	r0, r0, #127		@ align the kernel length
@@ -219,7 +219,7 @@
  */
 wont_overwrite:	mov	r0, r4
 		mov	r3, r7
-		bl	SYMBOL_NAME(decompress_kernel)
+		bl	decompress_kernel
 		b	call_kernel
 
 		.type	LC0, #object
diff -Nru a/arch/arm/boot/compressed/ll_char_wr.S b/arch/arm/boot/compressed/ll_char_wr.S
--- a/arch/arm/boot/compressed/ll_char_wr.S	Sun Apr 28 20:10:33 2002
+++ b/arch/arm/boot/compressed/ll_char_wr.S	Sun Apr 28 20:10:33 2002
@@ -27,10 +27,10 @@
 #define FLASH           0x08
 #define INVERSE         0x10
 
-LC0:		.word	SYMBOL_NAME(bytes_per_char_h)
-		.word	SYMBOL_NAME(video_size_row)
-		.word	SYMBOL_NAME(acorndata_8x8)
-		.word	SYMBOL_NAME(con_charconvtable)
+LC0:		.word	bytes_per_char_h
+		.word	video_size_row
+		.word	acorndata_8x8
+		.word	con_charconvtable
 
 ENTRY(ll_write_char)
 		stmfd	sp!, {r4 - r7, lr}
diff -Nru a/arch/arm/def-configs/a5k b/arch/arm/def-configs/a5k
--- a/arch/arm/def-configs/a5k	Sun Apr 28 20:10:34 2002
+++ b/arch/arm/def-configs/a5k	Sun Apr 28 20:10:34 2002
@@ -394,7 +394,7 @@
 # CONFIG_JOLIET is not set
 # CONFIG_MINIX_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/arm/def-configs/adi_evb b/arch/arm/def-configs/adi_evb
--- a/arch/arm/def-configs/adi_evb	Sun Apr 28 20:10:37 2002
+++ b/arch/arm/def-configs/adi_evb	Sun Apr 28 20:10:37 2002
@@ -528,7 +528,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_FREEVXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/arm/def-configs/adsbitsy b/arch/arm/def-configs/adsbitsy
--- a/arch/arm/def-configs/adsbitsy	Sun Apr 28 20:10:37 2002
+++ b/arch/arm/def-configs/adsbitsy	Sun Apr 28 20:10:37 2002
@@ -488,7 +488,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/arm/def-configs/anakin b/arch/arm/def-configs/anakin
--- a/arch/arm/def-configs/anakin	Sun Apr 28 20:10:31 2002
+++ b/arch/arm/def-configs/anakin	Sun Apr 28 20:10:31 2002
@@ -372,7 +372,7 @@
 # CONFIG_JOLIET is not set
 # CONFIG_MINIX_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/arm/def-configs/assabet b/arch/arm/def-configs/assabet
--- a/arch/arm/def-configs/assabet	Sun Apr 28 20:10:35 2002
+++ b/arch/arm/def-configs/assabet	Sun Apr 28 20:10:35 2002
@@ -667,7 +667,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_FREEVXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/arm/def-configs/badge4 b/arch/arm/def-configs/badge4
--- a/arch/arm/def-configs/badge4	Sun Apr 28 20:10:37 2002
+++ b/arch/arm/def-configs/badge4	Sun Apr 28 20:10:37 2002
@@ -854,7 +854,7 @@
 CONFIG_MINIX_FS=m
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_DEVFS_FS=y
diff -Nru a/arch/arm/def-configs/brutus b/arch/arm/def-configs/brutus
--- a/arch/arm/def-configs/brutus	Sun Apr 28 20:10:32 2002
+++ b/arch/arm/def-configs/brutus	Sun Apr 28 20:10:32 2002
@@ -222,7 +222,7 @@
 # CONFIG_JOLIET is not set
 # CONFIG_MINIX_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/arm/def-configs/cerfcube b/arch/arm/def-configs/cerfcube
--- a/arch/arm/def-configs/cerfcube	Sun Apr 28 20:10:30 2002
+++ b/arch/arm/def-configs/cerfcube	Sun Apr 28 20:10:30 2002
@@ -660,7 +660,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_FREEVXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/arm/def-configs/cerfpda b/arch/arm/def-configs/cerfpda
--- a/arch/arm/def-configs/cerfpda	Sun Apr 28 20:10:32 2002
+++ b/arch/arm/def-configs/cerfpda	Sun Apr 28 20:10:32 2002
@@ -698,7 +698,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_FREEVXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/arm/def-configs/cerfpod b/arch/arm/def-configs/cerfpod
--- a/arch/arm/def-configs/cerfpod	Sun Apr 28 20:10:39 2002
+++ b/arch/arm/def-configs/cerfpod	Sun Apr 28 20:10:39 2002
@@ -662,7 +662,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_FREEVXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/arm/def-configs/ebsa110 b/arch/arm/def-configs/ebsa110
--- a/arch/arm/def-configs/ebsa110	Sun Apr 28 20:10:30 2002
+++ b/arch/arm/def-configs/ebsa110	Sun Apr 28 20:10:30 2002
@@ -530,7 +530,7 @@
 CONFIG_MINIX_FS=y
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/arm/def-configs/edb7211 b/arch/arm/def-configs/edb7211
--- a/arch/arm/def-configs/edb7211	Sun Apr 28 20:10:30 2002
+++ b/arch/arm/def-configs/edb7211	Sun Apr 28 20:10:30 2002
@@ -329,7 +329,7 @@
 # CONFIG_JOLIET is not set
 CONFIG_MINIX_FS=y
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/arm/def-configs/epxa10db b/arch/arm/def-configs/epxa10db
--- a/arch/arm/def-configs/epxa10db	Sun Apr 28 20:10:34 2002
+++ b/arch/arm/def-configs/epxa10db	Sun Apr 28 20:10:34 2002
@@ -543,7 +543,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/arm/def-configs/flexanet b/arch/arm/def-configs/flexanet
--- a/arch/arm/def-configs/flexanet	Sun Apr 28 20:10:35 2002
+++ b/arch/arm/def-configs/flexanet	Sun Apr 28 20:10:35 2002
@@ -651,7 +651,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/arm/def-configs/footbridge b/arch/arm/def-configs/footbridge
--- a/arch/arm/def-configs/footbridge	Sun Apr 28 20:10:35 2002
+++ b/arch/arm/def-configs/footbridge	Sun Apr 28 20:10:35 2002
@@ -626,7 +626,7 @@
 CONFIG_JOLIET=y
 # CONFIG_MINIX_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/arm/def-configs/fortunet b/arch/arm/def-configs/fortunet
--- a/arch/arm/def-configs/fortunet	Sun Apr 28 20:10:39 2002
+++ b/arch/arm/def-configs/fortunet	Sun Apr 28 20:10:39 2002
@@ -454,7 +454,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/arm/def-configs/freebird b/arch/arm/def-configs/freebird
--- a/arch/arm/def-configs/freebird	Sun Apr 28 20:10:32 2002
+++ b/arch/arm/def-configs/freebird	Sun Apr 28 20:10:32 2002
@@ -501,7 +501,7 @@
 # CONFIG_JOLIET is not set
 # CONFIG_MINIX_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/arm/def-configs/freebird_new b/arch/arm/def-configs/freebird_new
--- a/arch/arm/def-configs/freebird_new	Sun Apr 28 20:10:30 2002
+++ b/arch/arm/def-configs/freebird_new	Sun Apr 28 20:10:30 2002
@@ -521,7 +521,7 @@
 # CONFIG_JOLIET is not set
 # CONFIG_MINIX_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/arm/def-configs/graphicsclient b/arch/arm/def-configs/graphicsclient
--- a/arch/arm/def-configs/graphicsclient	Sun Apr 28 20:10:36 2002
+++ b/arch/arm/def-configs/graphicsclient	Sun Apr 28 20:10:36 2002
@@ -594,7 +594,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/arm/def-configs/graphicsmaster b/arch/arm/def-configs/graphicsmaster
--- a/arch/arm/def-configs/graphicsmaster	Sun Apr 28 20:10:30 2002
+++ b/arch/arm/def-configs/graphicsmaster	Sun Apr 28 20:10:30 2002
@@ -569,7 +569,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/arm/def-configs/h3600 b/arch/arm/def-configs/h3600
--- a/arch/arm/def-configs/h3600	Sun Apr 28 20:10:33 2002
+++ b/arch/arm/def-configs/h3600	Sun Apr 28 20:10:33 2002
@@ -659,7 +659,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_FREEVXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/arm/def-configs/huw_webpanel b/arch/arm/def-configs/huw_webpanel
--- a/arch/arm/def-configs/huw_webpanel	Sun Apr 28 20:10:31 2002
+++ b/arch/arm/def-configs/huw_webpanel	Sun Apr 28 20:10:31 2002
@@ -335,7 +335,7 @@
 # CONFIG_JOLIET is not set
 # CONFIG_MINIX_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/arm/def-configs/integrator b/arch/arm/def-configs/integrator
--- a/arch/arm/def-configs/integrator	Sun Apr 28 20:10:31 2002
+++ b/arch/arm/def-configs/integrator	Sun Apr 28 20:10:31 2002
@@ -542,7 +542,7 @@
 CONFIG_MINIX_FS=y
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/arm/def-configs/iq80310 b/arch/arm/def-configs/iq80310
--- a/arch/arm/def-configs/iq80310	Sun Apr 28 20:10:32 2002
+++ b/arch/arm/def-configs/iq80310	Sun Apr 28 20:10:32 2002
@@ -649,7 +649,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/arm/def-configs/jornada720 b/arch/arm/def-configs/jornada720
--- a/arch/arm/def-configs/jornada720	Sun Apr 28 20:10:32 2002
+++ b/arch/arm/def-configs/jornada720	Sun Apr 28 20:10:32 2002
@@ -676,7 +676,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_DEVFS_FS=y
diff -Nru a/arch/arm/def-configs/lart b/arch/arm/def-configs/lart
--- a/arch/arm/def-configs/lart	Sun Apr 28 20:10:35 2002
+++ b/arch/arm/def-configs/lart	Sun Apr 28 20:10:35 2002
@@ -664,7 +664,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_FREEVXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/arm/def-configs/lusl7200 b/arch/arm/def-configs/lusl7200
--- a/arch/arm/def-configs/lusl7200	Sun Apr 28 20:10:32 2002
+++ b/arch/arm/def-configs/lusl7200	Sun Apr 28 20:10:32 2002
@@ -191,7 +191,7 @@
 # CONFIG_JOLIET is not set
 # CONFIG_MINIX_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/arm/def-configs/neponset b/arch/arm/def-configs/neponset
--- a/arch/arm/def-configs/neponset	Sun Apr 28 20:10:34 2002
+++ b/arch/arm/def-configs/neponset	Sun Apr 28 20:10:34 2002
@@ -653,7 +653,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_FREEVXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/arm/def-configs/omnimeter b/arch/arm/def-configs/omnimeter
--- a/arch/arm/def-configs/omnimeter	Sun Apr 28 20:10:35 2002
+++ b/arch/arm/def-configs/omnimeter	Sun Apr 28 20:10:35 2002
@@ -431,7 +431,7 @@
 # CONFIG_JOLIET is not set
 # CONFIG_MINIX_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/arm/def-configs/pangolin b/arch/arm/def-configs/pangolin
--- a/arch/arm/def-configs/pangolin	Sun Apr 28 20:10:37 2002
+++ b/arch/arm/def-configs/pangolin	Sun Apr 28 20:10:37 2002
@@ -568,7 +568,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/arm/def-configs/pfs168_mqtft b/arch/arm/def-configs/pfs168_mqtft
--- a/arch/arm/def-configs/pfs168_mqtft	Sun Apr 28 20:10:36 2002
+++ b/arch/arm/def-configs/pfs168_mqtft	Sun Apr 28 20:10:36 2002
@@ -608,7 +608,7 @@
 # CONFIG_JOLIET is not set
 # CONFIG_MINIX_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/arm/def-configs/pfs168_mqvga b/arch/arm/def-configs/pfs168_mqvga
--- a/arch/arm/def-configs/pfs168_mqvga	Sun Apr 28 20:10:30 2002
+++ b/arch/arm/def-configs/pfs168_mqvga	Sun Apr 28 20:10:30 2002
@@ -608,7 +608,7 @@
 # CONFIG_JOLIET is not set
 # CONFIG_MINIX_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/arm/def-configs/pfs168_sastn b/arch/arm/def-configs/pfs168_sastn
--- a/arch/arm/def-configs/pfs168_sastn	Sun Apr 28 20:10:31 2002
+++ b/arch/arm/def-configs/pfs168_sastn	Sun Apr 28 20:10:31 2002
@@ -601,7 +601,7 @@
 # CONFIG_JOLIET is not set
 # CONFIG_MINIX_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/arm/def-configs/pfs168_satft b/arch/arm/def-configs/pfs168_satft
--- a/arch/arm/def-configs/pfs168_satft	Sun Apr 28 20:10:30 2002
+++ b/arch/arm/def-configs/pfs168_satft	Sun Apr 28 20:10:30 2002
@@ -608,7 +608,7 @@
 # CONFIG_JOLIET is not set
 # CONFIG_MINIX_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/arm/def-configs/pleb b/arch/arm/def-configs/pleb
--- a/arch/arm/def-configs/pleb	Sun Apr 28 20:10:32 2002
+++ b/arch/arm/def-configs/pleb	Sun Apr 28 20:10:32 2002
@@ -459,7 +459,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/arm/def-configs/rpc b/arch/arm/def-configs/rpc
--- a/arch/arm/def-configs/rpc	Sun Apr 28 20:10:30 2002
+++ b/arch/arm/def-configs/rpc	Sun Apr 28 20:10:30 2002
@@ -629,7 +629,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/arm/def-configs/shark b/arch/arm/def-configs/shark
--- a/arch/arm/def-configs/shark	Sun Apr 28 20:10:31 2002
+++ b/arch/arm/def-configs/shark	Sun Apr 28 20:10:31 2002
@@ -691,7 +691,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_DEVFS_FS=y
diff -Nru a/arch/arm/def-configs/stork b/arch/arm/def-configs/stork
--- a/arch/arm/def-configs/stork	Sun Apr 28 20:10:35 2002
+++ b/arch/arm/def-configs/stork	Sun Apr 28 20:10:35 2002
@@ -658,7 +658,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_DEVFS_FS=y
diff -Nru a/arch/arm/def-configs/system3 b/arch/arm/def-configs/system3
--- a/arch/arm/def-configs/system3	Sun Apr 28 20:10:35 2002
+++ b/arch/arm/def-configs/system3	Sun Apr 28 20:10:35 2002
@@ -693,7 +693,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/arm/defconfig b/arch/arm/defconfig
--- a/arch/arm/defconfig	Sun Apr 28 20:10:34 2002
+++ b/arch/arm/defconfig	Sun Apr 28 20:10:34 2002
@@ -420,7 +420,7 @@
 # CONFIG_JOLIET is not set
 # CONFIG_MINIX_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/arm/kernel/calls.S b/arch/arm/kernel/calls.S
--- a/arch/arm/kernel/calls.S	Sun Apr 28 20:10:37 2002
+++ b/arch/arm/kernel/calls.S	Sun Apr 28 20:10:37 2002
@@ -14,248 +14,248 @@
 #else
 
 __syscall_start:
-/* 0 */		.long	SYMBOL_NAME(sys_ni_syscall)
-		.long	SYMBOL_NAME(sys_exit)
-		.long	SYMBOL_NAME(sys_fork_wrapper)
-		.long	SYMBOL_NAME(sys_read)
-		.long	SYMBOL_NAME(sys_write)
-/* 5 */		.long	SYMBOL_NAME(sys_open)
-		.long	SYMBOL_NAME(sys_close)
-		.long	SYMBOL_NAME(sys_ni_syscall)		/* was sys_waitpid */
-		.long	SYMBOL_NAME(sys_creat)
-		.long	SYMBOL_NAME(sys_link)
-/* 10 */	.long	SYMBOL_NAME(sys_unlink)
-		.long	SYMBOL_NAME(sys_execve_wrapper)
-		.long	SYMBOL_NAME(sys_chdir)
-		.long	SYMBOL_NAME(sys_time)			/* used by libc4 */
-		.long	SYMBOL_NAME(sys_mknod)
-/* 15 */	.long	SYMBOL_NAME(sys_chmod)
-		.long	SYMBOL_NAME(sys_lchown16)
-		.long	SYMBOL_NAME(sys_ni_syscall)		/* was sys_break */
-		.long	SYMBOL_NAME(sys_ni_syscall)		/* was sys_stat */
-		.long	SYMBOL_NAME(sys_lseek)
-/* 20 */	.long	SYMBOL_NAME(sys_getpid)
-		.long	SYMBOL_NAME(sys_mount)
-		.long	SYMBOL_NAME(sys_oldumount)		/* used by libc4 */
-		.long	SYMBOL_NAME(sys_setuid16)
-		.long	SYMBOL_NAME(sys_getuid16)
-/* 25 */	.long	SYMBOL_NAME(sys_stime)
-		.long	SYMBOL_NAME(sys_ptrace)
-		.long	SYMBOL_NAME(sys_alarm)			/* used by libc4 */
-		.long	SYMBOL_NAME(sys_ni_syscall)		/* was sys_fstat */
-		.long	SYMBOL_NAME(sys_pause)
-/* 30 */	.long	SYMBOL_NAME(sys_utime)			/* used by libc4 */
-		.long	SYMBOL_NAME(sys_ni_syscall)		/* was sys_stty */
-		.long	SYMBOL_NAME(sys_ni_syscall)		/* was sys_getty */
-		.long	SYMBOL_NAME(sys_access)
-		.long	SYMBOL_NAME(sys_nice)
-/* 35 */	.long	SYMBOL_NAME(sys_ni_syscall)		/* was sys_ftime */
-		.long	SYMBOL_NAME(sys_sync)
-		.long	SYMBOL_NAME(sys_kill)
-		.long	SYMBOL_NAME(sys_rename)
-		.long	SYMBOL_NAME(sys_mkdir)
-/* 40 */	.long	SYMBOL_NAME(sys_rmdir)
-		.long	SYMBOL_NAME(sys_dup)
-		.long	SYMBOL_NAME(sys_pipe)
-		.long	SYMBOL_NAME(sys_times)
-		.long	SYMBOL_NAME(sys_ni_syscall)		/* was sys_prof */
-/* 45 */	.long	SYMBOL_NAME(sys_brk)
-		.long	SYMBOL_NAME(sys_setgid16)
-		.long	SYMBOL_NAME(sys_getgid16)
-		.long	SYMBOL_NAME(sys_ni_syscall)		/* was sys_signal */
-		.long	SYMBOL_NAME(sys_geteuid16)
-/* 50 */	.long	SYMBOL_NAME(sys_getegid16)
-		.long	SYMBOL_NAME(sys_acct)
-		.long	SYMBOL_NAME(sys_umount)
-		.long	SYMBOL_NAME(sys_ni_syscall)		/* was sys_lock */
-		.long	SYMBOL_NAME(sys_ioctl)
-/* 55 */	.long	SYMBOL_NAME(sys_fcntl)
-		.long	SYMBOL_NAME(sys_ni_syscall)		/* was sys_mpx */
-		.long	SYMBOL_NAME(sys_setpgid)
-		.long	SYMBOL_NAME(sys_ni_syscall)		/* was sys_ulimit */
-		.long	SYMBOL_NAME(sys_ni_syscall)		/* was sys_olduname */
-/* 60 */	.long	SYMBOL_NAME(sys_umask)
-		.long	SYMBOL_NAME(sys_chroot)
-		.long	SYMBOL_NAME(sys_ustat)
-		.long	SYMBOL_NAME(sys_dup2)
-		.long	SYMBOL_NAME(sys_getppid)
-/* 65 */	.long	SYMBOL_NAME(sys_getpgrp)
-		.long	SYMBOL_NAME(sys_setsid)
-		.long	SYMBOL_NAME(sys_sigaction)
-		.long	SYMBOL_NAME(sys_ni_syscall)		/* was sys_sgetmask */
-		.long	SYMBOL_NAME(sys_ni_syscall)		/* was sys_ssetmask */
-/* 70 */	.long	SYMBOL_NAME(sys_setreuid16)
-		.long	SYMBOL_NAME(sys_setregid16)
-		.long	SYMBOL_NAME(sys_sigsuspend_wrapper)
-		.long	SYMBOL_NAME(sys_sigpending)
-		.long	SYMBOL_NAME(sys_sethostname)
-/* 75 */	.long	SYMBOL_NAME(sys_setrlimit)
-		.long	SYMBOL_NAME(sys_old_getrlimit)		/* used by libc4 */
-		.long	SYMBOL_NAME(sys_getrusage)
-		.long	SYMBOL_NAME(sys_gettimeofday)
-		.long	SYMBOL_NAME(sys_settimeofday)
-/* 80 */	.long	SYMBOL_NAME(sys_getgroups16)
-		.long	SYMBOL_NAME(sys_setgroups16)
-		.long	SYMBOL_NAME(old_select)			/* used by libc4 */
-		.long	SYMBOL_NAME(sys_symlink)
-		.long	SYMBOL_NAME(sys_ni_syscall)		/* was sys_lstat */
-/* 85 */	.long	SYMBOL_NAME(sys_readlink)
-		.long	SYMBOL_NAME(sys_uselib)
-		.long	SYMBOL_NAME(sys_swapon)
-		.long	SYMBOL_NAME(sys_reboot)
-		.long	SYMBOL_NAME(old_readdir)		/* used by libc4 */
-/* 90 */	.long	SYMBOL_NAME(old_mmap)			/* used by libc4 */
-		.long	SYMBOL_NAME(sys_munmap)
-		.long	SYMBOL_NAME(sys_truncate)
-		.long	SYMBOL_NAME(sys_ftruncate)
-		.long	SYMBOL_NAME(sys_fchmod)
-/* 95 */	.long	SYMBOL_NAME(sys_fchown16)
-		.long	SYMBOL_NAME(sys_getpriority)
-		.long	SYMBOL_NAME(sys_setpriority)
-		.long	SYMBOL_NAME(sys_ni_syscall)		/* was sys_profil */
-		.long	SYMBOL_NAME(sys_statfs)
-/* 100 */	.long	SYMBOL_NAME(sys_fstatfs)
-		.long	SYMBOL_NAME(sys_ni_syscall)
-		.long	SYMBOL_NAME(sys_socketcall)
-		.long	SYMBOL_NAME(sys_syslog)
-		.long	SYMBOL_NAME(sys_setitimer)
-/* 105 */	.long	SYMBOL_NAME(sys_getitimer)
-		.long	SYMBOL_NAME(sys_newstat)
-		.long	SYMBOL_NAME(sys_newlstat)
-		.long	SYMBOL_NAME(sys_newfstat)
-		.long	SYMBOL_NAME(sys_ni_syscall)		/* was sys_uname */
-/* 110 */	.long	SYMBOL_NAME(sys_ni_syscall)		/* was sys_iopl */
-		.long	SYMBOL_NAME(sys_vhangup)
-		.long	SYMBOL_NAME(sys_ni_syscall)
-		.long	SYMBOL_NAME(sys_syscall)		/* call a syscall */
-		.long	SYMBOL_NAME(sys_wait4)
-/* 115 */	.long	SYMBOL_NAME(sys_swapoff)
-		.long	SYMBOL_NAME(sys_sysinfo)
-		.long	SYMBOL_NAME(sys_ipc)
-		.long	SYMBOL_NAME(sys_fsync)
-		.long	SYMBOL_NAME(sys_sigreturn_wrapper)
-/* 120 */	.long	SYMBOL_NAME(sys_clone_wapper)
-		.long	SYMBOL_NAME(sys_setdomainname)
-		.long	SYMBOL_NAME(sys_newuname)
-		.long	SYMBOL_NAME(sys_ni_syscall)
-		.long	SYMBOL_NAME(sys_adjtimex)
-/* 125 */	.long	SYMBOL_NAME(sys_mprotect)
-		.long	SYMBOL_NAME(sys_sigprocmask)
-		.long	SYMBOL_NAME(sys_create_module)
-		.long	SYMBOL_NAME(sys_init_module)
-		.long	SYMBOL_NAME(sys_delete_module)
-/* 130 */	.long	SYMBOL_NAME(sys_get_kernel_syms)
-		.long	SYMBOL_NAME(sys_quotactl)
-		.long	SYMBOL_NAME(sys_getpgid)
-		.long	SYMBOL_NAME(sys_fchdir)
-		.long	SYMBOL_NAME(sys_bdflush)
-/* 135 */	.long	SYMBOL_NAME(sys_sysfs)
-		.long	SYMBOL_NAME(sys_personality)
-		.long	SYMBOL_NAME(sys_ni_syscall)		/* .long	_sys_afs_syscall */
-		.long	SYMBOL_NAME(sys_setfsuid16)
-		.long	SYMBOL_NAME(sys_setfsgid16)
-/* 140 */	.long	SYMBOL_NAME(sys_llseek)
-		.long	SYMBOL_NAME(sys_getdents)
-		.long	SYMBOL_NAME(sys_select)
-		.long	SYMBOL_NAME(sys_flock)
-		.long	SYMBOL_NAME(sys_msync)
-/* 145 */	.long	SYMBOL_NAME(sys_readv)
-		.long	SYMBOL_NAME(sys_writev)
-		.long	SYMBOL_NAME(sys_getsid)
-		.long	SYMBOL_NAME(sys_fdatasync)
-		.long	SYMBOL_NAME(sys_sysctl)
-/* 150 */	.long	SYMBOL_NAME(sys_mlock)
-		.long	SYMBOL_NAME(sys_munlock)
-		.long	SYMBOL_NAME(sys_mlockall)
-		.long	SYMBOL_NAME(sys_munlockall)
-		.long	SYMBOL_NAME(sys_sched_setparam)
-/* 155 */	.long	SYMBOL_NAME(sys_sched_getparam)
-		.long	SYMBOL_NAME(sys_sched_setscheduler)
-		.long	SYMBOL_NAME(sys_sched_getscheduler)
-		.long	SYMBOL_NAME(sys_sched_yield)
-		.long	SYMBOL_NAME(sys_sched_get_priority_max)
-/* 160 */	.long	SYMBOL_NAME(sys_sched_get_priority_min)
-		.long	SYMBOL_NAME(sys_sched_rr_get_interval)
-		.long	SYMBOL_NAME(sys_nanosleep)
-		.long	SYMBOL_NAME(sys_arm_mremap)
-		.long	SYMBOL_NAME(sys_setresuid16)
-/* 165 */	.long	SYMBOL_NAME(sys_getresuid16)
-		.long	SYMBOL_NAME(sys_ni_syscall)
-		.long	SYMBOL_NAME(sys_query_module)
-		.long	SYMBOL_NAME(sys_poll)
-		.long	SYMBOL_NAME(sys_nfsservctl)
-/* 170 */	.long	SYMBOL_NAME(sys_setresgid16)
-		.long	SYMBOL_NAME(sys_getresgid16)
-		.long	SYMBOL_NAME(sys_prctl)
-		.long	SYMBOL_NAME(sys_rt_sigreturn_wrapper)
-		.long	SYMBOL_NAME(sys_rt_sigaction)
-/* 175 */	.long	SYMBOL_NAME(sys_rt_sigprocmask)
-		.long	SYMBOL_NAME(sys_rt_sigpending)
-		.long	SYMBOL_NAME(sys_rt_sigtimedwait)
-		.long	SYMBOL_NAME(sys_rt_sigqueueinfo)
-		.long	SYMBOL_NAME(sys_rt_sigsuspend_wrapper)
-/* 180 */	.long	SYMBOL_NAME(sys_pread)
-		.long	SYMBOL_NAME(sys_pwrite)
-		.long	SYMBOL_NAME(sys_chown16)
-		.long	SYMBOL_NAME(sys_getcwd)
-		.long	SYMBOL_NAME(sys_capget)
-/* 185 */	.long	SYMBOL_NAME(sys_capset)
-		.long	SYMBOL_NAME(sys_sigaltstack_wrapper)
-		.long	SYMBOL_NAME(sys_sendfile)
-		.long	SYMBOL_NAME(sys_ni_syscall)
-		.long	SYMBOL_NAME(sys_ni_syscall)
-/* 190 */	.long	SYMBOL_NAME(sys_vfork_wrapper)
-		.long	SYMBOL_NAME(sys_getrlimit)
-		.long	SYMBOL_NAME(sys_mmap2)
-		.long	SYMBOL_NAME(sys_truncate64)
-		.long	SYMBOL_NAME(sys_ftruncate64)
-/* 195 */	.long	SYMBOL_NAME(sys_stat64)
-		.long	SYMBOL_NAME(sys_lstat64)
-		.long	SYMBOL_NAME(sys_fstat64)
-		.long	SYMBOL_NAME(sys_lchown)
-		.long	SYMBOL_NAME(sys_getuid)
-/* 200 */	.long	SYMBOL_NAME(sys_getgid)
-		.long	SYMBOL_NAME(sys_geteuid)
-		.long	SYMBOL_NAME(sys_getegid)
-		.long	SYMBOL_NAME(sys_setreuid)
-		.long	SYMBOL_NAME(sys_setregid)
-/* 205 */	.long	SYMBOL_NAME(sys_getgroups)
-		.long	SYMBOL_NAME(sys_setgroups)
-		.long	SYMBOL_NAME(sys_fchown)
-		.long	SYMBOL_NAME(sys_setresuid)
-		.long	SYMBOL_NAME(sys_getresuid)
-/* 210 */	.long	SYMBOL_NAME(sys_setresgid)
-		.long	SYMBOL_NAME(sys_getresgid)
-		.long	SYMBOL_NAME(sys_chown)
-		.long	SYMBOL_NAME(sys_setuid)
-		.long	SYMBOL_NAME(sys_setgid)
-/* 215 */	.long	SYMBOL_NAME(sys_setfsuid)
-		.long	SYMBOL_NAME(sys_setfsgid)
-		.long	SYMBOL_NAME(sys_getdents64)
-		.long	SYMBOL_NAME(sys_pivot_root)
-		.long	SYMBOL_NAME(sys_mincore)
-/* 220 */	.long	SYMBOL_NAME(sys_madvise)
-		.long	SYMBOL_NAME(sys_fcntl64)
-		.long	SYMBOL_NAME(sys_ni_syscall) /* TUX */
-		.long	SYMBOL_NAME(sys_ni_syscall) /* Security */
-		.long	SYMBOL_NAME(sys_gettid)
-/* 225 */	.long	SYMBOL_NAME(sys_readahead)
-		.long	SYMBOL_NAME(sys_setxattr)
-		.long	SYMBOL_NAME(sys_lsetxattr)
-		.long	SYMBOL_NAME(sys_fsetxattr)
-		.long	SYMBOL_NAME(sys_getxattr)
-/* 230 */	.long	SYMBOL_NAME(sys_lgetxattr)
-		.long	SYMBOL_NAME(sys_fgetxattr)
-		.long	SYMBOL_NAME(sys_listxattr)
-		.long	SYMBOL_NAME(sys_llistxattr)
-		.long	SYMBOL_NAME(sys_flistxattr)
-/* 235 */	.long	SYMBOL_NAME(sys_removexattr)
-		.long	SYMBOL_NAME(sys_lremovexattr)
-		.long	SYMBOL_NAME(sys_fremovexattr)
-		.long	SYMBOL_NAME(sys_tkill)
+/* 0 */		.long	sys_ni_syscall
+		.long	sys_exit
+		.long	sys_fork_wrapper
+		.long	sys_read
+		.long	sys_write
+/* 5 */		.long	sys_open
+		.long	sys_close
+		.long	sys_ni_syscall		/* was sys_waitpid */
+		.long	sys_creat
+		.long	sys_link
+/* 10 */	.long	sys_unlink
+		.long	sys_execve_wrapper
+		.long	sys_chdir
+		.long	sys_time		/* used by libc4 */
+		.long	sys_mknod
+/* 15 */	.long	sys_chmod
+		.long	sys_lchown16
+		.long	sys_ni_syscall		/* was sys_break */
+		.long	sys_ni_syscall		/* was sys_stat */
+		.long	sys_lseek
+/* 20 */	.long	sys_getpid
+		.long	sys_mount
+		.long	sys_oldumount		/* used by libc4 */
+		.long	sys_setuid16
+		.long	sys_getuid16
+/* 25 */	.long	sys_stime
+		.long	sys_ptrace
+		.long	sys_alarm		/* used by libc4 */
+		.long	sys_ni_syscall		/* was sys_fstat */
+		.long	sys_pause
+/* 30 */	.long	sys_utime		/* used by libc4 */
+		.long	sys_ni_syscall		/* was sys_stty */
+		.long	sys_ni_syscall		/* was sys_getty */
+		.long	sys_access
+		.long	sys_nice
+/* 35 */	.long	sys_ni_syscall		/* was sys_ftime */
+		.long	sys_sync
+		.long	sys_kill
+		.long	sys_rename
+		.long	sys_mkdir
+/* 40 */	.long	sys_rmdir
+		.long	sys_dup
+		.long	sys_pipe
+		.long	sys_times
+		.long	sys_ni_syscall		/* was sys_prof */
+/* 45 */	.long	sys_brk
+		.long	sys_setgid16
+		.long	sys_getgid16
+		.long	sys_ni_syscall		/* was sys_signal */
+		.long	sys_geteuid16
+/* 50 */	.long	sys_getegid16
+		.long	sys_acct
+		.long	sys_umount
+		.long	sys_ni_syscall		/* was sys_lock */
+		.long	sys_ioctl
+/* 55 */	.long	sys_fcntl
+		.long	sys_ni_syscall		/* was sys_mpx */
+		.long	sys_setpgid
+		.long	sys_ni_syscall		/* was sys_ulimit */
+		.long	sys_ni_syscall		/* was sys_olduname */
+/* 60 */	.long	sys_umask
+		.long	sys_chroot
+		.long	sys_ustat
+		.long	sys_dup2
+		.long	sys_getppid
+/* 65 */	.long	sys_getpgrp
+		.long	sys_setsid
+		.long	sys_sigaction
+		.long	sys_ni_syscall		/* was sys_sgetmask */
+		.long	sys_ni_syscall		/* was sys_ssetmask */
+/* 70 */	.long	sys_setreuid16
+		.long	sys_setregid16
+		.long	sys_sigsuspend_wrapper
+		.long	sys_sigpending
+		.long	sys_sethostname
+/* 75 */	.long	sys_setrlimit
+		.long	sys_old_getrlimit	/* used by libc4 */
+		.long	sys_getrusage
+		.long	sys_gettimeofday
+		.long	sys_settimeofday
+/* 80 */	.long	sys_getgroups16
+		.long	sys_setgroups16
+		.long	old_select		/* used by libc4 */
+		.long	sys_symlink
+		.long	sys_ni_syscall		/* was sys_lstat */
+/* 85 */	.long	sys_readlink
+		.long	sys_uselib
+		.long	sys_swapon
+		.long	sys_reboot
+		.long	old_readdir		/* used by libc4 */
+/* 90 */	.long	old_mmap		/* used by libc4 */
+		.long	sys_munmap
+		.long	sys_truncate
+		.long	sys_ftruncate
+		.long	sys_fchmod
+/* 95 */	.long	sys_fchown16
+		.long	sys_getpriority
+		.long	sys_setpriority
+		.long	sys_ni_syscall		/* was sys_profil */
+		.long	sys_statfs
+/* 100 */	.long	sys_fstatfs
+		.long	sys_ni_syscall
+		.long	sys_socketcall
+		.long	sys_syslog
+		.long	sys_setitimer
+/* 105 */	.long	sys_getitimer
+		.long	sys_newstat
+		.long	sys_newlstat
+		.long	sys_newfstat
+		.long	sys_ni_syscall		/* was sys_uname */
+/* 110 */	.long	sys_ni_syscall		/* was sys_iopl */
+		.long	sys_vhangup
+		.long	sys_ni_syscall
+		.long	sys_syscall		/* call a syscall */
+		.long	sys_wait4
+/* 115 */	.long	sys_swapoff
+		.long	sys_sysinfo
+		.long	sys_ipc
+		.long	sys_fsync
+		.long	sys_sigreturn_wrapper
+/* 120 */	.long	sys_clone_wapper
+		.long	sys_setdomainname
+		.long	sys_newuname
+		.long	sys_ni_syscall
+		.long	sys_adjtimex
+/* 125 */	.long	sys_mprotect
+		.long	sys_sigprocmask
+		.long	sys_create_module
+		.long	sys_init_module
+		.long	sys_delete_module
+/* 130 */	.long	sys_get_kernel_syms
+		.long	sys_quotactl
+		.long	sys_getpgid
+		.long	sys_fchdir
+		.long	sys_bdflush
+/* 135 */	.long	sys_sysfs
+		.long	sys_personality
+		.long	sys_ni_syscall		/* .long	_sys_afs_syscall */
+		.long	sys_setfsuid16
+		.long	sys_setfsgid16
+/* 140 */	.long	sys_llseek
+		.long	sys_getdents
+		.long	sys_select
+		.long	sys_flock
+		.long	sys_msync
+/* 145 */	.long	sys_readv
+		.long	sys_writev
+		.long	sys_getsid
+		.long	sys_fdatasync
+		.long	sys_sysctl
+/* 150 */	.long	sys_mlock
+		.long	sys_munlock
+		.long	sys_mlockall
+		.long	sys_munlockall
+		.long	sys_sched_setparam
+/* 155 */	.long	sys_sched_getparam
+		.long	sys_sched_setscheduler
+		.long	sys_sched_getscheduler
+		.long	sys_sched_yield
+		.long	sys_sched_get_priority_max
+/* 160 */	.long	sys_sched_get_priority_min
+		.long	sys_sched_rr_get_interval
+		.long	sys_nanosleep
+		.long	sys_arm_mremap
+		.long	sys_setresuid16
+/* 165 */	.long	sys_getresuid16
+		.long	sys_ni_syscall
+		.long	sys_query_module
+		.long	sys_poll
+		.long	sys_nfsservctl
+/* 170 */	.long	sys_setresgid16
+		.long	sys_getresgid16
+		.long	sys_prctl
+		.long	sys_rt_sigreturn_wrapper
+		.long	sys_rt_sigaction
+/* 175 */	.long	sys_rt_sigprocmask
+		.long	sys_rt_sigpending
+		.long	sys_rt_sigtimedwait
+		.long	sys_rt_sigqueueinfo
+		.long	sys_rt_sigsuspend_wrapper
+/* 180 */	.long	sys_pread
+		.long	sys_pwrite
+		.long	sys_chown16
+		.long	sys_getcwd
+		.long	sys_capget
+/* 185 */	.long	sys_capset
+		.long	sys_sigaltstack_wrapper
+		.long	sys_sendfile
+		.long	sys_ni_syscall
+		.long	sys_ni_syscall
+/* 190 */	.long	sys_vfork_wrapper
+		.long	sys_getrlimit
+		.long	sys_mmap2
+		.long	sys_truncate64
+		.long	sys_ftruncate64
+/* 195 */	.long	sys_stat64
+		.long	sys_lstat64
+		.long	sys_fstat64
+		.long	sys_lchown
+		.long	sys_getuid
+/* 200 */	.long	sys_getgid
+		.long	sys_geteuid
+		.long	sys_getegid
+		.long	sys_setreuid
+		.long	sys_setregid
+/* 205 */	.long	sys_getgroups
+		.long	sys_setgroups
+		.long	sys_fchown
+		.long	sys_setresuid
+		.long	sys_getresuid
+/* 210 */	.long	sys_setresgid
+		.long	sys_getresgid
+		.long	sys_chown
+		.long	sys_setuid
+		.long	sys_setgid
+/* 215 */	.long	sys_setfsuid
+		.long	sys_setfsgid
+		.long	sys_getdents64
+		.long	sys_pivot_root
+		.long	sys_mincore
+/* 220 */	.long	sys_madvise
+		.long	sys_fcntl64
+		.long	sys_ni_syscall /* TUX */
+		.long	sys_ni_syscall /* Security */
+		.long	sys_gettid
+/* 225 */	.long	sys_readahead
+		.long	sys_setxattr
+		.long	sys_lsetxattr
+		.long	sys_fsetxattr
+		.long	sys_getxattr
+/* 230 */	.long	sys_lgetxattr
+		.long	sys_fgetxattr
+		.long	sys_listxattr
+		.long	sys_llistxattr
+		.long	sys_flistxattr
+/* 235 */	.long	sys_removexattr
+		.long	sys_lremovexattr
+		.long	sys_fremovexattr
+		.long	sys_tkill
 __syscall_end:
 
 		.rept	NR_syscalls - (__syscall_end - __syscall_start) / 4
-			.long	SYMBOL_NAME(sys_ni_syscall)
+			.long	sys_ni_syscall
 		.endr
 #endif
diff -Nru a/arch/arm/kernel/entry-armo.S b/arch/arm/kernel/entry-armo.S
--- a/arch/arm/kernel/entry-armo.S	Sun Apr 28 20:10:33 2002
+++ b/arch/arm/kernel/entry-armo.S	Sun Apr 28 20:10:33 2002
@@ -147,7 +147,7 @@
 		mov	r0, r0
 		stmfd	sp!, {r0 - r3, ip, lr}
 		adr	r0, Lfiqmsg
-		bl	SYMBOL_NAME(printk)
+		bl	printk
 		ldmfd	sp!, {r0 - r3, ip, lr}
 		teqp	pc, #0x0c000001
 		mov	r0, r0
@@ -174,12 +174,12 @@
 		ldr	r4, .LC2
 		ldr	pc, [r4]			@ Call FP module USR entry point
 
-		.globl	SYMBOL_NAME(fpundefinstr)
-SYMBOL_NAME(fpundefinstr):				@ Called by FP module on undefined instr
+		.globl	fpundefinstr
+fpundefinstr:						@ Called by FP module on undefined instr
 		mov	r0, lr
 		mov	r1, sp
 		teqp	pc, #MODE_SVC
-		bl	SYMBOL_NAME(do_undefinstr)
+		bl	do_undefinstr
 		b	ret_from_exception		@ Normal FP exit
 
 __und_svc:	SVC_SAVE_ALL				@ Non-user mode
@@ -187,7 +187,7 @@
 		and	r2, lr, #3
 		sub	r0, r0, #4
 		mov	r1, sp
-		bl	SYMBOL_NAME(do_undefinstr)
+		bl	do_undefinstr
 		SVC_RESTORE_ALL
 
 #if defined CONFIG_FPE_NWFPE || defined CONFIG_FPE_FASTFPE
@@ -232,7 +232,7 @@
 		.word	0x0f0f0f00
 #endif
 
-.LC2:		.word	SYMBOL_NAME(fp_enter)
+.LC2:		.word	fp_enter
 
 /*=============================================================================
  * Prefetch abort handler
@@ -247,12 +247,12 @@
 		teqp	pc, #0x00000003		@ NOT a problem - doesnt change mode
 		mask_pc	r0, lr			@ Address of abort
 		mov	r1, sp			@ Tasks registers
-		bl	SYMBOL_NAME(do_PrefetchAbort)
+		bl	do_PrefetchAbort
 		teq	r0, #0			@ If non-zero, we believe this abort..
 		bne	ret_from_exception
 #ifdef DEBUG_UNDEF
 		adr	r0, t
-		bl	SYMBOL_NAME(printk)
+		bl	printk
 #endif
 		ldr	lr, [sp,#S_PC]		@ program to test this on.  I think its
 		b	.Lbug_undef		@ broken at the moment though!)
@@ -261,7 +261,7 @@
 		mov	r0, sp				@ Prefetch aborts are definitely *not*
 		mov	r1, #BAD_PREFETCH		@ allowed in non-user modes.  We cant
 		and	r2, lr, #3			@ recover from this problem.
-		b	SYMBOL_NAME(bad_mode)
+		b	bad_mode
 
 #ifdef DEBUG_UNDEF
 t:		.ascii "*** undef ***\r\n\0"
@@ -287,7 +287,7 @@
 		mov	r1, sp			@ Point to registers
 		mov	r2, #0x400
 		mov	lr, pc
-		bl	SYMBOL_NAME(do_excpt)
+		bl	do_excpt
 		b	ret_from_exception
 
 Laddrexcptn_not_user:
@@ -299,7 +299,7 @@
 		mask_pc	r0, lr
 		mov	r1, sp
 		orr	r2, r2, #0x400
-		bl	SYMBOL_NAME(do_excpt)
+		bl	do_excpt
 		ldmia	sp, {r0 - lr}		@ I cant remember the reason I changed this...
 		add	sp, sp, #15*4
 		movs	pc, lr
@@ -324,7 +324,7 @@
 		stmfd	sp!, {r0-r7}
 		mov	r0, sp
 		mov	r1, #BAD_ADDREXCPTN
-		b	SYMBOL_NAME(bad_mode)
+		b	bad_mode
 
 /*=============================================================================
  * Interrupt (IRQ) handler
@@ -382,7 +382,7 @@
 
 __irq_invalid:	mov	r0, sp
 		mov	r1, #BAD_IRQ
-		b	SYMBOL_NAME(bad_mode)
+		b	bad_mode
 
 /*=============================================================================
  * Data abort handler code
@@ -418,7 +418,7 @@
 Ldata_illegal_mode:
 		mov	r0, sp
 		mov	r1, #BAD_DATA
-		b	SYMBOL_NAME(bad_mode)
+		b	bad_mode
 
 Ldata_do:	mov	r3, sp
 		ldr	r4, [r0]		@ Get instruction
@@ -460,7 +460,7 @@
 #ifdef FAULT_CODE_LDRSTRPOST
 		orr	r2, r2, #FAULT_CODE_LDRSTRPOST
 #endif
-		b	SYMBOL_NAME(do_DataAbort)
+		b	do_DataAbort
 
 Ldata_ldrstr_numindex:
 		mov	r0, r4, lsr #14		@ Get Rn
@@ -476,7 +476,7 @@
 #ifdef FAULT_CODE_LDRSTRPRE
 		orr	r2, r2, #FAULT_CODE_LDRSTRPRE
 #endif
-		b	SYMBOL_NAME(do_DataAbort)
+		b	do_DataAbort
 
 Ldata_ldrstr_regindex:
 		mov	r0, r4, lsr #14		@ Get Rn
@@ -506,7 +506,7 @@
 #ifdef FAULT_CODE_LDRSTRREG
 		orr	r2, r2, #FAULT_CODE_LDRSTRREG
 #endif
-		b	SYMBOL_NAME(do_DataAbort)
+		b	do_DataAbort
 
 Ldata_ldmstm:
 		mov	r7, #0x11
@@ -543,7 +543,7 @@
 #ifdef FAULT_CODE_LDMSTM
 		orr	r2, r2, #FAULT_CODE_LDMSTM
 #endif
-		b	SYMBOL_NAME(do_DataAbort)
+		b	do_DataAbort
 
 Ldata_ldcstc_pre:
 		mov	r0, r4, lsr #14		@ Get Rn
@@ -559,7 +559,7 @@
 #ifdef FAULT_CODE_LDCSTC
 		orr	r2, r2, #FAULT_CODE_LDCSTC
 #endif
-		b	SYMBOL_NAME(do_DataAbort)
+		b	do_DataAbort
 
 
 /*
diff -Nru a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
--- a/arch/arm/kernel/entry-armv.S	Sun Apr 28 20:10:34 2002
+++ b/arch/arm/kernel/entry-armv.S	Sun Apr 28 20:10:34 2002
@@ -653,7 +653,7 @@
 		stmia	r4, {r5 - r7}			@ Save XXX pc, cpsr, old_r0
 		mov	r0, sp
 		and	r2, r6, #31			@ int mode
-		b	SYMBOL_NAME(bad_mode)
+		b	bad_mode
 
 #if defined CONFIG_FPE_NWFPE || defined CONFIG_FPE_FASTFPE
 		/* The FPE is always present */
@@ -725,7 +725,7 @@
 #endif
 		msr	cpsr_c, r9
 		mov	r2, sp
-		bl	SYMBOL_NAME(do_DataAbort)
+		bl	do_DataAbort
 		set_cpsr_c r0, #PSR_I_BIT | MODE_SVC
 		ldr	r0, [sp, #S_PSR]
 		msr	spsr, r0
@@ -778,7 +778,7 @@
 		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)
+		bl	schedule
 		set_cpsr_c r0, #PSR_I_BIT | MODE_SVC	@ disable IRQs
 		ldr	r0, [r8, #TI_FLAGS]		@ get new tasks TI_FLAGS
 		tst	r0, #_TIF_NEED_RESCHED
@@ -801,7 +801,7 @@
 
 		mov	r0, r5				@ unsigned long pc
 		mov	r1, sp				@ struct pt_regs *regs
-		bl	SYMBOL_NAME(do_undefinstr)
+		bl	do_undefinstr
 
 1:		set_cpsr_c r0, #PSR_I_BIT | MODE_SVC
 		ldr	lr, [sp, #S_PSR]		@ Get SVC cpsr
@@ -823,7 +823,7 @@
 		msr	cpsr_c, r9
 		mov	r0, r2				@ address (pc)
 		mov	r1, sp				@ regs
-		bl	SYMBOL_NAME(do_PrefetchAbort)	@ call abort handler
+		bl	do_PrefetchAbort		@ call abort handler
 		set_cpsr_c r0, #PSR_I_BIT | MODE_SVC
 		ldr	r0, [sp, #S_PSR]
 		msr	spsr, r0
@@ -834,11 +834,11 @@
 .LCund:		.word	__temp_und
 .LCabt:		.word	__temp_abt
 #ifdef MULTI_ABORT
-.LCprocfns:	.word	SYMBOL_NAME(processor)
+.LCprocfns:	.word	processor
 #endif
-.LCfp:		.word	SYMBOL_NAME(fp_enter)
+.LCfp:		.word	fp_enter
 #ifdef CONFIG_PREEMPT
-.LCirq_stat:	.word	SYMBOL_NAME(irq_stat)
+.LCirq_stat:	.word	irq_stat
 #endif
 
 		irq_prio_table
@@ -867,7 +867,7 @@
 		set_cpsr_c r2, #MODE_SVC		@ Enable interrupts
 		mov	r2, sp
 		adrsvc	al, lr, ret_from_exception
-		b	SYMBOL_NAME(do_DataAbort)
+		b	do_DataAbort
 
 		.align	5
 __irq_usr:	sub	sp, sp, #S_FRAME_SIZE
@@ -929,7 +929,7 @@
 		mov	r0, lr
 		mov	r1, sp
 		adrsvc	al, lr, ret_from_exception
-		b	SYMBOL_NAME(do_undefinstr)
+		b	do_undefinstr
 
 		.align	5
 __pabt_usr:	sub	sp, sp, #S_FRAME_SIZE		@ Allocate frame size in one go
@@ -944,7 +944,7 @@
 		set_cpsr_c r0, #MODE_SVC		@ Enable interrupts
 		mov	r0, r5				@ address (pc)
 		mov	r1, sp				@ regs
-		bl	SYMBOL_NAME(do_PrefetchAbort)	@ call abort handler
+		bl	do_PrefetchAbort		@ call abort handler
 		/* fall through */
 /*
  * This is the return code to user mode for abort handlers
@@ -1230,9 +1230,9 @@
 		.word	0				@ Saved spsr_abt
 		.word	-1				@ old_r0
 
-		.globl	SYMBOL_NAME(cr_alignment)
-		.globl	SYMBOL_NAME(cr_no_alignment)
-SYMBOL_NAME(cr_alignment):
+		.globl	cr_alignment
+		.globl	cr_no_alignment
+cr_alignment:
 		.space	4
-SYMBOL_NAME(cr_no_alignment):
+cr_no_alignment:
 		.space	4
diff -Nru a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S
--- a/arch/arm/kernel/entry-common.S	Sun Apr 28 20:10:30 2002
+++ b/arch/arm/kernel/entry-common.S	Sun Apr 28 20:10:30 2002
@@ -49,7 +49,7 @@
 	b	work_pending
 
 work_resched:
-	bl	SYMBOL_NAME(schedule)
+	bl	schedule
 /*
  * "slow" syscall return path.  "why" tells us if this was a real syscall.
  */
@@ -70,7 +70,7 @@
 __do_notify_resume:
 	mov	r0, sp				@ 'regs'
 	mov	r2, why				@ 'syscall'
-	b	SYMBOL_NAME(do_notify_resume)	@ note the bl above sets lr
+	b	do_notify_resume		@ note the bl above sets lr
 
 /*
  * This is how we return from a fork.
@@ -86,7 +86,7 @@
 	beq	ret_slow_syscall
 	mov	r1, sp
 	mov	r0, #1				@ trace exit [IP = 1]
-	bl	SYMBOL_NAME(syscall_trace)
+	bl	syscall_trace
 	b	ret_slow_syscall
 	
 
@@ -154,8 +154,8 @@
 2:	mov	why, #0				@ no longer a real syscall
 	cmp	scno, #ARMSWI_OFFSET
 	eor	r0, scno, #OS_NUMBER << 20	@ put OS number back
-	bcs	SYMBOL_NAME(arm_syscall)	
-	b	SYMBOL_NAME(sys_ni_syscall)	@ not private func
+	bcs	arm_syscall	
+	b	sys_ni_syscall			@ not private func
 
 	/*
 	 * This is the really slow path.  We're going to be doing
@@ -164,7 +164,7 @@
 __sys_trace:
 	add	r1, sp, #S_OFF
 	mov	r0, #0				@ trace entry [IP = 0]
-	bl	SYMBOL_NAME(syscall_trace)
+	bl	syscall_trace
 
 	adrsvc	al, lr, __sys_trace_return	@ return address
 	add	r1, sp, #S_R0 + S_OFF		@ pointer to regs
@@ -177,14 +177,14 @@
 	str	r0, [sp, #S_R0 + S_OFF]!	@ save returned r0
 	mov	r1, sp
 	mov	r0, #1				@ trace exit [IP = 1]
-	bl	SYMBOL_NAME(syscall_trace)
+	bl	syscall_trace
 	b	ret_slow_syscall
 
 	.align	5
 #ifdef CONFIG_ALIGNMENT_TRAP
 	.type	__cr_alignment, #object
 __cr_alignment:
-	.word	SYMBOL_NAME(cr_alignment)
+	.word	cr_alignment
 #endif
 
 	.type	sys_call_table, #object
@@ -197,7 +197,7 @@
 @ r0 = syscall number
 @ r5 = syscall table
 		.type	sys_syscall, #function
-SYMBOL_NAME(sys_syscall):
+sys_syscall:
 		eor	scno, r0, #OS_NUMBER << 20
 		cmp	scno, #NR_syscalls	@ check range
 		stmleia	sp, {r5, r6}		@ shuffle args
@@ -210,35 +210,35 @@
 
 sys_fork_wrapper:
 		add	r0, sp, #S_OFF
-		b	SYMBOL_NAME(sys_fork)
+		b	sys_fork
 
 sys_vfork_wrapper:
 		add	r0, sp, #S_OFF
-		b	SYMBOL_NAME(sys_vfork)
+		b	sys_vfork
 
 sys_execve_wrapper:
 		add	r3, sp, #S_OFF
-		b	SYMBOL_NAME(sys_execve)
+		b	sys_execve
 
 sys_clone_wapper:
 		add	r2, sp, #S_OFF
-		b	SYMBOL_NAME(sys_clone)
+		b	sys_clone
 
 sys_sigsuspend_wrapper:
 		add	r3, sp, #S_OFF
-		b	SYMBOL_NAME(sys_sigsuspend)
+		b	sys_sigsuspend
 
 sys_rt_sigsuspend_wrapper:
 		add	r2, sp, #S_OFF
-		b	SYMBOL_NAME(sys_rt_sigsuspend)
+		b	sys_rt_sigsuspend
 
 sys_sigreturn_wrapper:
 		add	r0, sp, #S_OFF
-		b	SYMBOL_NAME(sys_sigreturn)
+		b	sys_sigreturn
 
 sys_rt_sigreturn_wrapper:
 		add	r0, sp, #S_OFF
-		b	SYMBOL_NAME(sys_rt_sigreturn)
+		b	sys_rt_sigreturn
 
 sys_sigaltstack_wrapper:
 		ldr	r2, [sp, #S_OFF + S_SP]
diff -Nru a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S
--- a/arch/arm/kernel/head.S	Sun Apr 28 20:10:30 2002
+++ b/arch/arm/kernel/head.S	Sun Apr 28 20:10:30 2002
@@ -35,8 +35,8 @@
 #error TEXTADDR must start at 0xXXXX8000
 #endif
 
-		.globl	SYMBOL_NAME(swapper_pg_dir)
-		.equ	SYMBOL_NAME(swapper_pg_dir), TEXTADDR - 0x4000
+		.globl	swapper_pg_dir
+		.equ	swapper_pg_dir, TEXTADDR - 0x4000
 
 		.macro	pgtbl, reg, rambase
 		adr	\reg, stext
@@ -144,13 +144,13 @@
 
 		.type	__switch_data, %object
 __switch_data:	.long	__mmap_switched
-		.long	SYMBOL_NAME(compat)
-		.long	SYMBOL_NAME(__bss_start)
-		.long	SYMBOL_NAME(_end)
-		.long	SYMBOL_NAME(processor_id)
-		.long	SYMBOL_NAME(__machine_arch_type)
-		.long	SYMBOL_NAME(cr_alignment)
-		.long	SYMBOL_NAME(init_thread_union)+8192
+		.long	compat
+		.long	__bss_start
+		.long	_end
+		.long	processor_id
+		.long	__machine_arch_type
+		.long	cr_alignment
+		.long	init_thread_union+8192
 
 		.type	__ret, %function
 __ret:		ldr	lr, __switch_data
@@ -187,7 +187,7 @@
 #endif
 		bic	r2, r0, #2			@ Clear 'A' bit
 		stmia	r8, {r0, r2}			@ Save control register values
-		b	SYMBOL_NAME(start_kernel)
+		b	start_kernel
 
 
 
diff -Nru a/arch/arm/lib/backtrace.S b/arch/arm/lib/backtrace.S
--- a/arch/arm/lib/backtrace.S	Sun Apr 28 20:10:32 2002
+++ b/arch/arm/lib/backtrace.S	Sun Apr 28 20:10:32 2002
@@ -60,7 +60,7 @@
 		adr	r0, .Lfe
 		mov	r1, save
 		bic	r2, r2, mask
-		bl	SYMBOL_NAME(printk)	@ print pc and link register
+		bl	printk			@ print pc and link register
 
 		sub	r0, frame, #16
 1002:		ldr	r1, [save, #4]		@ get instruction at function+4
@@ -89,7 +89,7 @@
 		.align	0
 1004:		ldr	r0, =.Lbad
 		mov	r1, frame
-		bl	SYMBOL_NAME(printk)
+		bl	printk
 		LOADREGS(fd, sp!, {r4 - r8, pc})
 		.ltorg
 		.previous
@@ -121,12 +121,12 @@
 		ldr	r2, [stack], #-4
 		mov	r1, reg
 		adr	r0, .Lfp
-		bl	SYMBOL_NAME(printk)
+		bl	printk
 2:		subs	reg, reg, #1
 		bpl	1b
 		teq	r7, #0
 		adrne	r0, .Lcr
-		blne	SYMBOL_NAME(printk)
+		blne	printk
 		mov	r0, stack
 		LOADREGS(fd, sp!, {instr, reg, stack, r7, pc})
 
diff -Nru a/arch/arm/lib/delay.S b/arch/arm/lib/delay.S
--- a/arch/arm/lib/delay.S	Sun Apr 28 20:10:36 2002
+++ b/arch/arm/lib/delay.S	Sun Apr 28 20:10:36 2002
@@ -11,7 +11,7 @@
 #include <asm/assembler.h>
 		.text
 
-LC0:		.word	SYMBOL_NAME(loops_per_jiffy)
+LC0:		.word	loops_per_jiffy
 
 /*
  * 0 <= r0 <= 2000
@@ -53,5 +53,5 @@
 		RETINSTR(movls,pc,lr)
 		subs	r0, r0, #1
 #endif
-		bhi	SYMBOL_NAME(__delay)
+		bhi	__delay
 		RETINSTR(mov,pc,lr)
diff -Nru a/arch/arm/lib/floppydma.S b/arch/arm/lib/floppydma.S
--- a/arch/arm/lib/floppydma.S	Sun Apr 28 20:10:35 2002
+++ b/arch/arm/lib/floppydma.S	Sun Apr 28 20:10:35 2002
@@ -11,16 +11,16 @@
 #include <asm/assembler.h>
 		.text
 
-		.global	SYMBOL_NAME(floppy_fiqin_end)
+		.global	floppy_fiqin_end
 ENTRY(floppy_fiqin_start)
 		subs	r9, r9, #1
 		ldrgtb	r12, [r11, #-4]
 		ldrleb	r12, [r11], #0
 		strb	r12, [r10], #1
 		subs	pc, lr, #4
-SYMBOL_NAME(floppy_fiqin_end):
+floppy_fiqin_end:
 
-		.global	SYMBOL_NAME(floppy_fiqout_end)
+		.global	floppy_fiqout_end
 ENTRY(floppy_fiqout_start)
 		subs	r9, r9, #1
 		ldrgeb	r12, [r10], #1
@@ -29,4 +29,4 @@
 		subles	pc, lr, #4
 		strb	r12, [r11, #-4]
 		subs	pc, lr, #4
-SYMBOL_NAME(floppy_fiqout_end):
+floppy_fiqout_end:
diff -Nru a/arch/arm/lib/io-acorn.S b/arch/arm/lib/io-acorn.S
--- a/arch/arm/lib/io-acorn.S	Sun Apr 28 20:10:32 2002
+++ b/arch/arm/lib/io-acorn.S	Sun Apr 28 20:10:32 2002
@@ -52,7 +52,7 @@
 ENTRY(outsl)
 		adr	r0, .iosl_warning
 		mov	r1, lr
-		b	SYMBOL_NAME(printk)
+		b	printk
 
 @ Purpose: write a memc register
 @ Proto  : void memc_write(int register, int value);
diff -Nru a/arch/arm/lib/io-readsw-armv3.S b/arch/arm/lib/io-readsw-armv3.S
--- a/arch/arm/lib/io-readsw-armv3.S	Sun Apr 28 20:10:30 2002
+++ b/arch/arm/lib/io-readsw-armv3.S	Sun Apr 28 20:10:30 2002
@@ -14,7 +14,7 @@
 .insw_bad_alignment:
 		adr	r0, .insw_bad_align_msg
 		mov	r2, lr
-		b	SYMBOL_NAME(panic)
+		b	panic
 .insw_bad_align_msg:
 		.asciz	"insw: bad buffer alignment (0x%p, lr=0x%08lX)\n"
 		.align
diff -Nru a/arch/arm/lib/io-readsw-armv4.S b/arch/arm/lib/io-readsw-armv4.S
--- a/arch/arm/lib/io-readsw-armv4.S	Sun Apr 28 20:10:37 2002
+++ b/arch/arm/lib/io-readsw-armv4.S	Sun Apr 28 20:10:37 2002
@@ -14,7 +14,7 @@
 .insw_bad_alignment:
 		adr	r0, .insw_bad_align_msg
 		mov	r2, lr
-		b	SYMBOL_NAME(panic)
+		b	panic
 .insw_bad_align_msg:
 		.asciz	"insw: bad buffer alignment (0x%p, lr=0x%08lX)\n"
 		.align
diff -Nru a/arch/arm/lib/io-writesw-armv3.S b/arch/arm/lib/io-writesw-armv3.S
--- a/arch/arm/lib/io-writesw-armv3.S	Sun Apr 28 20:10:37 2002
+++ b/arch/arm/lib/io-writesw-armv3.S	Sun Apr 28 20:10:37 2002
@@ -14,7 +14,7 @@
 .outsw_bad_alignment:
 		adr	r0, .outsw_bad_align_msg
 		mov	r2, lr
-		b	SYMBOL_NAME(panic)
+		b	panic
 .outsw_bad_align_msg:
 		.asciz	"outsw: bad buffer alignment (0x%p, lr=0x%08lX)\n"
 		.align
diff -Nru a/arch/arm/lib/io-writesw-armv4.S b/arch/arm/lib/io-writesw-armv4.S
--- a/arch/arm/lib/io-writesw-armv4.S	Sun Apr 28 20:10:38 2002
+++ b/arch/arm/lib/io-writesw-armv4.S	Sun Apr 28 20:10:38 2002
@@ -14,7 +14,7 @@
 .outsw_bad_alignment:
 		adr	r0, .outsw_bad_align_msg
 		mov	r2, lr
-		b	SYMBOL_NAME(panic)
+		b	panic
 .outsw_bad_align_msg:
 		.asciz	"outsw: bad buffer alignment (0x%p, lr=0x%08lX)\n"
 		.align
diff -Nru a/arch/arm/lib/uaccess-armo.S b/arch/arm/lib/uaccess-armo.S
--- a/arch/arm/lib/uaccess-armo.S	Sun Apr 28 20:10:37 2002
+++ b/arch/arm/lib/uaccess-armo.S	Sun Apr 28 20:10:37 2002
@@ -15,8 +15,8 @@
 
 		.text
 
-		.globl	SYMBOL_NAME(uaccess_user)
-SYMBOL_NAME(uaccess_user):
+		.globl	uaccess_user
+uaccess_user:
 		.word	uaccess_user_put_byte
 		.word	uaccess_user_get_byte
 		.word	uaccess_user_put_half
@@ -84,8 +84,8 @@
 
 
 
-		.globl	SYMBOL_NAME(uaccess_kernel)
-SYMBOL_NAME(uaccess_kernel):
+		.globl	uaccess_kernel
+uaccess_kernel:
 		.word	uaccess_kernel_put_byte
 		.word	uaccess_kernel_get_byte
 		.word	uaccess_kernel_put_half
@@ -154,7 +154,7 @@
  */
 uaccess_kernel_copy:
 		stmfd	sp!, {lr}
-		bl	SYMBOL_NAME(memcpy)
+		bl	memcpy
 		mov	r0, #0
 		ldmfd	sp!, {pc}^
 
diff -Nru a/arch/arm/lib/uaccess.S b/arch/arm/lib/uaccess.S
--- a/arch/arm/lib/uaccess.S	Sun Apr 28 20:10:30 2002
+++ b/arch/arm/lib/uaccess.S	Sun Apr 28 20:10:30 2002
@@ -544,7 +544,7 @@
 		ldr	r1, [sp], #4			@ unsigned long count
 		subs	r4, r1, r2			@ bytes left to copy
 		movne	r1, r4
-		blne	SYMBOL_NAME(__memzero)
+		blne	__memzero
 		mov	r0, r4
 		LOADREGS(fd,sp!, {r4 - r7, pc})
 		.previous
diff -Nru a/arch/arm/mach-arc/head.S b/arch/arm/mach-arc/head.S
--- a/arch/arm/mach-arc/head.S	Sun Apr 28 20:10:37 2002
+++ b/arch/arm/mach-arc/head.S	Sun Apr 28 20:10:37 2002
@@ -13,8 +13,8 @@
 #include <linux/linkage.h>
 #include <asm/mach-types.h>
 
-		.globl	SYMBOL_NAME(swapper_pg_dir)
-		.equ	SYMBOL_NAME(swapper_pg_dir),	0x0207d000
+		.globl	swapper_pg_dir
+		.equ	swapper_pg_dir,	0x0207d000
 
 /*
  * Entry point.
@@ -38,14 +38,14 @@
 		str	r0, [r5]
 
 		mov	fp, #0
-		b	SYMBOL_NAME(start_kernel)
+		b	start_kernel
 
-LC0:		.word	SYMBOL_NAME(_stext)
-		.word	SYMBOL_NAME(__bss_start)		@ r2
-		.word	SYMBOL_NAME(_end)			@ r3
-		.word	SYMBOL_NAME(processor_id)		@ r4
-		.word	SYMBOL_NAME(__machine_arch_type)	@ r5
-		.word	SYMBOL_NAME(init_task_union)+8192	@ sp
+LC0:		.word	_stext
+		.word	__bss_start		@ r2
+		.word	_end			@ r3
+		.word	processor_id		@ r4
+		.word	__machine_arch_type	@ r5
+		.word	init_task_union+8192	@ sp
 arm2_id:	.long	0x41560200
 arm250_id:	.long	0x41560250
 		.align
diff -Nru a/arch/arm/mach-sa1100/sleep.S b/arch/arm/mach-sa1100/sleep.S
--- a/arch/arm/mach-sa1100/sleep.S	Sun Apr 28 20:10:33 2002
+++ b/arch/arm/mach-sa1100/sleep.S	Sun Apr 28 20:10:33 2002
@@ -72,12 +72,12 @@
 	@ delay 90us and set CPU PLL to lowest speed
 	@ fixes resume problem on high speed SA1110
 	mov	r0, #90
-	bl	SYMBOL_NAME(udelay)
+	bl	udelay
 	ldr	r0, =PPCR
 	mov	r1, #0
 	str	r1, [r0]
 	mov	r0, #90
-	bl	SYMBOL_NAME(udelay)
+	bl	udelay
 
 
 /* setup up register contents for jump to page containing SA1110 SDRAM controller bug fix suspend code
diff -Nru a/arch/arm/mm/proc-arm2,3.S b/arch/arm/mm/proc-arm2,3.S
--- a/arch/arm/mm/proc-arm2,3.S	Sun Apr 28 20:10:37 2002
+++ b/arch/arm/mm/proc-arm2,3.S	Sun Apr 28 20:10:37 2002
@@ -189,7 +189,7 @@
  * Params  : pgd	New page tables/MEMC mapping
  * Purpose : update MEMC hardware with new mapping
  */
-		.word	SYMBOL_NAME(page_nr)
+		.word	page_nr
 _arm3_set_pgd:	mcr	p15, 0, r1, c1, c0, 0		@ flush cache
 _arm2_set_pgd:	stmfd	sp!, {lr}
 		ldr	r1, _arm3_set_pgd - 4
@@ -285,8 +285,8 @@
  * Purpose : Function pointers used to access above functions - all calls
  *	     come through these
  */
-		.globl	SYMBOL_NAME(arm2_processor_functions)
-SYMBOL_NAME(arm2_processor_functions):
+		.globl	arm2_processor_functions
+arm2_processor_functions:
 		.word	_arm2_3_check_bugs
 		.word	_arm2_proc_init
 		.word	_arm2_proc_fin
@@ -298,8 +298,8 @@
 		.long	armvlsi_name
 		.long	_arm2_name
 
-		.globl	SYMBOL_NAME(arm250_processor_functions)
-SYMBOL_NAME(arm250_processor_functions):
+		.globl	arm250_processor_functions
+arm250_processor_functions:
 		.word	_arm2_3_check_bugs
 		.word	_arm2_proc_init
 		.word	_arm2_proc_fin
@@ -311,8 +311,8 @@
 		.long	armvlsi_name
 		.long	_arm250_name
 
-		.globl	SYMBOL_NAME(arm3_processor_functions)
-SYMBOL_NAME(arm3_processor_functions):
+		.globl	arm3_processor_functions
+arm3_processor_functions:
 		.word	_arm2_3_check_bugs
 		.word	_arm3_proc_init
 		.word	_arm3_proc_fin
@@ -340,7 +340,7 @@
 		.long	arm2_elf_name
 		.long	0
 		.long	cpu_arm2_info
-		.long	SYMBOL_NAME(arm2_processor_functions)
+		.long	arm2_processor_functions
 		.long	0
 		.long	0
 
@@ -352,7 +352,7 @@
 		.long	arm3_elf_name
 		.long	0
 		.long	cpu_arm250_info
-		.long	SYMBOL_NAME(arm250_processor_functions)
+		.long	arm250_processor_functions
 		.long	0
 		.long	0
 
@@ -364,7 +364,7 @@
 		.long	arm3_elf_name
 		.long	0
 		.long	cpu_arm3_info
-		.long	SYMBOL_NAME(arm3_processor_functions)
+		.long	arm3_processor_functions
 		.long	0
 		.long	0
 
diff -Nru a/arch/cris/Config.help b/arch/cris/Config.help
--- a/arch/cris/Config.help	Sun Apr 28 20:10:31 2002
+++ b/arch/cris/Config.help	Sun Apr 28 20:10:31 2002
@@ -392,3 +392,26 @@
 CONFIG_ETRAX_RESCUE_SER3
   Use serial port 3 as the rescue port.
 
+CONFIG_ETRAX_POWERBUTTON_BIT
+  Configure where power button is connected.
+
+CONFIG_ETRAX_ROOT_DEVICE
+  Specifies the device that should be mounted as root file system
+  when booting from flash. The axisflashmap driver adds an additional
+  mtd partition for the appended root file system image, so this option
+  should normally be the mtdblock device for the partition after the
+  last partition in the partition table.
+
+CONFIG_ETRAX_SHUTDOWN_BIT
+  Configure what pin on CSPO-port that is used for controlling power
+  supply.
+
+CONFIG_ETRAX_SOFT_SHUTDOWN
+  Enable this if Etrax is used with a power-supply that can be turned
+  off and on with PS_ON signal. Gives the possibility to detect
+  powerbutton and then do a power off after unmounting disks.
+
+CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
+  By enabling this you make sure that the watchdog does not bite while
+  printing oopses. Recommended for development systems but not for
+  production releases.
diff -Nru a/arch/cris/defconfig b/arch/cris/defconfig
--- a/arch/cris/defconfig	Sun Apr 28 20:10:34 2002
+++ b/arch/cris/defconfig	Sun Apr 28 20:10:34 2002
@@ -450,7 +450,7 @@
 # CONFIG_JOLIET is not set
 # CONFIG_MINIX_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/cris/drivers/Config.help b/arch/cris/drivers/Config.help
--- a/arch/cris/drivers/Config.help	Sun Apr 28 20:10:36 2002
+++ b/arch/cris/drivers/Config.help	Sun Apr 28 20:10:36 2002
@@ -572,3 +572,39 @@
 CONFIG_ETRAX_PARALLEL_PORT1
   Say Y here to enable parallel port 1.
 
+CONFIG_ETRAX_ETHERNET_LPSLAVE
+  This option enables a slave ETRAX 100 or ETRAX 100LX, connected to a
+  master ETRAX 100 or ETRAX 100LX through par0 and par1, to act as an
+  Ethernet controller.
+
+CONFIG_ETRAX_ETHERNET_LPSLAVE_HAS_LEDS
+  Enable if the slave has it's own LEDs.
+
+CONFIG_ETRAX_IDE
+  Enable this to get support for ATA/IDE.  You can't use parallel
+  ports or SCSI ports at the same time.
+
+CONFIG_ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY
+  Selecting LED_on_when_link will light the LED when there is a
+  connection and will flash off when there is activity.
+
+  Selecting LED_on_when_activity will light the LED only when
+  there is activity.
+
+  This setting will also affect the behaviour of other activity LEDs
+  e.g. Bluetooth.
+
+CONFIG_ETRAX_NETWORK_LED_ON_WHEN_LINK
+  Selecting LED_on_when_link will light the LED when there is a
+  connection and will flash off when there is activity.
+
+  Selecting LED_on_when_activity will light the LED only when
+  there is activity.
+
+  This setting will also affect the behaviour of other activity LEDs
+  e.g. Bluetooth.
+
+CONFIG_ETRAX_SERIAL_PORT0
+  Enables the ETRAX 100 serial driver for ser0 (ttyS0)
+  Normally you want this on, unless you use external DMA 1 that uses
+  the same DMA channels.
diff -Nru a/arch/cris/drivers/lpslave/e100lpslavenet.c b/arch/cris/drivers/lpslave/e100lpslavenet.c
--- a/arch/cris/drivers/lpslave/e100lpslavenet.c	Sun Apr 28 20:10:35 2002
+++ b/arch/cris/drivers/lpslave/e100lpslavenet.c	Sun Apr 28 20:10:35 2002
@@ -313,7 +313,7 @@
 		IO_STATE(R_PAR0_CONFIG, iautofd, noninv)    |
           /* Not used in reverse direction, don't care */
 		IO_STATE(R_PAR0_CONFIG, istrb,   noninv)    |
-          /* Not connected, don't care /
+          /* Not connected, don't care */
 		IO_STATE(R_PAR0_CONFIG, iinit,   noninv)    |
           /* perror is GND and reverse wants 0, noninv */
 		IO_STATE(R_PAR0_CONFIG, iperr,   noninv)    |
diff -Nru a/arch/cris/kernel/entry.S b/arch/cris/kernel/entry.S
--- a/arch/cris/kernel/entry.S	Sun Apr 28 20:10:35 2002
+++ b/arch/cris/kernel/entry.S	Sun Apr 28 20:10:35 2002
@@ -789,233 +789,233 @@
 
 	.section .rodata,"a"
 sys_call_table:	
-	.long SYMBOL_NAME(sys_ni_syscall)	/* 0  -  old "setup()" system call*/
-	.long SYMBOL_NAME(sys_exit)
-	.long SYMBOL_NAME(sys_fork)
-	.long SYMBOL_NAME(sys_read)
-	.long SYMBOL_NAME(sys_write)
-	.long SYMBOL_NAME(sys_open)		/* 5 */
-	.long SYMBOL_NAME(sys_close)
-	.long SYMBOL_NAME(sys_waitpid)
-	.long SYMBOL_NAME(sys_creat)
-	.long SYMBOL_NAME(sys_link)
-	.long SYMBOL_NAME(sys_unlink)		/* 10 */
-	.long SYMBOL_NAME(sys_execve)
-	.long SYMBOL_NAME(sys_chdir)
-	.long SYMBOL_NAME(sys_time)
-	.long SYMBOL_NAME(sys_mknod)
-	.long SYMBOL_NAME(sys_chmod)		/* 15 */
-	.long SYMBOL_NAME(sys_lchown16)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* old break syscall holder */
-	.long SYMBOL_NAME(sys_stat)
-	.long SYMBOL_NAME(sys_lseek)
-	.long SYMBOL_NAME(sys_getpid)		/* 20 */
-	.long SYMBOL_NAME(sys_mount)
-	.long SYMBOL_NAME(sys_oldumount)
-	.long SYMBOL_NAME(sys_setuid16)
-	.long SYMBOL_NAME(sys_getuid16)
-	.long SYMBOL_NAME(sys_stime)		/* 25 */
-	.long SYMBOL_NAME(sys_ptrace)
-	.long SYMBOL_NAME(sys_alarm)
-	.long SYMBOL_NAME(sys_fstat)
-	.long SYMBOL_NAME(sys_pause)
-	.long SYMBOL_NAME(sys_utime)		/* 30 */
-	.long SYMBOL_NAME(sys_ni_syscall)	/* old stty syscall holder */
-	.long SYMBOL_NAME(sys_ni_syscall)	/* old gtty syscall holder */
-	.long SYMBOL_NAME(sys_access)
-	.long SYMBOL_NAME(sys_nice)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* 35  old ftime syscall holder */
-	.long SYMBOL_NAME(sys_sync)
-	.long SYMBOL_NAME(sys_kill)
-	.long SYMBOL_NAME(sys_rename)
-	.long SYMBOL_NAME(sys_mkdir)
-	.long SYMBOL_NAME(sys_rmdir)		/* 40 */
-	.long SYMBOL_NAME(sys_dup)
-	.long SYMBOL_NAME(sys_pipe)
-	.long SYMBOL_NAME(sys_times)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* old prof syscall holder */
-	.long SYMBOL_NAME(sys_brk)		/* 45 */
-	.long SYMBOL_NAME(sys_setgid16)
-	.long SYMBOL_NAME(sys_getgid16)
-	.long SYMBOL_NAME(sys_signal)
-	.long SYMBOL_NAME(sys_geteuid16)
-	.long SYMBOL_NAME(sys_getegid16)	/* 50 */
-	.long SYMBOL_NAME(sys_acct)
-	.long SYMBOL_NAME(sys_umount)		/* recycled never used phys() */
-	.long SYMBOL_NAME(sys_ni_syscall)	/* old lock syscall holder */
-	.long SYMBOL_NAME(sys_ioctl)
-	.long SYMBOL_NAME(sys_fcntl)		/* 55 */
-	.long SYMBOL_NAME(sys_ni_syscall)	/* old mpx syscall holder */
-	.long SYMBOL_NAME(sys_setpgid)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* old ulimit syscall holder */
-	.long SYMBOL_NAME(sys_ni_syscall)       /* old sys_olduname holder */
-	.long SYMBOL_NAME(sys_umask)		/* 60 */
-	.long SYMBOL_NAME(sys_chroot)
-	.long SYMBOL_NAME(sys_ustat)
-	.long SYMBOL_NAME(sys_dup2)
-	.long SYMBOL_NAME(sys_getppid)
-	.long SYMBOL_NAME(sys_getpgrp)		/* 65 */
-	.long SYMBOL_NAME(sys_setsid)
-	.long SYMBOL_NAME(sys_sigaction)
-	.long SYMBOL_NAME(sys_sgetmask)
-	.long SYMBOL_NAME(sys_ssetmask)
-	.long SYMBOL_NAME(sys_setreuid16)	/* 70 */
-	.long SYMBOL_NAME(sys_setregid16)
-	.long SYMBOL_NAME(sys_sigsuspend)
-	.long SYMBOL_NAME(sys_sigpending)
-	.long SYMBOL_NAME(sys_sethostname)
-	.long SYMBOL_NAME(sys_setrlimit)	/* 75 */
-	.long SYMBOL_NAME(sys_old_getrlimit)
-	.long SYMBOL_NAME(sys_getrusage)
-	.long SYMBOL_NAME(sys_gettimeofday)
-	.long SYMBOL_NAME(sys_settimeofday)
-	.long SYMBOL_NAME(sys_getgroups16)	/* 80 */
-	.long SYMBOL_NAME(sys_setgroups16)
-	.long SYMBOL_NAME(sys_select)           /* was old_select in Linux/E100 */
-	.long SYMBOL_NAME(sys_symlink)
-	.long SYMBOL_NAME(sys_lstat)
-	.long SYMBOL_NAME(sys_readlink)		/* 85 */
-	.long SYMBOL_NAME(sys_uselib)
-	.long SYMBOL_NAME(sys_swapon)
-	.long SYMBOL_NAME(sys_reboot)
-	.long SYMBOL_NAME(old_readdir)
-	.long SYMBOL_NAME(old_mmap)		/* 90 */
-	.long SYMBOL_NAME(sys_munmap)
-	.long SYMBOL_NAME(sys_truncate)
-	.long SYMBOL_NAME(sys_ftruncate)
-	.long SYMBOL_NAME(sys_fchmod)
-	.long SYMBOL_NAME(sys_fchown16)		/* 95 */
-	.long SYMBOL_NAME(sys_getpriority)
-	.long SYMBOL_NAME(sys_setpriority)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* old profil syscall holder */
-	.long SYMBOL_NAME(sys_statfs)
-	.long SYMBOL_NAME(sys_fstatfs)		/* 100 */
-	.long SYMBOL_NAME(sys_ni_syscall)       /* sys_ioperm in i386 */
-	.long SYMBOL_NAME(sys_socketcall)
-	.long SYMBOL_NAME(sys_syslog)
-	.long SYMBOL_NAME(sys_setitimer)
-	.long SYMBOL_NAME(sys_getitimer)	/* 105 */
-	.long SYMBOL_NAME(sys_newstat)
-	.long SYMBOL_NAME(sys_newlstat)
-	.long SYMBOL_NAME(sys_newfstat)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* old sys_uname holder */
-	.long SYMBOL_NAME(sys_ni_syscall)	/* sys_iopl in i386 */
-	.long SYMBOL_NAME(sys_vhangup)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* old "idle" system call */
-	.long SYMBOL_NAME(sys_ni_syscall)	/* vm86old in i386 */
-	.long SYMBOL_NAME(sys_wait4)
-	.long SYMBOL_NAME(sys_swapoff)		/* 115 */
-	.long SYMBOL_NAME(sys_sysinfo)
-	.long SYMBOL_NAME(sys_ipc)
-	.long SYMBOL_NAME(sys_fsync)
-	.long SYMBOL_NAME(sys_sigreturn)
-	.long SYMBOL_NAME(sys_clone)		/* 120 */
-	.long SYMBOL_NAME(sys_setdomainname)
-	.long SYMBOL_NAME(sys_newuname)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* TODO sys_modify_ldt - do something ?*/
-	.long SYMBOL_NAME(sys_adjtimex)
-	.long SYMBOL_NAME(sys_mprotect)		/* 125 */
-	.long SYMBOL_NAME(sys_sigprocmask)
-	.long SYMBOL_NAME(sys_create_module)
-	.long SYMBOL_NAME(sys_init_module)
-	.long SYMBOL_NAME(sys_delete_module)
-	.long SYMBOL_NAME(sys_get_kernel_syms)	/* 130 */
-	.long SYMBOL_NAME(sys_quotactl)
-	.long SYMBOL_NAME(sys_getpgid)
-	.long SYMBOL_NAME(sys_fchdir)
-	.long SYMBOL_NAME(sys_bdflush)
-	.long SYMBOL_NAME(sys_sysfs)		/* 135 */
-	.long SYMBOL_NAME(sys_personality)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* for afs_syscall */
-	.long SYMBOL_NAME(sys_setfsuid16)
-	.long SYMBOL_NAME(sys_setfsgid16)
-	.long SYMBOL_NAME(sys_llseek)		/* 140 */
-	.long SYMBOL_NAME(sys_getdents)
-	.long SYMBOL_NAME(sys_select)
-	.long SYMBOL_NAME(sys_flock)
-	.long SYMBOL_NAME(sys_msync)
-	.long SYMBOL_NAME(sys_readv)		/* 145 */
-	.long SYMBOL_NAME(sys_writev)
-	.long SYMBOL_NAME(sys_getsid)
-	.long SYMBOL_NAME(sys_fdatasync)
-	.long SYMBOL_NAME(sys_sysctl)
-	.long SYMBOL_NAME(sys_mlock)		/* 150 */
-	.long SYMBOL_NAME(sys_munlock)
-	.long SYMBOL_NAME(sys_mlockall)
-	.long SYMBOL_NAME(sys_munlockall)
-	.long SYMBOL_NAME(sys_sched_setparam)
-	.long SYMBOL_NAME(sys_sched_getparam)   /* 155 */
-	.long SYMBOL_NAME(sys_sched_setscheduler)
-	.long SYMBOL_NAME(sys_sched_getscheduler)
-	.long SYMBOL_NAME(sys_sched_yield)
-	.long SYMBOL_NAME(sys_sched_get_priority_max)
-	.long SYMBOL_NAME(sys_sched_get_priority_min)  /* 160 */
-	.long SYMBOL_NAME(sys_sched_rr_get_interval)
-	.long SYMBOL_NAME(sys_nanosleep)
-	.long SYMBOL_NAME(sys_mremap)
-	.long SYMBOL_NAME(sys_setresuid16)
-	.long SYMBOL_NAME(sys_getresuid16)	/* 165 */
-	.long SYMBOL_NAME(sys_ni_syscall)	/* sys_vm86 */
-	.long SYMBOL_NAME(sys_query_module)
-	.long SYMBOL_NAME(sys_poll)
-	.long SYMBOL_NAME(sys_nfsservctl)
-	.long SYMBOL_NAME(sys_setresgid16)	/* 170 */
-	.long SYMBOL_NAME(sys_getresgid16)
-	.long SYMBOL_NAME(sys_prctl)
-	.long SYMBOL_NAME(sys_rt_sigreturn)
-	.long SYMBOL_NAME(sys_rt_sigaction)
-	.long SYMBOL_NAME(sys_rt_sigprocmask)	/* 175 */
-	.long SYMBOL_NAME(sys_rt_sigpending)
-	.long SYMBOL_NAME(sys_rt_sigtimedwait)
-	.long SYMBOL_NAME(sys_rt_sigqueueinfo)
-	.long SYMBOL_NAME(sys_rt_sigsuspend)
-	.long SYMBOL_NAME(sys_pread)		/* 180 */
-	.long SYMBOL_NAME(sys_pwrite)
-	.long SYMBOL_NAME(sys_chown16)
-	.long SYMBOL_NAME(sys_getcwd)
-	.long SYMBOL_NAME(sys_capget)
-	.long SYMBOL_NAME(sys_capset)           /* 185 */
-	.long SYMBOL_NAME(sys_sigaltstack)
-	.long SYMBOL_NAME(sys_sendfile)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* streams1 */
-	.long SYMBOL_NAME(sys_ni_syscall)	/* streams2 */
-	.long SYMBOL_NAME(sys_vfork)            /* 190 */
-	.long SYMBOL_NAME(sys_getrlimit)
-	.long SYMBOL_NAME(sys_mmap2)
-	.long SYMBOL_NAME(sys_truncate64)
-	.long SYMBOL_NAME(sys_ftruncate64)
-	.long SYMBOL_NAME(sys_stat64)		/* 195 */
-	.long SYMBOL_NAME(sys_lstat64)
-	.long SYMBOL_NAME(sys_fstat64)
-	.long SYMBOL_NAME(sys_lchown)
-	.long SYMBOL_NAME(sys_getuid)
-	.long SYMBOL_NAME(sys_getgid)		/* 200 */
-	.long SYMBOL_NAME(sys_geteuid)
-	.long SYMBOL_NAME(sys_getegid)
-	.long SYMBOL_NAME(sys_setreuid)
-	.long SYMBOL_NAME(sys_setregid)
-	.long SYMBOL_NAME(sys_getgroups)	/* 205 */
-	.long SYMBOL_NAME(sys_setgroups)
-	.long SYMBOL_NAME(sys_fchown)
-	.long SYMBOL_NAME(sys_setresuid)
-	.long SYMBOL_NAME(sys_getresuid)
-	.long SYMBOL_NAME(sys_setresgid)	/* 210 */
-	.long SYMBOL_NAME(sys_getresgid)
-	.long SYMBOL_NAME(sys_chown)
-	.long SYMBOL_NAME(sys_setuid)
-	.long SYMBOL_NAME(sys_setgid)
-	.long SYMBOL_NAME(sys_setfsuid)		/* 215 */
-	.long SYMBOL_NAME(sys_setfsgid)
-	.long SYMBOL_NAME(sys_pivot_root)
-	.long SYMBOL_NAME(sys_mincore)
-	.long SYMBOL_NAME(sys_madvise)
-	.long SYMBOL_NAME(sys_getdents64)       /* 220 */
-	.long SYMBOL_NAME(sys_fcntl64)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* reserved for TUX */
-        .long SYMBOL_NAME(sys_ni_syscall)       /* Reserved for Security */
-        .long SYMBOL_NAME(sys_gettid)
-        .long SYMBOL_NAME(sys_readahead)        /* 225 */
-        .long SYMBOL_NAME(sys_tkill)
+	.long sys_ni_syscall	/* 0  -  old "setup()" system call*/
+	.long sys_exit
+	.long sys_fork
+	.long sys_read
+	.long sys_write
+	.long sys_open		/* 5 */
+	.long sys_close
+	.long sys_waitpid
+	.long sys_creat
+	.long sys_link
+	.long sys_unlink	/* 10 */
+	.long sys_execve
+	.long sys_chdir
+	.long sys_time
+	.long sys_mknod
+	.long sys_chmod		/* 15 */
+	.long sys_lchown16
+	.long sys_ni_syscall	/* old break syscall holder */
+	.long sys_stat
+	.long sys_lseek
+	.long sys_getpid	/* 20 */
+	.long sys_mount
+	.long sys_oldumount
+	.long sys_setuid16
+	.long sys_getuid16
+	.long sys_stime		/* 25 */
+	.long sys_ptrace
+	.long sys_alarm
+	.long sys_fstat
+	.long sys_pause
+	.long sys_utime		/* 30 */
+	.long sys_ni_syscall	/* old stty syscall holder */
+	.long sys_ni_syscall	/* old gtty syscall holder */
+	.long sys_access
+	.long sys_nice
+	.long sys_ni_syscall	/* 35  old ftime syscall holder */
+	.long sys_sync
+	.long sys_kill
+	.long sys_rename
+	.long sys_mkdir
+	.long sys_rmdir		/* 40 */
+	.long sys_dup
+	.long sys_pipe
+	.long sys_times
+	.long sys_ni_syscall	/* old prof syscall holder */
+	.long sys_brk		/* 45 */
+	.long sys_setgid16
+	.long sys_getgid16
+	.long sys_signal
+	.long sys_geteuid16
+	.long sys_getegid16	/* 50 */
+	.long sys_acct
+	.long sys_umount	/* recycled never used phys() */
+	.long sys_ni_syscall	/* old lock syscall holder */
+	.long sys_ioctl
+	.long sys_fcntl		/* 55 */
+	.long sys_ni_syscall	/* old mpx syscall holder */
+	.long sys_setpgid
+	.long sys_ni_syscall	/* old ulimit syscall holder */
+	.long sys_ni_syscall	/* old sys_olduname holder */
+	.long sys_umask		/* 60 */
+	.long sys_chroot
+	.long sys_ustat
+	.long sys_dup2
+	.long sys_getppid
+	.long sys_getpgrp	/* 65 */
+	.long sys_setsid
+	.long sys_sigaction
+	.long sys_sgetmask
+	.long sys_ssetmask
+	.long sys_setreuid16	/* 70 */
+	.long sys_setregid16
+	.long sys_sigsuspend
+	.long sys_sigpending
+	.long sys_sethostname
+	.long sys_setrlimit	/* 75 */
+	.long sys_old_getrlimit
+	.long sys_getrusage
+	.long sys_gettimeofday
+	.long sys_settimeofday
+	.long sys_getgroups16	/* 80 */
+	.long sys_setgroups16
+	.long sys_select	/* was old_select in Linux/E100 */
+	.long sys_symlink
+	.long sys_lstat
+	.long sys_readlink	/* 85 */
+	.long sys_uselib
+	.long sys_swapon
+	.long sys_reboot
+	.long old_readdir
+	.long old_mmap		/* 90 */
+	.long sys_munmap
+	.long sys_truncate
+	.long sys_ftruncate
+	.long sys_fchmod
+	.long sys_fchown16	/* 95 */
+	.long sys_getpriority
+	.long sys_setpriority
+	.long sys_ni_syscall	/* old profil syscall holder */
+	.long sys_statfs
+	.long sys_fstatfs	/* 100 */
+	.long sys_ni_syscall	/* sys_ioperm in i386 */
+	.long sys_socketcall
+	.long sys_syslog
+	.long sys_setitimer
+	.long sys_getitimer	/* 105 */
+	.long sys_newstat
+	.long sys_newlstat
+	.long sys_newfstat
+	.long sys_ni_syscall	/* old sys_uname holder */
+	.long sys_ni_syscall	/* sys_iopl in i386 */
+	.long sys_vhangup
+	.long sys_ni_syscall	/* old "idle" system call */
+	.long sys_ni_syscall	/* vm86old in i386 */
+	.long sys_wait4
+	.long sys_swapoff	/* 115 */
+	.long sys_sysinfo
+	.long sys_ipc
+	.long sys_fsync
+	.long sys_sigreturn
+	.long sys_clone		/* 120 */
+	.long sys_setdomainname
+	.long sys_newuname
+	.long sys_ni_syscall	/* TODO sys_modify_ldt - do something ?*/
+	.long sys_adjtimex
+	.long sys_mprotect	/* 125 */
+	.long sys_sigprocmask
+	.long sys_create_module
+	.long sys_init_module
+	.long sys_delete_module
+	.long sys_get_kernel_syms	/* 130 */
+	.long sys_quotactl
+	.long sys_getpgid
+	.long sys_fchdir
+	.long sys_bdflush
+	.long sys_sysfs		/* 135 */
+	.long sys_personality
+	.long sys_ni_syscall	/* for afs_syscall */
+	.long sys_setfsuid16
+	.long sys_setfsgid16
+	.long sys_llseek	/* 140 */
+	.long sys_getdents
+	.long sys_select
+	.long sys_flock
+	.long sys_msync
+	.long sys_readv		/* 145 */
+	.long sys_writev
+	.long sys_getsid
+	.long sys_fdatasync
+	.long sys_sysctl
+	.long sys_mlock		/* 150 */
+	.long sys_munlock
+	.long sys_mlockall
+	.long sys_munlockall
+	.long sys_sched_setparam
+	.long sys_sched_getparam   /* 155 */
+	.long sys_sched_setscheduler
+	.long sys_sched_getscheduler
+	.long sys_sched_yield
+	.long sys_sched_get_priority_max
+	.long sys_sched_get_priority_min  /* 160 */
+	.long sys_sched_rr_get_interval
+	.long sys_nanosleep
+	.long sys_mremap
+	.long sys_setresuid16
+	.long sys_getresuid16	/* 165 */
+	.long sys_ni_syscall	/* sys_vm86 */
+	.long sys_query_module
+	.long sys_poll
+	.long sys_nfsservctl
+	.long sys_setresgid16	/* 170 */
+	.long sys_getresgid16
+	.long sys_prctl
+	.long sys_rt_sigreturn
+	.long sys_rt_sigaction
+	.long sys_rt_sigprocmask	/* 175 */
+	.long sys_rt_sigpending
+	.long sys_rt_sigtimedwait
+	.long sys_rt_sigqueueinfo
+	.long sys_rt_sigsuspend
+	.long sys_pread		/* 180 */
+	.long sys_pwrite
+	.long sys_chown16
+	.long sys_getcwd
+	.long sys_capget
+	.long sys_capset	/* 185 */
+	.long sys_sigaltstack
+	.long sys_sendfile
+	.long sys_ni_syscall	/* streams1 */
+	.long sys_ni_syscall	/* streams2 */
+	.long sys_vfork		/* 190 */
+	.long sys_getrlimit
+	.long sys_mmap2
+	.long sys_truncate64
+	.long sys_ftruncate64
+	.long sys_stat64	/* 195 */
+	.long sys_lstat64
+	.long sys_fstat64
+	.long sys_lchown
+	.long sys_getuid
+	.long sys_getgid	/* 200 */
+	.long sys_geteuid
+	.long sys_getegid
+	.long sys_setreuid
+	.long sys_setregid
+	.long sys_getgroups	/* 205 */
+	.long sys_setgroups
+	.long sys_fchown
+	.long sys_setresuid
+	.long sys_getresuid
+	.long sys_setresgid	/* 210 */
+	.long sys_getresgid
+	.long sys_chown
+	.long sys_setuid
+	.long sys_setgid
+	.long sys_setfsuid	/* 215 */
+	.long sys_setfsgid
+	.long sys_pivot_root
+	.long sys_mincore
+	.long sys_madvise
+	.long sys_getdents64	/* 220 */
+	.long sys_fcntl64
+	.long sys_ni_syscall	/* reserved for TUX */
+	.long sys_ni_syscall	/* Reserved for Security */
+	.long sys_gettid
+	.long sys_readahead	/* 225 */
+	.long sys_tkill
 
         /*
          * NOTE!! This doesn't have to be exact - we just have
@@ -1025,6 +1025,6 @@
          */
 
 	.rept NR_syscalls-(.-sys_call_table)/4
-		.long SYMBOL_NAME(sys_ni_syscall)
+		.long sys_ni_syscall
 	.endr
 	
diff -Nru a/arch/i386/Config.help b/arch/i386/Config.help
--- a/arch/i386/Config.help	Sun Apr 28 20:10:31 2002
+++ b/arch/i386/Config.help	Sun Apr 28 20:10:31 2002
@@ -826,6 +826,10 @@
   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)
+
+CONFIG_X86_MCE_P4THERMAL
+  Enabling this feature will cause a message to be printed when the P4
+  enters thermal throttling.
 				 
 CONFIG_TOSHIBA
   This adds a driver to safely access the System Management Mode of
diff -Nru a/arch/i386/boot/compressed/head.S b/arch/i386/boot/compressed/head.S
--- a/arch/i386/boot/compressed/head.S	Sun Apr 28 20:10:35 2002
+++ b/arch/i386/boot/compressed/head.S	Sun Apr 28 20:10:35 2002
@@ -37,7 +37,7 @@
 	movl %eax,%fs
 	movl %eax,%gs
 
-	lss SYMBOL_NAME(stack_start),%esp
+	lss stack_start,%esp
 	xorl %eax,%eax
 1:	incl %eax		# check that A20 really IS enabled
 	movl %eax,0x000000	# loop forever if it isn't
@@ -55,8 +55,8 @@
  * Clear BSS
  */
 	xorl %eax,%eax
-	movl $ SYMBOL_NAME(_edata),%edi
-	movl $ SYMBOL_NAME(_end),%ecx
+	movl $_edata,%edi
+	movl $_end,%ecx
 	subl %edi,%ecx
 	cld
 	rep
@@ -68,7 +68,7 @@
 	movl %esp,%eax
 	pushl %esi	# real mode pointer as second arg
 	pushl %eax	# address of structure as first arg
-	call SYMBOL_NAME(decompress_kernel)
+	call decompress_kernel
 	orl  %eax,%eax 
 	jnz  3f
 	popl %esi	# discard address
diff -Nru a/arch/i386/boot/setup.S b/arch/i386/boot/setup.S
--- a/arch/i386/boot/setup.S	Sun Apr 28 20:10:30 2002
+++ b/arch/i386/boot/setup.S	Sun Apr 28 20:10:30 2002
@@ -42,6 +42,9 @@
  * if CX/DX have been changed in the e801 call and if so use AX/BX .
  * Michael Miller, April 2001 <michaelm@mjmm.org>
  *
+ * New A20 code ported from SYSLINUX by H. Peter Anvin. AMD Elan bugfixes
+ * by Robert Schwebel, December 2001 <robert@schwebel.de>
+ *
  */
 
 #include <linux/config.h>
@@ -651,7 +654,18 @@
 #
 # Enable A20.  This is at the very best an annoying procedure.
 # A20 code ported from SYSLINUX 1.52-1.63 by H. Peter Anvin.
+# AMD Elan bug fix by Robert Schwebel.
 #
+
+#if defined(CONFIG_MELAN)
+	movb $0x02, %al			# alternate A20 gate
+	outb %al, $0x92			# this works on SC410/SC520
+a20_elan_wait:
+	call a20_test
+	jz a20_elan_wait
+	jmp a20_done
+#endif
+
 
 A20_TEST_LOOPS		=  32		# Iterations per wait
 A20_ENABLE_LOOPS	= 255		# Total loops to try		
diff -Nru a/arch/i386/config.in b/arch/i386/config.in
--- a/arch/i386/config.in	Sun Apr 28 20:10:34 2002
+++ b/arch/i386/config.in	Sun Apr 28 20:10:34 2002
@@ -154,7 +154,9 @@
 fi
 
 bool 'Machine Check Exception' CONFIG_X86_MCE
-dep_bool 'Check for non-fatal errors' CONFIG_X86_MCE_NONFATAL $CONFIG_X86_MCE
+dep_bool 'Check for non-fatal errors on Athlon/Duron' CONFIG_X86_MCE_NONFATAL $CONFIG_X86_MCE
+dep_bool 'check for P4 thermal throttling interrupt.' CONFIG_X86_MCE_P4THERMAL $CONFIG_X86_MCE $CONFIG_X86_LOCAL_APIC
+
 
 tristate 'Toshiba Laptop support' CONFIG_TOSHIBA
 tristate 'Dell laptop support' CONFIG_I8K
diff -Nru a/arch/i386/defconfig b/arch/i386/defconfig
--- a/arch/i386/defconfig	Sun Apr 28 20:10:31 2002
+++ b/arch/i386/defconfig	Sun Apr 28 20:10:31 2002
@@ -58,7 +58,8 @@
 CONFIG_X86_GOOD_APIC=y
 CONFIG_X86_USE_PPRO_CHECKSUM=y
 CONFIG_X86_MCE=y
-# CONFIG_X86_MCE_NONFATAL is not set
+CONFIG_X86_MCE_NONFATAL=y
+CONFIG_X86_MCE_P4THERMAL=y
 # CONFIG_TOSHIBA is not set
 # CONFIG_I8K is not set
 # CONFIG_MICROCODE is not set
@@ -662,7 +663,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/i386/kernel/acpi_wakeup.S b/arch/i386/kernel/acpi_wakeup.S
--- a/arch/i386/kernel/acpi_wakeup.S	Sun Apr 28 20:10:32 2002
+++ b/arch/i386/kernel/acpi_wakeup.S	Sun Apr 28 20:10:32 2002
@@ -36,7 +36,7 @@
 	orl     $0x80000001, %eax
 	movl	%eax, %cr0
 
-	ljmpl	$__KERNEL_CS,$SYMBOL_NAME(wakeup_pmode_return)
+	ljmpl	$__KERNEL_CS,$wakeup_pmode_return
 
 	.code32
 	ALIGN
diff -Nru a/arch/i386/kernel/apic.c b/arch/i386/kernel/apic.c
--- a/arch/i386/kernel/apic.c	Sun Apr 28 20:10:36 2002
+++ b/arch/i386/kernel/apic.c	Sun Apr 28 20:10:36 2002
@@ -78,6 +78,13 @@
 		apic_write_around(APIC_LVTPC, v | APIC_LVT_MASKED);
 	}
 
+/* lets not touch this if we didn't frob it */
+#ifdef CONFIG_X86_MCE_P4THERMAL
+	if (maxlvt >= 5) {
+		v = apic_read(APIC_LVTTHMR);
+		apic_write_around(APIC_LVTTHMR, v | APIC_LVT_MASKED);
+	}
+#endif
 	/*
 	 * Clean APIC state for other OSs:
 	 */
@@ -88,6 +95,11 @@
 		apic_write_around(APIC_LVTERR, APIC_LVT_MASKED);
 	if (maxlvt >= 4)
 		apic_write_around(APIC_LVTPC, APIC_LVT_MASKED);
+
+#ifdef CONFIG_X86_MCE_P4THERMAL
+	if (maxlvt >= 5)
+		apic_write_around(APIC_LVTTHMR, APIC_LVT_MASKED);
+#endif
 	v = GET_APIC_VERSION(apic_read(APIC_LVR));
 	if (APIC_INTEGRATED(v)) {	/* !82489DX */
 		if (maxlvt > 3)		/* Due to Pentium errata 3AP and 11AP. */
@@ -472,6 +484,7 @@
 	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();
diff -Nru a/arch/i386/kernel/apm.c b/arch/i386/kernel/apm.c
--- a/arch/i386/kernel/apm.c	Sun Apr 28 20:10:30 2002
+++ b/arch/i386/kernel/apm.c	Sun Apr 28 20:10:30 2002
@@ -578,7 +578,7 @@
 	__asm__ __volatile__(APM_DO_ZERO_SEGS
 		"pushl %%edi\n\t"
 		"pushl %%ebp\n\t"
-		"lcall *%%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t"
+		"lcall *%%cs:apm_bios_entry\n\t"
 		"setc %%al\n\t"
 		"popl %%ebp\n\t"
 		"popl %%edi\n\t"
@@ -625,7 +625,7 @@
 		__asm__ __volatile__(APM_DO_ZERO_SEGS
 			"pushl %%edi\n\t"
 			"pushl %%ebp\n\t"
-			"lcall *%%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t"
+			"lcall *%%cs:apm_bios_entry\n\t"
 			"setc %%bl\n\t"
 			"popl %%ebp\n\t"
 			"popl %%edi\n\t"
diff -Nru a/arch/i386/kernel/bluesmoke.c b/arch/i386/kernel/bluesmoke.c
--- a/arch/i386/kernel/bluesmoke.c	Sun Apr 28 20:10:34 2002
+++ b/arch/i386/kernel/bluesmoke.c	Sun Apr 28 20:10:34 2002
@@ -1,3 +1,6 @@
+/*
+ * arch/i386/kernel/bluesmoke.c - x86 Machine Check Exception Reporting
+ */
 
 #include <linux/init.h>
 #include <linux/types.h>
@@ -19,21 +22,12 @@
 
 static int banks;
 
-/*
- * 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();
-}
 
+#ifdef CONFIG_X86_MCE_P4THERMAL
 /*
  *	P4/Xeon Thermal transition interrupt handler
  */
 
-#ifdef CONFIG_X86_LOCAL_APIC
 static void intel_thermal_interrupt(struct pt_regs *regs)
 {
 	u32 l, h;
@@ -49,7 +43,6 @@
 		printk(KERN_INFO "CPU#%d: Temperature/speed normal\n", cpu);
 	}
 }
-#endif
 
 static void unexpected_thermal_interrupt(struct pt_regs *regs)
 {	
@@ -71,14 +64,13 @@
 
 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 */
@@ -96,16 +88,16 @@
 		printk(KERN_INFO "CPU#%d: Thermal monitoring enabled\n", cpu);
 	}
 
-	/* check wether a vector already exists */	
+	/* check whether 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 */
@@ -120,8 +112,9 @@
 	apic_write_around(APIC_LVTTHMR, l & ~APIC_LVT_MASKED);
 
 	return;
-#endif
 }
+#endif /* CONFIG_X86_MCE_P4THERMAL */
+
 
 /*
  *	Machine Check Handler For PII/PIII
@@ -134,32 +127,26 @@
 	u32 mcgstl, mcgsth;
 	int i;
 
-	flush_all();
-
 	rdmsr(MSR_IA32_MCG_STATUS, mcgstl, mcgsth);
 	if(mcgstl&(1<<0))	/* Recoverable ? */
 		recover=0;
 
 	printk(KERN_EMERG "CPU %d: Machine Check Exception: %08x%08x\n", smp_processor_id(), mcgsth, mcgstl);
-	
-	for(i=0;i<banks;i++)
-	{
+
+	for (i=0;i<banks;i++) {
 		rdmsr(MSR_IA32_MC0_STATUS+i*4,low, high);
-		if(high&(1<<31))
-		{
+		if(high&(1<<31)) {
 			if(high&(1<<29))
 				recover|=1;
 			if(high&(1<<25))
 				recover|=2;
 			printk(KERN_EMERG "Bank %d: %08x%08x", i, high, low);
 			high&=~(1<<31);
-			if(high&(1<<27))
-			{
+			if(high&(1<<27)) {
 				rdmsr(MSR_IA32_MC0_MISC+i*4, alow, ahigh);
 				printk("[%08x%08x]", ahigh, alow);
 			}
-			if(high&(1<<26))
-			{
+			if(high&(1<<26)) {
 				rdmsr(MSR_IA32_MC0_ADDR+i*4, alow, ahigh);
 				printk(" at %08x%08x", ahigh, alow);
 			}
@@ -170,7 +157,7 @@
 			wmb();
 		}
 	}
-	
+
 	if(recover&2)
 		panic("CPU context corrupt");
 	if(recover&1)
@@ -183,7 +170,7 @@
 /*
  *	Machine check handler for Pentium class Intel
  */
- 
+
 static void pentium_machine_check(struct pt_regs * regs, long error_code)
 {
 	u32 loaddr, hi, lotype;
@@ -197,7 +184,7 @@
 /*
  *	Machine check handler for WinChip C6
  */
- 
+
 static void winchip_machine_check(struct pt_regs * regs, long error_code)
 {
 	printk(KERN_EMERG "CPU#%d: Machine Check Exception.\n", smp_processor_id());
@@ -225,47 +212,50 @@
 
 
 #ifdef CONFIG_X86_MCE_NONFATAL
-struct timer_list mce_timer;
+static struct timer_list mce_timer;
+static int timerset = 0;
+
+#define MCE_RATE	15*HZ	/* timer rate is 15s */
 
-static void mce_checkregs (unsigned int cpu)
+static void mce_checkregs (void *info)
 {
 	u32 low, high;
 	int i;
+	unsigned int *cpu = info;
 
-	if (cpu!=smp_processor_id())
-		BUG();
+	BUG_ON (*cpu != smp_processor_id());
 
 	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. */
+			/* Scrub the error so we don't pick it up in MCE_RATE 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;
+	unsigned int i;
 
 	for (i=0; i<smp_num_cpus; i++) {
 		if (i == smp_processor_id())
-			mce_checkregs(i);
+			mce_checkregs(&i);
 		else
-			smp_call_function (mce_checkregs, i, 1, 1);
+			smp_call_function (mce_checkregs, &i, 1, 1);
 	}
+
+	/* Refresh the timer. */
+	mce_timer.expires = jiffies + MCE_RATE;
+	add_timer (&mce_timer);
 }
 #endif
 
@@ -286,11 +276,11 @@
 
 	if( !test_bit(X86_FEATURE_MCE, c->x86_capability) )
 		return;	
-	
+
 	/*
 	 *	Pentium machine check
 	 */
-	
+
 	if(c->x86 == 5)
 	{
 		/* Default P5 to off as its often misconnected */
@@ -308,20 +298,20 @@
 		printk(KERN_INFO "Intel old style machine check reporting enabled on CPU#%d.\n", smp_processor_id());
 		return;
 	}
-	
+
 
 	/*
 	 *	Check for PPro style MCA
 	 */
-	 		
+ 		
 	if( !test_bit(X86_FEATURE_MCA, c->x86_capability) )
 		return;
-		
+
 	/* Ok machine check is available */
-	
+
 	machine_check_vector = intel_machine_check;
 	wmb();
-	
+
 	if(done==0)
 		printk(KERN_INFO "Intel machine check architecture supported.\n");
 	rdmsr(MSR_IA32_MCG_CAP, l, h);
@@ -343,8 +333,12 @@
 
 	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);
+
+#ifdef CONFIG_X86_MCE_P4THERMAL
+	/* Only enable thermal throttling warning on Pentium 4. */
+	if (c->x86_vendor == X86_VENDOR_INTEL && c->x86 == 15)
+		intel_init_thermal(c);
+#endif
 
 	done=1;
 }
@@ -352,7 +346,7 @@
 /*
  *	Set up machine check reporting on the Winchip C6 series
  */
- 
+
 static void __init winchip_mcheck_init(struct cpuinfo_x86 *c)
 {
 	u32 lo, hi;
@@ -377,6 +371,7 @@
 
 void __init mcheck_init(struct cpuinfo_x86 *c)
 {
+
 	if(mce_disabled==1)
 		return;
 
@@ -387,12 +382,17 @@
 			if(c->x86 == 6) {
 				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);
+				if (timerset == 0) {
+					/* Set the timer to check for non-fatal
+					   errors every MCE_RATE seconds */
+					init_timer (&mce_timer);
+					mce_timer.expires = jiffies + MCE_RATE;
+					mce_timer.data = 0;
+					mce_timer.function = &mce_timerfunc;
+					add_timer (&mce_timer);
+					timerset = 1;
+					printk(KERN_INFO "Machine check exception polling timer started.\n");
+				}
 #endif
 			}
 			break;
diff -Nru a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S
--- a/arch/i386/kernel/entry.S	Sun Apr 28 20:10:33 2002
+++ b/arch/i386/kernel/entry.S	Sun Apr 28 20:10:33 2002
@@ -197,7 +197,7 @@
 
 ENTRY(ret_from_fork)
 #if CONFIG_SMP || CONFIG_PREEMPT
-	call SYMBOL_NAME(schedule_tail)
+	call schedule_tail
 #endif
 	GET_THREAD_INFO(%ebx)
 	jmp syscall_exit
@@ -236,12 +236,12 @@
 	movl TI_FLAGS(%ebx), %ecx
 	testb $_TIF_NEED_RESCHED, %cl
 	jz restore_all
-	movl SYMBOL_NAME(irq_stat)+local_bh_count CPU_IDX, %ecx
-	addl SYMBOL_NAME(irq_stat)+local_irq_count CPU_IDX, %ecx
+	movl irq_stat+local_bh_count CPU_IDX, %ecx
+	addl irq_stat+local_irq_count CPU_IDX, %ecx
 	jnz restore_all
 	movl $PREEMPT_ACTIVE,TI_PRE_COUNT(%ebx)
 	sti
-	call SYMBOL_NAME(schedule)
+	call schedule
 	movl $0,TI_PRE_COUNT(%ebx) 
 	jmp restore_all
 #endif
@@ -258,7 +258,7 @@
 	testb $_TIF_SYSCALL_TRACE,TI_FLAGS(%ebx)
 	jnz syscall_trace_entry
 syscall_call:
-	call *SYMBOL_NAME(sys_call_table)(,%eax,4)
+	call *sys_call_table(,%eax,4)
 	movl %eax,EAX(%esp)		# store the return value
 syscall_exit:
 	cli				# make sure we don't miss an interrupt
@@ -276,7 +276,7 @@
 	testb $_TIF_NEED_RESCHED, %cl
 	jz work_notifysig
 work_resched:
-	call SYMBOL_NAME(schedule)
+	call schedule
 	cli				# make sure we don't miss an interrupt
 					# setting need_resched or sigpending
 					# between sampling and the iret
@@ -294,17 +294,17 @@
 	jne work_notifysig_v86		# returning to kernel-space or
 					# vm86-space
 	xorl %edx, %edx
-	call SYMBOL_NAME(do_notify_resume)
+	call do_notify_resume
 	jmp restore_all
 
 	ALIGN
 work_notifysig_v86:
 	pushl %ecx
-	call SYMBOL_NAME(save_v86_state)
+	call save_v86_state
 	popl %ecx
 	movl %eax, %esp
 	xorl %edx, %edx
-	call SYMBOL_NAME(do_notify_resume)
+	call do_notify_resume
 	jmp restore_all
 
 	# perform syscall exit tracing
@@ -313,7 +313,7 @@
 	movl $-ENOSYS,EAX(%esp)
 	movl %esp, %eax
 	xorl %edx,%edx
-	call SYMBOL_NAME(do_syscall_trace)
+	call do_syscall_trace
 	movl ORIG_EAX(%esp), %eax
 	cmpl $(NR_syscalls), %eax
 	jnae syscall_call
@@ -328,7 +328,7 @@
 					# schedule() instead
 	movl %esp, %eax
 	movl $1, %edx
-	call SYMBOL_NAME(do_syscall_trace)
+	call do_syscall_trace
 	jmp resume_userspace
 
 	ALIGN
@@ -361,7 +361,7 @@
 	SAVE_ALL
 	GET_THREAD_INFO(%ebx)
 	INC_PRE_COUNT(%ebx)
-	call SYMBOL_NAME(do_IRQ)
+	call do_IRQ
 	jmp ret_from_intr
 
 #define BUILD_INTERRUPT(name, nr)	\
@@ -370,7 +370,7 @@
 	SAVE_ALL			\
 	GET_THREAD_INFO(%ebx);		\
 	INC_PRE_COUNT(%ebx)		\
-	call SYMBOL_NAME(smp_/**/name);	\
+	call smp_/**/name;	\
 	jmp ret_from_intr;
 
 /*
@@ -399,7 +399,7 @@
 
 ENTRY(divide_error)
 	pushl $0			# no error code
-	pushl $ SYMBOL_NAME(do_divide_error)
+	pushl $do_divide_error
 	ALIGN
 error_code:
 	pushl %ds
@@ -432,12 +432,12 @@
 
 ENTRY(coprocessor_error)
 	pushl $0
-	pushl $ SYMBOL_NAME(do_coprocessor_error)
+	pushl $do_coprocessor_error
 	jmp error_code
 
 ENTRY(simd_coprocessor_error)
 	pushl $0
-	pushl $ SYMBOL_NAME(do_simd_coprocessor_error)
+	pushl $do_simd_coprocessor_error
 	jmp error_code
 
 ENTRY(device_not_available)
@@ -448,18 +448,18 @@
 	testl $0x4, %eax		# EM (math emulation bit)
 	jne device_not_available_emulate
 	preempt_stop
-	call SYMBOL_NAME(math_state_restore)
+	call math_state_restore
 	jmp ret_from_exception
 device_not_available_emulate:
 	pushl $0			# temporary storage for ORIG_EIP
-	call  SYMBOL_NAME(math_emulate)
+	call math_emulate
 	addl $4, %esp
 	preempt_stop
 	jmp ret_from_exception
 
 ENTRY(debug)
 	pushl $0
-	pushl $ SYMBOL_NAME(do_debug)
+	pushl $do_debug
 	jmp error_code
 
 ENTRY(nmi)
@@ -468,320 +468,319 @@
 	movl %esp, %edx
 	pushl $0
 	pushl %edx
-	call SYMBOL_NAME(do_nmi)
+	call do_nmi
 	addl $8, %esp
 	RESTORE_ALL
 
 ENTRY(int3)
 	pushl $0
-	pushl $ SYMBOL_NAME(do_int3)
+	pushl $do_int3
 	jmp error_code
 
 ENTRY(overflow)
 	pushl $0
-	pushl $ SYMBOL_NAME(do_overflow)
+	pushl $do_overflow
 	jmp error_code
 
 ENTRY(bounds)
 	pushl $0
-	pushl $ SYMBOL_NAME(do_bounds)
+	pushl $do_bounds
 	jmp error_code
 
 ENTRY(invalid_op)
 	pushl $0
-	pushl $ SYMBOL_NAME(do_invalid_op)
+	pushl $do_invalid_op
 	jmp error_code
 
 ENTRY(coprocessor_segment_overrun)
 	pushl $0
-	pushl $ SYMBOL_NAME(do_coprocessor_segment_overrun)
+	pushl $do_coprocessor_segment_overrun
 	jmp error_code
 
 ENTRY(double_fault)
-	pushl $ SYMBOL_NAME(do_double_fault)
+	pushl $do_double_fault
 	jmp error_code
 
 ENTRY(invalid_TSS)
-	pushl $ SYMBOL_NAME(do_invalid_TSS)
+	pushl $do_invalid_TSS
 	jmp error_code
 
 ENTRY(segment_not_present)
-	pushl $ SYMBOL_NAME(do_segment_not_present)
+	pushl $do_segment_not_present
 	jmp error_code
 
 ENTRY(stack_segment)
-	pushl $ SYMBOL_NAME(do_stack_segment)
+	pushl $do_stack_segment
 	jmp error_code
 
 ENTRY(general_protection)
-	pushl $ SYMBOL_NAME(do_general_protection)
+	pushl $do_general_protection
 	jmp error_code
 
 ENTRY(alignment_check)
-	pushl $ SYMBOL_NAME(do_alignment_check)
+	pushl $do_alignment_check
 	jmp error_code
 
 ENTRY(page_fault)
-	pushl $ SYMBOL_NAME(do_page_fault)
+	pushl $do_page_fault
 	jmp error_code
 
 ENTRY(machine_check)
 	pushl $0
-	pushl $ SYMBOL_NAME(do_machine_check)
+	pushl $do_machine_check
 	jmp error_code
 
 ENTRY(spurious_interrupt_bug)
 	pushl $0
-	pushl $ SYMBOL_NAME(do_spurious_interrupt_bug)
+	pushl $do_spurious_interrupt_bug
 	jmp error_code
 
 .data
 ENTRY(sys_call_table)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* 0  -  old "setup()" system call*/
-	.long SYMBOL_NAME(sys_exit)
-	.long SYMBOL_NAME(sys_fork)
-	.long SYMBOL_NAME(sys_read)
-	.long SYMBOL_NAME(sys_write)
-	.long SYMBOL_NAME(sys_open)		/* 5 */
-	.long SYMBOL_NAME(sys_close)
-	.long SYMBOL_NAME(sys_waitpid)
-	.long SYMBOL_NAME(sys_creat)
-	.long SYMBOL_NAME(sys_link)
-	.long SYMBOL_NAME(sys_unlink)		/* 10 */
-	.long SYMBOL_NAME(sys_execve)
-	.long SYMBOL_NAME(sys_chdir)
-	.long SYMBOL_NAME(sys_time)
-	.long SYMBOL_NAME(sys_mknod)
-	.long SYMBOL_NAME(sys_chmod)		/* 15 */
-	.long SYMBOL_NAME(sys_lchown16)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* old break syscall holder */
-	.long SYMBOL_NAME(sys_stat)
-	.long SYMBOL_NAME(sys_lseek)
-	.long SYMBOL_NAME(sys_getpid)		/* 20 */
-	.long SYMBOL_NAME(sys_mount)
-	.long SYMBOL_NAME(sys_oldumount)
-	.long SYMBOL_NAME(sys_setuid16)
-	.long SYMBOL_NAME(sys_getuid16)
-	.long SYMBOL_NAME(sys_stime)		/* 25 */
-	.long SYMBOL_NAME(sys_ptrace)
-	.long SYMBOL_NAME(sys_alarm)
-	.long SYMBOL_NAME(sys_fstat)
-	.long SYMBOL_NAME(sys_pause)
-	.long SYMBOL_NAME(sys_utime)		/* 30 */
-	.long SYMBOL_NAME(sys_ni_syscall)	/* old stty syscall holder */
-	.long SYMBOL_NAME(sys_ni_syscall)	/* old gtty syscall holder */
-	.long SYMBOL_NAME(sys_access)
-	.long SYMBOL_NAME(sys_nice)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* 35 */
-						/* old ftime syscall holder */
-	.long SYMBOL_NAME(sys_sync)
-	.long SYMBOL_NAME(sys_kill)
-	.long SYMBOL_NAME(sys_rename)
-	.long SYMBOL_NAME(sys_mkdir)
-	.long SYMBOL_NAME(sys_rmdir)		/* 40 */
-	.long SYMBOL_NAME(sys_dup)
-	.long SYMBOL_NAME(sys_pipe)
-	.long SYMBOL_NAME(sys_times)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* old prof syscall holder */
-	.long SYMBOL_NAME(sys_brk)		/* 45 */
-	.long SYMBOL_NAME(sys_setgid16)
-	.long SYMBOL_NAME(sys_getgid16)
-	.long SYMBOL_NAME(sys_signal)
-	.long SYMBOL_NAME(sys_geteuid16)
-	.long SYMBOL_NAME(sys_getegid16)	/* 50 */
-	.long SYMBOL_NAME(sys_acct)
-	.long SYMBOL_NAME(sys_umount)		/* recycled never used phys() */
-	.long SYMBOL_NAME(sys_ni_syscall)	/* old lock syscall holder */
-	.long SYMBOL_NAME(sys_ioctl)
-	.long SYMBOL_NAME(sys_fcntl)		/* 55 */
-	.long SYMBOL_NAME(sys_ni_syscall)	/* old mpx syscall holder */
-	.long SYMBOL_NAME(sys_setpgid)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* old ulimit syscall holder */
-	.long SYMBOL_NAME(sys_olduname)
-	.long SYMBOL_NAME(sys_umask)		/* 60 */
-	.long SYMBOL_NAME(sys_chroot)
-	.long SYMBOL_NAME(sys_ustat)
-	.long SYMBOL_NAME(sys_dup2)
-	.long SYMBOL_NAME(sys_getppid)
-	.long SYMBOL_NAME(sys_getpgrp)		/* 65 */
-	.long SYMBOL_NAME(sys_setsid)
-	.long SYMBOL_NAME(sys_sigaction)
-	.long SYMBOL_NAME(sys_sgetmask)
-	.long SYMBOL_NAME(sys_ssetmask)
-	.long SYMBOL_NAME(sys_setreuid16)	/* 70 */
-	.long SYMBOL_NAME(sys_setregid16)
-	.long SYMBOL_NAME(sys_sigsuspend)
-	.long SYMBOL_NAME(sys_sigpending)
-	.long SYMBOL_NAME(sys_sethostname)
-	.long SYMBOL_NAME(sys_setrlimit)	/* 75 */
-	.long SYMBOL_NAME(sys_old_getrlimit)
-	.long SYMBOL_NAME(sys_getrusage)
-	.long SYMBOL_NAME(sys_gettimeofday)
-	.long SYMBOL_NAME(sys_settimeofday)
-	.long SYMBOL_NAME(sys_getgroups16)	/* 80 */
-	.long SYMBOL_NAME(sys_setgroups16)
-	.long SYMBOL_NAME(old_select)
-	.long SYMBOL_NAME(sys_symlink)
-	.long SYMBOL_NAME(sys_lstat)
-	.long SYMBOL_NAME(sys_readlink)		/* 85 */
-	.long SYMBOL_NAME(sys_uselib)
-	.long SYMBOL_NAME(sys_swapon)
-	.long SYMBOL_NAME(sys_reboot)
-	.long SYMBOL_NAME(old_readdir)
-	.long SYMBOL_NAME(old_mmap)		/* 90 */
-	.long SYMBOL_NAME(sys_munmap)
-	.long SYMBOL_NAME(sys_truncate)
-	.long SYMBOL_NAME(sys_ftruncate)
-	.long SYMBOL_NAME(sys_fchmod)
-	.long SYMBOL_NAME(sys_fchown16)		/* 95 */
-	.long SYMBOL_NAME(sys_getpriority)
-	.long SYMBOL_NAME(sys_setpriority)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* old profil syscall holder */
-	.long SYMBOL_NAME(sys_statfs)
-	.long SYMBOL_NAME(sys_fstatfs)		/* 100 */
-	.long SYMBOL_NAME(sys_ioperm)
-	.long SYMBOL_NAME(sys_socketcall)
-	.long SYMBOL_NAME(sys_syslog)
-	.long SYMBOL_NAME(sys_setitimer)
-	.long SYMBOL_NAME(sys_getitimer)	/* 105 */
-	.long SYMBOL_NAME(sys_newstat)
-	.long SYMBOL_NAME(sys_newlstat)
-	.long SYMBOL_NAME(sys_newfstat)
-	.long SYMBOL_NAME(sys_uname)
-	.long SYMBOL_NAME(sys_iopl)		/* 110 */
-	.long SYMBOL_NAME(sys_vhangup)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* old "idle" system call */
-	.long SYMBOL_NAME(sys_vm86old)
-	.long SYMBOL_NAME(sys_wait4)
-	.long SYMBOL_NAME(sys_swapoff)		/* 115 */
-	.long SYMBOL_NAME(sys_sysinfo)
-	.long SYMBOL_NAME(sys_ipc)
-	.long SYMBOL_NAME(sys_fsync)
-	.long SYMBOL_NAME(sys_sigreturn)
-	.long SYMBOL_NAME(sys_clone)		/* 120 */
-	.long SYMBOL_NAME(sys_setdomainname)
-	.long SYMBOL_NAME(sys_newuname)
-	.long SYMBOL_NAME(sys_modify_ldt)
-	.long SYMBOL_NAME(sys_adjtimex)
-	.long SYMBOL_NAME(sys_mprotect)		/* 125 */
-	.long SYMBOL_NAME(sys_sigprocmask)
-	.long SYMBOL_NAME(sys_create_module)
-	.long SYMBOL_NAME(sys_init_module)
-	.long SYMBOL_NAME(sys_delete_module)
-	.long SYMBOL_NAME(sys_get_kernel_syms)	/* 130 */
-	.long SYMBOL_NAME(sys_quotactl)
-	.long SYMBOL_NAME(sys_getpgid)
-	.long SYMBOL_NAME(sys_fchdir)
-	.long SYMBOL_NAME(sys_bdflush)
-	.long SYMBOL_NAME(sys_sysfs)		/* 135 */
-	.long SYMBOL_NAME(sys_personality)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* for afs_syscall */
-	.long SYMBOL_NAME(sys_setfsuid16)
-	.long SYMBOL_NAME(sys_setfsgid16)
-	.long SYMBOL_NAME(sys_llseek)		/* 140 */
-	.long SYMBOL_NAME(sys_getdents)
-	.long SYMBOL_NAME(sys_select)
-	.long SYMBOL_NAME(sys_flock)
-	.long SYMBOL_NAME(sys_msync)
-	.long SYMBOL_NAME(sys_readv)		/* 145 */
-	.long SYMBOL_NAME(sys_writev)
-	.long SYMBOL_NAME(sys_getsid)
-	.long SYMBOL_NAME(sys_fdatasync)
-	.long SYMBOL_NAME(sys_sysctl)
-	.long SYMBOL_NAME(sys_mlock)		/* 150 */
-	.long SYMBOL_NAME(sys_munlock)
-	.long SYMBOL_NAME(sys_mlockall)
-	.long SYMBOL_NAME(sys_munlockall)
-	.long SYMBOL_NAME(sys_sched_setparam)
-	.long SYMBOL_NAME(sys_sched_getparam)   /* 155 */
-	.long SYMBOL_NAME(sys_sched_setscheduler)
-	.long SYMBOL_NAME(sys_sched_getscheduler)
-	.long SYMBOL_NAME(sys_sched_yield)
-	.long SYMBOL_NAME(sys_sched_get_priority_max)
-	.long SYMBOL_NAME(sys_sched_get_priority_min)  /* 160 */
-	.long SYMBOL_NAME(sys_sched_rr_get_interval)
-	.long SYMBOL_NAME(sys_nanosleep)
-	.long SYMBOL_NAME(sys_mremap)
-	.long SYMBOL_NAME(sys_setresuid16)
-	.long SYMBOL_NAME(sys_getresuid16)	/* 165 */
-	.long SYMBOL_NAME(sys_vm86)
-	.long SYMBOL_NAME(sys_query_module)
-	.long SYMBOL_NAME(sys_poll)
-	.long SYMBOL_NAME(sys_nfsservctl)
-	.long SYMBOL_NAME(sys_setresgid16)	/* 170 */
-	.long SYMBOL_NAME(sys_getresgid16)
-	.long SYMBOL_NAME(sys_prctl)
-	.long SYMBOL_NAME(sys_rt_sigreturn)
-	.long SYMBOL_NAME(sys_rt_sigaction)
-	.long SYMBOL_NAME(sys_rt_sigprocmask)	/* 175 */
-	.long SYMBOL_NAME(sys_rt_sigpending)
-	.long SYMBOL_NAME(sys_rt_sigtimedwait)
-	.long SYMBOL_NAME(sys_rt_sigqueueinfo)
-	.long SYMBOL_NAME(sys_rt_sigsuspend)
-	.long SYMBOL_NAME(sys_pread)		/* 180 */
-	.long SYMBOL_NAME(sys_pwrite)
-	.long SYMBOL_NAME(sys_chown16)
-	.long SYMBOL_NAME(sys_getcwd)
-	.long SYMBOL_NAME(sys_capget)
-	.long SYMBOL_NAME(sys_capset)           /* 185 */
-	.long SYMBOL_NAME(sys_sigaltstack)
-	.long SYMBOL_NAME(sys_sendfile)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* streams1 */
-	.long SYMBOL_NAME(sys_ni_syscall)	/* streams2 */
-	.long SYMBOL_NAME(sys_vfork)            /* 190 */
-	.long SYMBOL_NAME(sys_getrlimit)
-	.long SYMBOL_NAME(sys_mmap2)
-	.long SYMBOL_NAME(sys_truncate64)
-	.long SYMBOL_NAME(sys_ftruncate64)
-	.long SYMBOL_NAME(sys_stat64)		/* 195 */
-	.long SYMBOL_NAME(sys_lstat64)
-	.long SYMBOL_NAME(sys_fstat64)
-	.long SYMBOL_NAME(sys_lchown)
-	.long SYMBOL_NAME(sys_getuid)
-	.long SYMBOL_NAME(sys_getgid)		/* 200 */
-	.long SYMBOL_NAME(sys_geteuid)
-	.long SYMBOL_NAME(sys_getegid)
-	.long SYMBOL_NAME(sys_setreuid)
-	.long SYMBOL_NAME(sys_setregid)
-	.long SYMBOL_NAME(sys_getgroups)	/* 205 */
-	.long SYMBOL_NAME(sys_setgroups)
-	.long SYMBOL_NAME(sys_fchown)
-	.long SYMBOL_NAME(sys_setresuid)
-	.long SYMBOL_NAME(sys_getresuid)
-	.long SYMBOL_NAME(sys_setresgid)	/* 210 */
-	.long SYMBOL_NAME(sys_getresgid)
-	.long SYMBOL_NAME(sys_chown)
-	.long SYMBOL_NAME(sys_setuid)
-	.long SYMBOL_NAME(sys_setgid)
-	.long SYMBOL_NAME(sys_setfsuid)		/* 215 */
-	.long SYMBOL_NAME(sys_setfsgid)
-	.long SYMBOL_NAME(sys_pivot_root)
-	.long SYMBOL_NAME(sys_mincore)
-	.long SYMBOL_NAME(sys_madvise)
-	.long SYMBOL_NAME(sys_getdents64)	/* 220 */
-	.long SYMBOL_NAME(sys_fcntl64)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* reserved for TUX */
-	.long SYMBOL_NAME(sys_ni_syscall)	/* Reserved for Security */
-	.long SYMBOL_NAME(sys_gettid)
-	.long SYMBOL_NAME(sys_readahead)	/* 225 */
-	.long SYMBOL_NAME(sys_setxattr)
-	.long SYMBOL_NAME(sys_lsetxattr)
-	.long SYMBOL_NAME(sys_fsetxattr)
-	.long SYMBOL_NAME(sys_getxattr)
-	.long SYMBOL_NAME(sys_lgetxattr)	/* 230 */
-	.long SYMBOL_NAME(sys_fgetxattr)
-	.long SYMBOL_NAME(sys_listxattr)
-	.long SYMBOL_NAME(sys_llistxattr)
-	.long SYMBOL_NAME(sys_flistxattr)
-	.long SYMBOL_NAME(sys_removexattr)	/* 235 */
-	.long SYMBOL_NAME(sys_lremovexattr)
-	.long SYMBOL_NAME(sys_fremovexattr)
-	.long SYMBOL_NAME(sys_tkill)
-	.long SYMBOL_NAME(sys_sendfile64)
-	.long SYMBOL_NAME(sys_futex)		/* 240 */
-	.long SYMBOL_NAME(sys_sched_setaffinity)
-	.long SYMBOL_NAME(sys_sched_getaffinity)
+	.long sys_ni_syscall	/* 0 - old "setup()" system call*/
+	.long sys_exit
+	.long sys_fork
+	.long sys_read
+	.long sys_write
+	.long sys_open		/* 5 */
+	.long sys_close
+	.long sys_waitpid
+	.long sys_creat
+	.long sys_link
+	.long sys_unlink	/* 10 */
+	.long sys_execve
+	.long sys_chdir
+	.long sys_time
+	.long sys_mknod
+	.long sys_chmod		/* 15 */
+	.long sys_lchown16
+	.long sys_ni_syscall	/* old break syscall holder */
+	.long sys_stat
+	.long sys_lseek
+	.long sys_getpid	/* 20 */
+	.long sys_mount
+	.long sys_oldumount
+	.long sys_setuid16
+	.long sys_getuid16
+	.long sys_stime		/* 25 */
+	.long sys_ptrace
+	.long sys_alarm
+	.long sys_fstat
+	.long sys_pause
+	.long sys_utime		/* 30 */
+	.long sys_ni_syscall	/* old stty syscall holder */
+	.long sys_ni_syscall	/* old gtty syscall holder */
+	.long sys_access
+	.long sys_nice
+	.long sys_ni_syscall	/* 35 - old ftime syscall holder */
+	.long sys_sync
+	.long sys_kill
+	.long sys_rename
+	.long sys_mkdir
+	.long sys_rmdir		/* 40 */
+	.long sys_dup
+	.long sys_pipe
+	.long sys_times
+	.long sys_ni_syscall	/* old prof syscall holder */
+	.long sys_brk		/* 45 */
+	.long sys_setgid16
+	.long sys_getgid16
+	.long sys_signal
+	.long sys_geteuid16
+	.long sys_getegid16	/* 50 */
+	.long sys_acct
+	.long sys_umount	/* recycled never used phys() */
+	.long sys_ni_syscall	/* old lock syscall holder */
+	.long sys_ioctl
+	.long sys_fcntl		/* 55 */
+	.long sys_ni_syscall	/* old mpx syscall holder */
+	.long sys_setpgid
+	.long sys_ni_syscall	/* old ulimit syscall holder */
+	.long sys_olduname
+	.long sys_umask		/* 60 */
+	.long sys_chroot
+	.long sys_ustat
+	.long sys_dup2
+	.long sys_getppid
+	.long sys_getpgrp	/* 65 */
+	.long sys_setsid
+	.long sys_sigaction
+	.long sys_sgetmask
+	.long sys_ssetmask
+	.long sys_setreuid16	/* 70 */
+	.long sys_setregid16
+	.long sys_sigsuspend
+	.long sys_sigpending
+	.long sys_sethostname
+	.long sys_setrlimit	/* 75 */
+	.long sys_old_getrlimit
+	.long sys_getrusage
+	.long sys_gettimeofday
+	.long sys_settimeofday
+	.long sys_getgroups16	/* 80 */
+	.long sys_setgroups16
+	.long old_select
+	.long sys_symlink
+	.long sys_lstat
+	.long sys_readlink	/* 85 */
+	.long sys_uselib
+	.long sys_swapon
+	.long sys_reboot
+	.long old_readdir
+	.long old_mmap		/* 90 */
+	.long sys_munmap
+	.long sys_truncate
+	.long sys_ftruncate
+	.long sys_fchmod
+	.long sys_fchown16	/* 95 */
+	.long sys_getpriority
+	.long sys_setpriority
+	.long sys_ni_syscall	/* old profil syscall holder */
+	.long sys_statfs
+	.long sys_fstatfs	/* 100 */
+	.long sys_ioperm
+	.long sys_socketcall
+	.long sys_syslog
+	.long sys_setitimer
+	.long sys_getitimer	/* 105 */
+	.long sys_newstat
+	.long sys_newlstat
+	.long sys_newfstat
+	.long sys_uname
+	.long sys_iopl		/* 110 */
+	.long sys_vhangup
+	.long sys_ni_syscall	/* old "idle" system call */
+	.long sys_vm86old
+	.long sys_wait4
+	.long sys_swapoff	/* 115 */
+	.long sys_sysinfo
+	.long sys_ipc
+	.long sys_fsync
+	.long sys_sigreturn
+	.long sys_clone		/* 120 */
+	.long sys_setdomainname
+	.long sys_newuname
+	.long sys_modify_ldt
+	.long sys_adjtimex
+	.long sys_mprotect	/* 125 */
+	.long sys_sigprocmask
+	.long sys_create_module
+	.long sys_init_module
+	.long sys_delete_module
+	.long sys_get_kernel_syms	/* 130 */
+	.long sys_quotactl
+	.long sys_getpgid
+	.long sys_fchdir
+	.long sys_bdflush
+	.long sys_sysfs		/* 135 */
+	.long sys_personality
+	.long sys_ni_syscall	/* reserved for afs_syscall */
+	.long sys_setfsuid16
+	.long sys_setfsgid16
+	.long sys_llseek	/* 140 */
+	.long sys_getdents
+	.long sys_select
+	.long sys_flock
+	.long sys_msync
+	.long sys_readv		/* 145 */
+	.long sys_writev
+	.long sys_getsid
+	.long sys_fdatasync
+	.long sys_sysctl
+	.long sys_mlock		/* 150 */
+	.long sys_munlock
+	.long sys_mlockall
+	.long sys_munlockall
+	.long sys_sched_setparam
+	.long sys_sched_getparam   /* 155 */
+	.long sys_sched_setscheduler
+	.long sys_sched_getscheduler
+	.long sys_sched_yield
+	.long sys_sched_get_priority_max
+	.long sys_sched_get_priority_min  /* 160 */
+	.long sys_sched_rr_get_interval
+	.long sys_nanosleep
+	.long sys_mremap
+	.long sys_setresuid16
+	.long sys_getresuid16	/* 165 */
+	.long sys_vm86
+	.long sys_query_module
+	.long sys_poll
+	.long sys_nfsservctl
+	.long sys_setresgid16	/* 170 */
+	.long sys_getresgid16
+	.long sys_prctl
+	.long sys_rt_sigreturn
+	.long sys_rt_sigaction
+	.long sys_rt_sigprocmask	/* 175 */
+	.long sys_rt_sigpending
+	.long sys_rt_sigtimedwait
+	.long sys_rt_sigqueueinfo
+	.long sys_rt_sigsuspend
+	.long sys_pread		/* 180 */
+	.long sys_pwrite
+	.long sys_chown16
+	.long sys_getcwd
+	.long sys_capget
+	.long sys_capset	/* 185 */
+	.long sys_sigaltstack
+	.long sys_sendfile
+	.long sys_ni_syscall	/* reserved for streams1 */
+	.long sys_ni_syscall	/* reserved for streams2 */
+	.long sys_vfork		/* 190 */
+	.long sys_getrlimit
+	.long sys_mmap2
+	.long sys_truncate64
+	.long sys_ftruncate64
+	.long sys_stat64	/* 195 */
+	.long sys_lstat64
+	.long sys_fstat64
+	.long sys_lchown
+	.long sys_getuid
+	.long sys_getgid	/* 200 */
+	.long sys_geteuid
+	.long sys_getegid
+	.long sys_setreuid
+	.long sys_setregid
+	.long sys_getgroups	/* 205 */
+	.long sys_setgroups
+	.long sys_fchown
+	.long sys_setresuid
+	.long sys_getresuid
+	.long sys_setresgid	/* 210 */
+	.long sys_getresgid
+	.long sys_chown
+	.long sys_setuid
+	.long sys_setgid
+	.long sys_setfsuid	/* 215 */
+	.long sys_setfsgid
+	.long sys_pivot_root
+	.long sys_mincore
+	.long sys_madvise
+	.long sys_getdents64	/* 220 */
+	.long sys_fcntl64
+	.long sys_ni_syscall	/* reserved for TUX */
+	.long sys_ni_syscall	/* reserved for Security */
+	.long sys_gettid
+	.long sys_readahead	/* 225 */
+	.long sys_setxattr
+	.long sys_lsetxattr
+	.long sys_fsetxattr
+	.long sys_getxattr
+	.long sys_lgetxattr	/* 230 */
+	.long sys_fgetxattr
+	.long sys_listxattr
+	.long sys_llistxattr
+	.long sys_flistxattr
+	.long sys_removexattr	/* 235 */
+	.long sys_lremovexattr
+	.long sys_fremovexattr
+	.long sys_tkill
+	.long sys_sendfile64
+	.long sys_futex		/* 240 */
+	.long sys_sched_setaffinity
+	.long sys_sched_getaffinity
 
 	.rept NR_syscalls-(.-sys_call_table)/4
-		.long SYMBOL_NAME(sys_ni_syscall)
+		.long sys_ni_syscall
 	.endr
diff -Nru a/arch/i386/kernel/head.S b/arch/i386/kernel/head.S
--- a/arch/i386/kernel/head.S	Sun Apr 28 20:10:31 2002
+++ b/arch/i386/kernel/head.S	Sun Apr 28 20:10:31 2002
@@ -26,7 +26,7 @@
  * References to members of the boot_cpu_data structure.
  */
 
-#define CPU_PARAMS	SYMBOL_NAME(boot_cpu_data)
+#define CPU_PARAMS	boot_cpu_data
 #define X86		CPU_PARAMS+0
 #define X86_VENDOR	CPU_PARAMS+1
 #define X86_MODEL	CPU_PARAMS+2
@@ -120,8 +120,8 @@
  * No need to cld as DF is already clear from cld above...
  */
 	xorl %eax,%eax
-	movl $ SYMBOL_NAME(__bss_start),%edi
-	movl $ SYMBOL_NAME(_end),%ecx
+	movl $__bss_start,%edi
+	movl $_end,%ecx
 	subl %edi,%ecx
 	rep
 	stosb
@@ -145,7 +145,7 @@
  *
  * Note: %esi still has the pointer to the real-mode data.
  */
-	movl $ SYMBOL_NAME(empty_zero_page),%edi
+	movl $empty_zero_page,%edi
 	movl $512,%ecx
 	cld
 	rep
@@ -154,7 +154,7 @@
 	movl $512,%ecx
 	rep
 	stosl
-	movl SYMBOL_NAME(empty_zero_page)+NEW_CL_POINTER,%esi
+	movl empty_zero_page+NEW_CL_POINTER,%esi
 	andl %esi,%esi
 	jnz 2f			# New command line protocol
 	cmpw $(OLD_CL_MAGIC),OLD_CL_MAGIC_ADDR
@@ -162,7 +162,7 @@
 	movzwl OLD_CL_OFFSET,%esi
 	addl $(OLD_CL_BASE_ADDR),%esi
 2:
-	movl $ SYMBOL_NAME(empty_zero_page)+2048,%edi
+	movl $empty_zero_page+2048,%edi
 	movl $512,%ecx
 	rep
 	movsl
@@ -263,11 +263,11 @@
 	cmpb $1,%cl
 	je 1f			# the first CPU calls start_kernel
 				# all other CPUs call initialize_secondary
-	call SYMBOL_NAME(initialize_secondary)
+	call initialize_secondary
 	jmp L6
 1:
 #endif
-	call SYMBOL_NAME(start_kernel)
+	call start_kernel
 L6:
 	jmp L6			# main should never return here, but
 				# just in case, we know what happens.
@@ -309,7 +309,7 @@
 	movw %dx,%ax		/* selector = 0x0010 = cs */
 	movw $0x8E00,%dx	/* interrupt gate - dpl=0, present */
 
-	lea SYMBOL_NAME(idt_table),%edi
+	lea idt_table,%edi
 	mov $256,%ecx
 rp_sidt:
 	movl %eax,(%edi)
@@ -320,7 +320,7 @@
 	ret
 
 ENTRY(stack_start)
-	.long SYMBOL_NAME(init_thread_union)+8192
+	.long init_thread_union+8192
 	.long __KERNEL_DS
 
 /* This is the default interrupt "handler" :-) */
@@ -338,7 +338,7 @@
 	movl %eax,%ds
 	movl %eax,%es
 	pushl $int_msg
-	call SYMBOL_NAME(printk)
+	call printk
 	popl %eax
 	popl %ds
 	popl %es
@@ -356,21 +356,21 @@
 #define GDT_ENTRIES	(__TSS(NR_CPUS))
 
 
-.globl SYMBOL_NAME(idt)
-.globl SYMBOL_NAME(gdt)
+.globl idt
+.globl gdt
 
 	ALIGN
 	.word 0
 idt_descr:
 	.word IDT_ENTRIES*8-1		# idt contains 256 entries
-SYMBOL_NAME(idt):
-	.long SYMBOL_NAME(idt_table)
+idt:
+	.long idt_table
 
 	.word 0
 gdt_descr:
 	.word GDT_ENTRIES*8-1
-SYMBOL_NAME(gdt):
-	.long SYMBOL_NAME(gdt_table)
+gdt:
+	.long gdt_table
 
 /*
  * This is initialized to create an identity-mapping at 0-8M (for bootup
diff -Nru a/arch/i386/kernel/i8259.c b/arch/i386/kernel/i8259.c
--- a/arch/i386/kernel/i8259.c	Sun Apr 28 20:10:38 2002
+++ b/arch/i386/kernel/i8259.c	Sun Apr 28 20:10:38 2002
@@ -391,6 +391,11 @@
 	/* IPI vectors for APIC spurious and error interrupts */
 	set_intr_gate(SPURIOUS_APIC_VECTOR, spurious_interrupt);
 	set_intr_gate(ERROR_APIC_VECTOR, error_interrupt);
+
+	/* thermal monitor LVT interrupt */
+#ifdef CONFIG_X86_MCE_P4THERMAL
+	set_intr_gate(THERMAL_APIC_VECTOR, smp_thermal_interrupt);
+#endif
 #endif
 
 	/*
diff -Nru a/arch/i386/kernel/ldt.c b/arch/i386/kernel/ldt.c
--- a/arch/i386/kernel/ldt.c	Sun Apr 28 20:10:32 2002
+++ b/arch/i386/kernel/ldt.c	Sun Apr 28 20:10:32 2002
@@ -12,37 +12,137 @@
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
 #include <linux/vmalloc.h>
+#include <linux/slab.h>
 
 #include <asm/uaccess.h>
 #include <asm/system.h>
 #include <asm/ldt.h>
 #include <asm/desc.h>
 
+#ifdef CONFIG_SMP /* avoids "defined but not used" warnig */
+static void flush_ldt(void *mm)
+{
+	if (current->mm)
+		load_LDT(&current->mm->context);
+}
+#endif
+
+static int alloc_ldt(mm_context_t *pc, int mincount, int reload)
+{
+	void *oldldt;
+	void *newldt;
+	int oldsize;
+
+	if (mincount <= pc->size)
+		return 0;
+	oldsize = pc->size;
+	mincount = (mincount+511)&(~511);
+	if (mincount*LDT_ENTRY_SIZE > PAGE_SIZE)
+		newldt = vmalloc(mincount*LDT_ENTRY_SIZE);
+	else
+		newldt = kmalloc(mincount*LDT_ENTRY_SIZE, GFP_KERNEL);
+
+	if (!newldt)
+		return -ENOMEM;
+
+	if (oldsize)
+		memcpy(newldt, pc->ldt, oldsize*LDT_ENTRY_SIZE);
+	oldldt = pc->ldt;
+	memset(newldt+oldsize*LDT_ENTRY_SIZE, 0, (mincount-oldsize)*LDT_ENTRY_SIZE);
+	wmb();
+	pc->ldt = newldt;
+	pc->size = mincount;
+	if (reload) {
+		load_LDT(pc);
+#ifdef CONFIG_SMP
+		if (current->mm->cpu_vm_mask != (1<<smp_processor_id()))
+			smp_call_function(flush_ldt, 0, 1, 1);
+#endif
+	}
+	wmb();
+	if (oldsize) {
+		if (oldsize*LDT_ENTRY_SIZE > PAGE_SIZE)
+			vfree(oldldt);
+		else
+			kfree(oldldt);
+	}
+	return 0;
+}
+
+static inline int copy_ldt(mm_context_t *new, mm_context_t *old)
+{
+	int err = alloc_ldt(new, old->size, 0);
+	if (err < 0) {
+		printk(KERN_WARNING "ldt allocation failed\n");
+		new->size = 0;
+		return err;
+	}
+	memcpy(new->ldt, old->ldt, old->size*LDT_ENTRY_SIZE);
+	return 0;
+}
+
 /*
- * read_ldt() is not really atomic - this is not a problem since
- * synchronization of reads and writes done to the LDT has to be
- * assured by user-space anyway. Writes are atomic, to protect
- * the security checks done on new descriptors.
+ * we do not have to muck with descriptors here, that is
+ * done in switch_mm() as needed.
  */
+int init_new_context(struct task_struct *tsk, struct mm_struct *mm)
+{
+	struct mm_struct * old_mm;
+	int retval = 0;
+
+	init_MUTEX(&mm->context.sem);
+	mm->context.size = 0;
+	old_mm = current->mm;
+	if (old_mm && old_mm->context.size > 0) {
+		down(&old_mm->context.sem);
+		retval = copy_ldt(&mm->context, &old_mm->context);
+		up(&old_mm->context.sem);
+	}
+	return retval;
+}
+
+/*
+ * No need to lock the MM as we are the last user
+ */
+void release_segments(struct mm_struct *mm)
+{
+	if (mm->context.size) {
+		clear_LDT();
+		if (mm->context.size*LDT_ENTRY_SIZE > PAGE_SIZE)
+			vfree(mm->context.ldt);
+		else
+			kfree(mm->context.ldt);
+		mm->context.size = 0;
+	}
+}
+
 static int read_ldt(void * ptr, unsigned long bytecount)
 {
 	int err;
 	unsigned long size;
 	struct mm_struct * mm = current->mm;
 
-	err = 0;
-	if (!mm->context.segments)
-		goto out;
+	if (!mm->context.size)
+		return 0;
+	if (bytecount > LDT_ENTRY_SIZE*LDT_ENTRIES)
+		bytecount = LDT_ENTRY_SIZE*LDT_ENTRIES;
 
-	size = LDT_ENTRIES*LDT_ENTRY_SIZE;
+	down(&mm->context.sem);
+	size = mm->context.size*LDT_ENTRY_SIZE;
 	if (size > bytecount)
 		size = bytecount;
 
-	err = size;
-	if (copy_to_user(ptr, mm->context.segments, size))
+	err = 0;
+	if (copy_to_user(ptr, mm->context.ldt, size))
 		err = -EFAULT;
-out:
-	return err;
+	up(&mm->context.sem);
+	if (err < 0)
+		return err;
+	if (size != bytecount) {
+		/* zero-fill the rest */
+		clear_user(ptr+size, bytecount-size);
+	}
+	return bytecount;
 }
 
 static int read_default_ldt(void * ptr, unsigned long bytecount)
@@ -53,7 +153,7 @@
 
 	err = 0;
 	address = &default_ldt[0];
-	size = sizeof(struct desc_struct);
+	size = 5*sizeof(struct desc_struct);
 	if (size > bytecount)
 		size = bytecount;
 
@@ -88,24 +188,14 @@
 			goto out;
 	}
 
-	/*
-	 * the GDT index of the LDT is allocated dynamically, and is
-	 * limited by MAX_LDT_DESCRIPTORS.
-	 */
-	down_write(&mm->mmap_sem);
-	if (!mm->context.segments) {
-		void * segments = vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE);
-		error = -ENOMEM;
-		if (!segments)
+	down(&mm->context.sem);
+	if (ldt_info.entry_number >= mm->context.size) {
+		error = alloc_ldt(&current->mm->context, ldt_info.entry_number+1, 1);
+		if (error < 0)
 			goto out_unlock;
-		memset(segments, 0, LDT_ENTRIES*LDT_ENTRY_SIZE);
-		wmb();
-		mm->context.segments = segments;
-		mm->context.cpuvalid = 1UL << smp_processor_id();
-		load_LDT(mm);
 	}
 
-	lp = (__u32 *) ((ldt_info.entry_number << 3) + (char *) mm->context.segments);
+	lp = (__u32 *) ((ldt_info.entry_number << 3) + (char *) mm->context.ldt);
 
    	/* Allow LDTs to be cleared by the user. */
    	if (ldt_info.base_addr == 0 && ldt_info.limit == 0) {
@@ -143,7 +233,7 @@
 	error = 0;
 
 out_unlock:
-	up_write(&mm->mmap_sem);
+	up(&mm->context.sem);
 out:
 	return error;
 }
diff -Nru a/arch/i386/kernel/mpparse.c b/arch/i386/kernel/mpparse.c
--- a/arch/i386/kernel/mpparse.c	Sun Apr 28 20:10:34 2002
+++ b/arch/i386/kernel/mpparse.c	Sun Apr 28 20:10:34 2002
@@ -116,6 +116,8 @@
 		case 0x0F:
 			if (model == 0x00)
 				return("Pentium 4(tm)");
+			if (model == 0x02)
+				return("XEON(tm)");
 			if (model == 0x0F)
 				return("Special controller");
 	}
diff -Nru a/arch/i386/kernel/pci-pc.c b/arch/i386/kernel/pci-pc.c
--- a/arch/i386/kernel/pci-pc.c	Sun Apr 28 20:10:35 2002
+++ b/arch/i386/kernel/pci-pc.c	Sun Apr 28 20:10:35 2002
@@ -421,8 +421,9 @@
 		    pci_sanity_check(&pci_direct_conf1)) {
 			outl (tmp, 0xCF8);
 			__restore_flags(flags);
-			printk("PCI: Using configuration type 1\n");
-			request_region(0xCF8, 8, "PCI conf1");
+			printk(KERN_INFO "PCI: Using configuration type 1\n");
+			if (!request_region(0xCF8, 8, "PCI conf1"))
+				return NULL;
 			return &pci_direct_conf1;
 		}
 		outl (tmp, 0xCF8);
@@ -438,8 +439,9 @@
 		if (inb (0xCF8) == 0x00 && inb (0xCFA) == 0x00 &&
 		    pci_sanity_check(&pci_direct_conf2)) {
 			__restore_flags(flags);
-			printk("PCI: Using configuration type 2\n");
-			request_region(0xCF8, 4, "PCI conf2");
+			printk(KERN_INFO "PCI: Using configuration type 2\n");
+			if (!request_region(0xCF8, 4, "PCI conf2"))
+				return NULL;
 			return &pci_direct_conf2;
 		}
 	}
@@ -546,10 +548,10 @@
 		case 0:
 			return address + entry;
 		case 0x80:	/* Not present */
-			printk("bios32_service(0x%lx): not present\n", service);
+			printk(KERN_WARNING "bios32_service(0x%lx): not present\n", service);
 			return 0;
 		default: /* Shouldn't happen */
-			printk("bios32_service(0x%lx): returned 0x%x -- BIOS bug!\n",
+			printk(KERN_WARNING "bios32_service(0x%lx): returned 0x%x -- BIOS bug!\n",
 				service, return_code);
 			return 0;
 	}
@@ -599,7 +601,7 @@
 				status, signature);
 			return 0;
 		}
-		printk("PCI: PCI BIOS revision %x.%02x entry at 0x%lx, last bus=%d\n",
+		printk(KERN_INFO "PCI: PCI BIOS revision %x.%02x entry at 0x%lx, last bus=%d\n",
 			major_ver, minor_ver, pcibios_entry, pcibios_last_bus);
 #ifdef CONFIG_PCI_DIRECT
 		if (!(hw_mech & PCIBIOS_HW_TYPE1))
@@ -901,7 +903,7 @@
 				}
 			}
 			if (ln == &pci_devices) {
-				printk("PCI: BIOS reporting unknown device %02x:%02x\n", bus, devfn);
+				printk(KERN_WARNING "PCI: BIOS reporting unknown device %02x:%02x\n", bus, devfn);
 				/*
 				 * We must not continue scanning as several buggy BIOSes
 				 * return garbage after the last device. Grr.
@@ -910,7 +912,7 @@
 			}
 		}
 		if (!found) {
-			printk("PCI: Device %02x:%02x not found by BIOS\n",
+			printk(KERN_WARNING "PCI: Device %02x:%02x not found by BIOS\n",
 				dev->bus->number, dev->devfn);
 			list_del(&dev->global_list);
 			list_add_tail(&dev->global_list, &sorted_devices);
@@ -970,7 +972,7 @@
 			rt->size = opt.size + sizeof(struct irq_routing_table);
 			rt->exclusive_irqs = map;
 			memcpy(rt->slots, (void *) page, opt.size);
-			printk("PCI: Using BIOS Interrupt Routing Table\n");
+			printk(KERN_INFO "PCI: Using BIOS Interrupt Routing Table\n");
 		}
 	}
 	free_page(page);
@@ -1035,7 +1037,7 @@
 	}
 	if (!seen_host_bridge)
 		return;
-	printk("PCI: Ignoring ghost devices on bus %02x\n", b->number);
+	printk(KERN_WARNING "PCI: Ignoring ghost devices on bus %02x\n", b->number);
 
 	ln = &b->devices;
 	while (ln->next != &b->devices) {
@@ -1073,7 +1075,7 @@
 			if (!pci_read_config_word(&dev, PCI_VENDOR_ID, &l) &&
 			    l != 0x0000 && l != 0xffff) {
 				DBG("Found device at %02x:%02x [%04x]\n", n, dev.devfn, l);
-				printk("PCI: Discovered peer bus %02x\n", n);
+				printk(KERN_INFO "PCI: Discovered peer bus %02x\n", n);
 				pci_scan_bus(n, pci_root_ops, NULL);
 				break;
 			}
@@ -1117,7 +1119,7 @@
 	 */
 	u8 busno;
 	pci_read_config_byte(d, 0x4a, &busno);
-	printk("PCI: i440KX/GX host bridge %s: secondary bus %02x\n", d->slot_name, busno);
+	printk(KERN_INFO "PCI: i440KX/GX host bridge %s: secondary bus %02x\n", d->slot_name, busno);
 	pci_scan_bus(busno, pci_root_ops, NULL);
 	pcibios_last_bus = -1;
 }
@@ -1130,11 +1132,23 @@
 	 */
 	int i;
 
-	printk("PCI: Fixing base address flags for device %s\n", d->slot_name);
+	printk(KERN_WARNING "PCI: Fixing base address flags for device %s\n", d->slot_name);
 	for(i=0; i<4; i++)
 		d->resource[i].flags |= PCI_BASE_ADDRESS_SPACE_IO;
 }
 
+static void __devinit  pci_fixup_ncr53c810(struct pci_dev *d)
+{
+    /*
+     * NCR 53C810 returns class code 0 (at least on some systems).
+     * Fix class to be PCI_CLASS_STORAGE_SCSI
+     */
+    if (!d->class) {
+        printk("PCI: fixing NCR 53C810 class code for %s\n", d->slot_name);
+        d->class = PCI_CLASS_STORAGE_SCSI << 8;
+    }
+}
+
 static void __devinit pci_fixup_ide_bases(struct pci_dev *d)
 {
 	int i;
@@ -1226,6 +1240,7 @@
 	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_VIA,	PCI_DEVICE_ID_VIA_8622,	        pci_fixup_via_northbridge_bug },
 	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_VIA,	PCI_DEVICE_ID_VIA_8361,	        pci_fixup_via_northbridge_bug },
 	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_VIA,	PCI_DEVICE_ID_VIA_8367_0,	pci_fixup_via_northbridge_bug },
+	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_NCR,	PCI_DEVICE_ID_NCR_53C810,	pci_fixup_ncr53c810 },
 	{ 0 }
 };
 
@@ -1261,6 +1276,8 @@
 
 void __devinit pcibios_config_init(void)
 {
+	struct pci_ops *tmp=NULL;
+
 	/*
 	 * Try all known PCI access methods. Note that we support using 
 	 * both PCI BIOS and direct access, with a preference for direct.
@@ -1275,6 +1292,7 @@
 		pci_config_write = pci_bios_write;
 	}
 #endif
+	tmp = pci_root_ops;
 
 #ifdef CONFIG_PCI_DIRECT
 	if ((pci_probe & (PCI_PROBE_CONF1 | PCI_PROBE_CONF2)) 
@@ -1289,6 +1307,10 @@
 		}
 	}
 #endif
+
+	/* if direct access failed, fall back to BIOS access. */
+	if (pci_root_ops == NULL)
+		pci_root_ops = tmp;
 
 	return;
 }
diff -Nru a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c
--- a/arch/i386/kernel/process.c	Sun Apr 28 20:10:30 2002
+++ b/arch/i386/kernel/process.c	Sun Apr 28 20:10:30 2002
@@ -476,23 +476,6 @@
 }
 
 /*
- * No need to lock the MM as we are the last user
- */
-void release_segments(struct mm_struct *mm)
-{
-	void * ldt = mm->context.segments;
-
-	/*
-	 * free the LDT
-	 */
-	if (ldt) {
-		mm->context.segments = NULL;
-		clear_LDT();
-		vfree(ldt);
-	}
-}
-
-/*
  * Create a kernel thread
  */
 int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
@@ -544,40 +527,15 @@
 void release_thread(struct task_struct *dead_task)
 {
 	if (dead_task->mm) {
-		void * ldt = dead_task->mm->context.segments;
-
 		// temporary debugging check
-		if (ldt) {
-			printk("WARNING: dead process %8s still has LDT? <%p>\n",
-					dead_task->comm, ldt);
+		if (dead_task->mm->context.size) {
+			printk("WARNING: dead process %8s still has LDT? <%p/%d>\n",
+					dead_task->comm,
+					dead_task->mm->context.ldt,
+					dead_task->mm->context.size);
 			BUG();
 		}
 	}
-}
-
-/*
- * we do not have to muck with descriptors here, that is
- * done in switch_mm() as needed.
- */
-void copy_segments(struct task_struct *p, struct mm_struct *new_mm)
-{
-	struct mm_struct * old_mm;
-	void *old_ldt, *ldt;
-
-	ldt = NULL;
-	old_mm = current->mm;
-	if (old_mm && (old_ldt = old_mm->context.segments) != NULL) {
-		/*
-		 * Completely new LDT, we initialize it from the parent:
-		 */
-		ldt = vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE);
-		if (!ldt)
-			printk(KERN_WARNING "ldt allocation failed\n");
-		else
-			memcpy(ldt, old_ldt, LDT_ENTRIES*LDT_ENTRY_SIZE);
-	}
-	new_mm->context.segments = ldt;
-	new_mm->context.cpuvalid = ~0UL;	/* valid on all CPU's - they can't have stale data */
 }
 
 /*
diff -Nru a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c
--- a/arch/i386/kernel/setup.c	Sun Apr 28 20:10:36 2002
+++ b/arch/i386/kernel/setup.c	Sun Apr 28 20:10:36 2002
@@ -2875,7 +2875,7 @@
 	set_tss_desc(nr,t);
 	gdt_table[__TSS(nr)].b &= 0xfffffdff;
 	load_TR(nr);
-	load_LDT(&init_mm);
+	load_LDT(&init_mm.context);
 
 	/* Clear %fs and %gs. */
 	asm volatile ("xorl %eax, %eax; movl %eax, %fs; movl %eax, %gs");
diff -Nru a/arch/i386/kernel/trampoline.S b/arch/i386/kernel/trampoline.S
--- a/arch/i386/kernel/trampoline.S	Sun Apr 28 20:10:35 2002
+++ b/arch/i386/kernel/trampoline.S	Sun Apr 28 20:10:35 2002
@@ -67,5 +67,5 @@
 	.word	0x0800			# gdt limit = 2048, 256 GDT entries
 	.long	gdt_table-__PAGE_OFFSET	# gdt base = gdt (first SMP CPU)
 
-.globl SYMBOL_NAME(trampoline_end)
-SYMBOL_NAME_LABEL(trampoline_end)
+.globl trampoline_end
+trampoline_end:
diff -Nru a/arch/i386/math-emu/fpu_asm.h b/arch/i386/math-emu/fpu_asm.h
--- a/arch/i386/math-emu/fpu_asm.h	Sun Apr 28 20:10:31 2002
+++ b/arch/i386/math-emu/fpu_asm.h	Sun Apr 28 20:10:31 2002
@@ -12,7 +12,7 @@
 
 #include <linux/linkage.h>
 
-#define	EXCEPTION	SYMBOL_NAME(FPU_exception)
+#define	EXCEPTION	FPU_exception
 
 
 #define PARAM1	8(%ebp)
diff -Nru a/arch/i386/math-emu/fpu_system.h b/arch/i386/math-emu/fpu_system.h
--- a/arch/i386/math-emu/fpu_system.h	Sun Apr 28 20:10:34 2002
+++ b/arch/i386/math-emu/fpu_system.h	Sun Apr 28 20:10:34 2002
@@ -20,7 +20,7 @@
    of the stack frame of math_emulate() */
 #define SETUP_DATA_AREA(arg)	FPU_info = (struct info *) &arg
 
-#define LDT_DESCRIPTOR(s)	(((struct desc_struct *)current->mm->context.segments)[(s) >> 3])
+#define LDT_DESCRIPTOR(s)	(((struct desc_struct *)current->mm->context.ldt)[(s) >> 3])
 #define SEG_D_SIZE(x)		((x).b & (3 << 21))
 #define SEG_G_BIT(x)		((x).b & (1 << 23))
 #define SEG_GRANULARITY(x)	(((x).b & (1 << 23)) ? 4096 : 1)
diff -Nru a/arch/i386/math-emu/reg_norm.S b/arch/i386/math-emu/reg_norm.S
--- a/arch/i386/math-emu/reg_norm.S	Sun Apr 28 20:10:35 2002
+++ b/arch/i386/math-emu/reg_norm.S	Sun Apr 28 20:10:35 2002
@@ -83,7 +83,7 @@
 	/* Convert the exponent to 80x87 form. */
 	addw	EXTENDED_Ebias,EXP(%ebx)
 	push	%ebx
-	call	SYMBOL_NAME(arith_underflow)
+	call	arith_underflow
 	pop	%ebx
 	jmp	L_exit
 
@@ -91,7 +91,7 @@
 	/* Convert the exponent to 80x87 form. */
 	addw	EXTENDED_Ebias,EXP(%ebx)
 	push	%ebx
-	call	SYMBOL_NAME(arith_overflow)
+	call	arith_overflow
 	pop	%ebx
 	jmp	L_exit
 
diff -Nru a/arch/i386/math-emu/reg_round.S b/arch/i386/math-emu/reg_round.S
--- a/arch/i386/math-emu/reg_round.S	Sun Apr 28 20:10:37 2002
+++ b/arch/i386/math-emu/reg_round.S	Sun Apr 28 20:10:37 2002
@@ -447,7 +447,7 @@
 L_precision_lost_up:
 	push	%edx
 	push	%eax
-	call	SYMBOL_NAME(set_precision_flag_up)
+	call	set_precision_flag_up
 	popl	%eax
 	popl	%edx
 	jmp	L_no_precision_loss
@@ -459,7 +459,7 @@
 L_precision_lost_down:
 	push	%edx
 	push	%eax
-	call	SYMBOL_NAME(set_precision_flag_down)
+	call	set_precision_flag_down
 	popl	%eax
 	popl	%edx
 	jmp	L_no_precision_loss
@@ -617,7 +617,7 @@
  */
 L_underflow_to_zero:
 	push	%eax
-	call	SYMBOL_NAME(set_precision_flag_down)
+	call	set_precision_flag_down
 	popl	%eax
 
 	push	%eax
@@ -636,7 +636,7 @@
 L_overflow:
 	addw	EXTENDED_Ebias,EXP(%edi)	/* Set for unmasked response. */
 	push	%edi
-	call	SYMBOL_NAME(arith_overflow)
+	call	arith_overflow
 	pop	%edi
 	jmp	fpu_reg_round_signed_special_exit
 
diff -Nru a/arch/ia64/Config.help b/arch/ia64/Config.help
--- a/arch/ia64/Config.help	Sun Apr 28 20:10:35 2002
+++ b/arch/ia64/Config.help	Sun Apr 28 20:10:35 2002
@@ -557,3 +557,8 @@
   best used in conjunction with the NMI watchdog so that spinlock
   deadlocks are also debuggable.
 
+CONFIG_IA64_GRANULE_16MB
+  IA64 identity-mapped regions use a large page size called "granules".
+
+  Select "16MB" for a small granule size.
+  Select "64MB" for a large granule size.  This is the current default.
diff -Nru a/arch/ia64/config.in b/arch/ia64/config.in
--- a/arch/ia64/config.in	Sun Apr 28 20:10:37 2002
+++ b/arch/ia64/config.in	Sun Apr 28 20:10:37 2002
@@ -83,7 +83,7 @@
 define_bool CONFIG_KCORE_ELF y	# On IA-64, we always want an ELF /proc/kcore.
 
 bool 'SMP support' CONFIG_SMP
-tristate 'Support running of Linux/x86 binaries' CONFIG_IA32_SUPPORT
+bool 'Support running of Linux/x86 binaries' CONFIG_IA32_SUPPORT
 bool 'Performance monitor support' CONFIG_PERFMON
 tristate '/proc/pal support' CONFIG_IA64_PALINFO
 tristate '/proc/efi/vars support' CONFIG_EFI_VARS
@@ -123,6 +123,7 @@
 source drivers/ieee1394/Config.in
 source drivers/message/i2o/Config.in
 source drivers/md/Config.in
+source drivers/message/fusion/Config.in
 
 mainmenu_option next_comment
 comment 'ATA/IDE/MFM/RLL support'
diff -Nru a/arch/ia64/defconfig b/arch/ia64/defconfig
--- a/arch/ia64/defconfig	Sun Apr 28 20:10:31 2002
+++ b/arch/ia64/defconfig	Sun Apr 28 20:10:31 2002
@@ -640,7 +640,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ia64/hp/common/sba_iommu.c b/arch/ia64/hp/common/sba_iommu.c
--- a/arch/ia64/hp/common/sba_iommu.c	Sun Apr 28 20:10:31 2002
+++ b/arch/ia64/hp/common/sba_iommu.c	Sun Apr 28 20:10:31 2002
@@ -214,6 +214,7 @@
 static struct sba_device *sba_list;
 static int sba_count;
 static int reserve_sba_gart = 1;
+static struct pci_dev sac_only_dev;
 
 #define sba_sg_iova(sg) (sg->address)
 #define sba_sg_len(sg) (sg->length)
@@ -950,7 +951,12 @@
 
 	if (ret) {
 		memset(ret, 0, size);
-		*dma_handle = sba_map_single(hwdev, ret, size, 0);
+		/*
+		 * REVISIT: if sba_map_single starts needing more
+		 * than dma_mask from the device, this needs to be
+		 * updated.
+		 */
+		*dma_handle = sba_map_single(&sac_only_dev, ret, size, 0);
 	}
 
 	return ret;
@@ -1807,1854 +1813,10 @@
 	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.
+	 * We pass this fake device from alloc_consistent to ensure
+	 * we only use SAC for alloc_consistent mappings.
 	 */
-	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;
+	sac_only_dev.dma_mask = 0xFFFFFFFFUL;
 
 	/*
 	 * We need to check for an AGP device, if we find one, then only
diff -Nru a/arch/ia64/hp/zx1/hpzx1_misc.c b/arch/ia64/hp/zx1/hpzx1_misc.c
--- a/arch/ia64/hp/zx1/hpzx1_misc.c	Sun Apr 28 20:10:36 2002
+++ b/arch/ia64/hp/zx1/hpzx1_misc.c	Sun Apr 28 20:10:36 2002
@@ -42,7 +42,7 @@
 
 static struct fake_pci_dev *fake_pci_head, **fake_pci_tail = &fake_pci_head;
 
-static struct pci_ops orig_pci_ops;
+static struct pci_ops *orig_pci_ops;
 
 static inline struct fake_pci_dev *
 fake_pci_find_slot(unsigned char bus, unsigned int devfn)
@@ -77,7 +77,7 @@
 { \
 	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); \
+		return orig_pci_ops->name(dev, where, value); \
 	\
 	switch (where) { \
 	case PCI_COMMAND: \
@@ -105,7 +105,7 @@
 { \
 	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); \
+		return orig_pci_ops->name(dev, where, value); \
 	\
 	switch (where) { \
 	case PCI_BASE_ADDRESS_0: \
@@ -295,7 +295,7 @@
 	if (status != AE_OK)
 		return status;
 
-	status = acpi_cf_evaluate_method(obj, METHOD_NAME__BBN, &busnum);
+	status = acpi_evaluate_integer(obj, METHOD_NAME__BBN, NULL, &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
@@ -313,7 +313,7 @@
 static void
 hpzx1_acpi_dev_init(void)
 {
-	extern struct pci_ops pci_conf;
+	extern struct pci_ops *pci_root_ops;
 
 	/*
 	 * Make fake PCI devices for the following hardware in the
@@ -383,8 +383,8 @@
 	/*
 	 * Replace PCI ops, but only if we made fake devices.
 	 */
-	orig_pci_ops = pci_conf;
-	pci_conf = hp_pci_conf;
+	orig_pci_ops = pci_root_ops;
+	pci_root_ops = &hp_pci_conf;
 }
 
 extern void sba_init(void);
diff -Nru a/arch/ia64/kernel/acpi.c b/arch/ia64/kernel/acpi.c
--- a/arch/ia64/kernel/acpi.c	Sun Apr 28 20:10:36 2002
+++ b/arch/ia64/kernel/acpi.c	Sun Apr 28 20:10:36 2002
@@ -56,18 +56,43 @@
 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)
 {
 #ifdef CONFIG_IA64_GENERIC
-	return "hpsim";
+	unsigned long rsdp_phys = 0;
+	struct acpi20_table_rsdp *rsdp;
+	struct acpi_table_xsdt *xsdt;
+	struct acpi_table_header *hdr;
+
+	if ((0 != acpi_find_rsdp(&rsdp_phys)) || !rsdp_phys) {
+		printk("ACPI 2.0 RSDP not found, default to \"dig\"\n");
+		return "dig";
+	}
+
+	rsdp = (struct acpi20_table_rsdp *) __va(rsdp_phys);
+	if (strncmp(rsdp->signature, RSDP_SIG, sizeof(RSDP_SIG) - 1)) {
+		printk("ACPI 2.0 RSDP signature incorrect, default to \"dig\"\n");
+		return "dig";
+	}
+
+	xsdt = (struct acpi_table_xsdt *) __va(rsdp->xsdt_address);
+	hdr = &xsdt->header;
+	if (strncmp(hdr->signature, XSDT_SIG, sizeof(XSDT_SIG) - 1)) {
+		printk("ACPI 2.0 XSDT signature incorrect, default to \"dig\"\n");
+		return "dig";
+	}
+
+	if (!strcmp(hdr->oem_id, "HP")) {
+		return "hpzx1";
+	}
+
+	return "dig";
 #else
 # if defined (CONFIG_IA64_HP_SIM)
 	return "hpsim";
+# elif defined (CONFIG_IA64_HP_ZX1)
+	return "hpzx1";
 # elif defined (CONFIG_IA64_SGI_SN1)
 	return "sn1";
 # elif defined (CONFIG_IA64_SGI_SN2)
@@ -79,6 +104,69 @@
 # endif
 #endif
 }
+
+#ifdef CONFIG_ACPI
+
+/**
+ * acpi_get_crs - Return the current resource settings for a device
+ * obj: A handle for this device
+ * buf: A buffer to be populated by this call.
+ *
+ * Pass a valid handle, typically obtained by walking the namespace and a
+ * pointer to an allocated buffer, and this function will fill in the buffer
+ * with a list of acpi_resource structures.
+ */
+acpi_status
+acpi_get_crs (acpi_handle obj, acpi_buffer *buf)
+{
+	acpi_status result;
+	buf->length = 0;
+	buf->pointer = NULL;
+
+	result = acpi_get_current_resources(obj, buf);
+	if (result != AE_BUFFER_OVERFLOW)
+		return result;
+	buf->pointer = kmalloc(buf->length, GFP_KERNEL);
+	if (!buf->pointer)
+		return -ENOMEM;
+
+	result = acpi_get_current_resources(obj, buf);
+
+	return result;
+}
+
+acpi_resource *
+acpi_get_crs_next (acpi_buffer *buf, int *offset)
+{
+	acpi_resource *res;
+
+	if (*offset >= buf->length)
+		return NULL;
+
+	res = buf->pointer + *offset;
+	*offset += res->length;
+	return res;
+}
+
+acpi_resource_data *
+acpi_get_crs_type (acpi_buffer *buf, int *offset, int type)
+{
+	for (;;) {
+		acpi_resource *res = acpi_get_crs_next(buf, offset);
+		if (!res)
+			return NULL;
+		if (res->id == type)
+			return &res->data;
+	}
+}
+
+void
+acpi_dispose_crs (acpi_buffer *buf)
+{
+	kfree(buf->pointer);
+}
+
+#endif /* CONFIG_ACPI */
 
 #ifdef CONFIG_ACPI_BOOT
 
diff -Nru a/arch/ia64/kernel/gate.S b/arch/ia64/kernel/gate.S
--- a/arch/ia64/kernel/gate.S	Sun Apr 28 20:10:33 2002
+++ b/arch/ia64/kernel/gate.S	Sun Apr 28 20:10:33 2002
@@ -29,7 +29,7 @@
 #	define UNAT_OFF		IA64_SIGCONTEXT_AR_UNAT_OFFSET
 #	define FPSR_OFF		IA64_SIGCONTEXT_AR_FPSR_OFFSET
 #	define PR_OFF		IA64_SIGCONTEXT_PR_OFFSET
-#	define RP_OFF		IA64_SIGCONTEXT_B0_OFFSET
+#	define RP_OFF		IA64_SIGCONTEXT_IP_OFFSET
 #	define SP_OFF		IA64_SIGCONTEXT_R12_OFFSET
 #	define RBS_BASE_OFF	IA64_SIGCONTEXT_RBS_BASE_OFFSET
 #	define LOADRS_OFF	IA64_SIGCONTEXT_LOADRS_OFFSET
diff -Nru a/arch/ia64/kernel/ia64_ksyms.c b/arch/ia64/kernel/ia64_ksyms.c
--- a/arch/ia64/kernel/ia64_ksyms.c	Sun Apr 28 20:10:36 2002
+++ b/arch/ia64/kernel/ia64_ksyms.c	Sun Apr 28 20:10:36 2002
@@ -6,11 +6,7 @@
 #include <linux/module.h>
 
 #include <linux/string.h>
-#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);
diff -Nru a/arch/ia64/kernel/iosapic.c b/arch/ia64/kernel/iosapic.c
--- a/arch/ia64/kernel/iosapic.c	Sun Apr 28 20:10:36 2002
+++ b/arch/ia64/kernel/iosapic.c	Sun Apr 28 20:10:36 2002
@@ -23,6 +23,7 @@
  *                              iosapic_set_affinity(), initializations for
  *                              /proc/irq/#/smp_affinity
  * 02/04/02	P. Diefenbaugh	Cleaned up ACPI PCI IRQ routing.
+ * 02/04/18	J.I. Lee	bug fix in iosapic_init_pci_irq
  */
 /*
  * Here is what the interrupt logic between a PCI device and the CPU looks like:
@@ -70,7 +71,7 @@
 
 
 #undef DEBUG_IRQ_ROUTING
-#undef	OVERRIDE_DEBUG
+#undef OVERRIDE_DEBUG
 
 static spinlock_t iosapic_lock = SPIN_LOCK_UNLOCKED;
 
@@ -676,6 +677,11 @@
 		       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
+
+		/*
+		 * Forget not to program the IOSAPIC RTE per ACPI _PRT
+		 */
+		set_rte(vector, (ia64_get_lid() >> 16) & 0xffff);
 	}
 }
 
diff -Nru a/arch/ia64/kernel/ivt.S b/arch/ia64/kernel/ivt.S
--- a/arch/ia64/kernel/ivt.S	Sun Apr 28 20:10:31 2002
+++ b/arch/ia64/kernel/ivt.S	Sun Apr 28 20:10:31 2002
@@ -330,12 +330,15 @@
 (p8)	br.cond.dptk dtlb_fault
 #endif
 	extr.u r23=r21,IA64_PSR_CPL0_BIT,2	// extract psr.cpl
+	and r22=IA64_ISR_CODE_MASK,r20		// get the isr.code field
 	tbit.nz p6,p7=r20,IA64_ISR_SP_BIT	// is speculation bit on?
-	and r19=r19,r16		// clear ed, reserved bits, and PTE control bits
-	shr.u r18=r16,57	// move address bit 61 to bit 4
+	shr.u r18=r16,57			// move address bit 61 to bit 4
+	and r19=r19,r16				// clear ed, reserved bits, and PTE control bits
+	tbit.nz p9,p0=r20,IA64_ISR_NA_BIT	// is non-access bit on?
 	;;
 	andcm r18=0x10,r18	// bit 4=~address-bit(61)
 	cmp.ne p8,p0=r0,r23
+(p9)	cmp.eq.or.andcm p6,p7=IA64_ISR_CODE_LFETCH,r22	// check isr.code field
 (p8)	br.cond.spnt page_fault
 
 	dep r21=-1,r21,IA64_PSR_ED_BIT,1
diff -Nru a/arch/ia64/kernel/setup.c b/arch/ia64/kernel/setup.c
--- a/arch/ia64/kernel/setup.c	Sun Apr 28 20:10:31 2002
+++ b/arch/ia64/kernel/setup.c	Sun Apr 28 20:10:31 2002
@@ -395,7 +395,7 @@
 
 	switch (c->family) {
 	      case 0x07:	memcpy(family, "Itanium", 8); break;
-	      case 0x1f:	memcpy(family, "McKinley", 9); break;
+	      case 0x1f:	memcpy(family, "Itanium 2", 9); break;
 	      default:		sprintf(family, "%u", c->family); break;
 	}
 
diff -Nru a/arch/ia64/kernel/signal.c b/arch/ia64/kernel/signal.c
--- a/arch/ia64/kernel/signal.c	Sun Apr 28 20:10:30 2002
+++ b/arch/ia64/kernel/signal.c	Sun Apr 28 20:10:30 2002
@@ -559,7 +559,7 @@
 				continue;
 
 			switch (signr) {
-			      case SIGCONT: case SIGCHLD: case SIGWINCH:
+			      case SIGCONT: case SIGCHLD: case SIGWINCH: case SIGURG:
 				continue;
 
 			      case SIGTSTP: case SIGTTIN: case SIGTTOU:
diff -Nru a/arch/ia64/kernel/traps.c b/arch/ia64/kernel/traps.c
--- a/arch/ia64/kernel/traps.c	Sun Apr 28 20:10:39 2002
+++ b/arch/ia64/kernel/traps.c	Sun Apr 28 20:10:39 2002
@@ -186,6 +186,10 @@
 		sig = SIGSEGV; code = __SEGV_PSTKOVF;
 		break;
 
+	      case 0x3f000 ... 0x3ffff:	/* bundle-update in progress */
+		sig = SIGILL; code = __ILL_BNDMOD;
+		break;
+
 	      default:
 		if (break_num < 0x40000 || break_num > 0x100000)
 			die_if_kernel("Bad break", regs, break_num);
@@ -443,30 +447,14 @@
 		"Unknown fault 13", "Unknown fault 14", "Unknown fault 15"
 	};
 
-#if 0
-	/* this is for minimal trust debugging; yeah this kind of stuff is useful at times... */
-
-	if (vector != 25) {
-		static unsigned long last_time;
-		static char count;
-		unsigned long n = vector;
-		char buf[32], *cp;
-
-		if (jiffies - last_time > 5*HZ)
-			count = 0;
-
-		if (count++ < 5) {
-			last_time = jiffies;
-			cp = buf + sizeof(buf);
-			*--cp = '\0';
-			while (n) {
-				*--cp = "0123456789abcdef"[n & 0xf];
-				n >>= 4;
-			}
-			printk("<0x%s>", cp);
-		}
+	if ((isr & IA64_ISR_NA) && ((isr & IA64_ISR_CODE_MASK) == IA64_ISR_CODE_LFETCH)) {
+		/*
+		 * This fault was due to lfetch.fault, set "ed" bit in the psr to cancel
+		 * the lfetch.
+		 */
+		ia64_psr(regs)->ed = 1;
+		return;
 	}
-#endif
 
 	switch (vector) {
 	      case 24: /* General Exception */
diff -Nru a/arch/ia64/lib/Makefile b/arch/ia64/lib/Makefile
--- a/arch/ia64/lib/Makefile	Sun Apr 28 20:10:35 2002
+++ b/arch/ia64/lib/Makefile	Sun Apr 28 20:10:35 2002
@@ -13,7 +13,7 @@
 	__divdi3.o __udivdi3.o __moddi3.o __umoddi3.o					\
 	checksum.o clear_page.o csum_partial_copy.o copy_page.o				\
 	copy_user.o clear_user.o strncpy_from_user.o strlen_user.o strnlen_user.o	\
-	flush.o io.o do_csum.o								\
+	flush.o io.o ip_fast_csum.o do_csum.o						\
 	memcpy.o memset.o strlen.o swiotlb.o
 
 obj-$(CONFIG_ITANIUM) += copy_page.o
diff -Nru a/arch/ia64/lib/checksum.c b/arch/ia64/lib/checksum.c
--- a/arch/ia64/lib/checksum.c	Sun Apr 28 20:10:34 2002
+++ b/arch/ia64/lib/checksum.c	Sun Apr 28 20:10:34 2002
@@ -15,7 +15,7 @@
 #include <asm/byteorder.h>
 
 static inline unsigned short
-from64to16(unsigned long x)
+from64to16 (unsigned long x)
 {
 	/* add up 32-bit words for 33 bits */
 	x = (x & 0xffffffff) + (x >> 32);
@@ -32,22 +32,17 @@
  * computes the checksum of the TCP/UDP pseudo-header
  * returns a 16-bit checksum, already complemented.
  */
-unsigned short int csum_tcpudp_magic(unsigned long saddr,
-				   unsigned long daddr,
-				   unsigned short len,
-				   unsigned short proto,
-				   unsigned int sum)
+unsigned short int
+csum_tcpudp_magic (unsigned long saddr, unsigned long daddr, unsigned short len,
+		   unsigned short proto, unsigned int sum)
 {
-	return ~from64to16(saddr + daddr + sum +
-		((unsigned long) ntohs(len) << 16) +
-		((unsigned long) proto << 8));
+	return ~from64to16(saddr + daddr + sum + ((unsigned long) ntohs(len) << 16) +
+			   ((unsigned long) proto << 8));
 }
 
-unsigned int csum_tcpudp_nofold(unsigned long saddr,
-				   unsigned long daddr,
-				   unsigned short len,
-				   unsigned short proto,
-				   unsigned int sum)
+unsigned int
+csum_tcpudp_nofold (unsigned long saddr, unsigned long daddr, unsigned short len,
+		    unsigned short proto, unsigned int sum)
 {
 	unsigned long result;
 
@@ -66,15 +61,6 @@
 extern unsigned long do_csum (const unsigned char *, long);
 
 /*
- *	This is a version of ip_compute_csum() optimized for IP headers,
- *	which always checksum on 4 octet boundaries.
- */
-unsigned short ip_fast_csum(unsigned char * iph, unsigned int ihl)
-{
-	return ~do_csum(iph, ihl*4);
-}
-
-/*
  * computes the checksum of a memory block at buff, length len,
  * and adds in "sum" (32-bit)
  *
@@ -86,7 +72,8 @@
  *
  * it's best to have buff aligned on a 32-bit boundary
  */
-unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum)
+unsigned int
+csum_partial (const unsigned char * buff, int len, unsigned int sum)
 {
 	unsigned long result = do_csum(buff, len);
 
@@ -102,7 +89,8 @@
  * this routine is used for miscellaneous IP-like checksums, mainly
  * in icmp.c
  */
-unsigned short ip_compute_csum(unsigned char * buff, int len)
+unsigned short
+ip_compute_csum (unsigned char * buff, int len)
 {
 	return ~do_csum(buff,len);
 }
diff -Nru a/arch/ia64/lib/copy_page.S b/arch/ia64/lib/copy_page.S
--- a/arch/ia64/lib/copy_page.S	Sun Apr 28 20:10:39 2002
+++ b/arch/ia64/lib/copy_page.S	Sun Apr 28 20:10:39 2002
@@ -30,6 +30,7 @@
 #define tgt2		r23
 #define srcf		r24
 #define tgtf		r25
+#define tgt_last	r26
 
 #define Nrot		((8*PIPE_DEPTH+7)&~7)
 
@@ -55,18 +56,21 @@
 
 	mov src1=in1
 	adds src2=8,in1
+	mov tgt_last = PAGE_SIZE
 	;;
 	adds tgt2=8,in0
 	add srcf=512,in1
 	mov ar.lc=lcount
 	mov tgt1=in0
 	add tgtf=512,in0
+	add tgt_last = tgt_last, in0
 	;;
 1:
 (p[0])	ld8 t1[0]=[src1],16
 (EPI)	st8 [tgt1]=t1[PIPE_DEPTH-1],16
 (p[0])	ld8 t2[0]=[src2],16
 (EPI)	st8 [tgt2]=t2[PIPE_DEPTH-1],16
+	cmp.ltu p6,p0 = tgtf, tgt_last
 	;;
 (p[0])	ld8 t3[0]=[src1],16
 (EPI)	st8 [tgt1]=t3[PIPE_DEPTH-1],16
@@ -83,8 +87,8 @@
 (p[0])	ld8 t8[0]=[src2],16
 (EPI)	st8 [tgt2]=t8[PIPE_DEPTH-1],16
 
-	lfetch [srcf], 64
-	lfetch [tgtf], 64
+(p6)	lfetch [srcf], 64
+(p6)	lfetch [tgtf], 64
 	br.ctop.sptk.few 1b
 	;;
 	mov pr=saved_pr,0xffffffffffff0000	// restore predicates
diff -Nru a/arch/ia64/lib/do_csum.S b/arch/ia64/lib/do_csum.S
--- a/arch/ia64/lib/do_csum.S	Sun Apr 28 20:10:32 2002
+++ b/arch/ia64/lib/do_csum.S	Sun Apr 28 20:10:32 2002
@@ -11,6 +11,9 @@
  * Copyright (C) 1999, 2001-2002 Hewlett-Packard Co
  *	Stephane Eranian <eranian@hpl.hp.com>
  *
+ * 02/04/22	Ken Chen <kenneth.w.chen@intel.com>
+ *		Data locality study on the checksum buffer.
+ *		More optimization cleanup - remove excessive stop bits.
  * 02/04/08	David Mosberger <davidm@hpl.hp.com>
  *		More cleanup and tuning.
  * 01/04/18	Jun Nakajima <jun.nakajima@intel.com>
@@ -80,6 +83,12 @@
 //	  type of packet or alignment we get. Like the ip_fast_csum() routine
 //	  where we know we have at least 20bytes worth of data to checksum.
 //	- Do a better job of handling small packets.
+//	- Note on prefetching: it was found that under various load, i.e. ftp read/write,
+//	  nfs read/write, the L1 cache hit rate is at 60% and L2 cache hit rate is at 99.8%
+//	  on the data that buffer points to (partly because the checksum is often preceded by
+//	  a copy_from_user()).  This finding indiate that lfetch will not be beneficial since
+//	  the data is already in the cache.
+//
 
 #define saved_pfs	r11
 #define hmask		r16
@@ -117,7 +126,7 @@
 GLOBAL_ENTRY(do_csum)
 	.prologue
 	.save ar.pfs, saved_pfs
-	alloc saved_pfs=ar.pfs,2,16,1,16
+	alloc saved_pfs=ar.pfs,2,16,0,16
 	.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
@@ -164,7 +173,7 @@
 
 (p8)	and hmask=hmask,tmask	// apply tail mask to head mask if 1 word only
 (p9)	and word2[0]=lastval,tmask	// mask last it as appropriate
-	shr.u count=count,3	// we do 8 bytes per loop (count)
+	shr.u count=count,3	// how many 8-byte?
 	;;
 	// If count is odd, finish this 8-byte word so that we can
 	// load two back-to-back 8-byte words per loop thereafter.
@@ -175,6 +184,7 @@
 (p9)	add result1[0]=word1[0],word2[0]
 	;;
 	cmp.ltu p6,p0=result1[0],word1[0]	// check the carry
+	cmp.eq.or.andcm p8,p0=0,count		// exit if zero 8-byte
 	;;
 (p6)	adds result1[0]=1,result1[0]
 (p8)	br.cond.dptk .do_csum_exit	// if (within an 8-byte word)
@@ -197,23 +207,19 @@
 	// Calculate the checksum loading two 8-byte words per loop.
 	//
 .do_csum16:
-	mov saved_lc=ar.lc
+	add first2=8,first1
 	shr.u count=count,1	// we do 16 bytes per loop
 	;;
-	cmp.eq p9,p10=r0,count	// if (count == 0)
 	adds count=-1,count
+	mov carry1=r0
+	mov carry2=r0
 	brp.loop.imp 1f,2f
 	;;
 	mov ar.ec=PIPE_DEPTH
 	mov ar.lc=count	// set lc
-
+	mov pr.rot=1<<16
 	// 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
 	;;
 	.align 32
 1:
@@ -223,7 +229,7 @@
 (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:]
+2:
 (p[0])	ld8 word1[0]=[first1],16
 (p[0])	ld8 word2[0]=[first2],16
 	br.ctop.sptk 1b
diff -Nru a/arch/ia64/lib/ip_fast_csum.S b/arch/ia64/lib/ip_fast_csum.S
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/arch/ia64/lib/ip_fast_csum.S	Sun Apr 28 20:10:39 2002
@@ -0,0 +1,90 @@
+/*
+ * Optmized version of the ip_fast_csum() function
+ * Used for calculating IP header checksum
+ *
+ * Return: 16bit checksum, complemented
+ *
+ * Inputs:
+ *      in0: address of buffer to checksum (char *)
+ *      in1: length of the buffer (int)
+ *
+ * Copyright (C) 2002 Intel Corp.
+ * Copyright (C) 2002 Ken Chen <kenneth.w.chen@intel.com>
+ */
+
+#include <asm/asmmacro.h>
+
+/*
+ * Since we know that most likely this function is called with buf aligned
+ * on 4-byte boundary and 20 bytes in length, we can execution rather quickly
+ * versus calling generic version of do_csum, which has lots of overhead in
+ * handling various alignments and sizes.  However, due to lack of constrains
+ * put on the function input argument, cases with alignment not on 4-byte or
+ * size not equal to 20 bytes will be handled by the generic do_csum function.
+ */
+
+#define in0	r32
+#define in1	r33
+#define ret0	r8
+
+GLOBAL_ENTRY(ip_fast_csum)
+	.prologue
+	.body
+	cmp.ne	p6,p7=5,in1	// size other than 20 byte?
+	and	r14=3,in0	// is it aligned on 4-byte?
+	add	r15=4,in0	// second source pointer
+	;;
+	cmp.ne.or.andcm p6,p7=r14,r0
+	;;
+(p7)	ld4	r20=[in0],8
+(p7)	ld4	r21=[r15],8
+(p6)	br.spnt	.generic
+	;;
+	ld4	r22=[in0],8
+	ld4	r23=[r15],8
+	;;
+	ld4	r24=[in0]
+	add	r20=r20,r21
+	add	r22=r22,r23
+	;;
+	add	r20=r20,r22
+	;;
+	add	r20=r20,r24
+	;;
+	shr.u	ret0=r20,16	// now need to add the carry
+	zxt2	r20=r20
+	;;
+	add	r20=ret0,r20
+	;;
+	shr.u	ret0=r20,16	// add carry again
+	zxt2	r20=r20
+	;;
+	add	r20=ret0,r20
+	;;
+	shr.u	ret0=r20,16
+	zxt2	r20=r20
+	;;
+	add	r20=ret0,r20
+	;;
+	andcm	ret0=-1,r20
+	.restore sp		// reset frame state
+	br.ret.sptk.many b0
+	;;
+
+.generic:
+	.prologue
+	.save ar.pfs, r35
+	alloc	r35=ar.pfs,2,2,2,0
+	.save rp, r34
+	mov	r34=b0
+	.body
+	dep.z	out1=in1,2,30
+	mov	out0=in0
+	;;
+	br.call.sptk.many b0=do_csum
+	;;
+	andcm	ret0=-1,ret0
+	mov	ar.pfs=r35
+	mov	b0=r34
+	br.ret.sptk.many b0
+END(ip_fast_csum)
diff -Nru a/arch/ia64/lib/memset.S b/arch/ia64/lib/memset.S
--- a/arch/ia64/lib/memset.S	Sun Apr 28 20:10:36 2002
+++ b/arch/ia64/lib/memset.S	Sun Apr 28 20:10:36 2002
@@ -1,123 +1,362 @@
-/*
- *
- * Optimized version of the standard memset() function
- *
- * Return: none
- *
- * Inputs:
- *	in0:	address of buffer
- * 	in1:	byte value to use for storing
- *	in2:	length of the buffer
- *
- * Copyright (C) 1999, 2001, 2002 Hewlett-Packard Co
- *	Stephane Eranian <eranian@hpl.hp.com>
- */
+/* Optimized version of the standard memset() function.
+
+   Copyright (c) 2002 Hewlett-Packard Co/CERN
+	Sverre Jarp <Sverre.Jarp@cern.ch>
+
+   Return: dest
+
+   Inputs:
+        in0:    dest
+        in1:    value
+        in2:    count
+
+   The algorithm is fairly straightforward: set byte by byte until we
+   we get to a 16B-aligned address, then loop on 128 B chunks using an
+   early store as prefetching, then loop on 32B chucks, then clear remaining
+   words, finally clear remaining bytes.
+   Since a stf.spill f0 can store 16B in one go, we use this instruction
+   to get peak speed when value = 0.  */
 
 #include <asm/asmmacro.h>
+#undef ret
+
+#define dest		in0
+#define value		in1
+#define	cnt		in2
 
-// arguments
-//
-#define buf		r32
-#define val		r33
-#define len		r34
-
-//
-// local registers
-//
-#define saved_pfs	r14
-#define cnt		r18
-#define buf2		r19
-#define saved_lc	r20
-#define tmp		r21
+#define tmp		r31
+#define save_lc		r30
+#define ptr0		r29
+#define ptr1		r28
+#define ptr2		r27
+#define ptr3		r26
+#define ptr9 		r24
+#define	loopcnt		r23
+#define linecnt		r22
+#define bytecnt		r21
 
-GLOBAL_ENTRY(__bzero)
+#define fvalue		f6
+
+// This routine uses only scratch predicate registers (p6 - p15)
+#define p_scr		p6			// default register for same-cycle branches
+#define p_nz		p7
+#define p_zr		p8
+#define p_unalgn	p9
+#define p_y		p11
+#define p_n		p12
+#define p_yy		p13
+#define p_nn		p14
+
+#define MIN1		15
+#define MIN1P1HALF	8
+#define LINE_SIZE	128
+#define LSIZE_SH        7			// shift amount
+#define PREF_AHEAD	8
+
+GLOBAL_ENTRY(memset)
+{ .mmi
 	.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!) */
+	alloc	tmp = ar.pfs, 3, 0, 0, 0
+	.body
+	lfetch.nt1 [dest]			//
+	.save   ar.lc, save_lc
+	mov.i	save_lc = ar.lc
+} { .mmi
+	mov	ret0 = dest			// return value
+	cmp.ne	p_nz, p_zr = value, r0		// use stf.spill if value is zero
+	cmp.eq	p_scr, p0 = cnt, r0
+;; }
+{ .mmi
+	and	ptr2 = -(MIN1+1), dest		// aligned address
+	and	tmp = MIN1, dest		// prepare to check for correct alignment
+	tbit.nz p_y, p_n = dest, 0		// Do we have an odd address? (M_B_U)
+} { .mib
+	mov	ptr1 = dest
+	mux1	value = value, @brcst		// create 8 identical bytes in word
+(p_scr)	br.ret.dpnt.many rp			// return immediately if count = 0
+;; }
+{ .mib
+	cmp.ne	p_unalgn, p0 = tmp, r0		//
+} { .mib
+	sub	bytecnt = (MIN1+1), tmp		// NB: # of bytes to move is 1 higher than loopcnt
+	cmp.gt	p_scr, p0 = 16, cnt		// is it a minimalistic task?
+(p_scr)	br.cond.dptk.many .move_bytes_unaligned	// go move just a few (M_B_U)
+;; }
+{ .mmi
+(p_unalgn) add	ptr1 = (MIN1+1), ptr2		// after alignment
+(p_unalgn) add	ptr2 = MIN1P1HALF, ptr2		// after alignment
+(p_unalgn) tbit.nz.unc p_y, p_n = bytecnt, 3	// should we do a st8 ?
+;; }
+{ .mib
+(p_y)	add	cnt = -8, cnt			//
+(p_unalgn) tbit.nz.unc p_yy, p_nn = bytecnt, 2	// should we do a st4 ?
+} { .mib
+(p_y)	st8	[ptr2] = value,-4		//
+(p_n)	add	ptr2 = 4, ptr2			//
+;; }
+{ .mib
+(p_yy)	add	cnt = -4, cnt			//
+(p_unalgn) tbit.nz.unc p_y, p_n = bytecnt, 1	// should we do a st2 ?
+} { .mib
+(p_yy)	st4	[ptr2] = value,-2		//
+(p_nn)	add	ptr2 = 2, ptr2			//
+;; }
+{ .mmi
+	mov	tmp = LINE_SIZE+1		// for compare
+(p_y)	add	cnt = -2, cnt			//
+(p_unalgn) tbit.nz.unc p_yy, p_nn = bytecnt, 0	// should we do a st1 ?
+} { .mmi
+	setf.sig fvalue=value			// transfer value to FLP side
+(p_y)	st2	[ptr2] = value,-1		//
+(p_n)	add	ptr2 = 1, ptr2			//
+;; }
+
+{ .mmi
+(p_yy)	st1	[ptr2] = value 			//
+  	cmp.gt	p_scr, p0 = tmp, cnt		// is it a minimalistic task?
+} { .mbb
+(p_yy)	add	cnt = -1, cnt			//
+(p_scr)	br.cond.dpnt.many .fraction_of_line	// go move just a few
+;; }
+
+{ .mib
 	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
-	cmp.eq p8,p0=r0,len	// check for zero length
-	.save ar.lc, saved_lc
-	mov saved_lc=ar.lc	// preserve ar.lc (slow)
-	;;
+	shr.u	linecnt = cnt, LSIZE_SH
+(p_zr)	br.cond.dptk.many .l1b			// Jump to use stf.spill
+;; }
 
-	.body
+	.align 32 // -------------------------- //  L1A: store ahead into cache lines; fill later
+{ .mmi
+	and	tmp = -(LINE_SIZE), cnt		// compute end of range
+	mov	ptr9 = ptr1			// used for prefetching
+	and	cnt = (LINE_SIZE-1), cnt	// remainder
+} { .mmi
+	mov	loopcnt = PREF_AHEAD-1		// default prefetch loop
+	cmp.gt	p_scr, p0 = PREF_AHEAD, linecnt	// check against actual value
+;; }
+{ .mmi
+(p_scr)	add	loopcnt = -1, linecnt		//
+	add	ptr2 = 8, ptr1			// start of stores (beyond prefetch stores)
+	add	ptr1 = tmp, ptr1		// first address beyond total range
+;; }
+{ .mmi
+	add	tmp = -1, linecnt		// next loop count
+	mov.i	ar.lc = loopcnt			//
+;; }
+.pref_l1a:
+{ .mib
+	stf8 [ptr9] = fvalue, 128		// Do stores one cache line apart
+	nop.i	0
+	br.cloop.dptk.few .pref_l1a
+;; }
+{ .mmi
+	add	ptr0 = 16, ptr2			// Two stores in parallel
+	mov.i	ar.lc = tmp			//
+;; }
+.l1ax:
+ { .mmi
+	stf8 [ptr2] = fvalue, 8
+	stf8 [ptr0] = fvalue, 8
+ ;; }
+ { .mmi
+	stf8 [ptr2] = fvalue, 24
+	stf8 [ptr0] = fvalue, 24
+ ;; }
+ { .mmi
+	stf8 [ptr2] = fvalue, 8
+	stf8 [ptr0] = fvalue, 8
+ ;; }
+ { .mmi
+	stf8 [ptr2] = fvalue, 24
+	stf8 [ptr0] = fvalue, 24
+ ;; }
+ { .mmi
+	stf8 [ptr2] = fvalue, 8
+	stf8 [ptr0] = fvalue, 8
+ ;; }
+ { .mmi
+	stf8 [ptr2] = fvalue, 24
+	stf8 [ptr0] = fvalue, 24
+ ;; }
+ { .mmi
+	stf8 [ptr2] = fvalue, 8
+	stf8 [ptr0] = fvalue, 32
+ 	cmp.lt	p_scr, p0 = ptr9, ptr1		// do we need more prefetching?
+ ;; }
+{ .mmb
+	stf8 [ptr2] = fvalue, 24
+(p_scr)	stf8 [ptr9] = fvalue, 128
+	br.cloop.dptk.few .l1ax
+;; }
+{ .mbb
+	cmp.le  p_scr, p0 = 8, cnt		// just a few bytes left ?
+(p_scr) br.cond.dpnt.many  .fraction_of_line	// Branch no. 2
+	br.cond.dpnt.many  .move_bytes_from_alignment	// Branch no. 3
+;; }
+
+	.align 32
+.l1b:	// ------------------------------------ //  L1B: store ahead into cache lines; fill later
+{ .mmi
+	and	tmp = -(LINE_SIZE), cnt		// compute end of range
+	mov	ptr9 = ptr1			// used for prefetching
+	and	cnt = (LINE_SIZE-1), cnt	// remainder
+} { .mmi
+	mov	loopcnt = PREF_AHEAD-1		// default prefetch loop
+	cmp.gt	p_scr, p0 = PREF_AHEAD, linecnt	// check against actual value
+;; }
+{ .mmi
+(p_scr)	add	loopcnt = -1, linecnt
+	add	ptr2 = 16, ptr1			// start of stores (beyond prefetch stores)
+	add	ptr1 = tmp, ptr1		// first address beyond total range
+;; }
+{ .mmi
+	add	tmp = -1, linecnt		// next loop count
+	mov.i	ar.lc = loopcnt
+;; }
+.pref_l1b:
+{ .mib
+	stf.spill [ptr9] = f0, 128		// Do stores one cache line apart
+	nop.i   0
+	br.cloop.dptk.few .pref_l1b
+;; }
+{ .mmi
+	add	ptr0 = 16, ptr2			// Two stores in parallel
+	mov.i	ar.lc = tmp
+;; }
+.l1bx:
+ { .mmi
+	stf.spill [ptr2] = f0, 32
+	stf.spill [ptr0] = f0, 32
+ ;; }
+ { .mmi
+	stf.spill [ptr2] = f0, 32
+	stf.spill [ptr0] = f0, 32
+ ;; }
+ { .mmi
+	stf.spill [ptr2] = f0, 32
+	stf.spill [ptr0] = f0, 64
+ 	cmp.lt	p_scr, p0 = ptr9, ptr1		// do we need more prefetching?
+ ;; }
+{ .mmb
+	stf.spill [ptr2] = f0, 32
+(p_scr)	stf.spill [ptr9] = f0, 128
+	br.cloop.dptk.few .l1bx
+;; }
+{ .mib
+	cmp.gt  p_scr, p0 = 8, cnt		// just a few bytes left ?
+(p_scr)	br.cond.dpnt.many  .move_bytes_from_alignment	//
+;; }
 
-	adds tmp=-1,len		// br.ctop is repeat/until
-	tbit.nz p6,p0=buf,0	// odd alignment
-(p8)	br.ret.spnt.many rp
-
-	cmp.lt p7,p0=16,len	// if len > 16 then long memset
-	mux1 val=val,@brcst	// prepare value
-(p7)	br.cond.dptk .long_memset
-	;;
-	mov ar.lc=tmp		// initialize lc for small count
-	;;			// avoid RAW and WAW on ar.lc
-1:				// worst case 15 cyles, avg 8 cycles
-	st1 [buf]=val,1
-	br.cloop.dptk.few 1b
-	;;				// avoid RAW on ar.lc
-	mov ar.lc=saved_lc
-	mov ar.pfs=saved_pfs
-	br.ret.sptk.many rp	// end of short memset
-
-	// at this point we know we have more than 16 bytes to copy
-	// so we focus on alignment
-.long_memset:
-(p6)	st1 [buf]=val,1		// 1-byte aligned
-(p6)	adds len=-1,len;;	// sync because buf is modified
-	tbit.nz p6,p0=buf,1
-	;;
-(p6)	st2 [buf]=val,2		// 2-byte aligned
-(p6)	adds len=-2,len;;
-	tbit.nz p6,p0=buf,2
-	;;
-(p6)	st4 [buf]=val,4		// 4-byte aligned
-(p6)	adds len=-4,len;;
-	tbit.nz p6,p0=buf,3
-	;;
-(p6)	st8 [buf]=val,8		// 8-byte aligned
-(p6)	adds len=-8,len;;
-	shr.u cnt=len,4		// number of 128-bit (2x64bit) words
-	;;
-	cmp.eq p6,p0=r0,cnt
-	adds tmp=-1,cnt
-(p6)	br.cond.dpnt .dotail	// we have less than 16 bytes left
-	;;
-	adds buf2=8,buf		// setup second base pointer
-	mov ar.lc=tmp
-	;;
-2:				// 16bytes/iteration
-	st8 [buf]=val,16
-	st8 [buf2]=val,16
-	br.cloop.dptk.few 2b
-	;;
-.dotail:			// tail correction based on len only
-	tbit.nz p6,p0=len,3
-	;;
-(p6)	st8 [buf]=val,8		// at least 8 bytes
-	tbit.nz p6,p0=len,2
-	;;
-(p6)	st4 [buf]=val,4		// at least 4 bytes
-	tbit.nz p6,p0=len,1
-	;;
-(p6)	st2 [buf]=val,2		// at least 2 bytes
-	tbit.nz p6,p0=len,0
-	mov ar.lc=saved_lc
-	;;
-(p6)	st1 [buf]=val		// only 1 byte left
+.fraction_of_line:
+{ .mib
+	add	ptr2 = 16, ptr1
+	shr.u	loopcnt = cnt, 5   		// loopcnt = cnt / 32
+;; }
+{ .mib
+	cmp.eq	p_scr, p0 = loopcnt, r0
+	add	loopcnt = -1, loopcnt
+(p_scr)	br.cond.dpnt.many .store_words
+;; }
+{ .mib
+	and	cnt = 0x1f, cnt			// compute the remaining cnt
+	mov.i   ar.lc = loopcnt
+;; }
+	.align 32
+.l2:	// ------------------------------------ //  L2A:  store 32B in 2 cycles
+{ .mmb
+	stf8	[ptr1] = fvalue, 8
+	stf8	[ptr2] = fvalue, 8
+;; } { .mmb
+	stf8	[ptr1] = fvalue, 24
+	stf8	[ptr2] = fvalue, 24
+	br.cloop.dptk.many .l2
+;; }
+.store_words:
+{ .mib
+	cmp.gt	p_scr, p0 = 8, cnt		// just a few bytes left ?
+(p_scr)	br.cond.dpnt.many .move_bytes_from_alignment	// Branch
+;; }
+
+{ .mmi
+	stf8	[ptr1] = fvalue, 8		// store
+	cmp.le	p_y, p_n = 16, cnt
+	add	cnt = -8, cnt			// subtract
+;; }
+{ .mmi
+(p_y)	stf8	[ptr1] = fvalue, 8		// store
+(p_y)	cmp.le.unc p_yy, p_nn = 16, cnt
+(p_y)	add	cnt = -8, cnt			// subtract
+;; }
+{ .mmi						// store
+(p_yy)	stf8	[ptr1] = fvalue, 8
+(p_yy)	add	cnt = -8, cnt			// subtract
+;; }
+
+.move_bytes_from_alignment:
+{ .mib
+	cmp.eq	p_scr, p0 = cnt, r0
+	tbit.nz.unc p_y, p0 = cnt, 2		// should we terminate with a st4 ?
+(p_scr)	br.cond.dpnt.few .restore_and_exit
+;; }
+{ .mib
+(p_y)	st4	[ptr1] = value,4
+	tbit.nz.unc p_yy, p0 = cnt, 1		// should we terminate with a st2 ?
+;; }
+{ .mib
+(p_yy)	st2	[ptr1] = value,2
+	tbit.nz.unc p_y, p0 = cnt, 0		// should we terminate with a st1 ?
+;; }
+
+{ .mib
+(p_y)	st1	[ptr1] = value
+;; }
+.restore_and_exit:
+{ .mib
+	nop.m	0
+	mov.i	ar.lc = save_lc
 	br.ret.sptk.many rp
-END(__memset_generic)
+;; }
 
-	.global memset
-memset = __memset_generic		// alias needed for gcc
+.move_bytes_unaligned:
+{ .mmi
+       .pred.rel "mutex",p_y, p_n
+       .pred.rel "mutex",p_yy, p_nn
+(p_n)	cmp.le  p_yy, p_nn = 4, cnt
+(p_y)	cmp.le  p_yy, p_nn = 5, cnt
+(p_n)	add	ptr2 = 2, ptr1
+} { .mmi
+(p_y)	add	ptr2 = 3, ptr1
+(p_y)	st1	[ptr1] = value, 1		// fill 1 (odd-aligned) byte [15, 14 (or less) left]
+(p_y)	add	cnt = -1, cnt
+;; }
+{ .mmi
+(p_yy)	cmp.le.unc p_y, p0 = 8, cnt
+	add	ptr3 = ptr1, cnt		// prepare last store
+	mov.i	ar.lc = save_lc
+} { .mmi
+(p_yy)	st2	[ptr1] = value, 4		// fill 2 (aligned) bytes
+(p_yy)	st2	[ptr2] = value, 4		// fill 2 (aligned) bytes [11, 10 (o less) left]
+(p_yy)	add	cnt = -4, cnt
+;; }
+{ .mmi
+(p_y)	cmp.le.unc p_yy, p0 = 8, cnt
+	add	ptr3 = -1, ptr3			// last store
+	tbit.nz p_scr, p0 = cnt, 1		// will there be a st2 at the end ?
+} { .mmi
+(p_y)	st2	[ptr1] = value, 4		// fill 2 (aligned) bytes
+(p_y)	st2	[ptr2] = value, 4		// fill 2 (aligned) bytes [7, 6 (or less) left]
+(p_y)	add	cnt = -4, cnt
+;; }
+{ .mmi
+(p_yy)	st2	[ptr1] = value, 4		// fill 2 (aligned) bytes
+(p_yy)	st2	[ptr2] = value, 4		// fill 2 (aligned) bytes [3, 2 (or less) left]
+	tbit.nz p_y, p0 = cnt, 0		// will there be a st1 at the end ?
+} { .mmi
+(p_yy)	add	cnt = -4, cnt
+;; }
+{ .mmb
+(p_scr)	st2	[ptr1] = value			// fill 2 (aligned) bytes
+(p_y)	st1	[ptr3] = value			// fill last byte (using ptr3)
+	br.ret.sptk.many rp
+}
+END(memset)
diff -Nru a/arch/ia64/mm/fault.c b/arch/ia64/mm/fault.c
--- a/arch/ia64/mm/fault.c	Sun Apr 28 20:10:39 2002
+++ b/arch/ia64/mm/fault.c	Sun Apr 28 20:10:39 2002
@@ -137,10 +137,13 @@
 
   bad_area:
 	up_read(&mm->mmap_sem);
-	if (isr & IA64_ISR_SP) {
+	if ((isr & IA64_ISR_SP)
+	    || ((isr & IA64_ISR_NA) && (isr & IA64_ISR_CODE_MASK) == IA64_ISR_CODE_LFETCH))
+	{
 		/*
-		 * This fault was due to a speculative load set the "ed" bit in the psr to
-		 * ensure forward progress (target register will get a NaT).
+		 * This fault was due to a speculative load or lfetch.fault, set the "ed"
+		 * bit in the psr to ensure forward progress.  (Target register will get a
+		 * NaT for ld.s, lfetch will be canceled.)
 		 */
 		ia64_psr(regs)->ed = 1;
 		return;
diff -Nru a/arch/ia64/sn/configs/sn1/defconfig-bigsur-mp b/arch/ia64/sn/configs/sn1/defconfig-bigsur-mp
--- a/arch/ia64/sn/configs/sn1/defconfig-bigsur-mp	Sun Apr 28 20:10:35 2002
+++ b/arch/ia64/sn/configs/sn1/defconfig-bigsur-mp	Sun Apr 28 20:10:35 2002
@@ -537,7 +537,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_DEVFS_FS=y
diff -Nru a/arch/ia64/sn/configs/sn1/defconfig-bigsur-sp b/arch/ia64/sn/configs/sn1/defconfig-bigsur-sp
--- a/arch/ia64/sn/configs/sn1/defconfig-bigsur-sp	Sun Apr 28 20:10:34 2002
+++ b/arch/ia64/sn/configs/sn1/defconfig-bigsur-sp	Sun Apr 28 20:10:34 2002
@@ -537,7 +537,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_DEVFS_FS=y
diff -Nru a/arch/ia64/sn/configs/sn1/defconfig-dig-mp b/arch/ia64/sn/configs/sn1/defconfig-dig-mp
--- a/arch/ia64/sn/configs/sn1/defconfig-dig-mp	Sun Apr 28 20:10:37 2002
+++ b/arch/ia64/sn/configs/sn1/defconfig-dig-mp	Sun Apr 28 20:10:37 2002
@@ -298,7 +298,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ia64/sn/configs/sn1/defconfig-dig-sp b/arch/ia64/sn/configs/sn1/defconfig-dig-sp
--- a/arch/ia64/sn/configs/sn1/defconfig-dig-sp	Sun Apr 28 20:10:31 2002
+++ b/arch/ia64/sn/configs/sn1/defconfig-dig-sp	Sun Apr 28 20:10:31 2002
@@ -298,7 +298,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ia64/sn/configs/sn1/defconfig-generic-mp b/arch/ia64/sn/configs/sn1/defconfig-generic-mp
--- a/arch/ia64/sn/configs/sn1/defconfig-generic-mp	Sun Apr 28 20:10:33 2002
+++ b/arch/ia64/sn/configs/sn1/defconfig-generic-mp	Sun Apr 28 20:10:33 2002
@@ -293,7 +293,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ia64/sn/configs/sn1/defconfig-generic-sp b/arch/ia64/sn/configs/sn1/defconfig-generic-sp
--- a/arch/ia64/sn/configs/sn1/defconfig-generic-sp	Sun Apr 28 20:10:34 2002
+++ b/arch/ia64/sn/configs/sn1/defconfig-generic-sp	Sun Apr 28 20:10:34 2002
@@ -293,7 +293,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ia64/sn/configs/sn1/defconfig-hp-sp b/arch/ia64/sn/configs/sn1/defconfig-hp-sp
--- a/arch/ia64/sn/configs/sn1/defconfig-hp-sp	Sun Apr 28 20:10:37 2002
+++ b/arch/ia64/sn/configs/sn1/defconfig-hp-sp	Sun Apr 28 20:10:37 2002
@@ -252,7 +252,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ia64/sn/configs/sn1/defconfig-prom-medusa b/arch/ia64/sn/configs/sn1/defconfig-prom-medusa
--- a/arch/ia64/sn/configs/sn1/defconfig-prom-medusa	Sun Apr 28 20:10:35 2002
+++ b/arch/ia64/sn/configs/sn1/defconfig-prom-medusa	Sun Apr 28 20:10:35 2002
@@ -373,7 +373,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_DEVFS_FS=y
diff -Nru a/arch/ia64/sn/configs/sn1/defconfig-sn1-mp b/arch/ia64/sn/configs/sn1/defconfig-sn1-mp
--- a/arch/ia64/sn/configs/sn1/defconfig-sn1-mp	Sun Apr 28 20:10:32 2002
+++ b/arch/ia64/sn/configs/sn1/defconfig-sn1-mp	Sun Apr 28 20:10:32 2002
@@ -507,7 +507,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_DEVFS_FS=y
diff -Nru a/arch/ia64/sn/configs/sn1/defconfig-sn1-mp-modules b/arch/ia64/sn/configs/sn1/defconfig-sn1-mp-modules
--- a/arch/ia64/sn/configs/sn1/defconfig-sn1-mp-modules	Sun Apr 28 20:10:37 2002
+++ b/arch/ia64/sn/configs/sn1/defconfig-sn1-mp-modules	Sun Apr 28 20:10:37 2002
@@ -509,7 +509,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_DEVFS_FS=y
diff -Nru a/arch/ia64/sn/configs/sn1/defconfig-sn1-mp-syn1-0 b/arch/ia64/sn/configs/sn1/defconfig-sn1-mp-syn1-0
--- a/arch/ia64/sn/configs/sn1/defconfig-sn1-mp-syn1-0	Sun Apr 28 20:10:35 2002
+++ b/arch/ia64/sn/configs/sn1/defconfig-sn1-mp-syn1-0	Sun Apr 28 20:10:35 2002
@@ -507,7 +507,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_DEVFS_FS=y
diff -Nru a/arch/ia64/sn/configs/sn1/defconfig-sn1-sp b/arch/ia64/sn/configs/sn1/defconfig-sn1-sp
--- a/arch/ia64/sn/configs/sn1/defconfig-sn1-sp	Sun Apr 28 20:10:30 2002
+++ b/arch/ia64/sn/configs/sn1/defconfig-sn1-sp	Sun Apr 28 20:10:30 2002
@@ -507,7 +507,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_DEVFS_FS=y
diff -Nru a/arch/ia64/sn/configs/sn2/defconfig-dig-numa b/arch/ia64/sn/configs/sn2/defconfig-dig-numa
--- a/arch/ia64/sn/configs/sn2/defconfig-dig-numa	Sun Apr 28 20:10:32 2002
+++ b/arch/ia64/sn/configs/sn2/defconfig-dig-numa	Sun Apr 28 20:10:32 2002
@@ -299,7 +299,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ia64/sn/configs/sn2/defconfig-sn2-dig-mp b/arch/ia64/sn/configs/sn2/defconfig-sn2-dig-mp
--- a/arch/ia64/sn/configs/sn2/defconfig-sn2-dig-mp	Sun Apr 28 20:10:38 2002
+++ b/arch/ia64/sn/configs/sn2/defconfig-sn2-dig-mp	Sun Apr 28 20:10:38 2002
@@ -298,7 +298,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ia64/sn/configs/sn2/defconfig-sn2-dig-sp b/arch/ia64/sn/configs/sn2/defconfig-sn2-dig-sp
--- a/arch/ia64/sn/configs/sn2/defconfig-sn2-dig-sp	Sun Apr 28 20:10:36 2002
+++ b/arch/ia64/sn/configs/sn2/defconfig-sn2-dig-sp	Sun Apr 28 20:10:36 2002
@@ -298,7 +298,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ia64/sn/configs/sn2/defconfig-sn2-mp b/arch/ia64/sn/configs/sn2/defconfig-sn2-mp
--- a/arch/ia64/sn/configs/sn2/defconfig-sn2-mp	Sun Apr 28 20:10:32 2002
+++ b/arch/ia64/sn/configs/sn2/defconfig-sn2-mp	Sun Apr 28 20:10:32 2002
@@ -506,7 +506,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_DEVFS_FS=y
diff -Nru a/arch/ia64/sn/configs/sn2/defconfig-sn2-mp-modules b/arch/ia64/sn/configs/sn2/defconfig-sn2-mp-modules
--- a/arch/ia64/sn/configs/sn2/defconfig-sn2-mp-modules	Sun Apr 28 20:10:37 2002
+++ b/arch/ia64/sn/configs/sn2/defconfig-sn2-mp-modules	Sun Apr 28 20:10:37 2002
@@ -508,7 +508,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_DEVFS_FS=y
diff -Nru a/arch/ia64/sn/configs/sn2/defconfig-sn2-prom-medusa b/arch/ia64/sn/configs/sn2/defconfig-sn2-prom-medusa
--- a/arch/ia64/sn/configs/sn2/defconfig-sn2-prom-medusa	Sun Apr 28 20:10:35 2002
+++ b/arch/ia64/sn/configs/sn2/defconfig-sn2-prom-medusa	Sun Apr 28 20:10:35 2002
@@ -381,7 +381,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_DEVFS_FS=y
diff -Nru a/arch/ia64/sn/configs/sn2/defconfig-sn2-sp b/arch/ia64/sn/configs/sn2/defconfig-sn2-sp
--- a/arch/ia64/sn/configs/sn2/defconfig-sn2-sp	Sun Apr 28 20:10:31 2002
+++ b/arch/ia64/sn/configs/sn2/defconfig-sn2-sp	Sun Apr 28 20:10:31 2002
@@ -506,7 +506,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_DEVFS_FS=y
diff -Nru a/arch/ia64/tools/print_offsets.c b/arch/ia64/tools/print_offsets.c
--- a/arch/ia64/tools/print_offsets.c	Sun Apr 28 20:10:38 2002
+++ b/arch/ia64/tools/print_offsets.c	Sun Apr 28 20:10:38 2002
@@ -143,6 +143,7 @@
     { "IA64_SWITCH_STACK_AR_RNAT_OFFSET",	offsetof (struct switch_stack, ar_rnat) },
     { "IA64_SWITCH_STACK_AR_BSPSTORE_OFFSET",	offsetof (struct switch_stack, ar_bspstore) },
     { "IA64_SWITCH_STACK_PR_OFFSET",	offsetof (struct switch_stack, pr) },
+    { "IA64_SIGCONTEXT_IP_OFFSET",	offsetof (struct sigcontext, sc_ip) },
     { "IA64_SIGCONTEXT_AR_BSP_OFFSET",	offsetof (struct sigcontext, sc_ar_bsp) },
     { "IA64_SIGCONTEXT_AR_FPSR_OFFSET", offsetof (struct sigcontext, sc_ar_fpsr) },
     { "IA64_SIGCONTEXT_AR_RNAT_OFFSET",	offsetof (struct sigcontext, sc_ar_rnat) },
diff -Nru a/arch/m68k/atari/ataints.c b/arch/m68k/atari/ataints.c
--- a/arch/m68k/atari/ataints.c	Sun Apr 28 20:10:32 2002
+++ b/arch/m68k/atari/ataints.c	Sun Apr 28 20:10:32 2002
@@ -168,8 +168,8 @@
 /* Dummy function to allow asm with operands.  */			   \
 void atari_slow_irq_##n##_dummy (void) {				   \
 __asm__ (__ALIGN_STR "\n"						   \
-SYMBOL_NAME_STR(atari_slow_irq_) #n "_handler:\t"			   \
-"	addql	#1,"SYMBOL_NAME_STR(irq_stat)"+8\n" /* local_irq_count */  \
+"atari_slow_irq_" #n "_handler:\t"					   \
+"	addql	#1,irq_stat+8\n" /* local_irq_count */			   \
 	SAVE_ALL_INT "\n"						   \
 	GET_CURRENT(%%d0) "\n"						   \
 "	andb	#~(1<<(%c3&7)),%a4:w\n"	/* mask this interrupt */	   \
@@ -190,7 +190,7 @@
 "	orw	#0x0600,%%sr\n"						   \
 "	andw	#0xfeff,%%sr\n"		/* set IPL = 6 again */		   \
 "	orb 	#(1<<(%c3&7)),%a4:w\n"	/* now unmask the int again */	   \
-"	jbra	"SYMBOL_NAME_STR(ret_from_interrupt)"\n"		   \
+"	jbra	ret_from_interrupt\n"					   \
 	 : : "i" (&kstat.irqs[0][n+8]), "i" (&irq_handler[n+8]),	   \
 	     "n" (PT_OFF_SR), "n" (n),					   \
 	     "i" (n & 8 ? (n & 16 ? &tt_mfp.int_mk_a : &mfp.int_mk_a)	   \
@@ -272,10 +272,10 @@
 /* Dummy function to allow asm with operands.  */
 void atari_fast_prio_irq_dummy (void) {
 __asm__ (__ALIGN_STR "\n"
-SYMBOL_NAME_STR(atari_fast_irq_handler) ":
+"atari_fast_irq_handler:
 	orw 	#0x700,%%sr		/* disable all interrupts */
-"SYMBOL_NAME_STR(atari_prio_irq_handler) ":\t
-	addql	#1,"SYMBOL_NAME_STR(irq_stat)"+8\n" /* local_irq_count */
+atari_prio_irq_handler:\t
+	addql	#1,irq_stat+8\n" /* local_irq_count */
 	SAVE_ALL_INT "\n"
 	GET_CURRENT(%%d0) "
 	/* get vector number from stack frame and convert to source */
@@ -285,7 +285,7 @@
 	addw	#(0x40-8-0x18),%%d0
 1:	lea	%a0,%%a0
 	addql	#1,%%a0@(%%d0:l:4)
-	lea	"SYMBOL_NAME_STR(irq_handler)",%%a0
+	lea	irq_handler,%%a0
 	lea	%%a0@(%%d0:l:8),%%a0
 	pea 	%%sp@			/* push frame address */
 	movel	%%a0@(4),%%sp@-		/* push handler data */
@@ -294,7 +294,7 @@
 	jsr	%%a0@			/* and call the handler */
 	addql	#8,%%sp
 	addql	#4,%%sp
-	jbra	"SYMBOL_NAME_STR(ret_from_interrupt)
+	jbra	ret_from_interrupt"
 	 : : "i" (&kstat.irqs[0]), "n" (PT_OFF_FORMATVEC)
 );
 }
@@ -306,7 +306,7 @@
 asmlinkage void falcon_hblhandler(void);
 asm(".text\n"
 __ALIGN_STR "\n"
-SYMBOL_NAME_STR(falcon_hblhandler) ":
+"falcon_hblhandler:
 	orw	#0x200,%sp@	/* set saved ipl to 2 */
 	rte");
 
diff -Nru a/arch/m68k/fpsp040/skeleton.S b/arch/m68k/fpsp040/skeleton.S
--- a/arch/m68k/fpsp040/skeleton.S	Sun Apr 28 20:10:32 2002
+++ b/arch/m68k/fpsp040/skeleton.S	Sun Apr 28 20:10:32 2002
@@ -73,9 +73,9 @@
 	SAVE_ALL_INT
 	GET_CURRENT(%d0)
 	movel	%sp,%sp@- 		| stack frame pointer argument
-	bsrl	SYMBOL_NAME(trap_c)
+	bsrl	trap_c
 	addql	#4,%sp
-	bral	SYMBOL_NAME(ret_from_exception)
+	bral	ret_from_exception
 
 |
 |	Inexact exception
@@ -164,9 +164,9 @@
 	SAVE_ALL_INT
 	GET_CURRENT(%d0)
 	movel	%sp,%sp@- 		| stack frame pointer argument
-	bsrl	SYMBOL_NAME(trap_c)
+	bsrl	trap_c
 	addql	#4,%sp
-	bral	SYMBOL_NAME(ret_from_exception)
+	bral	ret_from_exception
 	
 |
 |	Overflow exception
@@ -190,9 +190,9 @@
 	SAVE_ALL_INT
 	GET_CURRENT(%d0)
 	movel	%sp,%sp@- 		| stack frame pointer argument
-	bsrl	SYMBOL_NAME(trap_c)
+	bsrl	trap_c
 	addql	#4,%sp
-	bral	SYMBOL_NAME(ret_from_exception)
+	bral	ret_from_exception
 	
 |
 |	Underflow exception
@@ -216,9 +216,9 @@
 	SAVE_ALL_INT
 	GET_CURRENT(%d0)
 	movel	%sp,%sp@- 		| stack frame pointer argument
-	bsrl	SYMBOL_NAME(trap_c)
+	bsrl	trap_c
 	addql	#4,%sp
-	bral	SYMBOL_NAME(ret_from_exception)
+	bral	ret_from_exception
 	
 |
 |	Signalling NAN exception
@@ -238,9 +238,9 @@
 	SAVE_ALL_INT
 	GET_CURRENT(%d0)
 	movel	%sp,%sp@- 		| stack frame pointer argument
-	bsrl	SYMBOL_NAME(trap_c)
+	bsrl	trap_c
 	addql	#4,%sp
-	bral	SYMBOL_NAME(ret_from_exception)
+	bral	ret_from_exception
 	
 |
 |	Operand Error exception
@@ -260,9 +260,9 @@
 	SAVE_ALL_INT
 	GET_CURRENT(%d0)
 	movel	%sp,%sp@- 		| stack frame pointer argument
-	bsrl	SYMBOL_NAME(trap_c)
+	bsrl	trap_c
 	addql	#4,%sp
-	bral	SYMBOL_NAME(ret_from_exception)
+	bral	ret_from_exception
 
 	
 |
@@ -288,9 +288,9 @@
 	SAVE_ALL_INT
 	GET_CURRENT(%d0)
 	movel	%sp,%sp@- 		| stack frame pointer argument
-	bsrl	SYMBOL_NAME(trap_c)
+	bsrl	trap_c
 	addql	#4,%sp
-	bral	SYMBOL_NAME(ret_from_exception)
+	bral	ret_from_exception
 
 |
 |	F-line exception
@@ -309,9 +309,9 @@
 	SAVE_ALL_INT
 	GET_CURRENT(%d0)
 	movel	%sp,%sp@- 		| stack frame pointer argument
-	bsrl	SYMBOL_NAME(trap_c)
+	bsrl	trap_c
 	addql	#4,%sp
-	bral	SYMBOL_NAME(ret_from_exception)
+	bral	ret_from_exception
 
 |
 |	Unsupported data type exception
@@ -331,9 +331,9 @@
 	SAVE_ALL_INT
 	GET_CURRENT(%d0)
 	movel	%sp,%sp@- 		| stack frame pointer argument
-	bsrl	SYMBOL_NAME(trap_c)
+	bsrl	trap_c
 	addql	#4,%sp
-	bral	SYMBOL_NAME(ret_from_exception)
+	bral	ret_from_exception
 
 |
 |	Trace exception
@@ -341,7 +341,7 @@
 	.global	real_trace
 real_trace:
 	|
-	bral	SYMBOL_NAME(trap)
+	bral	trap
 
 |
 |	fpsp_fmt_error --- exit point for frame format error
@@ -382,7 +382,7 @@
 	SAVE_ALL_INT
 	GET_CURRENT(%d0)
 	tstl	%curptr@(TASK_NEEDRESCHED)
-	jne	SYMBOL_NAME(ret_from_exception)	| deliver signals,
+	jne	ret_from_exception	| deliver signals,
 						| reschedule etc..
 	RESTORE_ALL
 
@@ -505,7 +505,7 @@
 	.section .fixup,#alloc,#execinstr
 	.even
 1:
-	jbra	SYMBOL_NAME(fpsp040_die)
+	jbra	fpsp040_die
 
 	.section __ex_table,#alloc
 	.align	4
diff -Nru a/arch/m68k/ifpsp060/fskeleton.S b/arch/m68k/ifpsp060/fskeleton.S
--- a/arch/m68k/ifpsp060/fskeleton.S	Sun Apr 28 20:10:35 2002
+++ b/arch/m68k/ifpsp060/fskeleton.S	Sun Apr 28 20:10:35 2002
@@ -80,7 +80,7 @@
 	fsave		-(%sp)
 	move.w		#0x6000,0x2(%sp)
 	frestore	(%sp)+
-	bral		SYMBOL_NAME(trap)	| jump to trap handler
+	bral		trap	| jump to trap handler
 
 
 |
@@ -99,7 +99,7 @@
 	fsave		-(%sp)
 	move.w		#0x6000,0x2(%sp)
 	frestore	(%sp)+
-	bral		SYMBOL_NAME(trap)	| jump to trap handler
+	bral		trap	| jump to trap handler
 
 |
 | _060_real_operr():
@@ -118,7 +118,7 @@
 	fsave		-(%sp)
 	move.w		#0x6000,0x2(%sp)
 	frestore	(%sp)+
-	bral		SYMBOL_NAME(trap)	| jump to trap handler
+	bral		trap	| jump to trap handler
 
 |
 | _060_real_snan():
@@ -137,7 +137,7 @@
 	fsave		-(%sp)
 	move.w		#0x6000,0x2(%sp)
 	frestore	(%sp)+
-	bral		SYMBOL_NAME(trap)	| jump to trap handler
+	bral		trap	| jump to trap handler
 
 |
 | _060_real_dz():
@@ -156,7 +156,7 @@
 	fsave		-(%sp)
 	move.w		#0x6000,0x2(%sp)
 	frestore	(%sp)+
-	bral		SYMBOL_NAME(trap)	| jump to trap handler
+	bral		trap	| jump to trap handler
 
 |
 | _060_real_inex():
@@ -175,7 +175,7 @@
 	fsave		-(%sp)
 	move.w		#0x6000,0x2(%sp)
 	frestore	(%sp)+
-	bral		SYMBOL_NAME(trap)	| jump to trap handler
+	bral		trap	| jump to trap handler
 
 |
 | _060_real_bsun():
@@ -197,7 +197,7 @@
 	andi.b		#0xfe,(%sp)
 	fmove.l		(%sp)+,%fpsr
 
-	bral		SYMBOL_NAME(trap)	| jump to trap handler
+	bral		trap	| jump to trap handler
 
 |
 | _060_real_fline():
@@ -211,7 +211,7 @@
 | 
 	.global		_060_real_fline
 _060_real_fline:
-	bral		SYMBOL_NAME(trap)	| jump to trap handler
+	bral		trap	| jump to trap handler
 
 |
 | _060_real_fpu_disabled():
@@ -250,7 +250,7 @@
 |
 	.global		_060_real_trap
 _060_real_trap:
-	bral		SYMBOL_NAME(trap)	| jump to trap handler
+	bral		trap	| jump to trap handler
 
 |############################################################################
 
diff -Nru a/arch/m68k/ifpsp060/iskeleton.S b/arch/m68k/ifpsp060/iskeleton.S
--- a/arch/m68k/ifpsp060/iskeleton.S	Sun Apr 28 20:10:31 2002
+++ b/arch/m68k/ifpsp060/iskeleton.S	Sun Apr 28 20:10:31 2002
@@ -76,8 +76,8 @@
 	SAVE_ALL_INT
 	GET_CURRENT(%d0)
 	tstl	%curptr@(TASK_NEEDRESCHED)
-	jne	SYMBOL_NAME(ret_from_exception)	| deliver signals,
-						| reschedule etc..
+	jne	ret_from_exception	| deliver signals,
+					| reschedule etc..
 	RESTORE_ALL
 
 |
@@ -113,7 +113,7 @@
 |	bral		_060_real_trace
 
 real_chk_end:
-	bral		SYMBOL_NAME(trap)	| jump to trap handler
+	bral		trap			| jump to trap handler
 
 |
 | _060_real_divbyzero:
@@ -153,7 +153,7 @@
 |	bral		_060_real_trace
 
 real_divbyzero_end:
-	bral		SYMBOL_NAME(trap)	| jump to trap handler
+	bral		trap			| jump to trap handler
 
 |##########################
 
diff -Nru a/arch/m68k/ifpsp060/os.S b/arch/m68k/ifpsp060/os.S
--- a/arch/m68k/ifpsp060/os.S	Sun Apr 28 20:10:30 2002
+++ b/arch/m68k/ifpsp060/os.S	Sun Apr 28 20:10:30 2002
@@ -358,7 +358,7 @@
 |
 	.global		_060_real_trace
 _060_real_trace:
-	bral	SYMBOL_NAME(trap)
+	bral	trap
 
 |
 | _060_real_access():
@@ -374,7 +374,7 @@
 |
 	.global		_060_real_access
 _060_real_access:
-	bral	SYMBOL_NAME(buserr)
+	bral	buserr
 
 
 
diff -Nru a/arch/m68k/kernel/entry.S b/arch/m68k/kernel/entry.S
--- a/arch/m68k/kernel/entry.S	Sun Apr 28 20:10:30 2002
+++ b/arch/m68k/kernel/entry.S	Sun Apr 28 20:10:30 2002
@@ -44,36 +44,36 @@
 
 #include "m68k_defs.h"
 
-.globl SYMBOL_NAME(system_call), SYMBOL_NAME(buserr), SYMBOL_NAME(trap)
-.globl SYMBOL_NAME(resume), SYMBOL_NAME(ret_from_exception)
-.globl SYMBOL_NAME(ret_from_signal)
-.globl SYMBOL_NAME(inthandler), SYMBOL_NAME(sys_call_table)
-.globl SYMBOL_NAME(sys_fork), SYMBOL_NAME(sys_clone), SYMBOL_NAME(sys_vfork)
-.globl SYMBOL_NAME(ret_from_interrupt), SYMBOL_NAME(bad_interrupt)
+.globl system_call, buserr, trap
+.globl resume, ret_from_exception
+.globl ret_from_signal
+.globl inthandler, sys_call_table
+.globl sys_fork, sys_clone, sys_vfork
+.globl ret_from_interrupt, bad_interrupt
 
 .text
 ENTRY(buserr)
 	SAVE_ALL_INT
 	GET_CURRENT(%d0)
 	movel	%sp,%sp@- 		| stack frame pointer argument
-	bsrl	SYMBOL_NAME(buserr_c)
+	bsrl	buserr_c
 	addql	#4,%sp
-	jra	SYMBOL_NAME(ret_from_exception)
+	jra	ret_from_exception
 
 ENTRY(trap)
 	SAVE_ALL_INT
 	GET_CURRENT(%d0)
 	movel	%sp,%sp@- 		| stack frame pointer argument
-	bsrl	SYMBOL_NAME(trap_c)
+	bsrl	trap_c
 	addql	#4,%sp
-	jra	SYMBOL_NAME(ret_from_exception)
+	jra	ret_from_exception
 
 ENTRY(reschedule)
 	| save top of frame
 	movel	%sp,%curptr@(TASK_THREAD+THREAD_ESP0)
 
-	pea	SYMBOL_NAME(ret_from_exception)
-	jmp	SYMBOL_NAME(schedule)
+	pea	ret_from_exception
+	jmp	schedule
 
 	| After a fork we jump here directly from resume,
 	| so that %d1 contains the previous task
@@ -81,32 +81,32 @@
 	| what happens in schedule_tail() in future...
 ENTRY(ret_from_fork)
 	movel	%d1,%sp@-
-	jsr	SYMBOL_NAME(schedule_tail)
+	jsr	schedule_tail
 	addql	#4,%sp
-	jra	SYMBOL_NAME(ret_from_exception)
+	jra	ret_from_exception
 
 badsys:
 	movel	#-ENOSYS,%sp@(PT_D0)
-	jra	SYMBOL_NAME(ret_from_exception)
+	jra	ret_from_exception
 
 do_trace:
 	movel	#-ENOSYS,%sp@(PT_D0)	| needed for strace
 	subql	#4,%sp
 	SAVE_SWITCH_STACK
-	jbsr	SYMBOL_NAME(syscall_trace)
+	jbsr	syscall_trace
 	RESTORE_SWITCH_STACK
 	addql	#4,%sp
 	movel	%sp@(PT_ORIG_D0),%d1
 	movel	#-ENOSYS,%d0
 	cmpl	#NR_syscalls,%d1
 	jcc	1f
-	jbsr	@(SYMBOL_NAME(sys_call_table),%d1:l:4)@(0)
+	jbsr	@(sys_call_table,%d1:l:4)@(0)
 1:	movel	%d0,%sp@(PT_D0)		| save the return value
 	subql	#4,%sp			| dummy return address
 	SAVE_SWITCH_STACK
-	jbsr	SYMBOL_NAME(syscall_trace)
+	jbsr	syscall_trace
 
-SYMBOL_NAME_LABEL(ret_from_signal)
+ret_from_signal:
 	RESTORE_SWITCH_STACK 
 	addql	#4,%sp
 /* on 68040 complete pending writebacks if any */	
@@ -115,11 +115,11 @@
 	subql	#7,%d0				| bus error frame ?
 	jbne	1f
 	movel	%sp,%sp@-
-	jbsr	SYMBOL_NAME(berr_040cleanup)
+	jbsr	berr_040cleanup
 	addql	#4,%sp
 1:	
 #endif	
-	jra	SYMBOL_NAME(ret_from_exception)
+	jra	ret_from_exception
 
 ENTRY(system_call)
 	SAVE_ALL_SYS
@@ -132,10 +132,10 @@
 	jne	do_trace
 	cmpl	#NR_syscalls,%d0
 	jcc	badsys
-	jbsr	@(SYMBOL_NAME(sys_call_table),%d0:l:4)@(0)
+	jbsr	@(sys_call_table,%d0:l:4)@(0)
 	movel	%d0,%sp@(PT_D0)		| save the return value
 
-SYMBOL_NAME_LABEL(ret_from_exception)
+ret_from_exception:
 	btst	#5,%sp@(PT_SR)		| check if returning to kernel
 	bnes	2f			| if so, skip resched, signals
 	| only allow interrupts when we are really the last one on the
@@ -143,9 +143,9 @@
 	| heavy interrupt load
 	andw	#ALLOWINT,%sr
 	tstl	%curptr@(TASK_NEEDRESCHED)
-	jne	SYMBOL_NAME(reschedule)
+	jne	reschedule
 #if 0
-	cmpl	#SYMBOL_NAME(task),%curptr	| task[0] cannot have signals
+	cmpl	#task,%curptr		| task[0] cannot have signals
 	jeq	2f
 #endif
 					| check for delayed trace
@@ -153,7 +153,7 @@
 	jne	do_delayed_trace
 5:
 	tstl	%curptr@(TASK_STATE)	| state
-	jne	SYMBOL_NAME(reschedule)
+	jne	reschedule
 
 	tstl	%curptr@(TASK_SIGPENDING)
 	jne	Lsignal_return
@@ -164,7 +164,7 @@
 	SAVE_SWITCH_STACK
 	pea	%sp@(SWITCH_STACK_SIZE)
 	clrl	%sp@-
-	bsrl	SYMBOL_NAME(do_signal)
+	bsrl	do_signal
 	addql	#8,%sp
 	RESTORE_SWITCH_STACK
 	addql	#4,%sp
@@ -175,7 +175,7 @@
 	pea	1			| send SIGTRAP
 	movel	%curptr,%sp@-
 	pea	LSIGTRAP
-	jbsr	SYMBOL_NAME(send_sig)
+	jbsr	send_sig
 	addql	#8,%sp
 	addql	#4,%sp
 	jra	5b
@@ -183,15 +183,15 @@
 
 #if 0
 #if CONFIG_AMIGA
-SYMBOL_NAME_LABEL(ami_inthandler)
-	addql	#1,SYMBOL_NAME(irq_stat)+8	| local_irq_count
+ami_inthandler:
+	addql	#1,irq_stat+8		| local_irq_count
 	SAVE_ALL_INT
 	GET_CURRENT(%d0)
 
 	bfextu	%sp@(PT_VECTOR){#4,#12},%d0
 	movel	%d0,%a0
-	addql	#1,%a0@(SYMBOL_NAME(kstat)+STAT_IRQ-VECOFF(VEC_SPUR))
-	movel	%a0@(SYMBOL_NAME(autoirq_list)-VECOFF(VEC_SPUR)),%a0
+	addql	#1,%a0@(kstat+STAT_IRQ-VECOFF(VEC_SPUR))
+	movel	%a0@(autoirq_list-VECOFF(VEC_SPUR)),%a0
 
 | amiga vector int handler get the req mask instead of irq vector
 	lea	CUSTOMBASE,%a1
@@ -202,7 +202,7 @@
 	pea	%sp@
 	movel	%a0@(IRQ_DEVID),%sp@-
 	movel	%d0,%sp@-
-	pea	%pc@(SYMBOL_NAME(ret_from_interrupt):w)
+	pea	%pc@(ret_from_interrupt:w)
 	jbra	@(IRQ_HANDLER,%a0)@(0)
 
 ENTRY(nmi_handler)
@@ -213,10 +213,10 @@
 /*
 ** This is the main interrupt handler, responsible for calling process_int()
 */
-SYMBOL_NAME_LABEL(inthandler)
+inthandler:
 	SAVE_ALL_INT
 	GET_CURRENT(%d0)
-	addql	#1,SYMBOL_NAME(irq_stat)+8	| local_irq_count
+	addql	#1,irq_stat+8		| local_irq_count
 					|  put exception # in d0
 	bfextu %sp@(PT_VECTOR){#4,#10},%d0
 
@@ -227,15 +227,15 @@
 	jbeq	1f
 	btstb	#3,0xff000004		
 	jbeq	1f
-	jbsr	SYMBOL_NAME(floppy_hardint)
+	jbsr	floppy_hardint
 	jbra	3f
 1:
 #endif		
-	jbsr	SYMBOL_NAME(process_int)|  process the IRQ
+	jbsr	process_int|  process the IRQ
 3:     	addql	#8,%sp			|  pop parameters off stack
 
-SYMBOL_NAME_LABEL(ret_from_interrupt)
-	subql	#1,SYMBOL_NAME(irq_stat)+8	| local_irq_count
+ret_from_interrupt:
+	subql	#1,irq_stat+8		| local_irq_count
 	jeq	1f
 2:
 	RESTORE_ALL
@@ -249,24 +249,24 @@
 #endif
 	/* check if we need to do software interrupts */
 
-	movel	SYMBOL_NAME(irq_stat),%d0	| softirq_active
-	andl	SYMBOL_NAME(irq_stat)+4,%d0	| softirq_mask
-	jeq	SYMBOL_NAME(ret_from_exception)
+	movel	irq_stat,%d0		| softirq_active
+	andl	irq_stat+4,%d0		| softirq_mask
+	jeq	ret_from_exception
 
-	pea	SYMBOL_NAME(ret_from_exception)
-	jra	SYMBOL_NAME(do_softirq)
+	pea	ret_from_exception
+	jra	do_softirq
 
 
 /* Handler for uninitialized and spurious interrupts */
 
-SYMBOL_NAME_LABEL(bad_interrupt)
-	addql	#1,SYMBOL_NAME(num_spurious)
+bad_interrupt:
+	addql	#1,num_spurious
 	rte
 
 ENTRY(sys_fork)
 	SAVE_SWITCH_STACK	
 	pea	%sp@(SWITCH_STACK_SIZE)
-	jbsr	SYMBOL_NAME(m68k_fork)
+	jbsr	m68k_fork
 	addql	#4,%sp
 	RESTORE_SWITCH_STACK
 	rts
@@ -274,7 +274,7 @@
 ENTRY(sys_clone)
 	SAVE_SWITCH_STACK
 	pea	%sp@(SWITCH_STACK_SIZE)
-	jbsr	SYMBOL_NAME(m68k_clone)
+	jbsr	m68k_clone
 	addql	#4,%sp
 	RESTORE_SWITCH_STACK
 	rts
@@ -282,7 +282,7 @@
 ENTRY(sys_vfork)
 	SAVE_SWITCH_STACK	
 	pea	%sp@(SWITCH_STACK_SIZE)
-	jbsr	SYMBOL_NAME(m68k_vfork)
+	jbsr	m68k_vfork
 	addql	#4,%sp
 	RESTORE_SWITCH_STACK
 	rts
@@ -290,7 +290,7 @@
 ENTRY(sys_sigsuspend)
 	SAVE_SWITCH_STACK
 	pea	%sp@(SWITCH_STACK_SIZE)
-	jbsr	SYMBOL_NAME(do_sigsuspend)
+	jbsr	do_sigsuspend
 	addql	#4,%sp
 	RESTORE_SWITCH_STACK
 	rts
@@ -298,24 +298,24 @@
 ENTRY(sys_rt_sigsuspend)
 	SAVE_SWITCH_STACK
 	pea	%sp@(SWITCH_STACK_SIZE)
-	jbsr	SYMBOL_NAME(do_rt_sigsuspend)
+	jbsr	do_rt_sigsuspend
 	addql	#4,%sp
 	RESTORE_SWITCH_STACK
 	rts
 
 ENTRY(sys_sigreturn)
 	SAVE_SWITCH_STACK
-	jbsr	SYMBOL_NAME(do_sigreturn)
+	jbsr	do_sigreturn
 	RESTORE_SWITCH_STACK
 	rts
 
 ENTRY(sys_rt_sigreturn)
 	SAVE_SWITCH_STACK
-	jbsr	SYMBOL_NAME(do_rt_sigreturn)
+	jbsr	do_rt_sigreturn
 	RESTORE_SWITCH_STACK
 	rts
 
-SYMBOL_NAME_LABEL(resume)
+resume:
 	/*
 	 * Beware - when entering resume, prev (the current task) is
 	 * in a0, next (the new task) is in a1,so don't change these
@@ -343,14 +343,14 @@
 	/* save floating point context */
 #ifndef CONFIG_M68KFPU_EMU_ONLY
 #ifdef CONFIG_M68KFPU_EMU
-	tstl	SYMBOL_NAME(m68k_fputype)
+	tstl	m68k_fputype
 	jeq	3f
 #endif
 	fsave	%a0@(TASK_THREAD+THREAD_FPSTATE)
 
 #if defined(CONFIG_M68060)
 #if !defined(CPU_M68060_ONLY)
-	btst	#3,SYMBOL_NAME(m68k_cputype)+3
+	btst	#3,m68k_cputype+3
 	beqs	1f
 #endif
 	/* The 060 FPU keeps status in bits 15-8 of the first longword */
@@ -377,12 +377,12 @@
 	/* restore floating point context */
 #ifndef CONFIG_M68KFPU_EMU_ONLY
 #ifdef CONFIG_M68KFPU_EMU
-	tstl	SYMBOL_NAME(m68k_fputype)
+	tstl	m68k_fputype
 	jeq	4f
 #endif
 #if defined(CONFIG_M68060)
 #if !defined(CPU_M68060_ONLY)
-	btst	#3,SYMBOL_NAME(m68k_cputype)+3
+	btst	#3,m68k_cputype+3
 	beqs	1f
 #endif
 	/* The 060 FPU keeps status in bits 15-8 of the first longword */
@@ -424,231 +424,231 @@
 
 .data
 ALIGN
-SYMBOL_NAME_LABEL(sys_call_table)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* 0  -  old "setup()" system call*/
-	.long SYMBOL_NAME(sys_exit)
-	.long SYMBOL_NAME(sys_fork)
-	.long SYMBOL_NAME(sys_read)
-	.long SYMBOL_NAME(sys_write)
-	.long SYMBOL_NAME(sys_open)		/* 5 */
-	.long SYMBOL_NAME(sys_close)
-	.long SYMBOL_NAME(sys_waitpid)
-	.long SYMBOL_NAME(sys_creat)
-	.long SYMBOL_NAME(sys_link)
-	.long SYMBOL_NAME(sys_unlink)		/* 10 */
-	.long SYMBOL_NAME(sys_execve)
-	.long SYMBOL_NAME(sys_chdir)
-	.long SYMBOL_NAME(sys_time)
-	.long SYMBOL_NAME(sys_mknod)
-	.long SYMBOL_NAME(sys_chmod)		/* 15 */
-	.long SYMBOL_NAME(sys_chown16)
-	.long SYMBOL_NAME(sys_ni_syscall)				/* old break syscall holder */
-	.long SYMBOL_NAME(sys_stat)
-	.long SYMBOL_NAME(sys_lseek)
-	.long SYMBOL_NAME(sys_getpid)		/* 20 */
-	.long SYMBOL_NAME(sys_mount)
-	.long SYMBOL_NAME(sys_oldumount)
-	.long SYMBOL_NAME(sys_setuid16)
-	.long SYMBOL_NAME(sys_getuid16)
-	.long SYMBOL_NAME(sys_stime)		/* 25 */
-	.long SYMBOL_NAME(sys_ptrace)
-	.long SYMBOL_NAME(sys_alarm)
-	.long SYMBOL_NAME(sys_fstat)
-	.long SYMBOL_NAME(sys_pause)
-	.long SYMBOL_NAME(sys_utime)		/* 30 */
-	.long SYMBOL_NAME(sys_ni_syscall)				/* old stty syscall holder */
-	.long SYMBOL_NAME(sys_ni_syscall)				/* old gtty syscall holder */
-	.long SYMBOL_NAME(sys_access)
-	.long SYMBOL_NAME(sys_nice)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* 35 */		/* old ftime syscall holder */
-	.long SYMBOL_NAME(sys_sync)
-	.long SYMBOL_NAME(sys_kill)
-	.long SYMBOL_NAME(sys_rename)
-	.long SYMBOL_NAME(sys_mkdir)
-	.long SYMBOL_NAME(sys_rmdir)		/* 40 */
-	.long SYMBOL_NAME(sys_dup)
-	.long SYMBOL_NAME(sys_pipe)
-	.long SYMBOL_NAME(sys_times)
-	.long SYMBOL_NAME(sys_ni_syscall)				/* old prof syscall holder */
-	.long SYMBOL_NAME(sys_brk)		/* 45 */
-	.long SYMBOL_NAME(sys_setgid16)
-	.long SYMBOL_NAME(sys_getgid16)
-	.long SYMBOL_NAME(sys_signal)
-	.long SYMBOL_NAME(sys_geteuid16)
-	.long SYMBOL_NAME(sys_getegid16)	/* 50 */
-	.long SYMBOL_NAME(sys_acct)
-	.long SYMBOL_NAME(sys_umount)					/* recycled never used phys() */
-	.long SYMBOL_NAME(sys_ni_syscall)				/* old lock syscall holder */
-	.long SYMBOL_NAME(sys_ioctl)
-	.long SYMBOL_NAME(sys_fcntl)		/* 55 */
-	.long SYMBOL_NAME(sys_ni_syscall)				/* old mpx syscall holder */
-	.long SYMBOL_NAME(sys_setpgid)
-	.long SYMBOL_NAME(sys_ni_syscall)				/* old ulimit syscall holder */
-	.long SYMBOL_NAME(sys_ni_syscall)
-	.long SYMBOL_NAME(sys_umask)		/* 60 */
-	.long SYMBOL_NAME(sys_chroot)
-	.long SYMBOL_NAME(sys_ustat)
-	.long SYMBOL_NAME(sys_dup2)
-	.long SYMBOL_NAME(sys_getppid)
-	.long SYMBOL_NAME(sys_getpgrp)		/* 65 */
-	.long SYMBOL_NAME(sys_setsid)
-	.long SYMBOL_NAME(sys_sigaction)
-	.long SYMBOL_NAME(sys_sgetmask)
-	.long SYMBOL_NAME(sys_ssetmask)
-	.long SYMBOL_NAME(sys_setreuid16)	/* 70 */
-	.long SYMBOL_NAME(sys_setregid16)
-	.long SYMBOL_NAME(sys_sigsuspend)
-	.long SYMBOL_NAME(sys_sigpending)
-	.long SYMBOL_NAME(sys_sethostname)
-	.long SYMBOL_NAME(sys_setrlimit)	/* 75 */
-	.long SYMBOL_NAME(sys_old_getrlimit)
-	.long SYMBOL_NAME(sys_getrusage)
-	.long SYMBOL_NAME(sys_gettimeofday)
-	.long SYMBOL_NAME(sys_settimeofday)
-	.long SYMBOL_NAME(sys_getgroups16)	/* 80 */
-	.long SYMBOL_NAME(sys_setgroups16)
-	.long SYMBOL_NAME(old_select)
-	.long SYMBOL_NAME(sys_symlink)
-	.long SYMBOL_NAME(sys_lstat)
-	.long SYMBOL_NAME(sys_readlink)		/* 85 */
-	.long SYMBOL_NAME(sys_uselib)
-	.long SYMBOL_NAME(sys_swapon)
-	.long SYMBOL_NAME(sys_reboot)
-	.long SYMBOL_NAME(old_readdir)
-	.long SYMBOL_NAME(old_mmap)		/* 90 */
-	.long SYMBOL_NAME(sys_munmap)
-	.long SYMBOL_NAME(sys_truncate)
-	.long SYMBOL_NAME(sys_ftruncate)
-	.long SYMBOL_NAME(sys_fchmod)
-	.long SYMBOL_NAME(sys_fchown16)		/* 95 */
-	.long SYMBOL_NAME(sys_getpriority)
-	.long SYMBOL_NAME(sys_setpriority)
-	.long SYMBOL_NAME(sys_ni_syscall)				/* old profil syscall holder */
-	.long SYMBOL_NAME(sys_statfs)
-	.long SYMBOL_NAME(sys_fstatfs)		/* 100 */
-	.long SYMBOL_NAME(sys_ioperm)
-	.long SYMBOL_NAME(sys_socketcall)
-	.long SYMBOL_NAME(sys_syslog)
-	.long SYMBOL_NAME(sys_setitimer)
-	.long SYMBOL_NAME(sys_getitimer)	/* 105 */
-	.long SYMBOL_NAME(sys_newstat)
-	.long SYMBOL_NAME(sys_newlstat)
-	.long SYMBOL_NAME(sys_newfstat)
-	.long SYMBOL_NAME(sys_ni_syscall)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* iopl for i386 */ /* 110 */
-	.long SYMBOL_NAME(sys_vhangup)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* obsolete idle() syscall */
-	.long SYMBOL_NAME(sys_ni_syscall)	/* vm86old for i386 */
-	.long SYMBOL_NAME(sys_wait4)
-	.long SYMBOL_NAME(sys_swapoff)		/* 115 */
-	.long SYMBOL_NAME(sys_sysinfo)
-	.long SYMBOL_NAME(sys_ipc)
-	.long SYMBOL_NAME(sys_fsync)
-	.long SYMBOL_NAME(sys_sigreturn)
-	.long SYMBOL_NAME(sys_clone)		/* 120 */
-	.long SYMBOL_NAME(sys_setdomainname)
-	.long SYMBOL_NAME(sys_newuname)
-	.long SYMBOL_NAME(sys_cacheflush)	/* modify_ldt for i386 */
-	.long SYMBOL_NAME(sys_adjtimex)
-	.long SYMBOL_NAME(sys_mprotect)		/* 125 */
-	.long SYMBOL_NAME(sys_sigprocmask)
-	.long SYMBOL_NAME(sys_create_module)
-	.long SYMBOL_NAME(sys_init_module)
-	.long SYMBOL_NAME(sys_delete_module)
-	.long SYMBOL_NAME(sys_get_kernel_syms)	/* 130 */
-	.long SYMBOL_NAME(sys_quotactl)
-	.long SYMBOL_NAME(sys_getpgid)
-	.long SYMBOL_NAME(sys_fchdir)
-	.long SYMBOL_NAME(sys_bdflush)
-	.long SYMBOL_NAME(sys_sysfs)		/* 135 */
-	.long SYMBOL_NAME(sys_personality)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* for afs_syscall */
-	.long SYMBOL_NAME(sys_setfsuid16)
-	.long SYMBOL_NAME(sys_setfsgid16)
-	.long SYMBOL_NAME(sys_llseek)		/* 140 */
-	.long SYMBOL_NAME(sys_getdents)
-	.long SYMBOL_NAME(sys_select)
-	.long SYMBOL_NAME(sys_flock)
-	.long SYMBOL_NAME(sys_msync)
-	.long SYMBOL_NAME(sys_readv)		/* 145 */
-	.long SYMBOL_NAME(sys_writev)
-	.long SYMBOL_NAME(sys_getsid)
-	.long SYMBOL_NAME(sys_fdatasync)
-	.long SYMBOL_NAME(sys_sysctl)
-	.long SYMBOL_NAME(sys_mlock)		/* 150 */
-	.long SYMBOL_NAME(sys_munlock)
-	.long SYMBOL_NAME(sys_mlockall)
-	.long SYMBOL_NAME(sys_munlockall)
-	.long SYMBOL_NAME(sys_sched_setparam)
-	.long SYMBOL_NAME(sys_sched_getparam)   /* 155 */
-	.long SYMBOL_NAME(sys_sched_setscheduler)
-	.long SYMBOL_NAME(sys_sched_getscheduler)
-	.long SYMBOL_NAME(sys_sched_yield)
-	.long SYMBOL_NAME(sys_sched_get_priority_max)
-	.long SYMBOL_NAME(sys_sched_get_priority_min)  /* 160 */
-	.long SYMBOL_NAME(sys_sched_rr_get_interval)
-	.long SYMBOL_NAME(sys_nanosleep)
-	.long SYMBOL_NAME(sys_mremap)
-	.long SYMBOL_NAME(sys_setresuid16)
-	.long SYMBOL_NAME(sys_getresuid16)	/* 165 */
-	.long SYMBOL_NAME(sys_getpagesize)
-	.long SYMBOL_NAME(sys_query_module)
-	.long SYMBOL_NAME(sys_poll)
-	.long SYMBOL_NAME(sys_nfsservctl)
-	.long SYMBOL_NAME(sys_setresgid16)	/* 170 */
-	.long SYMBOL_NAME(sys_getresgid16)
-	.long SYMBOL_NAME(sys_prctl)
-	.long SYMBOL_NAME(sys_rt_sigreturn)
-	.long SYMBOL_NAME(sys_rt_sigaction)
-	.long SYMBOL_NAME(sys_rt_sigprocmask)	/* 175 */
-	.long SYMBOL_NAME(sys_rt_sigpending)
-	.long SYMBOL_NAME(sys_rt_sigtimedwait)
-	.long SYMBOL_NAME(sys_rt_sigqueueinfo)
-	.long SYMBOL_NAME(sys_rt_sigsuspend)
-	.long SYMBOL_NAME(sys_pread)		/* 180 */
-	.long SYMBOL_NAME(sys_pwrite)
-	.long SYMBOL_NAME(sys_lchown16);
-	.long SYMBOL_NAME(sys_getcwd)
-	.long SYMBOL_NAME(sys_capget)
-	.long SYMBOL_NAME(sys_capset)           /* 185 */
-	.long SYMBOL_NAME(sys_sigaltstack)
-	.long SYMBOL_NAME(sys_sendfile)
-	.long SYMBOL_NAME(sys_ni_syscall)		/* streams1 */
-	.long SYMBOL_NAME(sys_ni_syscall)		/* streams2 */
-	.long SYMBOL_NAME(sys_vfork)            /* 190 */
-	.long SYMBOL_NAME(sys_getrlimit)
-	.long SYMBOL_NAME(sys_mmap2)
-	.long SYMBOL_NAME(sys_truncate64)
-	.long SYMBOL_NAME(sys_ftruncate64)
-	.long SYMBOL_NAME(sys_stat64)		/* 195 */
-	.long SYMBOL_NAME(sys_lstat64)
-	.long SYMBOL_NAME(sys_fstat64)
-	.long SYMBOL_NAME(sys_chown)
-	.long SYMBOL_NAME(sys_getuid)
-	.long SYMBOL_NAME(sys_getgid)		/* 200 */
-	.long SYMBOL_NAME(sys_geteuid)
-	.long SYMBOL_NAME(sys_getegid)
-	.long SYMBOL_NAME(sys_setreuid)
-	.long SYMBOL_NAME(sys_setregid)
-	.long SYMBOL_NAME(sys_getgroups)	/* 205 */
-	.long SYMBOL_NAME(sys_setgroups)
-	.long SYMBOL_NAME(sys_fchown)
-	.long SYMBOL_NAME(sys_setresuid)
-	.long SYMBOL_NAME(sys_getresuid)
-	.long SYMBOL_NAME(sys_setresgid)	/* 210 */
-	.long SYMBOL_NAME(sys_getresgid)
-	.long SYMBOL_NAME(sys_lchown)
-	.long SYMBOL_NAME(sys_setuid)
-	.long SYMBOL_NAME(sys_setgid)
-	.long SYMBOL_NAME(sys_setfsuid)		/* 215 */
-	.long SYMBOL_NAME(sys_setfsgid)
-	.long SYMBOL_NAME(sys_ni_syscall)
-	.long SYMBOL_NAME(sys_ni_syscall)
-	.long SYMBOL_NAME(sys_ni_syscall)
-	.long SYMBOL_NAME(sys_getdents64)	/* 220 */
-	.long SYMBOL_NAME(sys_gettid)
-	.long SYMBOL_NAME(sys_tkill)
+sys_call_table:
+	.long sys_ni_syscall	/* 0  -  old "setup()" system call*/
+	.long sys_exit
+	.long sys_fork
+	.long sys_read
+	.long sys_write
+	.long sys_open		/* 5 */
+	.long sys_close
+	.long sys_waitpid
+	.long sys_creat
+	.long sys_link
+	.long sys_unlink	/* 10 */
+	.long sys_execve
+	.long sys_chdir
+	.long sys_time
+	.long sys_mknod
+	.long sys_chmod		/* 15 */
+	.long sys_chown16
+	.long sys_ni_syscall				/* old break syscall holder */
+	.long sys_stat
+	.long sys_lseek
+	.long sys_getpid	/* 20 */
+	.long sys_mount
+	.long sys_oldumount
+	.long sys_setuid16
+	.long sys_getuid16
+	.long sys_stime		/* 25 */
+	.long sys_ptrace
+	.long sys_alarm
+	.long sys_fstat
+	.long sys_pause
+	.long sys_utime		/* 30 */
+	.long sys_ni_syscall				/* old stty syscall holder */
+	.long sys_ni_syscall				/* old gtty syscall holder */
+	.long sys_access
+	.long sys_nice
+	.long sys_ni_syscall	/* 35 */		/* old ftime syscall holder */
+	.long sys_sync
+	.long sys_kill
+	.long sys_rename
+	.long sys_mkdir
+	.long sys_rmdir		/* 40 */
+	.long sys_dup
+	.long sys_pipe
+	.long sys_times
+	.long sys_ni_syscall				/* old prof syscall holder */
+	.long sys_brk		/* 45 */
+	.long sys_setgid16
+	.long sys_getgid16
+	.long sys_signal
+	.long sys_geteuid16
+	.long sys_getegid16	/* 50 */
+	.long sys_acct
+	.long sys_umount					/* recycled never used phys() */
+	.long sys_ni_syscall				/* old lock syscall holder */
+	.long sys_ioctl
+	.long sys_fcntl		/* 55 */
+	.long sys_ni_syscall				/* old mpx syscall holder */
+	.long sys_setpgid
+	.long sys_ni_syscall				/* old ulimit syscall holder */
+	.long sys_ni_syscall
+	.long sys_umask		/* 60 */
+	.long sys_chroot
+	.long sys_ustat
+	.long sys_dup2
+	.long sys_getppid
+	.long sys_getpgrp	/* 65 */
+	.long sys_setsid
+	.long sys_sigaction
+	.long sys_sgetmask
+	.long sys_ssetmask
+	.long sys_setreuid16	/* 70 */
+	.long sys_setregid16
+	.long sys_sigsuspend
+	.long sys_sigpending
+	.long sys_sethostname
+	.long sys_setrlimit	/* 75 */
+	.long sys_old_getrlimit
+	.long sys_getrusage
+	.long sys_gettimeofday
+	.long sys_settimeofday
+	.long sys_getgroups16	/* 80 */
+	.long sys_setgroups16
+	.long old_select
+	.long sys_symlink
+	.long sys_lstat
+	.long sys_readlink	/* 85 */
+	.long sys_uselib
+	.long sys_swapon
+	.long sys_reboot
+	.long old_readdir
+	.long old_mmap		/* 90 */
+	.long sys_munmap
+	.long sys_truncate
+	.long sys_ftruncate
+	.long sys_fchmod
+	.long sys_fchown16	/* 95 */
+	.long sys_getpriority
+	.long sys_setpriority
+	.long sys_ni_syscall				/* old profil syscall holder */
+	.long sys_statfs
+	.long sys_fstatfs	/* 100 */
+	.long sys_ioperm
+	.long sys_socketcall
+	.long sys_syslog
+	.long sys_setitimer
+	.long sys_getitimer	/* 105 */
+	.long sys_newstat
+	.long sys_newlstat
+	.long sys_newfstat
+	.long sys_ni_syscall
+	.long sys_ni_syscall	/* iopl for i386 */ /* 110 */
+	.long sys_vhangup
+	.long sys_ni_syscall	/* obsolete idle() syscall */
+	.long sys_ni_syscall	/* vm86old for i386 */
+	.long sys_wait4
+	.long sys_swapoff	/* 115 */
+	.long sys_sysinfo
+	.long sys_ipc
+	.long sys_fsync
+	.long sys_sigreturn
+	.long sys_clone		/* 120 */
+	.long sys_setdomainname
+	.long sys_newuname
+	.long sys_cacheflush	/* modify_ldt for i386 */
+	.long sys_adjtimex
+	.long sys_mprotect	/* 125 */
+	.long sys_sigprocmask
+	.long sys_create_module
+	.long sys_init_module
+	.long sys_delete_module
+	.long sys_get_kernel_syms	/* 130 */
+	.long sys_quotactl
+	.long sys_getpgid
+	.long sys_fchdir
+	.long sys_bdflush
+	.long sys_sysfs		/* 135 */
+	.long sys_personality
+	.long sys_ni_syscall	/* for afs_syscall */
+	.long sys_setfsuid16
+	.long sys_setfsgid16
+	.long sys_llseek	/* 140 */
+	.long sys_getdents
+	.long sys_select
+	.long sys_flock
+	.long sys_msync
+	.long sys_readv		/* 145 */
+	.long sys_writev
+	.long sys_getsid
+	.long sys_fdatasync
+	.long sys_sysctl
+	.long sys_mlock		/* 150 */
+	.long sys_munlock
+	.long sys_mlockall
+	.long sys_munlockall
+	.long sys_sched_setparam
+	.long sys_sched_getparam   /* 155 */
+	.long sys_sched_setscheduler
+	.long sys_sched_getscheduler
+	.long sys_sched_yield
+	.long sys_sched_get_priority_max
+	.long sys_sched_get_priority_min  /* 160 */
+	.long sys_sched_rr_get_interval
+	.long sys_nanosleep
+	.long sys_mremap
+	.long sys_setresuid16
+	.long sys_getresuid16	/* 165 */
+	.long sys_getpagesize
+	.long sys_query_module
+	.long sys_poll
+	.long sys_nfsservctl
+	.long sys_setresgid16	/* 170 */
+	.long sys_getresgid16
+	.long sys_prctl
+	.long sys_rt_sigreturn
+	.long sys_rt_sigaction
+	.long sys_rt_sigprocmask	/* 175 */
+	.long sys_rt_sigpending
+	.long sys_rt_sigtimedwait
+	.long sys_rt_sigqueueinfo
+	.long sys_rt_sigsuspend
+	.long sys_pread		/* 180 */
+	.long sys_pwrite
+	.long sys_lchown16;
+	.long sys_getcwd
+	.long sys_capget
+	.long sys_capset	/* 185 */
+	.long sys_sigaltstack
+	.long sys_sendfile
+	.long sys_ni_syscall	/* streams1 */
+	.long sys_ni_syscall	/* streams2 */
+	.long sys_vfork		/* 190 */
+	.long sys_getrlimit
+	.long sys_mmap2
+	.long sys_truncate64
+	.long sys_ftruncate64
+	.long sys_stat64	/* 195 */
+	.long sys_lstat64
+	.long sys_fstat64
+	.long sys_chown
+	.long sys_getuid
+	.long sys_getgid	/* 200 */
+	.long sys_geteuid
+	.long sys_getegid
+	.long sys_setreuid
+	.long sys_setregid
+	.long sys_getgroups	/* 205 */
+	.long sys_setgroups
+	.long sys_fchown
+	.long sys_setresuid
+	.long sys_getresuid
+	.long sys_setresgid	/* 210 */
+	.long sys_getresgid
+	.long sys_lchown
+	.long sys_setuid
+	.long sys_setgid
+	.long sys_setfsuid	/* 215 */
+	.long sys_setfsgid
+	.long sys_ni_syscall
+	.long sys_ni_syscall
+	.long sys_ni_syscall
+	.long sys_getdents64	/* 220 */
+	.long sys_gettid
+	.long sys_tkill
 
-	.rept NR_syscalls-(.-SYMBOL_NAME(sys_call_table))/4
-		.long SYMBOL_NAME(sys_ni_syscall)
+	.rept NR_syscalls-(.-sys_call_table)/4
+		.long sys_ni_syscall
 	.endr
diff -Nru a/arch/m68k/kernel/head.S b/arch/m68k/kernel/head.S
--- a/arch/m68k/kernel/head.S	Sun Apr 28 20:10:36 2002
+++ b/arch/m68k/kernel/head.S	Sun Apr 28 20:10:36 2002
@@ -302,15 +302,15 @@
 /* #define FONT_8x16 */	/* 2nd choice */
 /* #define FONT_6x11 */	/* 3rd choice */
 
-.globl SYMBOL_NAME(kernel_pg_dir)
-.globl SYMBOL_NAME(availmem)
-.globl SYMBOL_NAME(m68k_pgtable_cachemode)
-.globl SYMBOL_NAME(m68k_supervisor_cachemode)
+.globl kernel_pg_dir
+.globl availmem
+.globl m68k_pgtable_cachemode
+.globl m68k_supervisor_cachemode
 #ifdef CONFIG_MVME16x
-.globl SYMBOL_NAME(mvme_bdid)
+.globl mvme_bdid
 #endif
 #ifdef CONFIG_Q40
-.globl SYMBOL_NAME(q40_mem_cptr)	
+.globl q40_mem_cptr	
 #endif		
 
 CPUTYPE_040	= 1	/* indicates an 040 */
@@ -585,40 +585,40 @@
 	.long	MACH_MAC, MAC_BOOTI_VERSION
 	.long	MACH_Q40, Q40_BOOTI_VERSION	
 	.long	0
-1:	jra	SYMBOL_NAME(__start)
+1:	jra	__start
 
-.equ	SYMBOL_NAME(kernel_pg_dir),SYMBOL_NAME(_stext)
+.equ	kernel_pg_dir,_stext
 
-.equ	.,SYMBOL_NAME(_stext)+PAGESIZE
+.equ	.,_stext+PAGESIZE
 
 ENTRY(_start)
-	jra	SYMBOL_NAME(__start)
+	jra	__start
 __INIT
 ENTRY(__start)
 
 /*
  * Setup initial stack pointer
  */
-	lea	%pc@(SYMBOL_NAME(_stext)),%sp
+	lea	%pc@(_stext),%sp
 
 /*
  * Record the CPU and machine type.
  */
 
 	get_bi_record	BI_MACHTYPE
-	lea	%pc@(SYMBOL_NAME(m68k_machtype)),%a1
+	lea	%pc@(m68k_machtype),%a1
 	movel	%a0@,%a1@
 
 	get_bi_record	BI_FPUTYPE
-	lea	%pc@(SYMBOL_NAME(m68k_fputype)),%a1
+	lea	%pc@(m68k_fputype),%a1
 	movel	%a0@,%a1@
 
 	get_bi_record	BI_MMUTYPE
-	lea	%pc@(SYMBOL_NAME(m68k_mmutype)),%a1
+	lea	%pc@(m68k_mmutype),%a1
 	movel	%a0@,%a1@
 
 	get_bi_record	BI_CPUTYPE
-	lea	%pc@(SYMBOL_NAME(m68k_cputype)),%a1
+	lea	%pc@(m68k_cputype),%a1
 	movel	%a0@,%a1@
 
 #ifdef CONFIG_MAC
@@ -682,7 +682,7 @@
  * and is converted here from a booter type definition to a separate bit
  * number which allows for the standard is_0x0 macro tests.
  */
-	movel	%pc@(SYMBOL_NAME(m68k_cputype)),%d0
+	movel	%pc@(m68k_cputype),%d0
 	/*
 	 * Assume it's an 030
 	 */
@@ -766,9 +766,9 @@
 L(save_cachetype):
 	/* Save cache mode for supervisor mode and page tables
 	 */
-	lea	%pc@(SYMBOL_NAME(m68k_supervisor_cachemode)),%a0
+	lea	%pc@(m68k_supervisor_cachemode),%a0
 	movel	%d0,%a0@
-	lea	%pc@(SYMBOL_NAME(m68k_pgtable_cachemode)),%a0
+	lea	%pc@(m68k_pgtable_cachemode),%a0
 	movel	%d1,%a0@
 
 /*
@@ -804,7 +804,7 @@
 	tstl	%d0
 	jbmi	1f
 	movel	%a0@,%d3
-	lea	%pc@(SYMBOL_NAME(atari_mch_type)),%a0
+	lea	%pc@(atari_mch_type),%a0
 	movel	%d3,%a0@
 1:
 	/* On the Hades, the iobase must be set up before opening the
@@ -834,7 +834,7 @@
 	tstl	%d0
 	jbmi	1f
 	movel	%a0@,%d3
-	lea	%pc@(SYMBOL_NAME(vme_brdtype)),%a0
+	lea	%pc@(vme_brdtype),%a0
 	movel	%d3,%a0@
 1:
 #ifdef CONFIG_MVME16x
@@ -854,7 +854,7 @@
 	.word	0x70		/* trap 0x70 - .BRD_ID */
 	movel	%sp@+,%a0
 1:
-	lea	%pc@(SYMBOL_NAME(mvme_bdid)),%a1
+	lea	%pc@(mvme_bdid),%a1
 	/* Structure is 32 bytes long */
 	movel	%a0@+,%a1@+
 	movel	%a0@+,%a1@+
@@ -894,16 +894,16 @@
 	putc	'\n'
 	putc	'A'
 	dputn	%pc@(L(cputype))
-	dputn	%pc@(SYMBOL_NAME(m68k_supervisor_cachemode))
-	dputn	%pc@(SYMBOL_NAME(m68k_pgtable_cachemode))
+	dputn	%pc@(m68k_supervisor_cachemode)
+	dputn	%pc@(m68k_pgtable_cachemode)
 	dputc	'\n'
 
 /*
  * Save physical start address of kernel
  */
 	lea	%pc@(L(phys_kernel_start)),%a0
-	lea	%pc@(SYMBOL_NAME(_stext)),%a1
-	subl	#SYMBOL_NAME(_stext),%a1
+	lea	%pc@(_stext),%a1
+	subl	#_stext,%a1
 	addl	#PAGE_OFFSET,%a1
 	movel	%a1,%a0@
 
@@ -920,7 +920,7 @@
  */
 
 	mmu_map	#PAGE_OFFSET,%pc@(L(phys_kernel_start)),#4*1024*1024,\
-		%pc@(SYMBOL_NAME(m68k_supervisor_cachemode))
+		%pc@(m68k_supervisor_cachemode)
 
 	putc	'C'
 
@@ -984,7 +984,7 @@
 
 	/* I/O base addr for non-Medusa, non-Hades: 0x00000000 */
 	moveq	#0,%d0
-	movel	%pc@(SYMBOL_NAME(atari_mch_type)),%d3
+	movel	%pc@(atari_mch_type),%d3
 	cmpl	#ATARI_MACH_MEDUSA,%d3
 	jbeq	2f
 	cmpl	#ATARI_MACH_HADES,%d3
@@ -1263,7 +1263,7 @@
 	 */
 	movel	%pc@(L(phys_kernel_start)),%d0
 	subl	#PAGE_OFFSET,%d0
-	lea	%pc@(SYMBOL_NAME(_stext)),%a0
+	lea	%pc@(_stext),%a0
 	subl	%d0,%a0
 	mmu_fixup_page_mmu_cache	%a0
 
@@ -1461,7 +1461,7 @@
 /*
  * Setup initial stack pointer
  */
-	lea	SYMBOL_NAME(init_task_union),%curptr
+	lea	init_task_union,%curptr
 	lea	0x2000(%curptr),%sp
 
 	putc	'K'
@@ -1471,14 +1471,14 @@
 /*
  * The new 64bit printf support requires an early exception initialization.
  */
-	jbsr	SYMBOL_NAME(base_trap_init)
+	jbsr	base_trap_init
 
 /* jump to the kernel start */
 
 	putc	'\n'
 	leds	0x55
 
-	jbsr	SYMBOL_NAME(start_kernel)
+	jbsr	start_kernel
 
 /*
  * Find a tag record in the bootinfo structure
@@ -1489,7 +1489,7 @@
 func_start	get_bi_record,%d1
 
 	movel	ARG1,%d0
-	lea	%pc@(SYMBOL_NAME(_end)),%a0
+	lea	%pc@(_end),%a0
 1:	tstw	%a0@(BIR_TAG)
 	jeq	3f
 	cmpw	%a0@(BIR_TAG),%d0
@@ -1669,7 +1669,7 @@
 	jbra	1b
 #endif /* MMU 040 Dumping code that's gory and detailed */
 
-	lea	%pc@(SYMBOL_NAME(kernel_pg_dir)),%a5
+	lea	%pc@(kernel_pg_dir),%a5
 	movel	%a5,%a0			/* a0 has the address of the root table ptr */
 	movel	#0x00000000,%a4		/* logical address */
 	moveql	#0,%d0
@@ -2286,7 +2286,7 @@
 
 	movel	%a0@,%d0
 	andil	#_CACHEMASK040,%d0
-	orl	%pc@(SYMBOL_NAME(m68k_pgtable_cachemode)),%d0
+	orl	%pc@(m68k_pgtable_cachemode),%d0
 	movel	%d0,%a0@
 
 	dputc	'\n'
@@ -2516,7 +2516,7 @@
 	 * in mm/init.c simply expects kernel_pg_dir there, the rest of
 	 * page is used for further ptr tables in get_ptr_table.
 	 */
-	lea	%pc@(SYMBOL_NAME(_stext)),%a0
+	lea	%pc@(_stext),%a0
 	lea	%pc@(L(mmu_cached_pointer_tables)),%a1
 	movel	%a0,%a1@
 	addl	#ROOT_TABLE_SIZE*4,%a1@
@@ -2907,7 +2907,7 @@
 	is_not_q40(2f)
 /* debug output goes into SRAM, so we don't do it unless requested
    - check for '%LX$' signature in SRAM   */
-	lea	%pc@(SYMBOL_NAME(q40_mem_cptr)),%a1
+	lea	%pc@(q40_mem_cptr),%a1
 	move.l	#0xff020010,%a1@  /* must be inited - also used by debug=mem */
 	move.l	#0xff020000,%a1   
 	cmp.b	#'%',%a1@
@@ -3040,7 +3040,7 @@
 	 * the SRAM, which is non-standard.
 	 */
 	moveml	%d0-%d7/%a2-%a6,%sp@-
-	movel	SYMBOL_NAME(vme_brdtype),%d1
+	movel	vme_brdtype,%d1
 	jeq	1f			| No tag - use the Bug
 	cmpi	#VME_TYPE_MVME162,%d1
 	jeq	6f
@@ -3112,7 +3112,7 @@
 	is_not_q40(2f)
 	tst.l	%pc@(L(q40_do_debug))	/* only debug if requested */
 	beq	2f
-	lea	%pc@(SYMBOL_NAME(q40_mem_cptr)),%a1
+	lea	%pc@(q40_mem_cptr),%a1
 	move.l	%a1@,%a0
 	move.b	%d0,%a0@
 	addq.l	#4,%a0
@@ -3287,13 +3287,13 @@
 	/* Calculate font size */
 
 #if   defined(FONT_8x8)
-	lea	%pc@(SYMBOL_NAME(font_vga_8x8)), %a0
+	lea	%pc@(font_vga_8x8), %a0
 #elif defined(FONT_8x16)
-	lea	%pc@(SYMBOL_NAME(font_vga_8x16)),%a0
+	lea	%pc@(font_vga_8x16),%a0
 #elif defined(FONT_6x11)
-	lea	%pc@(SYMBOL_NAME(font_vga_6x11)),%a0
+	lea	%pc@(font_vga_6x11),%a0
 #else	/*   (FONT_8x8) default */
-	lea	%pc@(SYMBOL_NAME(font_vga_8x8)), %a0
+	lea	%pc@(font_vga_8x8), %a0
 #endif
 
 	/*
@@ -3347,11 +3347,11 @@
 	putn	%pc@(L(mac_videobase))		/* video addr. */
 
 	puts	"\n  _stext:"
-	lea	%pc@(SYMBOL_NAME(_stext)),%a0
+	lea	%pc@(_stext),%a0
 	putn	%a0
 
 	puts	"\nbootinfo:"
-	lea	%pc@(SYMBOL_NAME(_end)),%a0
+	lea	%pc@(_end),%a0
 	putn	%a0
 
 	puts	"\ncpuid:"
@@ -3380,7 +3380,7 @@
 	subil	#64,%d0		/* snug up against the right edge */
 	clrl	%d1		/* start at the top */
 	movel	#73,%d7
-	lea	%pc@(SYMBOL_NAME(that_penguin)),%a1
+	lea	%pc@(that_penguin),%a1
 console_penguin_row:
 	movel	#31,%d6
 console_penguin_pixel_pair:
@@ -3846,18 +3846,18 @@
 	.data
 	.align	4
 
-SYMBOL_NAME_LABEL(availmem)
+availmem:
 	.long	0
-SYMBOL_NAME_LABEL(m68k_pgtable_cachemode)
+m68k_pgtable_cachemode:
 	.long	0
-SYMBOL_NAME_LABEL(m68k_supervisor_cachemode)
+m68k_supervisor_cachemode:
 	.long	0
 #if defined(CONFIG_MVME16x)
-SYMBOL_NAME_LABEL(mvme_bdid)
+mvme_bdid:
 	.long	0,0,0,0,0,0,0,0
 #endif
 #if defined(CONFIG_Q40)
-SYMBOL_NAME_LABEL(q40_mem_cptr)
+q40_mem_cptr:
 	.long 0
 L(q40_do_debug):	
 	.long 0	
diff -Nru a/arch/m68k/kernel/signal.c b/arch/m68k/kernel/signal.c
--- a/arch/m68k/kernel/signal.c	Sun Apr 28 20:10:32 2002
+++ b/arch/m68k/kernel/signal.c	Sun Apr 28 20:10:32 2002
@@ -408,7 +408,7 @@
 			 "2: movesl %4@+,%2\n\t"
 			 "3: movel %2,%/a0@+\n\t"
 			 "   dbra %1,2b\n\t"
-			 "   bral " SYMBOL_NAME_STR(ret_from_signal) "\n"
+			 "   bral ret_from_signal\n"
 			 "4:\n"
 			 ".section __ex_table,\"a\"\n"
 			 "   .align 4\n"
@@ -507,7 +507,7 @@
 			 "2: movesl %4@+,%2\n\t"
 			 "3: movel %2,%/a0@+\n\t"
 			 "   dbra %1,2b\n\t"
-			 "   bral " SYMBOL_NAME_STR(ret_from_signal) "\n"
+			 "   bral ret_from_signal\n"
 			 "4:\n"
 			 ".section __ex_table,\"a\"\n"
 			 "   .align 4\n"
diff -Nru a/arch/m68k/kernel/sun3-head.S b/arch/m68k/kernel/sun3-head.S
--- a/arch/m68k/kernel/sun3-head.S	Sun Apr 28 20:10:30 2002
+++ b/arch/m68k/kernel/sun3-head.S	Sun Apr 28 20:10:30 2002
@@ -12,27 +12,27 @@
 ROOT_TABLE_SIZE = 128
 PAGESIZE	= 8192
 SUN3_INVALID_PMEG = 255
-.globl SYMBOL_NAME(bootup_user_stack)
-.globl SYMBOL_NAME(bootup_kernel_stack)
-.globl SYMBOL_NAME(pg0)
-.globl SYMBOL_NAME(empty_bad_page)
-.globl SYMBOL_NAME(empty_bad_page_table)
-.globl SYMBOL_NAME(empty_zero_page)
-.globl SYMBOL_NAME(swapper_pg_dir)
-.globl SYMBOL_NAME(kernel_pmd_table)
-.globl SYMBOL_NAME(availmem)
-.global SYMBOL_NAME(m68k_pgtable_cachemode)
-.global SYMBOL_NAME(kpt)
+.globl bootup_user_stack
+.globl bootup_kernel_stack
+.globl pg0
+.globl empty_bad_page
+.globl empty_bad_page_table
+.globl empty_zero_page
+.globl swapper_pg_dir
+.globl kernel_pmd_table
+.globl availmem
+.global m68k_pgtable_cachemode
+.global kpt
 | todo: all these should be in bss!
-SYMBOL_NAME(swapper_pg_dir):                .skip 0x2000
-SYMBOL_NAME(pg0):                           .skip 0x2000
-SYMBOL_NAME(empty_bad_page):                .skip 0x2000
-SYMBOL_NAME(empty_bad_page_table):          .skip 0x2000
-SYMBOL_NAME(kernel_pmd_table):              .skip 0x2000
-SYMBOL_NAME(empty_zero_page):               .skip 0x2000
+swapper_pg_dir:                .skip 0x2000
+pg0:                           .skip 0x2000
+empty_bad_page:                .skip 0x2000
+empty_bad_page_table:          .skip 0x2000
+kernel_pmd_table:              .skip 0x2000
+empty_zero_page:               .skip 0x2000
 
-.globl SYMBOL_NAME(kernel_pg_dir)
-.equ    SYMBOL_NAME(kernel_pg_dir),SYMBOL_NAME(kernel_pmd_table)
+.globl kernel_pg_dir
+.equ    kernel_pg_dir,kernel_pmd_table
 
 	.section .head
 ENTRY(_stext)
@@ -69,11 +69,11 @@
 	jmp	1f:l		
 
 /* Following code executes at high addresses (0xE000xxx). */
-1:	lea	SYMBOL_NAME(init_task_union),%a2	| get initial thread...
+1:	lea	init_task_union,%a2			| get initial thread...
 	lea	%a2@(KTHREAD_SIZE),%sp			| ...and its stack.
 
 /* copy bootinfo records from the loader to _end */
-	lea	SYMBOL_NAME(_end), %a1
+	lea	_end, %a1
 	lea	BI_START, %a0
 	/* number of longs to copy */
 	movel	%a0@, %d0
@@ -88,22 +88,22 @@
 	moveq	#-1,%d0
 	movsb	%d0,(AC_SEGMAP+0x0)
 
-	jbsr	SYMBOL_NAME(sun3_init)
+	jbsr	sun3_init
 
-	jbsr	SYMBOL_NAME(base_trap_init)
+	jbsr	base_trap_init
 			
-        jbsr    SYMBOL_NAME(start_kernel)
+        jbsr    start_kernel
 	trap	#15			
 
         .data
         .even
-SYMBOL_NAME_LABEL(kpt)
+kpt:
         .long 0
-SYMBOL_NAME_LABEL(availmem)
+availmem:
         .long 0
 | todo: remove next two. --m
-SYMBOL_NAME_LABEL(is_medusa)
+is_medusa:
         .long 0
-SYMBOL_NAME_LABEL(m68k_pgtable_cachemode)
+m68k_pgtable_cachemode:
         .long 0
 
diff -Nru a/arch/m68k/kernel/traps.c b/arch/m68k/kernel/traps.c
--- a/arch/m68k/kernel/traps.c	Sun Apr 28 20:10:36 2002
+++ b/arch/m68k/kernel/traps.c	Sun Apr 28 20:10:36 2002
@@ -64,7 +64,7 @@
 /* nmi handler for the Amiga */
 asm(".text\n"
     __ALIGN_STR "\n"
-    SYMBOL_NAME_STR(nmihandler) ": rte");
+    "nmihandler: rte");
 
 /*
  * this must be called very early as the kernel might
diff -Nru a/arch/m68k/lib/semaphore.S b/arch/m68k/lib/semaphore.S
--- a/arch/m68k/lib/semaphore.S	Sun Apr 28 20:10:30 2002
+++ b/arch/m68k/lib/semaphore.S	Sun Apr 28 20:10:30 2002
@@ -18,7 +18,7 @@
 ENTRY(__down_failed)
 	moveml %a0/%d0/%d1,-(%sp)
 	movel %a1,-(%sp)
-	jbsr SYMBOL_NAME(__down)
+	jbsr __down
 	movel (%sp)+,%a1
 	moveml (%sp)+,%a0/%d0/%d1
 	rts
@@ -27,7 +27,7 @@
 	movel %a0,-(%sp)
 	movel %d1,-(%sp)
 	movel %a1,-(%sp)
-	jbsr SYMBOL_NAME(__down_interruptible)
+	jbsr __down_interruptible
 	movel (%sp)+,%a1
 	movel (%sp)+,%d1
 	movel (%sp)+,%a0
@@ -37,7 +37,7 @@
 	movel %a0,-(%sp)
 	movel %d1,-(%sp)
 	movel %a1,-(%sp)
-	jbsr SYMBOL_NAME(__down_trylock)
+	jbsr __down_trylock
 	movel (%sp)+,%a1
 	movel (%sp)+,%d1
 	movel (%sp)+,%a0
@@ -46,7 +46,7 @@
 ENTRY(__up_wakeup)
 	moveml %a0/%d0/%d1,-(%sp)
 	movel %a1,-(%sp)
-	jbsr SYMBOL_NAME(__up)
+	jbsr __up
 	movel (%sp)+,%a1
 	moveml (%sp)+,%a0/%d0/%d1
 	rts
diff -Nru a/arch/m68k/mac/baboon.c b/arch/m68k/mac/baboon.c
--- a/arch/m68k/mac/baboon.c	Sun Apr 28 20:10:31 2002
+++ b/arch/m68k/mac/baboon.c	Sun Apr 28 20:10:31 2002
@@ -27,7 +27,9 @@
 
 void baboon_irq(int, void *, struct pt_regs *);
 
+#if 0
 extern int macide_ack_intr(struct ata_channel *);
+#endif
 
 /*
  * Baboon initialization.
diff -Nru a/arch/m68k/math-emu/fp_entry.S b/arch/m68k/math-emu/fp_entry.S
--- a/arch/m68k/math-emu/fp_entry.S	Sun Apr 28 20:10:31 2002
+++ b/arch/m68k/math-emu/fp_entry.S	Sun Apr 28 20:10:31 2002
@@ -41,17 +41,17 @@
 
 #include "fp_emu.h"
 
-	.globl	SYMBOL_NAME(fpu_emu)
+	.globl	fpu_emu
 	.globl	fp_debugprint
 	.globl	fp_err_ua1,fp_err_ua2
 
 	.text
-SYMBOL_NAME_LABEL(fpu_emu)
+fpu_emu:
 	SAVE_ALL_INT
 	GET_CURRENT(%d0)
 
 #if defined(CPU_M68020_OR_M68030) && defined(CPU_M68040_OR_M68060)
-        tst.l	SYMBOL_NAME(m68k_is040or060)
+        tst.l	m68k_is040or060
         jeq	1f
 #endif
 #if defined(CPU_M68040_OR_M68060)
@@ -63,7 +63,7 @@
 
 #if defined(CONFIG_M68060)
 #if !defined(CPU_M68060_ONLY)
-	btst	#3,SYMBOL_NAME(m68k_cputype)+3
+	btst	#3,m68k_cputype+3
 	jeq	1f
 #endif
 	btst	#7,(FPS_SR,%sp)
@@ -72,11 +72,11 @@
 1:
 	| emulation successful?
 	tst.l	%d0
-	jeq	SYMBOL_NAME(ret_from_exception)
+	jeq	ret_from_exception
 
 	| send some signal to program here
 
-	jra	SYMBOL_NAME(ret_from_exception)
+	jra	ret_from_exception
 
 	| we jump here after an access error while trying to access
 	| user space, we correct stackpointer and send a SIGSEGV to
@@ -88,9 +88,9 @@
 	move.l	%a0,-(%sp)
 	pea	SEGV_MAPERR
 	pea	SIGSEGV
-	jsr	SYMBOL_NAME(fpemu_signal)
+	jsr	fpemu_signal
 	add.w	#12,%sp
-	jra	SYMBOL_NAME(ret_from_exception)
+	jra	ret_from_exception
 
 #if defined(CONFIG_M68060)
 	| send a trace signal if we are debugged
@@ -99,9 +99,9 @@
 	move.l	(FPS_PC,%sp),-(%sp)
 	pea	TRAP_TRACE
 	pea	SIGTRAP
-	jsr	SYMBOL_NAME(fpemu_signal)
+	jsr	fpemu_signal
 	add.w	#12,%sp
-	jra	SYMBOL_NAME(ret_from_exception)
+	jra	ret_from_exception
 #endif
 
 	.globl	fp_get_data_reg, fp_put_data_reg
diff -Nru a/arch/mips/defconfig b/arch/mips/defconfig
--- a/arch/mips/defconfig	Sun Apr 28 20:10:31 2002
+++ b/arch/mips/defconfig	Sun Apr 28 20:10:31 2002
@@ -442,7 +442,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_FREEVXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/mips/defconfig-atlas b/arch/mips/defconfig-atlas
--- a/arch/mips/defconfig-atlas	Sun Apr 28 20:10:35 2002
+++ b/arch/mips/defconfig-atlas	Sun Apr 28 20:10:35 2002
@@ -432,7 +432,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_FREEVXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/mips/defconfig-ddb5476 b/arch/mips/defconfig-ddb5476
--- a/arch/mips/defconfig-ddb5476	Sun Apr 28 20:10:35 2002
+++ b/arch/mips/defconfig-ddb5476	Sun Apr 28 20:10:35 2002
@@ -478,7 +478,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_FREEVXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/mips/defconfig-ddb5477 b/arch/mips/defconfig-ddb5477
--- a/arch/mips/defconfig-ddb5477	Sun Apr 28 20:10:32 2002
+++ b/arch/mips/defconfig-ddb5477	Sun Apr 28 20:10:32 2002
@@ -393,7 +393,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_FREEVXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/mips/defconfig-decstation b/arch/mips/defconfig-decstation
--- a/arch/mips/defconfig-decstation	Sun Apr 28 20:10:37 2002
+++ b/arch/mips/defconfig-decstation	Sun Apr 28 20:10:37 2002
@@ -429,7 +429,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_FREEVXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/mips/defconfig-ip22 b/arch/mips/defconfig-ip22
--- a/arch/mips/defconfig-ip22	Sun Apr 28 20:10:39 2002
+++ b/arch/mips/defconfig-ip22	Sun Apr 28 20:10:39 2002
@@ -442,7 +442,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_FREEVXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/mips/defconfig-it8172 b/arch/mips/defconfig-it8172
--- a/arch/mips/defconfig-it8172	Sun Apr 28 20:10:35 2002
+++ b/arch/mips/defconfig-it8172	Sun Apr 28 20:10:35 2002
@@ -557,7 +557,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_FREEVXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/mips/defconfig-malta b/arch/mips/defconfig-malta
--- a/arch/mips/defconfig-malta	Sun Apr 28 20:10:34 2002
+++ b/arch/mips/defconfig-malta	Sun Apr 28 20:10:34 2002
@@ -458,7 +458,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_FREEVXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/mips/defconfig-nino b/arch/mips/defconfig-nino
--- a/arch/mips/defconfig-nino	Sun Apr 28 20:10:37 2002
+++ b/arch/mips/defconfig-nino	Sun Apr 28 20:10:37 2002
@@ -269,7 +269,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_FREEVXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/mips/defconfig-ocelot b/arch/mips/defconfig-ocelot
--- a/arch/mips/defconfig-ocelot	Sun Apr 28 20:10:36 2002
+++ b/arch/mips/defconfig-ocelot	Sun Apr 28 20:10:36 2002
@@ -394,7 +394,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_FREEVXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/mips/defconfig-pb1000 b/arch/mips/defconfig-pb1000
--- a/arch/mips/defconfig-pb1000	Sun Apr 28 20:10:35 2002
+++ b/arch/mips/defconfig-pb1000	Sun Apr 28 20:10:35 2002
@@ -381,7 +381,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_FREEVXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/mips/defconfig-rm200 b/arch/mips/defconfig-rm200
--- a/arch/mips/defconfig-rm200	Sun Apr 28 20:10:38 2002
+++ b/arch/mips/defconfig-rm200	Sun Apr 28 20:10:38 2002
@@ -312,7 +312,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_FREEVXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/mips/lib/ide-no.c b/arch/mips/lib/ide-no.c
--- a/arch/mips/lib/ide-no.c	Sun Apr 28 20:10:35 2002
+++ b/arch/mips/lib/ide-no.c	Sun Apr 28 20:10:35 2002
@@ -30,42 +30,8 @@
 {
 }
 
-static int no_ide_request_irq(unsigned int irq,
-                              void (*handler)(int,void *, struct pt_regs *),
-                              unsigned long flags, const char *device,
-                              void *dev_id)
-{
-	panic("no_no_ide_request_irq called - shouldn't happen");
-}			
-
-static void no_ide_free_irq(unsigned int irq, void *dev_id)
-{
-	panic("no_ide_free_irq called - shouldn't happen");
-}
-
-static int no_ide_check_region(ide_ioreg_t from, unsigned int extent)
-{
-	panic("no_ide_check_region called - shouldn't happen");
-}
-
-static void no_ide_request_region(ide_ioreg_t from, unsigned int extent,
-                                    const char *name)
-{
-	panic("no_ide_request_region called - shouldn't happen");
-}
-
-static void no_ide_release_region(ide_ioreg_t from, unsigned int extent)
-{
-	panic("no_ide_release_region called - shouldn't happen");
-}
-
 struct ide_ops no_ide_ops = {
 	&no_ide_default_irq,
 	&no_ide_default_io_base,
-	&no_ide_init_hwif_ports,
-	&no_ide_request_irq,
-	&no_ide_free_irq,
-	&no_ide_check_region,
-	&no_ide_request_region,
-	&no_ide_release_region
+	&no_ide_init_hwif_ports
 };
diff -Nru a/arch/mips/lib/ide-std.c b/arch/mips/lib/ide-std.c
--- a/arch/mips/lib/ide-std.c	Sun Apr 28 20:10:37 2002
+++ b/arch/mips/lib/ide-std.c	Sun Apr 28 20:10:37 2002
@@ -62,42 +62,8 @@
 	hw->io_ports[IDE_IRQ_OFFSET] = 0;
 }
 
-static int std_ide_request_irq(unsigned int irq,
-                                void (*handler)(int,void *, struct pt_regs *),
-                                unsigned long flags, const char *device,
-                                void *dev_id)
-{
-	return request_irq(irq, handler, flags, device, dev_id);
-}			
-
-static void std_ide_free_irq(unsigned int irq, void *dev_id)
-{
-	free_irq(irq, dev_id);
-}
-
-static int std_ide_check_region(ide_ioreg_t from, unsigned int extent)
-{
-	return check_region(from, extent);
-}
-
-static void std_ide_request_region(ide_ioreg_t from, unsigned int extent,
-                                    const char *name)
-{
-	request_region(from, extent, name);
-}
-
-static void std_ide_release_region(ide_ioreg_t from, unsigned int extent)
-{
-	release_region(from, extent);
-}
-
 struct ide_ops std_ide_ops = {
 	&std_ide_default_irq,
 	&std_ide_default_io_base,
-	&std_ide_init_hwif_ports,
-	&std_ide_request_irq,
-	&std_ide_free_irq,
-	&std_ide_check_region,
-	&std_ide_request_region,
-	&std_ide_release_region
+	&std_ide_init_hwif_ports
 };
diff -Nru a/arch/mips64/defconfig b/arch/mips64/defconfig
--- a/arch/mips64/defconfig	Sun Apr 28 20:10:32 2002
+++ b/arch/mips64/defconfig	Sun Apr 28 20:10:32 2002
@@ -399,7 +399,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_FREEVXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/mips64/defconfig-ip22 b/arch/mips64/defconfig-ip22
--- a/arch/mips64/defconfig-ip22	Sun Apr 28 20:10:30 2002
+++ b/arch/mips64/defconfig-ip22	Sun Apr 28 20:10:30 2002
@@ -404,7 +404,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_FREEVXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/mips64/defconfig-ip27 b/arch/mips64/defconfig-ip27
--- a/arch/mips64/defconfig-ip27	Sun Apr 28 20:10:36 2002
+++ b/arch/mips64/defconfig-ip27	Sun Apr 28 20:10:36 2002
@@ -399,7 +399,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_FREEVXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/mips64/defconfig-ip32 b/arch/mips64/defconfig-ip32
--- a/arch/mips64/defconfig-ip32	Sun Apr 28 20:10:39 2002
+++ b/arch/mips64/defconfig-ip32	Sun Apr 28 20:10:39 2002
@@ -429,7 +429,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/mips64/kernel/ioctl32.c b/arch/mips64/kernel/ioctl32.c
--- a/arch/mips64/kernel/ioctl32.c	Sun Apr 28 20:10:36 2002
+++ b/arch/mips64/kernel/ioctl32.c	Sun Apr 28 20:10:36 2002
@@ -732,15 +732,12 @@
 	IOCTL32_HANDLER(HDIO_GETGEO, hdio_getgeo),	/* hdreg.h ioctls  */
 	IOCTL32_HANDLER(HDIO_GET_UNMASKINTR, hdio_ioctl_trans),
 	IOCTL32_HANDLER(HDIO_GET_MULTCOUNT, hdio_ioctl_trans),
-	// HDIO_OBSOLETE_IDENTITY
 	IOCTL32_HANDLER(HDIO_GET_KEEPSETTINGS, hdio_ioctl_trans),
 	IOCTL32_HANDLER(HDIO_GET_32BIT, hdio_ioctl_trans),
 	IOCTL32_HANDLER(HDIO_GET_NOWERR, hdio_ioctl_trans),
 	IOCTL32_HANDLER(HDIO_GET_DMA, hdio_ioctl_trans),
 	IOCTL32_HANDLER(HDIO_GET_NICE, hdio_ioctl_trans),
 	IOCTL32_DEFAULT(HDIO_GET_IDENTITY),
-	// HDIO_TRISTATE_HWIF				/* not implemented */
-	// HDIO_DRIVE_TASK				/* To do, need specs */
 	IOCTL32_DEFAULT(HDIO_DRIVE_CMD),
 	IOCTL32_DEFAULT(HDIO_SET_MULTCOUNT),
 	IOCTL32_DEFAULT(HDIO_SET_UNMASKINTR),
diff -Nru a/arch/mips64/lib/ide-no.c b/arch/mips64/lib/ide-no.c
--- a/arch/mips64/lib/ide-no.c	Sun Apr 28 20:10:30 2002
+++ b/arch/mips64/lib/ide-no.c	Sun Apr 28 20:10:30 2002
@@ -29,42 +29,8 @@
 {
 }
 
-static int no_ide_request_irq(unsigned int irq,
-                              void (*handler)(int,void *, struct pt_regs *),
-                              unsigned long flags, const char *device,
-                              void *dev_id)
-{
-	panic("no_no_ide_request_irq called - shouldn't happen");
-}			
-
-static void no_ide_free_irq(unsigned int irq, void *dev_id)
-{
-	panic("no_ide_free_irq called - shouldn't happen");
-}
-
-static int no_ide_check_region(ide_ioreg_t from, unsigned int extent)
-{
-	panic("no_ide_check_region called - shouldn't happen");
-}
-
-static void no_ide_request_region(ide_ioreg_t from, unsigned int extent,
-                                    const char *name)
-{
-	panic("no_ide_request_region called - shouldn't happen");
-}
-
-static void no_ide_release_region(ide_ioreg_t from, unsigned int extent)
-{
-	panic("no_ide_release_region called - shouldn't happen");
-}
-
 struct ide_ops no_ide_ops = {
 	&no_ide_default_irq,
 	&no_ide_default_io_base,
-	&no_ide_init_hwif_ports,
-	&no_ide_request_irq,
-	&no_ide_free_irq,
-	&no_ide_check_region,
-	&no_ide_request_region,
-	&no_ide_release_region
+	&no_ide_init_hwif_ports
 };
diff -Nru a/arch/mips64/lib/ide-std.c b/arch/mips64/lib/ide-std.c
--- a/arch/mips64/lib/ide-std.c	Sun Apr 28 20:10:30 2002
+++ b/arch/mips64/lib/ide-std.c	Sun Apr 28 20:10:30 2002
@@ -62,42 +62,8 @@
 	hw->io_ports[IDE_IRQ_OFFSET] = 0;
 }
 
-static int std_ide_request_irq(unsigned int irq,
-                                void (*handler)(int,void *, struct pt_regs *),
-                                unsigned long flags, const char *device,
-                                void *dev_id)
-{
-	return request_irq(irq, handler, flags, device, dev_id);
-}			
-
-static void std_ide_free_irq(unsigned int irq, void *dev_id)
-{
-	free_irq(irq, dev_id);
-}
-
-static int std_ide_check_region(ide_ioreg_t from, unsigned int extent)
-{
-	return check_region(from, extent);
-}
-
-static void std_ide_request_region(ide_ioreg_t from, unsigned int extent,
-                                    const char *name)
-{
-	request_region(from, extent, name);
-}
-
-static void std_ide_release_region(ide_ioreg_t from, unsigned int extent)
-{
-	release_region(from, extent);
-}
-
 struct ide_ops std_ide_ops = {
 	&std_ide_default_irq,
 	&std_ide_default_io_base,
-	&std_ide_init_hwif_ports,
-	&std_ide_request_irq,
-	&std_ide_free_irq,
-	&std_ide_check_region,
-	&std_ide_request_region,
-	&std_ide_release_region
+	&std_ide_init_hwif_ports
 };
diff -Nru a/arch/parisc/defconfig b/arch/parisc/defconfig
--- a/arch/parisc/defconfig	Sun Apr 28 20:10:30 2002
+++ b/arch/parisc/defconfig	Sun Apr 28 20:10:30 2002
@@ -293,7 +293,7 @@
 # CONFIG_JOLIET is not set
 # CONFIG_MINIX_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ppc/configs/FADS_defconfig b/arch/ppc/configs/FADS_defconfig
--- a/arch/ppc/configs/FADS_defconfig	Sun Apr 28 20:10:32 2002
+++ b/arch/ppc/configs/FADS_defconfig	Sun Apr 28 20:10:32 2002
@@ -375,7 +375,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ppc/configs/IVMS8_defconfig b/arch/ppc/configs/IVMS8_defconfig
--- a/arch/ppc/configs/IVMS8_defconfig	Sun Apr 28 20:10:31 2002
+++ b/arch/ppc/configs/IVMS8_defconfig	Sun Apr 28 20:10:31 2002
@@ -413,7 +413,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ppc/configs/SM850_defconfig b/arch/ppc/configs/SM850_defconfig
--- a/arch/ppc/configs/SM850_defconfig	Sun Apr 28 20:10:35 2002
+++ b/arch/ppc/configs/SM850_defconfig	Sun Apr 28 20:10:35 2002
@@ -371,7 +371,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ppc/configs/SPD823TS_defconfig b/arch/ppc/configs/SPD823TS_defconfig
--- a/arch/ppc/configs/SPD823TS_defconfig	Sun Apr 28 20:10:36 2002
+++ b/arch/ppc/configs/SPD823TS_defconfig	Sun Apr 28 20:10:36 2002
@@ -370,7 +370,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ppc/configs/TQM823L_defconfig b/arch/ppc/configs/TQM823L_defconfig
--- a/arch/ppc/configs/TQM823L_defconfig	Sun Apr 28 20:10:32 2002
+++ b/arch/ppc/configs/TQM823L_defconfig	Sun Apr 28 20:10:32 2002
@@ -371,7 +371,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ppc/configs/TQM8260_defconfig b/arch/ppc/configs/TQM8260_defconfig
--- a/arch/ppc/configs/TQM8260_defconfig	Sun Apr 28 20:10:33 2002
+++ b/arch/ppc/configs/TQM8260_defconfig	Sun Apr 28 20:10:33 2002
@@ -344,7 +344,7 @@
 # CONFIG_JOLIET is not set
 # CONFIG_MINIX_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ppc/configs/TQM850L_defconfig b/arch/ppc/configs/TQM850L_defconfig
--- a/arch/ppc/configs/TQM850L_defconfig	Sun Apr 28 20:10:37 2002
+++ b/arch/ppc/configs/TQM850L_defconfig	Sun Apr 28 20:10:37 2002
@@ -371,7 +371,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ppc/configs/TQM860L_defconfig b/arch/ppc/configs/TQM860L_defconfig
--- a/arch/ppc/configs/TQM860L_defconfig	Sun Apr 28 20:10:34 2002
+++ b/arch/ppc/configs/TQM860L_defconfig	Sun Apr 28 20:10:34 2002
@@ -426,7 +426,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ppc/configs/adir_defconfig b/arch/ppc/configs/adir_defconfig
--- a/arch/ppc/configs/adir_defconfig	Sun Apr 28 20:10:32 2002
+++ b/arch/ppc/configs/adir_defconfig	Sun Apr 28 20:10:32 2002
@@ -513,7 +513,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ppc/configs/apus_defconfig b/arch/ppc/configs/apus_defconfig
--- a/arch/ppc/configs/apus_defconfig	Sun Apr 28 20:10:33 2002
+++ b/arch/ppc/configs/apus_defconfig	Sun Apr 28 20:10:33 2002
@@ -669,7 +669,7 @@
 CONFIG_MINIX_FS=y
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ppc/configs/ash_defconfig b/arch/ppc/configs/ash_defconfig
--- a/arch/ppc/configs/ash_defconfig	Sun Apr 28 20:10:31 2002
+++ b/arch/ppc/configs/ash_defconfig	Sun Apr 28 20:10:31 2002
@@ -431,7 +431,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ppc/configs/bseip_defconfig b/arch/ppc/configs/bseip_defconfig
--- a/arch/ppc/configs/bseip_defconfig	Sun Apr 28 20:10:32 2002
+++ b/arch/ppc/configs/bseip_defconfig	Sun Apr 28 20:10:32 2002
@@ -370,7 +370,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ppc/configs/ceder_defconfig b/arch/ppc/configs/ceder_defconfig
--- a/arch/ppc/configs/ceder_defconfig	Sun Apr 28 20:10:37 2002
+++ b/arch/ppc/configs/ceder_defconfig	Sun Apr 28 20:10:37 2002
@@ -433,7 +433,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ppc/configs/common_defconfig b/arch/ppc/configs/common_defconfig
--- a/arch/ppc/configs/common_defconfig	Sun Apr 28 20:10:34 2002
+++ b/arch/ppc/configs/common_defconfig	Sun Apr 28 20:10:34 2002
@@ -737,7 +737,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_DEVFS_FS=y
diff -Nru a/arch/ppc/configs/cpci405_defconfig b/arch/ppc/configs/cpci405_defconfig
--- a/arch/ppc/configs/cpci405_defconfig	Sun Apr 28 20:10:35 2002
+++ b/arch/ppc/configs/cpci405_defconfig	Sun Apr 28 20:10:35 2002
@@ -468,7 +468,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ppc/configs/ep405_defconfig b/arch/ppc/configs/ep405_defconfig
--- a/arch/ppc/configs/ep405_defconfig	Sun Apr 28 20:10:32 2002
+++ b/arch/ppc/configs/ep405_defconfig	Sun Apr 28 20:10:32 2002
@@ -427,7 +427,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ppc/configs/est8260_defconfig b/arch/ppc/configs/est8260_defconfig
--- a/arch/ppc/configs/est8260_defconfig	Sun Apr 28 20:10:37 2002
+++ b/arch/ppc/configs/est8260_defconfig	Sun Apr 28 20:10:37 2002
@@ -354,7 +354,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ppc/configs/ev64260_defconfig b/arch/ppc/configs/ev64260_defconfig
--- a/arch/ppc/configs/ev64260_defconfig	Sun Apr 28 20:10:35 2002
+++ b/arch/ppc/configs/ev64260_defconfig	Sun Apr 28 20:10:35 2002
@@ -472,7 +472,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_DEVFS_FS=y
diff -Nru a/arch/ppc/configs/gemini_defconfig b/arch/ppc/configs/gemini_defconfig
--- a/arch/ppc/configs/gemini_defconfig	Sun Apr 28 20:10:35 2002
+++ b/arch/ppc/configs/gemini_defconfig	Sun Apr 28 20:10:35 2002
@@ -452,7 +452,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_DEVFS_FS=y
diff -Nru a/arch/ppc/configs/iSeries_defconfig b/arch/ppc/configs/iSeries_defconfig
--- a/arch/ppc/configs/iSeries_defconfig	Sun Apr 28 20:10:30 2002
+++ b/arch/ppc/configs/iSeries_defconfig	Sun Apr 28 20:10:30 2002
@@ -363,7 +363,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_DEVFS_FS=y
diff -Nru a/arch/ppc/configs/ibmchrp_defconfig b/arch/ppc/configs/ibmchrp_defconfig
--- a/arch/ppc/configs/ibmchrp_defconfig	Sun Apr 28 20:10:36 2002
+++ b/arch/ppc/configs/ibmchrp_defconfig	Sun Apr 28 20:10:36 2002
@@ -592,7 +592,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_DEVFS_FS=y
diff -Nru a/arch/ppc/configs/k2_defconfig b/arch/ppc/configs/k2_defconfig
--- a/arch/ppc/configs/k2_defconfig	Sun Apr 28 20:10:30 2002
+++ b/arch/ppc/configs/k2_defconfig	Sun Apr 28 20:10:30 2002
@@ -505,7 +505,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ppc/configs/lopec_defconfig b/arch/ppc/configs/lopec_defconfig
--- a/arch/ppc/configs/lopec_defconfig	Sun Apr 28 20:10:31 2002
+++ b/arch/ppc/configs/lopec_defconfig	Sun Apr 28 20:10:31 2002
@@ -471,7 +471,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ppc/configs/mbx_defconfig b/arch/ppc/configs/mbx_defconfig
--- a/arch/ppc/configs/mbx_defconfig	Sun Apr 28 20:10:30 2002
+++ b/arch/ppc/configs/mbx_defconfig	Sun Apr 28 20:10:30 2002
@@ -363,7 +363,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ppc/configs/mcpn765_defconfig b/arch/ppc/configs/mcpn765_defconfig
--- a/arch/ppc/configs/mcpn765_defconfig	Sun Apr 28 20:10:37 2002
+++ b/arch/ppc/configs/mcpn765_defconfig	Sun Apr 28 20:10:37 2002
@@ -403,7 +403,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ppc/configs/menf1_defconfig b/arch/ppc/configs/menf1_defconfig
--- a/arch/ppc/configs/menf1_defconfig	Sun Apr 28 20:10:30 2002
+++ b/arch/ppc/configs/menf1_defconfig	Sun Apr 28 20:10:30 2002
@@ -514,7 +514,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ppc/configs/mvme5100_defconfig b/arch/ppc/configs/mvme5100_defconfig
--- a/arch/ppc/configs/mvme5100_defconfig	Sun Apr 28 20:10:31 2002
+++ b/arch/ppc/configs/mvme5100_defconfig	Sun Apr 28 20:10:31 2002
@@ -563,7 +563,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ppc/configs/oak_defconfig b/arch/ppc/configs/oak_defconfig
--- a/arch/ppc/configs/oak_defconfig	Sun Apr 28 20:10:31 2002
+++ b/arch/ppc/configs/oak_defconfig	Sun Apr 28 20:10:31 2002
@@ -406,7 +406,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ppc/configs/pcore_defconfig b/arch/ppc/configs/pcore_defconfig
--- a/arch/ppc/configs/pcore_defconfig	Sun Apr 28 20:10:37 2002
+++ b/arch/ppc/configs/pcore_defconfig	Sun Apr 28 20:10:37 2002
@@ -503,7 +503,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ppc/configs/pmac_defconfig b/arch/ppc/configs/pmac_defconfig
--- a/arch/ppc/configs/pmac_defconfig	Sun Apr 28 20:10:38 2002
+++ b/arch/ppc/configs/pmac_defconfig	Sun Apr 28 20:10:38 2002
@@ -817,7 +817,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_DEVFS_FS=y
diff -Nru a/arch/ppc/configs/power3_defconfig b/arch/ppc/configs/power3_defconfig
--- a/arch/ppc/configs/power3_defconfig	Sun Apr 28 20:10:32 2002
+++ b/arch/ppc/configs/power3_defconfig	Sun Apr 28 20:10:32 2002
@@ -576,7 +576,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ppc/configs/pplus_defconfig b/arch/ppc/configs/pplus_defconfig
--- a/arch/ppc/configs/pplus_defconfig	Sun Apr 28 20:10:37 2002
+++ b/arch/ppc/configs/pplus_defconfig	Sun Apr 28 20:10:37 2002
@@ -607,7 +607,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ppc/configs/prpmc750_defconfig b/arch/ppc/configs/prpmc750_defconfig
--- a/arch/ppc/configs/prpmc750_defconfig	Sun Apr 28 20:10:32 2002
+++ b/arch/ppc/configs/prpmc750_defconfig	Sun Apr 28 20:10:32 2002
@@ -439,7 +439,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ppc/configs/prpmc800_defconfig b/arch/ppc/configs/prpmc800_defconfig
--- a/arch/ppc/configs/prpmc800_defconfig	Sun Apr 28 20:10:35 2002
+++ b/arch/ppc/configs/prpmc800_defconfig	Sun Apr 28 20:10:35 2002
@@ -453,7 +453,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ppc/configs/redwood5_defconfig b/arch/ppc/configs/redwood5_defconfig
--- a/arch/ppc/configs/redwood5_defconfig	Sun Apr 28 20:10:34 2002
+++ b/arch/ppc/configs/redwood5_defconfig	Sun Apr 28 20:10:34 2002
@@ -442,7 +442,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ppc/configs/redwood_defconfig b/arch/ppc/configs/redwood_defconfig
--- a/arch/ppc/configs/redwood_defconfig	Sun Apr 28 20:10:36 2002
+++ b/arch/ppc/configs/redwood_defconfig	Sun Apr 28 20:10:36 2002
@@ -371,7 +371,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ppc/configs/rpxcllf_defconfig b/arch/ppc/configs/rpxcllf_defconfig
--- a/arch/ppc/configs/rpxcllf_defconfig	Sun Apr 28 20:10:33 2002
+++ b/arch/ppc/configs/rpxcllf_defconfig	Sun Apr 28 20:10:33 2002
@@ -370,7 +370,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ppc/configs/rpxlite_defconfig b/arch/ppc/configs/rpxlite_defconfig
--- a/arch/ppc/configs/rpxlite_defconfig	Sun Apr 28 20:10:32 2002
+++ b/arch/ppc/configs/rpxlite_defconfig	Sun Apr 28 20:10:32 2002
@@ -370,7 +370,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ppc/configs/sandpoint_defconfig b/arch/ppc/configs/sandpoint_defconfig
--- a/arch/ppc/configs/sandpoint_defconfig	Sun Apr 28 20:10:36 2002
+++ b/arch/ppc/configs/sandpoint_defconfig	Sun Apr 28 20:10:36 2002
@@ -484,7 +484,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ppc/configs/spruce_defconfig b/arch/ppc/configs/spruce_defconfig
--- a/arch/ppc/configs/spruce_defconfig	Sun Apr 28 20:10:31 2002
+++ b/arch/ppc/configs/spruce_defconfig	Sun Apr 28 20:10:31 2002
@@ -401,7 +401,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ppc/configs/walnut_defconfig b/arch/ppc/configs/walnut_defconfig
--- a/arch/ppc/configs/walnut_defconfig	Sun Apr 28 20:10:34 2002
+++ b/arch/ppc/configs/walnut_defconfig	Sun Apr 28 20:10:34 2002
@@ -427,7 +427,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/ppc/configs/zx4500_defconfig b/arch/ppc/configs/zx4500_defconfig
--- a/arch/ppc/configs/zx4500_defconfig	Sun Apr 28 20:10:32 2002
+++ b/arch/ppc/configs/zx4500_defconfig	Sun Apr 28 20:10:32 2002
@@ -408,7 +408,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_DEVFS_FS=y
diff -Nru a/arch/ppc/defconfig b/arch/ppc/defconfig
--- a/arch/ppc/defconfig	Sun Apr 28 20:10:33 2002
+++ b/arch/ppc/defconfig	Sun Apr 28 20:10:33 2002
@@ -737,7 +737,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_DEVFS_FS=y
diff -Nru a/arch/ppc/kernel/ppc4xx_setup.c b/arch/ppc/kernel/ppc4xx_setup.c
--- a/arch/ppc/kernel/ppc4xx_setup.c	Sun Apr 28 20:10:30 2002
+++ b/arch/ppc/kernel/ppc4xx_setup.c	Sun Apr 28 20:10:30 2002
@@ -256,29 +256,6 @@
  * IDE stuff.
  * should be generic for every IDE PCI chipset
  */
-#if defined(CONFIG_BLK_DEV_IDE)
-static int
-ppc4xx_ide_check_region(ide_ioreg_t from, unsigned int extent)
-{
-	return check_region(from, extent);
-}
-
-static void
-ppc4xx_ide_request_region(ide_ioreg_t from, unsigned int extent,
-			  const char *name)
-{
-	request_region(from, extent, name);
-	return;
-}
-
-static void
-ppc4xx_ide_release_region(ide_ioreg_t from, unsigned int extent)
-{
-	release_region(from, extent);
-	return;
-}
-#endif
-
 #if defined(CONFIG_BLK_DEV_IDEPCI)
 static void
 ppc4xx_ide_init_hwif_ports(hw_regs_t * hw, ide_ioreg_t data_port,
@@ -398,16 +375,13 @@
 **   m8xx_setup.c, prep_setup.c use
 **     defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
 */
-#if defined (CONFIG_IDE)
-	ppc_ide_md.ide_request_region = ppc4xx_ide_request_region;
-	ppc_ide_md.ide_release_region = ppc4xx_ide_release_region;
-	ppc_ide_md.ide_check_region = ppc4xx_ide_check_region;
-#if defined(CONFIG_BLK_DEV_IDEPCI)
+#ifdef CONFIG_IDE
+# if defined(CONFIG_BLK_DEV_IDEPCI)
 	ppc_ide_md.ide_init_hwif = ppc4xx_ide_init_hwif_ports;
-#elif defined (CONFIG_DMA_NONPCI)	/* ON board IDE */
+# elif defined (CONFIG_DMA_NONPCI)	/* ON board IDE */
 	ppc_ide_md.default_irq = nonpci_ide_default_irq;
 	ppc_ide_md.ide_init_hwif = nonpci_ide_init_hwif_ports;
-#endif
+# endif
 #endif
 	board_init();
 
diff -Nru a/arch/ppc/platforms/chrp_setup.c b/arch/ppc/platforms/chrp_setup.c
--- a/arch/ppc/platforms/chrp_setup.c	Sun Apr 28 20:10:34 2002
+++ b/arch/ppc/platforms/chrp_setup.c	Sun Apr 28 20:10:34 2002
@@ -463,33 +463,6 @@
 #endif /* CONFIG_VT && (CONFIG_ADB_KEYBOARD || CONFIG_INPUT) */
 }
 
-#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
-/*
- * IDE stuff.
- */
-
-static int __chrp
-chrp_ide_check_region(ide_ioreg_t from, unsigned int extent)
-{
-        return check_region(from, extent);
-}
-
-static void __chrp
-chrp_ide_request_region(ide_ioreg_t from,
-			unsigned int extent,
-			const char *name)
-{
-        request_region(from, extent, name);
-}
-
-static void __chrp
-chrp_ide_release_region(ide_ioreg_t from,
-			unsigned int extent)
-{
-        release_region(from, extent);
-}
-#endif
-
 /*
  * One of the main thing these mappings are needed for is so that
  * xmon can get to the serial port early on.  We probably should
@@ -596,12 +569,6 @@
 #ifdef CONFIG_SMP
 	ppc_md.smp_ops = &chrp_smp_ops;
 #endif /* CONFIG_SMP */
-
-#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
-        ppc_ide_md.ide_check_region = chrp_ide_check_region;
-        ppc_ide_md.ide_request_region = chrp_ide_request_region;
-        ppc_ide_md.ide_release_region = chrp_ide_release_region;
-#endif
 
 	/*
 	 * Print the banner, then scroll down so boot progress
diff -Nru a/arch/ppc/platforms/k2_setup.c b/arch/ppc/platforms/k2_setup.c
--- a/arch/ppc/platforms/k2_setup.c	Sun Apr 28 20:10:37 2002
+++ b/arch/ppc/platforms/k2_setup.c	Sun Apr 28 20:10:37 2002
@@ -55,24 +55,6 @@
 
 #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
 /* IDE functions */
-static int
-k2_ide_check_region(ide_ioreg_t from, unsigned int extent)
-{
-        return check_region(from, extent);
-}
-
-static void
-k2_ide_request_region(ide_ioreg_t from, unsigned int extent,
-		const char *name)
-{
-        request_region(from, extent, name);
-}
-
-static void
-k2_ide_release_region(ide_ioreg_t from, unsigned int extent)
-{
-        release_region(from, extent);
-}
 
 static void __init
 k2_ide_init_hwif_ports (hw_regs_t *hw, ide_ioreg_t data_port,
@@ -373,9 +355,6 @@
 	ppc_md.nvram_write_val = todc_direct_write_val;
 
 #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
-	ppc_ide_md.ide_check_region = k2_ide_check_region;
-	ppc_ide_md.ide_request_region = k2_ide_request_region;
-	ppc_ide_md.ide_release_region = k2_ide_release_region;
 	ppc_ide_md.ide_init_hwif = k2_ide_init_hwif_ports;
 #endif
 }
diff -Nru a/arch/ppc/platforms/lopec_setup.c b/arch/ppc/platforms/lopec_setup.c
--- a/arch/ppc/platforms/lopec_setup.c	Sun Apr 28 20:10:30 2002
+++ b/arch/ppc/platforms/lopec_setup.c	Sun Apr 28 20:10:30 2002
@@ -154,13 +154,6 @@
 		return 0;
 }
 
-static void
-lopec_ide_request_region(ide_ioreg_t from, unsigned int to,
-			 const char *name)
-{
-	request_region(from, to, name);
-}
-
 static ide_ioreg_t
 lopec_ide_default_io_base(int index)
 {
@@ -169,19 +162,6 @@
 	return lopec_ide_regbase[index];
 }
 
-static int
-lopec_ide_check_region(ide_ioreg_t from, unsigned int to)
-{
-	return check_region(from, to);
-}
-
-static void
-lopec_ide_release_region(ide_ioreg_t from, unsigned int to)
-{
-	release_region(from, to);
-}
-
-
 static void __init
 lopec_ide_init_hwif_ports(hw_regs_t *hw, ide_ioreg_t data,
 			  ide_ioreg_t ctl, int *irq)
@@ -350,9 +330,6 @@
 #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_ID_MODULE)
 	ppc_ide_md.default_irq = lopec_ide_default_irq;
 	ppc_ide_md.default_io_base = lopec_ide_default_io_base;
-	ppc_ide_md.ide_request_region = lopec_ide_request_region;
-	ppc_ide_md.ide_check_region = lopec_ide_check_region;
-	ppc_ide_md.ide_release_region = lopec_ide_release_region;
 	ppc_ide_md.ide_init_hwif = lopec_ide_init_hwif_ports;
 #endif
 }
diff -Nru a/arch/ppc/platforms/mcpn765_setup.c b/arch/ppc/platforms/mcpn765_setup.c
--- a/arch/ppc/platforms/mcpn765_setup.c	Sun Apr 28 20:10:31 2002
+++ b/arch/ppc/platforms/mcpn765_setup.c	Sun Apr 28 20:10:31 2002
@@ -322,29 +322,6 @@
 	return mcpn765_ide_regbase[index];
 }
 
-static int
-mcpn765_ide_check_region(ide_ioreg_t from, unsigned int extent)
-{
-	return check_region(from, extent);
-}
-
-static void
-mcpn765_ide_request_region(ide_ioreg_t from,
-			unsigned int extent,
-			const char *name)
-{
-        request_region(from, extent, name);
-	return;
-}
-
-static void
-mcpn765_ide_release_region(ide_ioreg_t from,
-			unsigned int extent)
-{
-        release_region(from, extent);
-	return;
-}
-
 static void __init
 mcpn765_ide_init_hwif_ports(hw_regs_t *hw, ide_ioreg_t data_port,
 			      ide_ioreg_t ctrl_port, int *irq)
@@ -492,9 +469,6 @@
 #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
         ppc_ide_md.default_irq = mcpn765_ide_default_irq;
         ppc_ide_md.default_io_base = mcpn765_ide_default_io_base;
-        ppc_ide_md.ide_check_region = mcpn765_ide_check_region;
-        ppc_ide_md.ide_request_region = mcpn765_ide_request_region;
-        ppc_ide_md.ide_release_region = mcpn765_ide_release_region;
         ppc_ide_md.ide_init_hwif = mcpn765_ide_init_hwif_ports;
 #endif
 
diff -Nru a/arch/ppc/platforms/menf1_setup.c b/arch/ppc/platforms/menf1_setup.c
--- a/arch/ppc/platforms/menf1_setup.c	Sun Apr 28 20:10:34 2002
+++ b/arch/ppc/platforms/menf1_setup.c	Sun Apr 28 20:10:34 2002
@@ -199,27 +199,6 @@
 #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
 /* IDE functions */
 
-static int
-menf1_ide_check_region(ide_ioreg_t from, unsigned int extent)
-{
-        return check_region(from, extent);
-}
-
-static void
-menf1_ide_request_region(ide_ioreg_t from,
-			unsigned int extent,
-			const char *name)
-{
-        request_region(from, extent, name);
-}
-
-static void
-menf1_ide_release_region(ide_ioreg_t from,
-			unsigned int extent)
-{
-        release_region(from, extent);
-}
-
 static void __init
 menf1_ide_init_hwif_ports (hw_regs_t *hw, ide_ioreg_t data_port,
 		ide_ioreg_t ctrl_port, int *irq)
@@ -305,9 +284,6 @@
 #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
 	ppc_ide_md.default_io_base = menf1_ide_default_io_base;
 	ppc_ide_md.default_irq = menf1_ide_default_irq;
-	ppc_ide_md.ide_check_region = menf1_ide_check_region;
-	ppc_ide_md.ide_request_region = menf1_ide_request_region;
-	ppc_ide_md.ide_release_region = menf1_ide_release_region;
 	ppc_ide_md.ide_init_hwif = menf1_ide_init_hwif_ports;
 #endif
 }
diff -Nru a/arch/ppc/platforms/pmac_setup.c b/arch/ppc/platforms/pmac_setup.c
--- a/arch/ppc/platforms/pmac_setup.c	Sun Apr 28 20:10:31 2002
+++ b/arch/ppc/platforms/pmac_setup.c	Sun Apr 28 20:10:31 2002
@@ -601,45 +601,6 @@
    pmac_power_off();
 }
 
-
-#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
-/*
- * IDE stuff.
- */
-static int __pmac
-pmac_ide_check_region(ide_ioreg_t from, unsigned int extent)
-{
-#ifdef CONFIG_BLK_DEV_IDE_PMAC
-	if (pmac_ide_check_base(from) >= 0)
-		return 0;
-#endif
-	return check_region(from, extent);
-}
-
-static void __pmac
-pmac_ide_request_region(ide_ioreg_t from,
-			unsigned int extent,
-			const char *name)
-{
-#ifdef CONFIG_BLK_DEV_IDE_PMAC
-	if (pmac_ide_check_base(from) >= 0)
-		return;
-#endif
-	request_region(from, extent, name);
-}
-
-static void __pmac
-pmac_ide_release_region(ide_ioreg_t from,
-			unsigned int extent)
-{
-#ifdef CONFIG_BLK_DEV_IDE_PMAC
-	if (pmac_ide_check_base(from) >= 0)
-		return;
-#endif
-	release_region(from, extent);
-}
-#endif /* defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) */
-
 /*
  * Read in a property describing some pieces of memory.
  */
@@ -795,12 +756,6 @@
 	ppc_md.feature_call   = pmac_do_feature_call;
 
 	select_adb_keyboard();
-
-#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
-        ppc_ide_md.ide_check_region	= pmac_ide_check_region;
-        ppc_ide_md.ide_request_region	= pmac_ide_request_region;
-        ppc_ide_md.ide_release_region	= pmac_ide_release_region;
-#endif /* defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) */
 
 #ifdef CONFIG_BOOTX_TEXT
 	ppc_md.progress = pmac_progress;
diff -Nru a/arch/ppc/platforms/pplus_setup.c b/arch/ppc/platforms/pplus_setup.c
--- a/arch/ppc/platforms/pplus_setup.c	Sun Apr 28 20:10:39 2002
+++ b/arch/ppc/platforms/pplus_setup.c	Sun Apr 28 20:10:39 2002
@@ -263,27 +263,6 @@
 	}
 }
 
-static int 
-pplus_ide_check_region(ide_ioreg_t from, unsigned int extent)
-{
-	return check_region(from, extent);
-}
-
-static void 
-pplus_ide_request_region(ide_ioreg_t from,
-			unsigned int extent,
-			const char *name)
-{
-	request_region(from, extent, name);
-}
-
-static void 
-pplus_ide_release_region(ide_ioreg_t from,
-			unsigned int extent)
-{
-	release_region(from, extent);
-}
-
 static void __init
 pplus_ide_init_hwif_ports (hw_regs_t *hw, ide_ioreg_t data_port, ide_ioreg_t ctrl_port, int *irq)
 {
@@ -528,9 +507,6 @@
 #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
 	ppc_ide_md.default_irq = pplus_ide_default_irq;
 	ppc_ide_md.default_io_base = pplus_ide_default_io_base;
-	ppc_ide_md.ide_check_region = pplus_ide_check_region;
-	ppc_ide_md.ide_request_region = pplus_ide_request_region;
-	ppc_ide_md.ide_release_region = pplus_ide_release_region;
 	ppc_ide_md.ide_init_hwif = pplus_ide_init_hwif_ports;
 #endif
 
diff -Nru a/arch/ppc/platforms/prep_setup.c b/arch/ppc/platforms/prep_setup.c
--- a/arch/ppc/platforms/prep_setup.c	Sun Apr 28 20:10:38 2002
+++ b/arch/ppc/platforms/prep_setup.c	Sun Apr 28 20:10:38 2002
@@ -733,27 +733,6 @@
 			return 0;
 	}
 }
-
-static int __prep
-prep_ide_check_region(ide_ioreg_t from, unsigned int extent)
-{
-	return check_region(from, extent);
-}
-
-static void __prep
-prep_ide_request_region(ide_ioreg_t from,
-			unsigned int extent,
-			const char *name)
-{
-	request_region(from, extent, name);
-}
-
-static void __prep
-prep_ide_release_region(ide_ioreg_t from,
-			unsigned int extent)
-{
-	release_region(from, extent);
-}
 #endif
 
 #ifdef CONFIG_SMP
@@ -916,9 +895,6 @@
 #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
 	ppc_ide_md.default_irq = prep_ide_default_irq;
 	ppc_ide_md.default_io_base = prep_ide_default_io_base;
-	ppc_ide_md.ide_check_region = prep_ide_check_region;
-	ppc_ide_md.ide_request_region = prep_ide_request_region;
-	ppc_ide_md.ide_release_region = prep_ide_release_region;
 #endif
 
 #ifdef CONFIG_VT
diff -Nru a/arch/ppc/platforms/rpxclassic.h b/arch/ppc/platforms/rpxclassic.h
--- a/arch/ppc/platforms/rpxclassic.h	Sun Apr 28 20:10:31 2002
+++ b/arch/ppc/platforms/rpxclassic.h	Sun Apr 28 20:10:31 2002
@@ -85,8 +85,8 @@
 
 /* for pcmcia sandisk */
 #ifdef CONFIG_IDE
-#define MAX_HWIFS 1
-#define ide_request_irq(irq,hand,flg,dev,id)    request_8xxirq((irq),(hand),(flg),(dev),(id))
+# define MAX_HWIFS 1
+# define request_irq(irq,hand,flg,dev,id)    request_8xxirq((irq),(hand),(flg),(dev),(id))
 #endif
 #endif
 
diff -Nru a/arch/ppc/platforms/rpxlite.h b/arch/ppc/platforms/rpxlite.h
--- a/arch/ppc/platforms/rpxlite.h	Sun Apr 28 20:10:31 2002
+++ b/arch/ppc/platforms/rpxlite.h	Sun Apr 28 20:10:31 2002
@@ -72,8 +72,8 @@
 #define _IO_BASE_SIZE 0x1000
 
 #ifdef CONFIG_IDE
-#define MAX_HWIFS 1
-#define ide_request_irq(irq,hand,flg,dev,id)    request_8xxirq((irq),(hand),(flg),(dev),(id))
+# define MAX_HWIFS 1
+# define request_irq(irq,hand,flg,dev,id)    request_8xxirq((irq),(hand),(flg),(dev),(id))
 #endif
 
 /* We don't use the 8259.
diff -Nru a/arch/ppc/platforms/sandpoint_setup.c b/arch/ppc/platforms/sandpoint_setup.c
--- a/arch/ppc/platforms/sandpoint_setup.c	Sun Apr 28 20:10:39 2002
+++ b/arch/ppc/platforms/sandpoint_setup.c	Sun Apr 28 20:10:39 2002
@@ -491,29 +491,6 @@
 	return sandpoint_ide_regbase[index];
 }
 
-static int
-sandpoint_ide_check_region(ide_ioreg_t from, unsigned int extent)
-{
-	return check_region(from, extent);
-}
-
-static void
-sandpoint_ide_request_region(ide_ioreg_t from,
-			unsigned int extent,
-			const char *name)
-{
-        request_region(from, extent, name);
-	return;
-}
-
-static void
-sandpoint_ide_release_region(ide_ioreg_t from,
-			unsigned int extent)
-{
-        release_region(from, extent);
-	return;
-}
-
 static void __init
 sandpoint_ide_init_hwif_ports(hw_regs_t *hw, ide_ioreg_t data_port,
 			      ide_ioreg_t ctrl_port, int *irq)
@@ -684,9 +661,6 @@
 #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
         ppc_ide_md.default_irq = sandpoint_ide_default_irq;
         ppc_ide_md.default_io_base = sandpoint_ide_default_io_base;
-        ppc_ide_md.ide_check_region = sandpoint_ide_check_region;
-        ppc_ide_md.ide_request_region = sandpoint_ide_request_region;
-        ppc_ide_md.ide_release_region = sandpoint_ide_release_region;
         ppc_ide_md.ide_init_hwif = sandpoint_ide_init_hwif_ports;
 #endif
 
diff -Nru a/arch/ppc64/defconfig b/arch/ppc64/defconfig
--- a/arch/ppc64/defconfig	Sun Apr 28 20:10:33 2002
+++ b/arch/ppc64/defconfig	Sun Apr 28 20:10:33 2002
@@ -493,7 +493,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/s390/defconfig b/arch/s390/defconfig
--- a/arch/s390/defconfig	Sun Apr 28 20:10:34 2002
+++ b/arch/s390/defconfig	Sun Apr 28 20:10:34 2002
@@ -205,7 +205,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_FREEVXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_DEVFS_FS=y
diff -Nru a/arch/s390x/defconfig b/arch/s390x/defconfig
--- a/arch/s390x/defconfig	Sun Apr 28 20:10:30 2002
+++ b/arch/s390x/defconfig	Sun Apr 28 20:10:30 2002
@@ -205,7 +205,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_FREEVXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_DEVFS_FS=y
diff -Nru a/arch/sh/defconfig b/arch/sh/defconfig
--- a/arch/sh/defconfig	Sun Apr 28 20:10:30 2002
+++ b/arch/sh/defconfig	Sun Apr 28 20:10:30 2002
@@ -165,7 +165,7 @@
 # CONFIG_JOLIET is not set
 # CONFIG_MINIX_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/sh/kernel/entry.S b/arch/sh/kernel/entry.S
--- a/arch/sh/kernel/entry.S	Sun Apr 28 20:10:35 2002
+++ b/arch/sh/kernel/entry.S	Sun Apr 28 20:10:35 2002
@@ -195,8 +195,8 @@
 
 	.align 2
 1:	.long	MMU_TEA
-2:	.long	SYMBOL_NAME(__do_page_fault)
-3:	.long	SYMBOL_NAME(do_page_fault)
+2:	.long	__do_page_fault
+3:	.long	do_page_fault
 
 	.align	2
 address_error_load:
@@ -217,7 +217,7 @@
 
 	.align 2
 1:	.long	MMU_TEA
-2:	.long   SYMBOL_NAME(do_address_error)
+2:	.long   do_address_error
 
 #if defined(CONFIG_SH_STANDARD_BIOS)
 	.align	2
@@ -257,7 +257,7 @@
 	 ldc	k1, ssr
 	.align	2
 1:	.long	0x300000f0
-2:	.long	SYMBOL_NAME(gdb_vbr_vector)
+2:	.long	gdb_vbr_vector
 #endif
 
 	.align	2
@@ -275,7 +275,7 @@
 	 nop
 
 	.align	2
-1:	.long	SYMBOL_NAME(break_point_trap_software)
+1:	.long	break_point_trap_software
 
 	.align	2
 error:	
@@ -285,7 +285,7 @@
 	jmp	@r0
 	 nop
 	.align	2
-1:	.long	SYMBOL_NAME(do_exception_error)
+1:	.long	do_exception_error
 
 
 !
@@ -308,7 +308,7 @@
 	 nop	 
 
 	.align	2
-1:	.long	SYMBOL_NAME(schedule_tail)
+1:	.long	schedule_tail
 
 /*
  * Old syscall interface:
@@ -422,7 +422,7 @@
 syscall_ret_trace:
 	mov.l	r0, @(OFF_R0,r15)		! save the return value
 	mov.l	__syscall_trace, r1
-	mova	SYMBOL_NAME(ret_from_syscall), r0
+	mova	ret_from_syscall, r0
 	jmp	@r1    	! Call syscall_trace() which notifies superior
 	 lds	r0, pr    	! Then return to ret_from_syscall()
 
@@ -497,9 +497,9 @@
 	.align	2
 __TRA:	.long	TRA
 __syscall_trace:
-#error    	.long	SYMBOL_NAME(syscall_trace)
+#error    	.long	syscall_trace
 __n_sys:.long	NR_syscalls
-__sct:	.long	SYMBOL_NAME(sys_call_table)
+__sct:	.long	sys_call_table
 __syscall_ret_trace:
 	.long	syscall_ret_trace
 __syscall_ret:
@@ -510,12 +510,12 @@
 
 	.align	2
 reschedule:
-	mova	SYMBOL_NAME(ret_from_syscall), r0
+	mova	ret_from_syscall, r0
 	mov.l	1f, r1
 	jmp	@r1
 	 lds	r0, pr
 	.align	2
-1:	.long	SYMBOL_NAME(schedule)
+1:	.long	schedule
 
 ret_from_irq:
 ret_from_exception:
@@ -560,9 +560,9 @@
 	 lds	r0, pr
 	.align	2
 __do_signal:
-#error	.long	SYMBOL_NAME(do_signal)
+#error	.long	do_signal
 __irq_stat:
-	.long	SYMBOL_NAME(irq_stat)
+	.long	irq_stat
 
 	.align 2
 restore_all:
@@ -729,7 +729,7 @@
 	jmp	@r9
 	 nop
 	.align	2
-1:	.long	SYMBOL_NAME(exception_handling_table)
+1:	.long	exception_handling_table
 3:	.long	0x000000f0	! FD=0, IMASK=15
 5:	.long	0xcfffffff	! RB=0, BL=0
 6:	.long	0x00080000	! SZ=0, PR=1
@@ -750,7 +750,7 @@
 	.long	address_error_load
 	.long	address_error_store
 #if defined(__SH4__)
-	.long	SYMBOL_NAME(do_fpu_error)
+	.long	do_fpu_error
 #else
 	.long	error	! fpu_exception
 #endif
@@ -764,93 +764,93 @@
 	.long	break_point_trap
 ENTRY(interrupt_table)
 	! external hardware
-	.long	SYMBOL_NAME(do_IRQ)	! 0000
-	.long	SYMBOL_NAME(do_IRQ)	! 0001
-	.long	SYMBOL_NAME(do_IRQ)	! 0010
-	.long	SYMBOL_NAME(do_IRQ)	! 0011
-	.long	SYMBOL_NAME(do_IRQ)	! 0100
-	.long	SYMBOL_NAME(do_IRQ)	! 0101
-	.long	SYMBOL_NAME(do_IRQ)	! 0110
-	.long	SYMBOL_NAME(do_IRQ)	! 0111
-	.long	SYMBOL_NAME(do_IRQ)	! 1000
-	.long	SYMBOL_NAME(do_IRQ)	! 1001
-	.long	SYMBOL_NAME(do_IRQ)	! 1010
-	.long	SYMBOL_NAME(do_IRQ)	! 1011
-	.long	SYMBOL_NAME(do_IRQ)	! 1100
-	.long	SYMBOL_NAME(do_IRQ)	! 1101
-	.long	SYMBOL_NAME(do_IRQ)	! 1110
+	.long	do_IRQ			! 0000
+	.long	do_IRQ			! 0001
+	.long	do_IRQ			! 0010
+	.long	do_IRQ			! 0011
+	.long	do_IRQ			! 0100
+	.long	do_IRQ			! 0101
+	.long	do_IRQ			! 0110
+	.long	do_IRQ			! 0111
+	.long	do_IRQ			! 1000
+	.long	do_IRQ			! 1001
+	.long	do_IRQ			! 1010
+	.long	do_IRQ			! 1011
+	.long	do_IRQ			! 1100
+	.long	do_IRQ			! 1101
+	.long	do_IRQ			! 1110
 	.long	error
 	! Internal hardware
-	.long	SYMBOL_NAME(do_IRQ)	! TMU0 tuni0
-	.long	SYMBOL_NAME(do_IRQ)	! TMU1 tuni1
-	.long	SYMBOL_NAME(do_IRQ)	! TMU2 tuni2
-	.long	SYMBOL_NAME(do_IRQ)	!      ticpi2
-	.long	SYMBOL_NAME(do_IRQ)	! RTC  ati
-	.long	SYMBOL_NAME(do_IRQ)	!      pri
-	.long	SYMBOL_NAME(do_IRQ)	!      cui
-	.long	SYMBOL_NAME(do_IRQ)	! SCI  eri
-	.long	SYMBOL_NAME(do_IRQ)	!      rxi
-	.long	SYMBOL_NAME(do_IRQ)	!      txi
-	.long	SYMBOL_NAME(do_IRQ)	!      tei
-	.long	SYMBOL_NAME(do_IRQ)	! WDT  iti
-	.long	SYMBOL_NAME(do_IRQ)	! REF  rcmi
-	.long	SYMBOL_NAME(do_IRQ)	!      rovi
-	.long	SYMBOL_NAME(do_IRQ)
-	.long	SYMBOL_NAME(do_IRQ)
+	.long	do_IRQ			! TMU0 tuni0
+	.long	do_IRQ			! TMU1 tuni1
+	.long	do_IRQ			! TMU2 tuni2
+	.long	do_IRQ			!      ticpi2
+	.long	do_IRQ			! RTC  ati
+	.long	do_IRQ			!      pri
+	.long	do_IRQ			!      cui
+	.long	do_IRQ			! SCI  eri
+	.long	do_IRQ			!      rxi
+	.long	do_IRQ			!      txi
+	.long	do_IRQ			!      tei
+	.long	do_IRQ			! WDT  iti
+	.long	do_IRQ			! REF  rcmi
+	.long	do_IRQ			!      rovi
+	.long	do_IRQ		
+	.long	do_IRQ		
 #if  defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709)
-	.long	SYMBOL_NAME(do_IRQ)	! 32 IRQ  irq0
-	.long	SYMBOL_NAME(do_IRQ)	! 33      irq1
-	.long	SYMBOL_NAME(do_IRQ)	! 34      irq2
-	.long	SYMBOL_NAME(do_IRQ)	! 35      irq3
-	.long	SYMBOL_NAME(do_IRQ)	! 36      irq4
-	.long	SYMBOL_NAME(do_IRQ)	! 37      irq5
-	.long	SYMBOL_NAME(do_IRQ)	! 38
-	.long	SYMBOL_NAME(do_IRQ)	! 39
-	.long	SYMBOL_NAME(do_IRQ)	! 40 PINT pint0-7
-	.long	SYMBOL_NAME(do_IRQ)	! 41      pint8-15
-	.long	SYMBOL_NAME(do_IRQ)	! 42
-	.long	SYMBOL_NAME(do_IRQ)	! 43
-	.long	SYMBOL_NAME(do_IRQ)	! 44
-	.long	SYMBOL_NAME(do_IRQ)	! 45
-	.long	SYMBOL_NAME(do_IRQ)	! 46
-	.long	SYMBOL_NAME(do_IRQ)	! 47
-	.long	SYMBOL_NAME(do_IRQ)	! 48 DMAC dei0
-	.long	SYMBOL_NAME(do_IRQ)	! 49      dei1
-	.long	SYMBOL_NAME(do_IRQ)	! 50      dei2
-	.long	SYMBOL_NAME(do_IRQ)	! 51      dei3
-	.long	SYMBOL_NAME(do_IRQ)	! 52 IrDA eri1
-	.long	SYMBOL_NAME(do_IRQ)	! 53      rxi1
-	.long	SYMBOL_NAME(do_IRQ)	! 54      bri1
-	.long	SYMBOL_NAME(do_IRQ)	! 55      txi1
-	.long	SYMBOL_NAME(do_IRQ)	! 56 SCIF eri2
-	.long	SYMBOL_NAME(do_IRQ)	! 57      rxi2
-	.long	SYMBOL_NAME(do_IRQ)	! 58      bri2
-	.long	SYMBOL_NAME(do_IRQ)	! 59      txi2
-	.long	SYMBOL_NAME(do_IRQ)	! 60 ADC  adi
+	.long	do_IRQ			! 32 IRQ  irq0
+	.long	do_IRQ			! 33      irq1
+	.long	do_IRQ			! 34      irq2
+	.long	do_IRQ			! 35      irq3
+	.long	do_IRQ			! 36      irq4
+	.long	do_IRQ			! 37      irq5
+	.long	do_IRQ			! 38
+	.long	do_IRQ			! 39
+	.long	do_IRQ			! 40 PINT pint0-7
+	.long	do_IRQ			! 41      pint8-15
+	.long	do_IRQ			! 42
+	.long	do_IRQ			! 43
+	.long	do_IRQ			! 44
+	.long	do_IRQ			! 45
+	.long	do_IRQ			! 46
+	.long	do_IRQ			! 47
+	.long	do_IRQ			! 48 DMAC dei0
+	.long	do_IRQ			! 49      dei1
+	.long	do_IRQ			! 50      dei2
+	.long	do_IRQ			! 51      dei3
+	.long	do_IRQ			! 52 IrDA eri1
+	.long	do_IRQ			! 53      rxi1
+	.long	do_IRQ			! 54      bri1
+	.long	do_IRQ			! 55      txi1
+	.long	do_IRQ			! 56 SCIF eri2
+	.long	do_IRQ			! 57      rxi2
+	.long	do_IRQ			! 58      bri2
+	.long	do_IRQ			! 59      txi2
+	.long	do_IRQ			! 60 ADC  adi
 #if defined(CONFIG_CPU_SUBTYPE_SH7707)
-	.long   SYMBOL_NAME(do_IRQ)	! 61 LCDC lcdi
-	.long   SYMBOL_NAME(do_IRQ)	! 62 PCC  pcc0i
-	.long   SYMBOL_NAME(do_IRQ)	! 63      pcc1i
+	.long   do_IRQ			! 61 LCDC lcdi
+	.long   do_IRQ			! 62 PCC  pcc0i
+	.long   do_IRQ			! 63      pcc1i
 #endif
 #elif defined(__SH4__)
-	.long	SYMBOL_NAME(do_IRQ)	! 32 Hitachi UDI
-	.long	SYMBOL_NAME(do_IRQ)	! 33 GPIO
-	.long	SYMBOL_NAME(do_IRQ)	! 34 DMAC dmte0
-	.long	SYMBOL_NAME(do_IRQ)	! 35      dmte1
-	.long	SYMBOL_NAME(do_IRQ)	! 36      dmte2
-	.long	SYMBOL_NAME(do_IRQ)	! 37      dmte3
-	.long	SYMBOL_NAME(do_IRQ)	! 38      dmae
+	.long	do_IRQ			! 32 Hitachi UDI
+	.long	do_IRQ			! 33 GPIO
+	.long	do_IRQ			! 34 DMAC dmte0
+	.long	do_IRQ			! 35      dmte1
+	.long	do_IRQ			! 36      dmte2
+	.long	do_IRQ			! 37      dmte3
+	.long	do_IRQ			! 38      dmae
 	.long	error			! 39
-	.long	SYMBOL_NAME(do_IRQ)	! 40 SCIF eri
-	.long	SYMBOL_NAME(do_IRQ)	! 41      rxi
-	.long	SYMBOL_NAME(do_IRQ)	! 42      bri
-	.long	SYMBOL_NAME(do_IRQ)	! 43      txi
+	.long	do_IRQ			! 40 SCIF eri
+	.long	do_IRQ			! 41      rxi
+	.long	do_IRQ			! 42      bri
+	.long	do_IRQ			! 43      txi
 	.long	error			! 44
 	.long	error			! 45
 	.long	error			! 46
 	.long	error			! 47
-	.long	SYMBOL_NAME(do_fpu_state_restore)	! 48
-	.long	SYMBOL_NAME(do_fpu_state_restore)	! 49
+	.long	do_fpu_state_restore	! 48
+	.long	do_fpu_state_restore	! 49
 #endif
 #if defined(CONFIG_CPU_SUBTYPE_SH7751)
 	.long	error
@@ -867,14 +867,14 @@
 	.long	error
 	.long	error
 	.long	error
-	.long	SYMBOL_NAME(do_IRQ)	! PCI serr
-	.long	SYMBOL_NAME(do_IRQ)	!     dma3
-	.long	SYMBOL_NAME(do_IRQ)	!     dma2
-	.long	SYMBOL_NAME(do_IRQ)	!     dma1
-	.long	SYMBOL_NAME(do_IRQ)	!     dma0
-	.long	SYMBOL_NAME(do_IRQ)	!     pwon
-	.long	SYMBOL_NAME(do_IRQ)	!     pwdwn
-	.long	SYMBOL_NAME(do_IRQ)	!     err
+	.long	do_IRQ			! PCI serr
+	.long	do_IRQ			!     dma3
+	.long	do_IRQ			!     dma2
+	.long	do_IRQ			!     dma1
+	.long	do_IRQ			!     dma0
+	.long	do_IRQ			!     pwon
+	.long	do_IRQ			!     pwdwn
+	.long	do_IRQ			!     err
 #elif defined(CONFIG_CPU_SUBTYPE_ST40STB1)
 	.long	error			!  50 0x840
 	.long	error			!  51 0x860
@@ -890,25 +890,25 @@
 	.long	error			!  61 0x9a0
 	.long	error			!  62 0x9c0
 	.long	error			!  63 0x9e0
-	.long	SYMBOL_NAME(do_IRQ)	!  64 0xa00 PCI serr
-	.long	SYMBOL_NAME(do_IRQ)	!  65 0xa20     err
-	.long	SYMBOL_NAME(do_IRQ)	!  66 0xa40     ad
-	.long	SYMBOL_NAME(do_IRQ)	!  67 0xa60     pwr_dwn
+	.long	do_IRQ			!  64 0xa00 PCI serr
+	.long	do_IRQ			!  65 0xa20     err
+	.long	do_IRQ			!  66 0xa40     ad
+	.long	do_IRQ			!  67 0xa60     pwr_dwn
 	.long	error			!  68 0xa80
 	.long	error			!  69 0xaa0
 	.long	error			!  70 0xac0
 	.long	error			!  71 0xae0
-	.long	SYMBOL_NAME(do_IRQ)	!  72 0xb00 DMA INT0
-	.long	SYMBOL_NAME(do_IRQ)	!  73 0xb20     INT1
-	.long	SYMBOL_NAME(do_IRQ)	!  74 0xb40     INT2
-	.long	SYMBOL_NAME(do_IRQ)	!  75 0xb60     INT3
-	.long	SYMBOL_NAME(do_IRQ)	!  76 0xb80     INT4
+	.long	do_IRQ			!  72 0xb00 DMA INT0
+	.long	do_IRQ			!  73 0xb20     INT1
+	.long	do_IRQ			!  74 0xb40     INT2
+	.long	do_IRQ			!  75 0xb60     INT3
+	.long	do_IRQ			!  76 0xb80     INT4
 	.long	error			!  77 0xba0
-	.long	SYMBOL_NAME(do_IRQ)	!  78 0xbc0 DMA ERR
+	.long	do_IRQ			!  78 0xbc0 DMA ERR
 	.long	error			!  79 0xbe0
-	.long	SYMBOL_NAME(do_IRQ)	!  80 0xc00 PIO0
-	.long	SYMBOL_NAME(do_IRQ)	!  81 0xc20 PIO1
-	.long	SYMBOL_NAME(do_IRQ)	!  82 0xc40 PIO2
+	.long	do_IRQ			!  80 0xc00 PIO0
+	.long	do_IRQ			!  81 0xc20 PIO1
+	.long	do_IRQ			!  82 0xc40 PIO2
 	.long	error			!  83 0xc60
 	.long	error			!  84 0xc80
 	.long	error			!  85 0xca0
@@ -938,7 +938,7 @@
 	.long	error			! 109 0xfa0
 	.long	error			! 110 0xfc0
 	.long	error			! 111 0xfe0
-	.long	SYMBOL_NAME(do_IRQ)	! 112 0x1000 Mailbox
+	.long	do_IRQ			! 112 0x1000 Mailbox
 	.long	error			! 113 0x1020
 	.long	error			! 114 0x1040
 	.long	error			! 115 0x1060
@@ -966,237 +966,237 @@
 	.long	error			! 137 0x1320
 	.long	error			! 138 0x1340
 	.long	error			! 139 0x1360
-	.long	SYMBOL_NAME(do_IRQ)	! 140 0x1380 EMPI INV_ADDR
+	.long	do_IRQ			! 140 0x1380 EMPI INV_ADDR
 	.long	error			! 141 0x13a0
 	.long	error			! 142 0x13c0
 	.long	error			! 143 0x13e0
 #endif
 
 ENTRY(sys_call_table)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* 0  -  old "setup()" system call*/
-	.long SYMBOL_NAME(sys_exit)
-	.long SYMBOL_NAME(sys_fork)
-	.long SYMBOL_NAME(sys_read)
-	.long SYMBOL_NAME(sys_write)
-	.long SYMBOL_NAME(sys_open)		/* 5 */
-	.long SYMBOL_NAME(sys_close)
-	.long SYMBOL_NAME(sys_waitpid)
-	.long SYMBOL_NAME(sys_creat)
-	.long SYMBOL_NAME(sys_link)
-	.long SYMBOL_NAME(sys_unlink)		/* 10 */
-	.long SYMBOL_NAME(sys_execve)
-	.long SYMBOL_NAME(sys_chdir)
-	.long SYMBOL_NAME(sys_time)
-	.long SYMBOL_NAME(sys_mknod)
-	.long SYMBOL_NAME(sys_chmod)		/* 15 */
-	.long SYMBOL_NAME(sys_lchown16)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* old break syscall holder */
-	.long SYMBOL_NAME(sys_stat)
-	.long SYMBOL_NAME(sys_lseek)
-	.long SYMBOL_NAME(sys_getpid)		/* 20 */
-	.long SYMBOL_NAME(sys_mount)
-	.long SYMBOL_NAME(sys_oldumount)
-	.long SYMBOL_NAME(sys_setuid16)
-	.long SYMBOL_NAME(sys_getuid16)
-	.long SYMBOL_NAME(sys_stime)		/* 25 */
-	.long SYMBOL_NAME(sys_ptrace)
-	.long SYMBOL_NAME(sys_alarm)
-	.long SYMBOL_NAME(sys_fstat)
-	.long SYMBOL_NAME(sys_pause)
-	.long SYMBOL_NAME(sys_utime)		/* 30 */
-	.long SYMBOL_NAME(sys_ni_syscall)	/* old stty syscall holder */
-	.long SYMBOL_NAME(sys_ni_syscall)	/* old gtty syscall holder */
-	.long SYMBOL_NAME(sys_access)
-	.long SYMBOL_NAME(sys_nice)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* 35 */		/* old ftime syscall holder */
-	.long SYMBOL_NAME(sys_sync)
-	.long SYMBOL_NAME(sys_kill)
-	.long SYMBOL_NAME(sys_rename)
-	.long SYMBOL_NAME(sys_mkdir)
-	.long SYMBOL_NAME(sys_rmdir)		/* 40 */
-	.long SYMBOL_NAME(sys_dup)
-	.long SYMBOL_NAME(sys_pipe)
-	.long SYMBOL_NAME(sys_times)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* old prof syscall holder */
-	.long SYMBOL_NAME(sys_brk)		/* 45 */
-	.long SYMBOL_NAME(sys_setgid16)
-	.long SYMBOL_NAME(sys_getgid16)
-	.long SYMBOL_NAME(sys_signal)
-	.long SYMBOL_NAME(sys_geteuid16)
-	.long SYMBOL_NAME(sys_getegid16)	/* 50 */
-	.long SYMBOL_NAME(sys_acct)
-	.long SYMBOL_NAME(sys_umount)		/* recycled never used phys() */
-	.long SYMBOL_NAME(sys_ni_syscall)	/* old lock syscall holder */
-	.long SYMBOL_NAME(sys_ioctl)
-	.long SYMBOL_NAME(sys_fcntl)		/* 55 */
-	.long SYMBOL_NAME(sys_ni_syscall)	/* old mpx syscall holder */
-	.long SYMBOL_NAME(sys_setpgid)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* old ulimit syscall holder */
-	.long SYMBOL_NAME(sys_ni_syscall)	/* sys_olduname */
-	.long SYMBOL_NAME(sys_umask)		/* 60 */
-	.long SYMBOL_NAME(sys_chroot)
-	.long SYMBOL_NAME(sys_ustat)
-	.long SYMBOL_NAME(sys_dup2)
-	.long SYMBOL_NAME(sys_getppid)
-	.long SYMBOL_NAME(sys_getpgrp)		/* 65 */
-	.long SYMBOL_NAME(sys_setsid)
-	.long SYMBOL_NAME(sys_sigaction)
-	.long SYMBOL_NAME(sys_sgetmask)
-	.long SYMBOL_NAME(sys_ssetmask)
-	.long SYMBOL_NAME(sys_setreuid16)	/* 70 */
-	.long SYMBOL_NAME(sys_setregid16)
-	.long SYMBOL_NAME(sys_sigsuspend)
-	.long SYMBOL_NAME(sys_sigpending)
-	.long SYMBOL_NAME(sys_sethostname)
-	.long SYMBOL_NAME(sys_setrlimit)	/* 75 */
-	.long SYMBOL_NAME(sys_old_getrlimit)
-	.long SYMBOL_NAME(sys_getrusage)
-	.long SYMBOL_NAME(sys_gettimeofday)
-	.long SYMBOL_NAME(sys_settimeofday)
-	.long SYMBOL_NAME(sys_getgroups16)	/* 80 */
-	.long SYMBOL_NAME(sys_setgroups16)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* sys_oldselect */
-	.long SYMBOL_NAME(sys_symlink)
-	.long SYMBOL_NAME(sys_lstat)
-	.long SYMBOL_NAME(sys_readlink)		/* 85 */
-	.long SYMBOL_NAME(sys_uselib)
-	.long SYMBOL_NAME(sys_swapon)
-	.long SYMBOL_NAME(sys_reboot)
-	.long SYMBOL_NAME(old_readdir)
-	.long SYMBOL_NAME(old_mmap)		/* 90 */
-	.long SYMBOL_NAME(sys_munmap)
-	.long SYMBOL_NAME(sys_truncate)
-	.long SYMBOL_NAME(sys_ftruncate)
-	.long SYMBOL_NAME(sys_fchmod)
-	.long SYMBOL_NAME(sys_fchown16)		/* 95 */
-	.long SYMBOL_NAME(sys_getpriority)
-	.long SYMBOL_NAME(sys_setpriority)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* old profil syscall holder */
-	.long SYMBOL_NAME(sys_statfs)
-	.long SYMBOL_NAME(sys_fstatfs)		/* 100 */
-	.long SYMBOL_NAME(sys_ni_syscall)	/* ioperm */
-	.long SYMBOL_NAME(sys_socketcall)
-	.long SYMBOL_NAME(sys_syslog)
-	.long SYMBOL_NAME(sys_setitimer)
-	.long SYMBOL_NAME(sys_getitimer)	/* 105 */
-	.long SYMBOL_NAME(sys_newstat)
-	.long SYMBOL_NAME(sys_newlstat)
-	.long SYMBOL_NAME(sys_newfstat)
-	.long SYMBOL_NAME(sys_uname)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* 110 */ /* iopl */
-	.long SYMBOL_NAME(sys_vhangup)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* idle */
-	.long SYMBOL_NAME(sys_ni_syscall)	/* vm86old */
-	.long SYMBOL_NAME(sys_wait4)
-	.long SYMBOL_NAME(sys_swapoff)		/* 115 */
-	.long SYMBOL_NAME(sys_sysinfo)
-	.long SYMBOL_NAME(sys_ipc)
-	.long SYMBOL_NAME(sys_fsync)
-	.long SYMBOL_NAME(sys_sigreturn)
-	.long SYMBOL_NAME(sys_clone)		/* 120 */
-	.long SYMBOL_NAME(sys_setdomainname)
-	.long SYMBOL_NAME(sys_newuname)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* sys_modify_ldt */
-	.long SYMBOL_NAME(sys_adjtimex)
-	.long SYMBOL_NAME(sys_mprotect)		/* 125 */
-	.long SYMBOL_NAME(sys_sigprocmask)
-	.long SYMBOL_NAME(sys_create_module)
-	.long SYMBOL_NAME(sys_init_module)
-	.long SYMBOL_NAME(sys_delete_module)
-	.long SYMBOL_NAME(sys_get_kernel_syms)	/* 130 */
-	.long SYMBOL_NAME(sys_quotactl)
-	.long SYMBOL_NAME(sys_getpgid)
-	.long SYMBOL_NAME(sys_fchdir)
-	.long SYMBOL_NAME(sys_bdflush)
-	.long SYMBOL_NAME(sys_sysfs)		/* 135 */
-	.long SYMBOL_NAME(sys_personality)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* for afs_syscall */
-	.long SYMBOL_NAME(sys_setfsuid16)
-	.long SYMBOL_NAME(sys_setfsgid16)
-	.long SYMBOL_NAME(sys_llseek)		/* 140 */
-	.long SYMBOL_NAME(sys_getdents)
-	.long SYMBOL_NAME(sys_select)
-	.long SYMBOL_NAME(sys_flock)
-	.long SYMBOL_NAME(sys_msync)
-	.long SYMBOL_NAME(sys_readv)		/* 145 */
-	.long SYMBOL_NAME(sys_writev)
-	.long SYMBOL_NAME(sys_getsid)
-	.long SYMBOL_NAME(sys_fdatasync)
-	.long SYMBOL_NAME(sys_sysctl)
-	.long SYMBOL_NAME(sys_mlock)		/* 150 */
-	.long SYMBOL_NAME(sys_munlock)
-	.long SYMBOL_NAME(sys_mlockall)
-	.long SYMBOL_NAME(sys_munlockall)
-	.long SYMBOL_NAME(sys_sched_setparam)
-	.long SYMBOL_NAME(sys_sched_getparam)   /* 155 */
-	.long SYMBOL_NAME(sys_sched_setscheduler)
-	.long SYMBOL_NAME(sys_sched_getscheduler)
-	.long SYMBOL_NAME(sys_sched_yield)
-	.long SYMBOL_NAME(sys_sched_get_priority_max)
-	.long SYMBOL_NAME(sys_sched_get_priority_min)  /* 160 */
-	.long SYMBOL_NAME(sys_sched_rr_get_interval)
-	.long SYMBOL_NAME(sys_nanosleep)
-	.long SYMBOL_NAME(sys_mremap)
-	.long SYMBOL_NAME(sys_setresuid16)
-	.long SYMBOL_NAME(sys_getresuid16)	/* 165 */
-	.long SYMBOL_NAME(sys_ni_syscall)	/* vm86 */
-	.long SYMBOL_NAME(sys_query_module)
-	.long SYMBOL_NAME(sys_poll)
-	.long SYMBOL_NAME(sys_nfsservctl)
-	.long SYMBOL_NAME(sys_setresgid16)	/* 170 */
-	.long SYMBOL_NAME(sys_getresgid16)
-	.long SYMBOL_NAME(sys_prctl)
-	.long SYMBOL_NAME(sys_rt_sigreturn)
-	.long SYMBOL_NAME(sys_rt_sigaction)
-	.long SYMBOL_NAME(sys_rt_sigprocmask)	/* 175 */
-	.long SYMBOL_NAME(sys_rt_sigpending)
-	.long SYMBOL_NAME(sys_rt_sigtimedwait)
-	.long SYMBOL_NAME(sys_rt_sigqueueinfo)
-	.long SYMBOL_NAME(sys_rt_sigsuspend)
-	.long SYMBOL_NAME(sys_pread)		/* 180 */
-	.long SYMBOL_NAME(sys_pwrite)
-	.long SYMBOL_NAME(sys_chown16)
-	.long SYMBOL_NAME(sys_getcwd)
-	.long SYMBOL_NAME(sys_capget)
-	.long SYMBOL_NAME(sys_capset)           /* 185 */
-	.long SYMBOL_NAME(sys_sigaltstack)
-	.long SYMBOL_NAME(sys_sendfile)
-	.long SYMBOL_NAME(sys_ni_syscall)	/* streams1 */
-	.long SYMBOL_NAME(sys_ni_syscall)	/* streams2 */
-	.long SYMBOL_NAME(sys_vfork)            /* 190 */
-	.long SYMBOL_NAME(sys_getrlimit)
-	.long SYMBOL_NAME(sys_mmap2)
-	.long SYMBOL_NAME(sys_truncate64)
-	.long SYMBOL_NAME(sys_ftruncate64)
-	.long SYMBOL_NAME(sys_stat64)		/* 195 */
-	.long SYMBOL_NAME(sys_lstat64)
-	.long SYMBOL_NAME(sys_fstat64)
-	.long SYMBOL_NAME(sys_lchown)
-	.long SYMBOL_NAME(sys_getuid)
-	.long SYMBOL_NAME(sys_getgid)		/* 200 */
-	.long SYMBOL_NAME(sys_geteuid)
-	.long SYMBOL_NAME(sys_getegid)
-	.long SYMBOL_NAME(sys_setreuid)
-	.long SYMBOL_NAME(sys_setregid)
-	.long SYMBOL_NAME(sys_getgroups)	/* 205 */
-	.long SYMBOL_NAME(sys_setgroups)
-	.long SYMBOL_NAME(sys_fchown)
-	.long SYMBOL_NAME(sys_setresuid)
-	.long SYMBOL_NAME(sys_getresuid)
-	.long SYMBOL_NAME(sys_setresgid)	/* 210 */
-	.long SYMBOL_NAME(sys_getresgid)
-	.long SYMBOL_NAME(sys_chown)
-	.long SYMBOL_NAME(sys_setuid)
-	.long SYMBOL_NAME(sys_setgid)
-	.long SYMBOL_NAME(sys_setfsuid)		/* 215 */
-	.long SYMBOL_NAME(sys_setfsgid)
-	.long SYMBOL_NAME(sys_pivot_root)
-	.long SYMBOL_NAME(sys_mincore)
-	.long SYMBOL_NAME(sys_madvise)
-	.long SYMBOL_NAME(sys_getdents64)	/* 220 */
-	.long SYMBOL_NAME(sys_fcntl64)
-	.long SYMBOL_NAME(sys_gettid)
-	.long SYMBOL_NAME(sys_tkill)
+	.long sys_ni_syscall	/* 0  -  old "setup()" system call*/
+	.long sys_exit
+	.long sys_fork
+	.long sys_read
+	.long sys_write
+	.long sys_open		/* 5 */
+	.long sys_close
+	.long sys_waitpid
+	.long sys_creat
+	.long sys_link
+	.long sys_unlink	/* 10 */
+	.long sys_execve
+	.long sys_chdir
+	.long sys_time
+	.long sys_mknod
+	.long sys_chmod		/* 15 */
+	.long sys_lchown16
+	.long sys_ni_syscall	/* old break syscall holder */
+	.long sys_stat
+	.long sys_lseek
+	.long sys_getpid	/* 20 */
+	.long sys_mount
+	.long sys_oldumount
+	.long sys_setuid16
+	.long sys_getuid16
+	.long sys_stime		/* 25 */
+	.long sys_ptrace
+	.long sys_alarm
+	.long sys_fstat
+	.long sys_pause
+	.long sys_utime		/* 30 */
+	.long sys_ni_syscall	/* old stty syscall holder */
+	.long sys_ni_syscall	/* old gtty syscall holder */
+	.long sys_access
+	.long sys_nice
+	.long sys_ni_syscall	/* 35 */		/* old ftime syscall holder */
+	.long sys_sync
+	.long sys_kill
+	.long sys_rename
+	.long sys_mkdir
+	.long sys_rmdir		/* 40 */
+	.long sys_dup
+	.long sys_pipe
+	.long sys_times
+	.long sys_ni_syscall	/* old prof syscall holder */
+	.long sys_brk		/* 45 */
+	.long sys_setgid16
+	.long sys_getgid16
+	.long sys_signal
+	.long sys_geteuid16
+	.long sys_getegid16	/* 50 */
+	.long sys_acct
+	.long sys_umount	/* recycled never used phys() */
+	.long sys_ni_syscall	/* old lock syscall holder */
+	.long sys_ioctl
+	.long sys_fcntl		/* 55 */
+	.long sys_ni_syscall	/* old mpx syscall holder */
+	.long sys_setpgid
+	.long sys_ni_syscall	/* old ulimit syscall holder */
+	.long sys_ni_syscall	/* sys_olduname */
+	.long sys_umask		/* 60 */
+	.long sys_chroot
+	.long sys_ustat
+	.long sys_dup2
+	.long sys_getppid
+	.long sys_getpgrp	/* 65 */
+	.long sys_setsid
+	.long sys_sigaction
+	.long sys_sgetmask
+	.long sys_ssetmask
+	.long sys_setreuid16	/* 70 */
+	.long sys_setregid16
+	.long sys_sigsuspend
+	.long sys_sigpending
+	.long sys_sethostname
+	.long sys_setrlimit	/* 75 */
+	.long sys_old_getrlimit
+	.long sys_getrusage
+	.long sys_gettimeofday
+	.long sys_settimeofday
+	.long sys_getgroups16	/* 80 */
+	.long sys_setgroups16
+	.long sys_ni_syscall	/* sys_oldselect */
+	.long sys_symlink
+	.long sys_lstat
+	.long sys_readlink	/* 85 */
+	.long sys_uselib
+	.long sys_swapon
+	.long sys_reboot
+	.long old_readdir
+	.long old_mmap		/* 90 */
+	.long sys_munmap
+	.long sys_truncate
+	.long sys_ftruncate
+	.long sys_fchmod
+	.long sys_fchown16	/* 95 */
+	.long sys_getpriority
+	.long sys_setpriority
+	.long sys_ni_syscall	/* old profil syscall holder */
+	.long sys_statfs
+	.long sys_fstatfs	/* 100 */
+	.long sys_ni_syscall	/* ioperm */
+	.long sys_socketcall
+	.long sys_syslog
+	.long sys_setitimer
+	.long sys_getitimer	/* 105 */
+	.long sys_newstat
+	.long sys_newlstat
+	.long sys_newfstat
+	.long sys_uname
+	.long sys_ni_syscall	/* 110 */ /* iopl */
+	.long sys_vhangup
+	.long sys_ni_syscall	/* idle */
+	.long sys_ni_syscall	/* vm86old */
+	.long sys_wait4
+	.long sys_swapoff	/* 115 */
+	.long sys_sysinfo
+	.long sys_ipc
+	.long sys_fsync
+	.long sys_sigreturn
+	.long sys_clone		/* 120 */
+	.long sys_setdomainname
+	.long sys_newuname
+	.long sys_ni_syscall	/* sys_modify_ldt */
+	.long sys_adjtimex
+	.long sys_mprotect	/* 125 */
+	.long sys_sigprocmask
+	.long sys_create_module
+	.long sys_init_module
+	.long sys_delete_module
+	.long sys_get_kernel_syms	/* 130 */
+	.long sys_quotactl
+	.long sys_getpgid
+	.long sys_fchdir
+	.long sys_bdflush
+	.long sys_sysfs		/* 135 */
+	.long sys_personality
+	.long sys_ni_syscall	/* for afs_syscall */
+	.long sys_setfsuid16
+	.long sys_setfsgid16
+	.long sys_llseek	/* 140 */
+	.long sys_getdents
+	.long sys_select
+	.long sys_flock
+	.long sys_msync
+	.long sys_readv		/* 145 */
+	.long sys_writev
+	.long sys_getsid
+	.long sys_fdatasync
+	.long sys_sysctl
+	.long sys_mlock		/* 150 */
+	.long sys_munlock
+	.long sys_mlockall
+	.long sys_munlockall
+	.long sys_sched_setparam
+	.long sys_sched_getparam   /* 155 */
+	.long sys_sched_setscheduler
+	.long sys_sched_getscheduler
+	.long sys_sched_yield
+	.long sys_sched_get_priority_max
+	.long sys_sched_get_priority_min  /* 160 */
+	.long sys_sched_rr_get_interval
+	.long sys_nanosleep
+	.long sys_mremap
+	.long sys_setresuid16
+	.long sys_getresuid16	/* 165 */
+	.long sys_ni_syscall	/* vm86 */
+	.long sys_query_module
+	.long sys_poll
+	.long sys_nfsservctl
+	.long sys_setresgid16	/* 170 */
+	.long sys_getresgid16
+	.long sys_prctl
+	.long sys_rt_sigreturn
+	.long sys_rt_sigaction
+	.long sys_rt_sigprocmask	/* 175 */
+	.long sys_rt_sigpending
+	.long sys_rt_sigtimedwait
+	.long sys_rt_sigqueueinfo
+	.long sys_rt_sigsuspend
+	.long sys_pread		/* 180 */
+	.long sys_pwrite
+	.long sys_chown16
+	.long sys_getcwd
+	.long sys_capget
+	.long sys_capset	/* 185 */
+	.long sys_sigaltstack
+	.long sys_sendfile
+	.long sys_ni_syscall	/* streams1 */
+	.long sys_ni_syscall	/* streams2 */
+	.long sys_vfork		/* 190 */
+	.long sys_getrlimit
+	.long sys_mmap2
+	.long sys_truncate64
+	.long sys_ftruncate64
+	.long sys_stat64	/* 195 */
+	.long sys_lstat64
+	.long sys_fstat64
+	.long sys_lchown
+	.long sys_getuid
+	.long sys_getgid	/* 200 */
+	.long sys_geteuid
+	.long sys_getegid
+	.long sys_setreuid
+	.long sys_setregid
+	.long sys_getgroups	/* 205 */
+	.long sys_setgroups
+	.long sys_fchown
+	.long sys_setresuid
+	.long sys_getresuid
+	.long sys_setresgid	/* 210 */
+	.long sys_getresgid
+	.long sys_chown
+	.long sys_setuid
+	.long sys_setgid
+	.long sys_setfsuid	/* 215 */
+	.long sys_setfsgid
+	.long sys_pivot_root
+	.long sys_mincore
+	.long sys_madvise
+	.long sys_getdents64	/* 220 */
+	.long sys_fcntl64
+	.long sys_gettid
+	.long sys_tkill
 
 	/*
 	 * NOTE!! This doesn't have to be exact - we just have
@@ -1205,7 +1205,7 @@
 	 * been shrunk every time we add a new system call.
 	 */
 	.rept NR_syscalls-221
-		.long SYMBOL_NAME(sys_ni_syscall)
+		.long sys_ni_syscall
 	.endr
 
 /* End of entry.S */
diff -Nru a/arch/sh/kernel/head.S b/arch/sh/kernel/head.S
--- a/arch/sh/kernel/head.S	Sun Apr 28 20:10:35 2002
+++ b/arch/sh/kernel/head.S	Sun Apr 28 20:10:35 2002
@@ -69,8 +69,8 @@
 
 	.balign 4
 1:	.long	0x400080F0		! MD=1, RB=0, BL=0, FD=1, IMASK=0xF
-2:	.long	SYMBOL_NAME(stack)
-3:	.long	SYMBOL_NAME(__bss_start)
-4:	.long	SYMBOL_NAME(_end)
-5:	.long	SYMBOL_NAME(start_kernel)
-6:	.long	SYMBOL_NAME(cache_init)
+2:	.long	stack
+3:	.long	__bss_start
+4:	.long	_end
+5:	.long	start_kernel
+6:	.long	cache_init
diff -Nru a/arch/sh/lib/memmove.S b/arch/sh/lib/memmove.S
--- a/arch/sh/lib/memmove.S	Sun Apr 28 20:10:30 2002
+++ b/arch/sh/lib/memmove.S	Sun Apr 28 20:10:30 2002
@@ -20,7 +20,7 @@
 	jmp	@r0
 	 nop
 	.balign 4
-2:	.long	SYMBOL_NAME(memcpy)
+2:	.long	memcpy
 1:
 	sub	r5,r4		! From here, r4 has the distance to r0
 	tst	r6,r6
diff -Nru a/arch/sparc/defconfig b/arch/sparc/defconfig
--- a/arch/sparc/defconfig	Sun Apr 28 20:10:37 2002
+++ b/arch/sparc/defconfig	Sun Apr 28 20:10:37 2002
@@ -300,7 +300,7 @@
 CONFIG_MINIX_FS=m
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 CONFIG_HPFS_FS=m
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/sparc64/defconfig b/arch/sparc64/defconfig
--- a/arch/sparc64/defconfig	Sun Apr 28 20:10:35 2002
+++ b/arch/sparc64/defconfig	Sun Apr 28 20:10:35 2002
@@ -654,7 +654,7 @@
 CONFIG_MINIX_FS=m
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 CONFIG_HPFS_FS=m
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/x86_64/boot/compressed/head.S b/arch/x86_64/boot/compressed/head.S
--- a/arch/x86_64/boot/compressed/head.S	Sun Apr 28 20:10:39 2002
+++ b/arch/x86_64/boot/compressed/head.S	Sun Apr 28 20:10:39 2002
@@ -41,7 +41,7 @@
 	movl %eax,%fs
 	movl %eax,%gs
 
-	lss SYMBOL_NAME(stack_start),%esp
+	lss stack_start,%esp
 	xorl %eax,%eax
 1:	incl %eax		# check that A20 really IS enabled
 	movl %eax,0x000000	# loop forever if it isn't
@@ -59,8 +59,8 @@
  * Clear BSS
  */
 	xorl %eax,%eax
-	movl $ SYMBOL_NAME(_edata),%edi
-	movl $ SYMBOL_NAME(_end),%ecx
+	movl $_edata,%edi
+	movl $_end,%ecx
 	subl %edi,%ecx
 	cld
 	rep
@@ -72,7 +72,7 @@
 	movl %esp,%eax
 	pushl %esi	# real mode pointer as second arg
 	pushl %eax	# address of structure as first arg
-	call SYMBOL_NAME(decompress_kernel)
+	call decompress_kernel
 	orl  %eax,%eax 
 	jnz  3f
 	addl $8,%esp
diff -Nru a/arch/x86_64/defconfig b/arch/x86_64/defconfig
--- a/arch/x86_64/defconfig	Sun Apr 28 20:10:37 2002
+++ b/arch/x86_64/defconfig	Sun Apr 28 20:10:37 2002
@@ -401,7 +401,7 @@
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
-# CONFIG_NTFS_RW is not set
+# CONFIG_NTFS_DEBUG is not set
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 # CONFIG_DEVFS_FS is not set
diff -Nru a/arch/x86_64/kernel/bluesmoke.c b/arch/x86_64/kernel/bluesmoke.c
--- a/arch/x86_64/kernel/bluesmoke.c	Sun Apr 28 20:10:38 2002
+++ b/arch/x86_64/kernel/bluesmoke.c	Sun Apr 28 20:10:38 2002
@@ -1,3 +1,6 @@
+/*
+ * arch/x86_64/kernel/bluesmoke.c - x86-64 Machine Check Exception Reporting
+ */
 
 #include <linux/init.h>
 #include <linux/types.h>
@@ -13,151 +16,44 @@
 #include <asm/pgtable.h>
 #include <asm/tlbflush.h>
 
+#ifdef CONFIG_X86_MCE
+
 static int mce_disabled __initdata = 0;
 
 static int banks;
 
-/*
- * 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();
-}
-
-/*
- *	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
+ *	Machine Check Handler For Hammer
  */
 
-static void intel_machine_check(struct pt_regs * regs, long error_code)
+static void hammer_machine_check(struct pt_regs * regs, long error_code)
 {
 	int recover=1;
 	u32 alow, ahigh, high, low;
 	u32 mcgstl, mcgsth;
 	int i;
-	
-	flush_all();
 
 	rdmsr(MSR_IA32_MCG_STATUS, mcgstl, mcgsth);
 	if(mcgstl&(1<<0))	/* Recoverable ? */
 		recover=0;
 
 	printk(KERN_EMERG "CPU %d: Machine Check Exception: %08x%08x\n", smp_processor_id(), mcgsth, mcgstl);
-	
-	for(i=0;i<banks;i++)
-	{
+
+	for (i=0;i<banks;i++) {
 		rdmsr(MSR_IA32_MC0_STATUS+i*4,low, high);
-		if(high&(1<<31))
-		{
+		if(high&(1<<31)) {
 			if(high&(1<<29))
 				recover|=1;
 			if(high&(1<<25))
 				recover|=2;
 			printk(KERN_EMERG "Bank %d: %08x%08x", i, high, low);
 			high&=~(1<<31);
-			if(high&(1<<27))
-			{
+			if(high&(1<<27)) {
 				rdmsr(MSR_IA32_MC0_MISC+i*4, alow, ahigh);
 				printk("[%08x%08x]", ahigh, alow);
 			}
-			if(high&(1<<26))
-			{
+			if(high&(1<<26)) {
 				rdmsr(MSR_IA32_MC0_ADDR+i*4, alow, ahigh);
 				printk(" at %08x%08x", ahigh, alow);
 			}
@@ -168,7 +64,7 @@
 			wmb();
 		}
 	}
-	
+
 	if(recover&2)
 		panic("CPU context corrupt");
 	if(recover&1)
@@ -178,19 +74,20 @@
 	wrmsr(MSR_IA32_MCG_STATUS,mcgstl, mcgsth);
 }
 
+
 /*
  *	Handle unconfigured int18 (should never happen)
  */
 
 static void unexpected_machine_check(struct pt_regs * regs, long error_code)
-{ 
+{	
 	printk(KERN_ERR "CPU#%d: Unexpected int18 (Machine Check).\n", smp_processor_id());
-} 
+}
 
 /*
  *	Call the installed machine check handler for this CPU setup.
- */ 
- 
+ */
+
 static void (*machine_check_vector)(struct pt_regs *, long error_code) = unexpected_machine_check;
 
 asmlinkage void do_machine_check(struct pt_regs * regs, long error_code)
@@ -200,47 +97,50 @@
 
 
 #ifdef CONFIG_X86_MCE_NONFATAL
-struct timer_list mce_timer;
+static struct timer_list mce_timer;
+static int timerset = 0;
+
+#define MCE_RATE	15*HZ	/* timer rate is 15s */
 
-static void mce_checkregs (unsigned int cpu)
+static void mce_checkregs (void *info)
 {
 	u32 low, high;
 	int i;
+	unsigned int *cpu = info;
 
-	if (cpu!=smp_processor_id())
-		BUG();
+	BUG_ON (*cpu != smp_processor_id());
 
 	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. */
+			/* Scrub the error so we don't pick it up in MCE_RATE 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;
+	unsigned int i;
 
 	for (i=0; i<smp_num_cpus; i++) {
 		if (i == smp_processor_id())
-			mce_checkregs(i);
+			mce_checkregs(&i);
 		else
-			smp_call_function (mce_checkregs, i, 1, 1);
+			smp_call_function (mce_checkregs, &i, 1, 1);
 	}
+
+	/* Refresh the timer. */
+	mce_timer.expires = jiffies + MCE_RATE;
+	add_timer (&mce_timer);
 }
 #endif
 
@@ -249,7 +149,7 @@
  *	Set up machine check reporting for processors with Intel style MCE
  */
 
-static void __init intel_mcheck_init(struct cpuinfo_x86 *c)
+static void __init hammer_mcheck_init(struct cpuinfo_x86 *c)
 {
 	u32 l, h;
 	int i;
@@ -261,42 +161,30 @@
 
 	if( !test_bit(X86_FEATURE_MCE, c->x86_capability) )
 		return;	
-	
-	/*
-	 *	Check for PPro style MCA
-	 */
-	 		
+
+	/* Check for PPro style MCA */
 	if( !test_bit(X86_FEATURE_MCA, c->x86_capability) )
 		return;
-		
+
 	/* Ok machine check is available */
-	
-	machine_check_vector = intel_machine_check;
+	machine_check_vector = hammer_machine_check;
 	wmb();
-	
+
 	if(done==0)
-		printk(KERN_INFO "Intel machine check architecture supported.\n");
+		printk(KERN_INFO "Machine check architecture supported.\n");
 	rdmsr(MSR_IA32_MCG_CAP, l, h);
 	if(l&(1<<8))	/* Control register present ? */
 		wrmsr(MSR_IA32_MCG_CTL, 0xffffffff, 0xffffffff);
 	banks = l&0xff;
 
-	/* 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++)
+	for(i=0; i<banks; i++)
 		wrmsr(MSR_IA32_MC0_CTL+4*i, 0xffffffff, 0xffffffff);
-	}
 
 	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);
+	printk(KERN_INFO "Machine check reporting enabled on CPU#%d.\n", smp_processor_id());
 
 	done=1;
 }
@@ -307,27 +195,27 @@
 
 void __init mcheck_init(struct cpuinfo_x86 *c)
 {
+
 	if(mce_disabled==1)
 		return;
-		
+
 	switch(c->x86_vendor)
 	{
 		case X86_VENDOR_AMD:
-			if(c->x86 == 6 || c->x86 == 15) {
-				intel_mcheck_init(c);
+			hammer_mcheck_init(c);
 #ifdef CONFIG_X86_MCE_NONFATAL
-				/* Set the timer to check for non-fatal errors every 5 seconds */
+			if (timerset == 0) {
+				/* Set the timer to check for non-fatal
+				   errors every MCE_RATE seconds */
 				init_timer (&mce_timer);
-				mce_timer.expires = jiffies + 5 * HZ;
+				mce_timer.expires = jiffies + MCE_RATE;
 				mce_timer.data = 0;
 				mce_timer.function = &mce_timerfunc;
 				add_timer (&mce_timer);
-#endif
+				timerset = 1;
+				printk(KERN_INFO "Machine check exception polling timer started.\n");
 			}
-			break;
-
-		case X86_VENDOR_INTEL:
-			intel_mcheck_init(c);
+#endif
 			break;
 
 		default:
@@ -349,3 +237,8 @@
 
 __setup("nomce", mcheck_disable);
 __setup("mce", mcheck_enable);
+
+#else
+asmlinkage void do_machine_check(struct pt_regs * regs, long error_code) {}
+void __init mcheck_init(struct cpuinfo_x86 *c) {}
+#endif
diff -Nru a/arch/x86_64/kernel/head.S b/arch/x86_64/kernel/head.S
--- a/arch/x86_64/kernel/head.S	Sun Apr 28 20:10:31 2002
+++ b/arch/x86_64/kernel/head.S	Sun Apr 28 20:10:31 2002
@@ -305,14 +305,14 @@
 .org 0xb000
 .data
 
-.globl SYMBOL_NAME(gdt)
+.globl gdt
 
 	.word 0
 	.align 16
 	.word 0
 pGDT64:
 	.word	gdt_end-gdt_table
-SYMBOL_NAME_LABEL(gdt)
+gdt:
 	.quad	gdt_table
 	
 
diff -Nru a/drivers/acorn/block/fd1772.c b/drivers/acorn/block/fd1772.c
--- a/drivers/acorn/block/fd1772.c	Sun Apr 28 20:10:35 2002
+++ b/drivers/acorn/block/fd1772.c	Sun Apr 28 20:10:35 2002
@@ -373,6 +373,7 @@
 static void config_types(void);
 static int floppy_open(struct inode *inode, struct file *filp);
 static int floppy_release(struct inode *inode, struct file *filp);
+static void do_fd_request(request_queue_t *);
 
 /************************* End of Prototypes **************************/
 
@@ -1302,7 +1303,7 @@
 	}
 }
 
-void do_fd_request(request_queue_t* q)
+static void do_fd_request(request_queue_t* q)
 {
 	unsigned long flags;
 
@@ -1614,7 +1615,7 @@
 
 	blk_size[MAJOR_NR] = floppy_sizes;
 	blksize_size[MAJOR_NR] = floppy_blocksizes;
-	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
+	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_fd_request);
 
 	config_types();
 
diff -Nru a/drivers/acorn/block/mfmhd.c b/drivers/acorn/block/mfmhd.c
--- a/drivers/acorn/block/mfmhd.c	Sun Apr 28 20:10:32 2002
+++ b/drivers/acorn/block/mfmhd.c	Sun Apr 28 20:10:32 2002
@@ -196,7 +196,6 @@
 static struct hd_struct mfm[MFM_MAXDRIVES << 6];
 static int mfm_sizes[MFM_MAXDRIVES << 6];
 static int mfm_blocksizes[MFM_MAXDRIVES << 6];
-static int mfm_sectsizes[MFM_MAXDRIVES << 6];
 static DECLARE_WAIT_QUEUE_HEAD(mfm_wait_open);
 
 /* Stuff from the assembly routines */
@@ -1319,10 +1318,8 @@
 	for (i = 0; i < (MFM_MAXDRIVES << 6); i++) {
 		/* Can't increase this - if you do all hell breaks loose */
 		mfm_blocksizes[i] = 1024;
-		mfm_sectsizes[i] = 512;
 	}
 	blksize_size[MAJOR_NR] = mfm_blocksizes;
-	hardsect_size[MAJOR_NR] = mfm_sectsizes;
 
 	mfm_drives = mfm_initdrives();
 
@@ -1430,7 +1427,7 @@
 	hdc63463_irqpolladdress	= mfm_IRQPollLoc;
 	hdc63463_irqpollmask	= irqmask;
 
-	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
+	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_mfm_request);
 
 	add_gendisk(&mfm_gendisk);
 
diff -Nru a/drivers/atm/fore200e.c b/drivers/atm/fore200e.c
--- a/drivers/atm/fore200e.c	Sun Apr 28 20:10:36 2002
+++ b/drivers/atm/fore200e.c	Sun Apr 28 20:10:36 2002
@@ -251,7 +251,7 @@
 fore200e_spin(int msecs)
 {
     unsigned long timeout = jiffies + MSECS(msecs);
-    while (jiffies < timeout);
+    while (time_before(jiffies, timeout));
 }
 
 
@@ -266,7 +266,7 @@
 	if ((ok = (*addr == val)) || (*addr & STATUS_ERROR))
 	    break;
 
-    } while (jiffies < timeout);
+    } while (time_before(jiffies, timeout));
 
 #if 1
     if (!ok) {
@@ -289,7 +289,7 @@
 	if ((ok = (fore200e->bus->read(addr) == val)))
 	    break;
 
-    } while (jiffies < timeout);
+    } while (time_before(jiffies, timeout));
 
 #if 1
     if (!ok) {
@@ -2416,7 +2416,7 @@
     unsigned long      timeout = jiffies + MSECS(50);
     int                c;
 
-    while (jiffies < timeout) {
+    while (time_before(jiffies, timeout)) {
 
 	c = (int) fore200e->bus->read(&monitor->soft_uart.recv);
 
diff -Nru a/drivers/atm/iphase.c b/drivers/atm/iphase.c
--- a/drivers/atm/iphase.c	Sun Apr 28 20:10:39 2002
+++ b/drivers/atm/iphase.c	Sun Apr 28 20:10:39 2002
@@ -54,7 +54,6 @@
 #include <linux/sonet.h>  
 #include <linux/skbuff.h>  
 #include <linux/time.h>  
-#include <linux/sched.h> /* for xtime */  
 #include <linux/delay.h>  
 #include <linux/uio.h>  
 #include <linux/init.h>  
@@ -1777,8 +1776,9 @@
         memset((caddr_t)ia_vcc, 0, sizeof(*ia_vcc));
         if (vcc->qos.txtp.max_sdu > 
                          (iadev->tx_buf_sz - sizeof(struct cpcs_trailer))){
-           printk("IA:  SDU size over the configured SDU size %d\n",
-                                                          iadev->tx_buf_sz);
+           printk("IA:  SDU size over (%d) the configured SDU size %d\n",
+		  vcc->qos.txtp.max_sdu,iadev->tx_buf_sz);
+	   INPH_IA_VCC(vcc) = NULL;  
            kfree(ia_vcc);
            return -EINVAL; 
         }
diff -Nru a/drivers/base/core.c b/drivers/base/core.c
--- a/drivers/base/core.c	Sun Apr 28 20:10:31 2002
+++ b/drivers/base/core.c	Sun Apr 28 20:10:31 2002
@@ -8,6 +8,7 @@
 #include <linux/device.h>
 #include <linux/module.h>
 #include <linux/slab.h>
+#include <linux/err.h>
 #include <linux/init.h>
 #include <linux/err.h>
 #include "base.h"
diff -Nru a/drivers/block/DAC960.c b/drivers/block/DAC960.c
--- a/drivers/block/DAC960.c	Sun Apr 28 20:10:37 2002
+++ b/drivers/block/DAC960.c	Sun Apr 28 20:10:37 2002
@@ -2944,6 +2944,10 @@
   BufferHeader->bi_end_io(BufferHeader);
 }
 
+static inline int DAC960_PartitionByCommand(DAC960_Command_T *Command)
+{
+	return DAC960_PartitionNumber(to_kdev_t(Command->BufferHeader->bi_bdev->bd_dev)); 
+}
 
 /*
   DAC960_V1_ReadWriteError prints an appropriate error message for Command
@@ -2995,11 +2999,11 @@
 	       Controller, Controller->ControllerNumber,
 	       Command->LogicalDriveNumber, Command->BlockNumber,
 	       Command->BlockNumber + Command->BlockCount - 1);
-  if (DAC960_PartitionNumber(Command->BufferHeader->bi_dev) > 0)
+  if (DAC960_PartitionByCommand(Command) > 0)
     DAC960_Error("  /dev/rd/c%dd%dp%d: relative blocks %u..%u\n",
 		 Controller, Controller->ControllerNumber,
 		 Command->LogicalDriveNumber,
-		 DAC960_PartitionNumber(Command->BufferHeader->bi_dev),
+		 DAC960_PartitionByCommand(Command),
 		 Command->BufferHeader->bi_sector,
 		 Command->BufferHeader->bi_sector + Command->BlockCount - 1);
 }
@@ -3859,11 +3863,11 @@
 	       Controller, Controller->ControllerNumber,
 	       Command->LogicalDriveNumber, Command->BlockNumber,
 	       Command->BlockNumber + Command->BlockCount - 1);
-  if (DAC960_PartitionNumber(Command->BufferHeader->bi_dev) > 0)
+  if (DAC960_PartitionByCommand(Command) > 0)
     DAC960_Error("  /dev/rd/c%dd%dp%d: relative blocks %u..%u\n",
 		 Controller, Controller->ControllerNumber,
 		 Command->LogicalDriveNumber,
-		 DAC960_PartitionNumber(Command->BufferHeader->bi_dev),
+		 DAC960_PartitionByCommand(Command),
 		 Command->BufferHeader->bi_sector,
 		 Command->BufferHeader->bi_sector + Command->BlockCount - 1);
 }
diff -Nru a/drivers/block/acsi.c b/drivers/block/acsi.c
--- a/drivers/block/acsi.c	Sun Apr 28 20:10:36 2002
+++ b/drivers/block/acsi.c	Sun Apr 28 20:10:36 2002
@@ -1784,7 +1784,7 @@
 	phys_acsi_buffer = virt_to_phys( acsi_buffer );
 	STramMask = ATARIHW_PRESENT(EXTD_DMA) ? 0x00000000 : 0xff000000;
 	
-	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST, &acsi_lock);
+	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_acsi_request, &acsi_lock);
 	add_gendisk(&acsi_gendisk);
 
 #ifdef CONFIG_ATARI_SLM
diff -Nru a/drivers/block/amiflop.c b/drivers/block/amiflop.c
--- a/drivers/block/amiflop.c	Sun Apr 28 20:10:36 2002
+++ b/drivers/block/amiflop.c	Sun Apr 28 20:10:36 2002
@@ -1857,7 +1857,7 @@
 	post_write_timer.data = 0;
 	post_write_timer.function = post_write;
   
-	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST, &amiflop_lock);
+	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_fd_request, &amiflop_lock);
 	blksize_size[MAJOR_NR] = floppy_blocksizes;
 	blk_size[MAJOR_NR] = floppy_sizes;
 
diff -Nru a/drivers/block/ataflop.c b/drivers/block/ataflop.c
--- a/drivers/block/ataflop.c	Sun Apr 28 20:10:32 2002
+++ b/drivers/block/ataflop.c	Sun Apr 28 20:10:32 2002
@@ -2013,7 +2013,7 @@
 
 	blk_size[MAJOR_NR] = floppy_sizes;
 	blksize_size[MAJOR_NR] = floppy_blocksizes;
-	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST, &ataflop_lock);
+	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_fd_request, &ataflop_lock);
 
 	printk(KERN_INFO "Atari floppy driver: max. %cD, %strack buffering\n",
 	       DriveType == 0 ? 'D' : DriveType == 1 ? 'H' : 'E',
diff -Nru a/drivers/block/blkpg.c b/drivers/block/blkpg.c
--- a/drivers/block/blkpg.c	Sun Apr 28 20:10:30 2002
+++ b/drivers/block/blkpg.c	Sun Apr 28 20:10:30 2002
@@ -211,7 +211,6 @@
 /*
  * Common ioctl's for block devices
  */
-extern int block_ioctl(kdev_t dev, unsigned int cmd, unsigned long arg);
 int blk_ioctl(struct block_device *bdev, unsigned int cmd, unsigned long arg)
 {
 	request_queue_t *q;
@@ -221,7 +220,7 @@
 	kdev_t dev = to_kdev_t(bdev->bd_dev);
 	int holder;
 
-	intval = block_ioctl(dev, cmd, arg);
+	intval = block_ioctl(bdev, cmd, arg);
 	if (intval != -ENOTTY)
 		return intval;
 
@@ -241,13 +240,13 @@
 		case BLKFRASET:
 			if(!capable(CAP_SYS_ADMIN))
 				return -EACCES;
-			return blk_set_readahead(dev, arg);
+			return blk_set_readahead(bdev, arg);
 
 		case BLKRAGET:
 		case BLKFRAGET:
 			if (!arg)
 				return -EINVAL;
-			return put_user(blk_get_readahead(dev), (long *)arg);
+			return put_user(blk_get_readahead(bdev), (long *)arg);
 
 		case BLKSECTGET:
 			if ((q = blk_get_queue(dev)) == NULL)
@@ -266,7 +265,7 @@
 
 		case BLKSSZGET:
 			/* get block device hardware sector size */
-			intval = get_hardsect_size(dev);
+			intval = bdev_hardsect_size(bdev);
 			return put_user(intval, (int *) arg);
 
 		case BLKGETSIZE:
diff -Nru a/drivers/block/block_ioctl.c b/drivers/block/block_ioctl.c
--- a/drivers/block/block_ioctl.c	Sun Apr 28 20:10:36 2002
+++ b/drivers/block/block_ioctl.c	Sun Apr 28 20:10:36 2002
@@ -51,13 +51,13 @@
 	return err;
 }
 
-int block_ioctl(kdev_t dev, unsigned int cmd, unsigned long arg)
+int block_ioctl(struct block_device *bdev, unsigned int cmd, unsigned long arg)
 {
 	request_queue_t *q;
 	struct request *rq;
 	int close = 0, err;
 
-	q = blk_get_queue(dev);
+	q = blk_get_queue(to_kdev_t(bdev->bd_dev));
 	if (!q)
 		return -ENXIO;
 
diff -Nru a/drivers/block/cpqarray.c b/drivers/block/cpqarray.c
--- a/drivers/block/cpqarray.c	Sun Apr 28 20:10:31 2002
+++ b/drivers/block/cpqarray.c	Sun Apr 28 20:10:31 2002
@@ -52,6 +52,7 @@
 MODULE_LICENSE("GPL");
 
 #define MAJOR_NR COMPAQ_SMART2_MAJOR
+#define LOCAL_END_REQUEST
 #include <linux/blk.h>
 #include <linux/blkdev.h>
 #include <linux/genhd.h>
diff -Nru a/drivers/block/elevator.c b/drivers/block/elevator.c
--- a/drivers/block/elevator.c	Sun Apr 28 20:10:37 2002
+++ b/drivers/block/elevator.c	Sun Apr 28 20:10:37 2002
@@ -111,7 +111,8 @@
 	/*
 	 * same device and no special stuff set, merge is ok
 	 */
-	if (kdev_same(rq->rq_dev, bio->bi_dev) && !rq->waiting && !rq->special)
+	if (kdev_same(rq->rq_dev, to_kdev_t(bio->bi_bdev->bd_dev)) &&
+	    !rq->waiting && !rq->special)
 		return 1;
 
 	return 0;
diff -Nru a/drivers/block/floppy.c b/drivers/block/floppy.c
--- a/drivers/block/floppy.c	Sun Apr 28 20:10:32 2002
+++ b/drivers/block/floppy.c	Sun Apr 28 20:10:32 2002
@@ -230,6 +230,7 @@
 
 static int irqdma_allocated;
 
+#define LOCAL_END_REQUEST
 #define MAJOR_NR FLOPPY_MAJOR
 
 #include <linux/blk.h>
@@ -2274,17 +2275,32 @@
  * =============================
  */
 
+static inline void end_request(struct request *req, int uptodate)
+{
+	kdev_t dev = req->rq_dev;
+
+	if (end_that_request_first(req, uptodate, req->hard_cur_sectors))
+		return;
+	add_blkdev_randomness(major(dev));
+	floppy_off(DEVICE_NR(dev));
+	blkdev_dequeue_request(req);
+	end_that_request_last(req);
+}
+
+
 /* new request_done. Can handle physical sectors which are smaller than a
  * logical buffer */
 static void request_done(int uptodate)
 {
+	struct request_queue *q = QUEUE;
+	struct request *req = elv_next_request(q);
 	unsigned long flags;
 	int block;
 
 	probing = 0;
 	reschedule_timeout(MAXTIMEOUT, "request done %d", uptodate);
 
-	if (QUEUE_EMPTY){
+	if (blk_queue_empty(q)) {
 		DPRINT("request list destroyed in floppy request done\n");
 		return;
 	}
@@ -2292,48 +2308,48 @@
 	if (uptodate){
 		/* maintain values for invalidation on geometry
 		 * change */
-		block = current_count_sectors + CURRENT->sector;
+		block = current_count_sectors + req->sector;
 		INFBOUND(DRS->maxblock, block);
 		if (block > _floppy->sect)
 			DRS->maxtrack = 1;
 
 		/* unlock chained buffers */
-		spin_lock_irqsave(QUEUE->queue_lock, flags);
-		while (current_count_sectors && !QUEUE_EMPTY &&
-		       current_count_sectors >= CURRENT->current_nr_sectors){
-			current_count_sectors -= CURRENT->current_nr_sectors;
-			CURRENT->nr_sectors -= CURRENT->current_nr_sectors;
-			CURRENT->sector += CURRENT->current_nr_sectors;
-			end_request(1);
+		spin_lock_irqsave(q->queue_lock, flags);
+		while (current_count_sectors && !blk_queue_empty(q) &&
+		       current_count_sectors >= req->current_nr_sectors){
+			current_count_sectors -= req->current_nr_sectors;
+			req->nr_sectors -= req->current_nr_sectors;
+			req->sector += req->current_nr_sectors;
+			end_request(req, 1);
 		}
-		spin_unlock_irqrestore(QUEUE->queue_lock, flags);
+		spin_unlock_irqrestore(q->queue_lock, flags);
 
-		if (current_count_sectors && !QUEUE_EMPTY){
+		if (current_count_sectors && !blk_queue_empty(q)) {
 			/* "unlock" last subsector */
-			CURRENT->buffer += current_count_sectors <<9;
-			CURRENT->current_nr_sectors -= current_count_sectors;
-			CURRENT->nr_sectors -= current_count_sectors;
-			CURRENT->sector += current_count_sectors;
+			req->buffer += current_count_sectors <<9;
+			req->current_nr_sectors -= current_count_sectors;
+			req->nr_sectors -= current_count_sectors;
+			req->sector += current_count_sectors;
 			return;
 		}
 
-		if (current_count_sectors && QUEUE_EMPTY)
+		if (current_count_sectors && blk_queue_empty(q))
 			DPRINT("request list destroyed in floppy request done\n");
 
 	} else {
-		if (rq_data_dir(CURRENT) == WRITE) {
+		if (rq_data_dir(req) == WRITE) {
 			/* record write error information */
 			DRWE->write_errors++;
 			if (DRWE->write_errors == 1) {
-				DRWE->first_error_sector = CURRENT->sector;
+				DRWE->first_error_sector = req->sector;
 				DRWE->first_error_generation = DRS->generation;
 			}
-			DRWE->last_error_sector = CURRENT->sector;
+			DRWE->last_error_sector = req->sector;
 			DRWE->last_error_generation = DRS->generation;
 		}
-		spin_lock_irqsave(QUEUE->queue_lock, flags);
-		end_request(0);
-		spin_unlock_irqrestore(QUEUE->queue_lock, flags);
+		spin_lock_irqsave(q->queue_lock, flags);
+		end_request(req, 0);
+		spin_unlock_irqrestore(q->queue_lock, flags);
 	}
 }
 
@@ -4168,7 +4184,7 @@
 
 	blk_size[MAJOR_NR] = floppy_sizes;
 	blksize_size[MAJOR_NR] = floppy_blocksizes;
-	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST, &floppy_lock);
+	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_fd_request, &floppy_lock);
 	reschedule_timeout(MAXTIMEOUT, "floppy init", MAXTIMEOUT);
 	config_types();
 
diff -Nru a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c
--- a/drivers/block/ll_rw_blk.c	Sun Apr 28 20:10:31 2002
+++ b/drivers/block/ll_rw_blk.c	Sun Apr 28 20:10:31 2002
@@ -115,13 +115,14 @@
  *
  * Returns zero on success, else negative errno
  */
-int blk_set_readahead(kdev_t dev, unsigned sectors)
+int blk_set_readahead(struct block_device *bdev, unsigned sectors)
 {
 	int ret = -EINVAL;
-	request_queue_t *q = blk_get_queue(dev);
+	request_queue_t *q = blk_get_queue(to_kdev_t(bdev->bd_dev));
 
 	if (q) {
 		q->ra_sectors = sectors;
+		blk_put_queue(q);
 		ret = 0;
 	}
 	return ret;
@@ -139,13 +140,15 @@
  * Will return zero if the queue has never had its readahead
  * setting altered.
  */
-unsigned blk_get_readahead(kdev_t dev)
+unsigned blk_get_readahead(struct block_device *bdev)
 {
 	unsigned ret = 0;
-	request_queue_t *q = blk_get_queue(dev);
+	request_queue_t *q = blk_get_queue(to_kdev_t(bdev->bd_dev));
 
-	if (q)
+	if (q) {
 		ret = q->ra_sectors;
+		blk_put_queue(q);
+	}
 	return ret;
 }
 
@@ -184,6 +187,7 @@
 	q->max_phys_segments = MAX_PHYS_SEGMENTS;
 	q->max_hw_segments = MAX_HW_SEGMENTS;
 	q->make_request_fn = mfn;
+	q->ra_sectors = VM_MAX_READAHEAD << (10 - 9);	/* kbytes->sectors */
 	blk_queue_max_sectors(q, MAX_SECTORS);
 	blk_queue_hardsect_size(q, 512);
 
@@ -334,7 +338,7 @@
 
 static char *rq_flags[] = { "REQ_RW", "REQ_RW_AHEAD", "REQ_BARRIER",
 			   "REQ_CMD", "REQ_NOMERGE", "REQ_STARTED",
-			   "REQ_DONTPREP", "REQ_DRIVE_CMD", "REQ_DRIVE_TASK",
+			   "REQ_DONTPREP", "REQ_DRIVE_CMD",
 			   "REQ_DRIVE_ACB", "REQ_PC", "REQ_BLOCK_PC",
 			   "REQ_SENSE", "REQ_SPECIAL" };
 
@@ -363,7 +367,7 @@
  */
 int ll_10byte_cmd_build(request_queue_t *q, struct request *rq)
 {
-	int hard_sect = get_hardsect_size(rq->rq_dev);
+	int hard_sect = queue_hardsect_size(q);
 	sector_t block = rq->hard_sector / (hard_sect >> 9);
 	unsigned long blocks = rq->hard_nr_sectors / (hard_sect >> 9);
 
@@ -851,7 +855,6 @@
 	q->plug_tq.data		= q;
 	q->queue_flags		= (1 << QUEUE_FLAG_CLUSTER);
 	q->queue_lock		= lock;
-	q->ra_sectors		= 0;		/* Use VM default */
 
 	blk_queue_segment_boundary(q, 0xffffffff);
 
@@ -1251,7 +1254,7 @@
 	req->buffer = bio_data(bio);	/* see ->buffer comment above */
 	req->waiting = NULL;
 	req->bio = req->biotail = bio;
-	req->rq_dev = bio->bi_dev;
+	req->rq_dev = to_kdev_t(bio->bi_bdev->bd_dev);
 	add_request(q, req, insert_here);
 out:
 	if (freereq)
@@ -1270,23 +1273,19 @@
  */
 static inline void blk_partition_remap(struct bio *bio)
 {
-	int major, minor, drive, minor0;
+	struct block_device *bdev = bio->bi_bdev;
 	struct gendisk *g;
-	kdev_t dev0;
 
-	major = major(bio->bi_dev);
-	if ((g = get_gendisk(bio->bi_dev))) {
-		minor = minor(bio->bi_dev);
-		drive = (minor >> g->minor_shift);
-		minor0 = (drive << g->minor_shift); /* whole disk device */
-		/* that is, minor0 = (minor & ~((1<<g->minor_shift)-1)); */
-		dev0 = mk_kdev(major, minor0);
-		if (!kdev_same(dev0, bio->bi_dev)) {
-			bio->bi_dev = dev0;
-			bio->bi_sector += g->part[minor].start_sect;
-		}
-		/* lots of checks are possible */
-	}
+	if (bdev == bdev->bd_contains)
+		return;
+
+	g = get_gendisk(to_kdev_t(bdev->bd_dev));
+	if (!g)
+		BUG();
+
+	bio->bi_sector += g->part[minor(to_kdev_t((bdev->bd_dev)))].start_sect;
+	bio->bi_bdev = bdev->bd_contains;
+	/* lots of checks are possible */
 }
 
 /**
@@ -1321,7 +1320,7 @@
 	int ret, nr_sectors = bio_sectors(bio);
 
 	/* Test device or partition size, when known. */
-	maxsector = (blkdev_size_in_bytes(bio->bi_dev) >> 9);
+	maxsector = bio->bi_bdev->bd_inode->i_size >> 9;
 	if (maxsector) {
 		sector_t sector = bio->bi_sector;
 
@@ -1333,7 +1332,8 @@
 			printk(KERN_INFO
 			       "attempt to access beyond end of device\n");
 			printk(KERN_INFO "%s: rw=%ld, want=%ld, limit=%Lu\n",
-			       kdevname(bio->bi_dev), bio->bi_rw,
+			       kdevname(to_kdev_t(bio->bi_bdev->bd_dev)),
+			       bio->bi_rw,
 			       sector + nr_sectors,
 			       (long long) maxsector);
 
@@ -1351,11 +1351,12 @@
 	 * Stacking drivers are expected to know what they are doing.
 	 */
 	do {
-		q = blk_get_queue(bio->bi_dev);
+		q = blk_get_queue(to_kdev_t(bio->bi_bdev->bd_dev));
 		if (!q) {
 			printk(KERN_ERR
 			       "generic_make_request: Trying to access nonexistent block-device %s (%Lu)\n",
-			       kdevname(bio->bi_dev), (long long) bio->bi_sector);
+			       kdevname(to_kdev_t(bio->bi_bdev->bd_dev)),
+			       (long long) bio->bi_sector);
 end_io:
 			bio->bi_end_io(bio);
 			break;
@@ -1442,7 +1443,7 @@
 	bio = bio_alloc(GFP_NOIO, 1);
 
 	bio->bi_sector = bh->b_blocknr * (bh->b_size >> 9);
-	bio->bi_dev = bh->b_dev;
+	bio->bi_bdev = bh->b_bdev;
 	bio->bi_io_vec[0].bv_page = bh->b_page;
 	bio->bi_io_vec[0].bv_len = bh->b_size;
 	bio->bi_io_vec[0].bv_offset = bh_offset(bh);
@@ -1498,10 +1499,10 @@
 	if (!nr)
 		return;
 
-	major = major(bhs[0]->b_dev);
+	major = major(to_kdev_t(bhs[0]->b_bdev->bd_dev));
 
 	/* Determine correct block size for this device. */
-	correct_size = get_hardsect_size(bhs[0]->b_dev);
+	correct_size = bdev_hardsect_size(bhs[0]->b_bdev);
 
 	/* Verify requested block sizes. */
 	for (i = 0; i < nr; i++) {
@@ -1509,15 +1510,15 @@
 		if (bh->b_size & (correct_size - 1)) {
 			printk(KERN_NOTICE "ll_rw_block: device %s: "
 			       "only %d-char blocks implemented (%u)\n",
-			       kdevname(bhs[0]->b_dev),
+			       bdevname(bhs[0]->b_bdev),
 			       correct_size, bh->b_size);
 			goto sorry;
 		}
 	}
 
-	if ((rw & WRITE) && is_read_only(bhs[0]->b_dev)) {
+	if ((rw & WRITE) && is_read_only(to_kdev_t(bhs[0]->b_bdev->bd_dev))) {
 		printk(KERN_NOTICE "Can't write to read-only device %s\n",
-		       kdevname(bhs[0]->b_dev));
+		       bdevname(bhs[0]->b_bdev));
 		goto sorry;
 	}
 
diff -Nru a/drivers/block/loop.c b/drivers/block/loop.c
--- a/drivers/block/loop.c	Sun Apr 28 20:10:30 2002
+++ b/drivers/block/loop.c	Sun Apr 28 20:10:30 2002
@@ -155,20 +155,17 @@
 #define MAX_DISK_SIZE 1024*1024*1024
 
 static unsigned long
-compute_loop_size(struct loop_device *lo,
-		  struct dentry * lo_dentry, kdev_t lodev)
+compute_loop_size(struct loop_device *lo, struct dentry * lo_dentry)
 {
-	loff_t size = 0;
-
-	size = lo_dentry->d_inode->i_mapping->host->i_size;
+	loff_t size = lo_dentry->d_inode->i_mapping->host->i_size;
 	return (size - lo->lo_offset) >> BLOCK_SIZE_BITS;
 }
 
 static void figure_loop_size(struct loop_device *lo)
 {
 	loop_sizes[lo->lo_number] = compute_loop_size(lo,
-					lo->lo_backing_file->f_dentry,
-					lo->lo_device);
+					lo->lo_backing_file->f_dentry);
+					
 }
 
 static int lo_send(struct loop_device *lo, struct bio *bio, int bsize, loff_t pos)
@@ -287,7 +284,7 @@
 
 static inline int loop_get_bs(struct loop_device *lo)
 {
-	return block_size(lo->lo_device);
+	return block_size(to_kdev_t(lo->lo_device->bd_dev));
 }
 
 static inline unsigned long loop_get_iv(struct loop_device *lo,
@@ -383,7 +380,7 @@
 static void loop_end_io_transfer(struct bio *bio)
 {
 	struct bio *rbh = bio->bi_private;
-	struct loop_device *lo = &loop_dev[minor(rbh->bi_dev)];
+	struct loop_device *lo = &loop_dev[minor(to_kdev_t(rbh->bi_bdev->bd_dev))];
 	int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
 
 	if (!uptodate || bio_rw(bio) == WRITE) {
@@ -416,7 +413,7 @@
 	bio->bi_sector = rbh->bi_sector + (lo->lo_offset >> 9);
 	bio->bi_rw = rbh->bi_rw;
 	spin_lock_irq(&lo->lo_lock);
-	bio->bi_dev = lo->lo_device;
+	bio->bi_bdev = lo->lo_device;
 	spin_unlock_irq(&lo->lo_lock);
 
 	return bio;
@@ -428,11 +425,12 @@
 	struct loop_device *lo;
 	unsigned long IV;
 	int rw = bio_rw(rbh);
+	int unit = minor(to_kdev_t(rbh->bi_bdev->bd_dev));
 
-	if (minor(rbh->bi_dev) >= max_loop)
+	if (unit >= max_loop)
 		goto out;
 
-	lo = &loop_dev[minor(rbh->bi_dev)];
+	lo = &loop_dev[unit];
 	spin_lock_irq(&lo->lo_lock);
 	if (lo->lo_state != Lo_bound)
 		goto inactive;
@@ -585,12 +583,13 @@
 	return 0;
 }
 
-static int loop_set_fd(struct loop_device *lo, struct file *lo_file, kdev_t dev,
-		       unsigned int arg)
+static int loop_set_fd(struct loop_device *lo, struct file *lo_file,
+		       struct block_device *bdev, unsigned int arg)
 {
 	struct file	*file;
 	struct inode	*inode;
-	kdev_t		lo_device;
+	kdev_t		dev = to_kdev_t(bdev->bd_dev);
+	struct block_device *lo_device;
 	int		lo_flags = 0;
 	int		error;
 
@@ -612,8 +611,8 @@
 		lo_flags |= LO_FLAGS_READ_ONLY;
 
 	if (S_ISBLK(inode->i_mode)) {
-		lo_device = inode->i_rdev;
-		if (kdev_same(lo_device, dev)) {
+		lo_device = inode->i_bdev;
+		if (lo_device == bdev) {
 			error = -EBUSY;
 			goto out;
 		}
@@ -629,7 +628,7 @@
 		if (!aops->prepare_write || !aops->commit_write)
 			lo_flags |= LO_FLAGS_READ_ONLY;
 
-		lo_device = inode->i_dev;
+		lo_device = inode->i_sb->s_bdev;
 		lo_flags |= LO_FLAGS_DO_BMAP;
 		error = 0;
 	} else
@@ -637,7 +636,7 @@
 
 	get_file(file);
 
-	if (IS_RDONLY (inode) || is_read_only(lo_device)
+	if (IS_RDONLY (inode) || is_read_only(to_kdev_t(lo_device->bd_dev))
 	    || !(lo_file->f_mode & FMODE_WRITE))
 		lo_flags |= LO_FLAGS_READ_ONLY;
 
@@ -652,7 +651,7 @@
 	lo->old_gfp_mask = inode->i_mapping->gfp_mask;
 	inode->i_mapping->gfp_mask = GFP_NOIO;
 
-	set_blocksize(dev, block_size(lo_device));
+	set_blocksize(dev, block_size(to_kdev_t(lo_device->bd_dev)));
 
 	lo->lo_bio = lo->lo_biotail = NULL;
 	kernel_thread(loop_thread, lo, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
@@ -723,7 +722,7 @@
 	loop_release_xfer(lo);
 	lo->transfer = NULL;
 	lo->ioctl = NULL;
-	lo->lo_device = NODEV;
+	lo->lo_device = NULL;
 	lo->lo_encrypt_type = 0;
 	lo->lo_offset = 0;
 	lo->lo_encrypt_key_size = 0;
@@ -795,7 +794,7 @@
 	info.lo_number = lo->lo_number;
 	info.lo_device = kdev_t_to_nr(file->f_dentry->d_inode->i_dev);
 	info.lo_inode = file->f_dentry->d_inode->i_ino;
-	info.lo_rdevice = kdev_t_to_nr(lo->lo_device);
+	info.lo_rdevice = lo->lo_device->bd_dev;
 	info.lo_offset = lo->lo_offset;
 	info.lo_flags = lo->lo_flags;
 	strncpy(info.lo_name, lo->lo_name, LO_NAME_SIZE);
@@ -828,7 +827,7 @@
 	down(&lo->lo_ctl_mutex);
 	switch (cmd) {
 	case LOOP_SET_FD:
-		err = loop_set_fd(lo, file, inode->i_rdev, arg);
+		err = loop_set_fd(lo, file, inode->i_bdev, arg);
 		break;
 	case LOOP_CLR_FD:
 		err = loop_clr_fd(lo, inode->i_bdev);
diff -Nru a/drivers/block/paride/pcd.c b/drivers/block/paride/pcd.c
--- a/drivers/block/paride/pcd.c	Sun Apr 28 20:10:36 2002
+++ b/drivers/block/paride/pcd.c	Sun Apr 28 20:10:36 2002
@@ -181,9 +181,7 @@
 
 #define MAJOR_NR	major
 #define DEVICE_NAME "PCD"
-#define DEVICE_REQUEST do_pcd_request
 #define DEVICE_NR(device) (minor(device))
-#define DEVICE_ON(device)
 #define DEVICE_OFF(device)
 
 #include <linux/blk.h>
@@ -357,7 +355,7 @@
 		}
 	}
 
-	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST, &pcd_lock);
+	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_pcd_request, &pcd_lock);
 
 	for (i=0;i<PCD_UNITS;i++) pcd_blocksizes[i] = 1024;
         blksize_size[MAJOR_NR] = pcd_blocksizes;
diff -Nru a/drivers/block/paride/pd.c b/drivers/block/paride/pd.c
--- a/drivers/block/paride/pd.c	Sun Apr 28 20:10:32 2002
+++ b/drivers/block/paride/pd.c	Sun Apr 28 20:10:32 2002
@@ -205,9 +205,7 @@
 
 #define MAJOR_NR   major
 #define DEVICE_NAME "PD"
-#define DEVICE_REQUEST do_pd_request
 #define DEVICE_NR(device) (minor(device)>>PD_BITS)
-#define DEVICE_ON(device)
 #define DEVICE_OFF(device)
 
 #include <linux/blk.h>
@@ -395,9 +393,9 @@
                 return -1;
         }
 	q = BLK_DEFAULT_QUEUE(MAJOR_NR);
-	blk_init_queue(q, DEVICE_REQUEST, &pd_lock);
+	blk_init_queue(q, do_pd_request, &pd_lock);
 	blk_queue_max_sectors(q, cluster);
-        
+
 	pd_gendisk.major = major;
 	pd_gendisk.major_name = name;
 	add_gendisk(&pd_gendisk);
diff -Nru a/drivers/block/paride/pf.c b/drivers/block/paride/pf.c
--- a/drivers/block/paride/pf.c	Sun Apr 28 20:10:31 2002
+++ b/drivers/block/paride/pf.c	Sun Apr 28 20:10:31 2002
@@ -201,9 +201,7 @@
 
 #define MAJOR_NR   major
 #define DEVICE_NAME "PF"
-#define DEVICE_REQUEST do_pf_request
 #define DEVICE_NR(device) minor(device)
-#define DEVICE_ON(device)
 #define DEVICE_OFF(device)
 
 #include <linux/blk.h>
@@ -360,10 +358,10 @@
                 return -1;
         }
 	q = BLK_DEFAULT_QUEUE(MAJOR_NR);
-	blk_init_queue(q, DEVICE_REQUEST, &pf_spin_lock);
+	blk_init_queue(q, do_pf_request, &pf_spin_lock);
 	blk_queue_max_phys_segments(q, cluster);
 	blk_queue_max_hw_segments(q, cluster);
-        
+
 	for (i=0;i<PF_UNITS;i++) pf_blocksizes[i] = 1024;
 	blksize_size[MAJOR_NR] = pf_blocksizes;
 	for (i=0;i<PF_UNITS;i++)
diff -Nru a/drivers/block/ps2esdi.c b/drivers/block/ps2esdi.c
--- a/drivers/block/ps2esdi.c	Sun Apr 28 20:10:30 2002
+++ b/drivers/block/ps2esdi.c	Sun Apr 28 20:10:30 2002
@@ -176,7 +176,8 @@
 		return -1;
 	}
 	/* set up some global information - indicating device specific info */
-	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST, &ps2esdi_lock);
+	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_ps2esdi_request,
+			&ps2esdi_lock);
 
 	/* some minor housekeeping - setup the global gendisk structure */
 	add_gendisk(&ps2esdi_gendisk);
diff -Nru a/drivers/block/rd.c b/drivers/block/rd.c
--- a/drivers/block/rd.c	Sun Apr 28 20:10:31 2002
+++ b/drivers/block/rd.c	Sun Apr 28 20:10:31 2002
@@ -244,7 +244,7 @@
 	unsigned long offset, len;
 	int rw = sbh->bi_rw;
 
-	minor = minor(sbh->bi_dev);
+	minor = minor(to_kdev_t(sbh->bi_bdev->bd_dev));
 
 	if (minor >= NUM_RAMDISKS)
 		goto fail;
diff -Nru a/drivers/block/swim3.c b/drivers/block/swim3.c
--- a/drivers/block/swim3.c	Sun Apr 28 20:10:35 2002
+++ b/drivers/block/swim3.c	Sun Apr 28 20:10:35 2002
@@ -1032,7 +1032,8 @@
 			       MAJOR_NR);
 			return -EBUSY;
 		}
-		blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST,&swim3_lock);
+		blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_fd_request,
+				&swim3_lock);
 		blksize_size[MAJOR_NR] = floppy_blocksizes;
 		blk_size[MAJOR_NR] = floppy_sizes;
 	}
diff -Nru a/drivers/block/swim_iop.c b/drivers/block/swim_iop.c
--- a/drivers/block/swim_iop.c	Sun Apr 28 20:10:30 2002
+++ b/drivers/block/swim_iop.c	Sun Apr 28 20:10:30 2002
@@ -149,7 +149,8 @@
 		       MAJOR_NR);
 		return -EBUSY;
 	}
-	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST, &swim_iop_lock);
+	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_fd_request,
+			&swim_iop_lock);
 	blksize_size[MAJOR_NR] = floppy_blocksizes;
 	blk_size[MAJOR_NR] = floppy_sizes;
 
diff -Nru a/drivers/block/xd.c b/drivers/block/xd.c
--- a/drivers/block/xd.c	Sun Apr 28 20:10:33 2002
+++ b/drivers/block/xd.c	Sun Apr 28 20:10:33 2002
@@ -170,7 +170,7 @@
 		return -1;
 	}
 	devfs_handle = devfs_mk_dir (NULL, xd_gendisk.major_name, NULL);
-	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST, &xd_lock);
+	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_xd_request, &xd_lock);
 	add_gendisk(&xd_gendisk);
 	xd_geninit();
 
diff -Nru a/drivers/cdrom/aztcd.c b/drivers/cdrom/aztcd.c
--- a/drivers/cdrom/aztcd.c	Sun Apr 28 20:10:38 2002
+++ b/drivers/cdrom/aztcd.c	Sun Apr 28 20:10:38 2002
@@ -1925,7 +1925,7 @@
 		       MAJOR_NR);
 		return -EIO;
 	}
-	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST, &aztSpin);
+	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_aztcd_request, &aztSpin);
 	blksize_size[MAJOR_NR] = aztcd_blocksizes;
 	register_disk(NULL, mk_kdev(MAJOR_NR, 0), 1, &azt_fops, 0);
 
diff -Nru a/drivers/cdrom/cdu31a.c b/drivers/cdrom/cdu31a.c
--- a/drivers/cdrom/cdu31a.c	Sun Apr 28 20:10:31 2002
+++ b/drivers/cdrom/cdu31a.c	Sun Apr 28 20:10:31 2002
@@ -3440,7 +3440,7 @@
 		    strcmp("CD-ROM CDU31A", drive_config.product_id) == 0;
 
 		blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR),
-			       DEVICE_REQUEST,
+			       do_cdu31a_request,
 			       &cdu31a_lock);
 		cdu31a_block_size = 1024;	/* 1kB default block size */
 		/* use 'mount -o block=2048' */
diff -Nru a/drivers/cdrom/cm206.c b/drivers/cdrom/cm206.c
--- a/drivers/cdrom/cm206.c	Sun Apr 28 20:10:35 2002
+++ b/drivers/cdrom/cm206.c	Sun Apr 28 20:10:35 2002
@@ -1500,7 +1500,7 @@
 		return -EIO;
 	}
 	devfs_plain_cdrom(&cm206_info, &cm206_bdops);
-	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST,
+	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_cm206_request,
 		       &cm206_lock);
 	blksize_size[MAJOR_NR] = cm206_blocksizes;
 	init_bh(CM206_BH, cm206_bh);
diff -Nru a/drivers/cdrom/gscd.c b/drivers/cdrom/gscd.c
--- a/drivers/cdrom/gscd.c	Sun Apr 28 20:10:30 2002
+++ b/drivers/cdrom/gscd.c	Sun Apr 28 20:10:30 2002
@@ -1020,7 +1020,7 @@
 	devfs_register(NULL, "gscd", DEVFS_FL_DEFAULT, MAJOR_NR, 0,
 		       S_IFBLK | S_IRUGO | S_IWUGO, &gscd_fops, NULL);
 
-	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST, &gscd_lock);
+	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_gscd_request, &gscd_lock);
 	blksize_size[MAJOR_NR] = gscd_blocksizes;
 
 	disk_state = 0;
diff -Nru a/drivers/cdrom/mcd.c b/drivers/cdrom/mcd.c
--- a/drivers/cdrom/mcd.c	Sun Apr 28 20:10:31 2002
+++ b/drivers/cdrom/mcd.c	Sun Apr 28 20:10:31 2002
@@ -1073,7 +1073,7 @@
 	}
 
 	blksize_size[MAJOR_NR] = mcd_blocksizes;
-	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST,
+	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_mcd_request,
 		       &mcd_spinlock);
 
 	/* check for card */
diff -Nru a/drivers/cdrom/mcdx.c b/drivers/cdrom/mcdx.c
--- a/drivers/cdrom/mcdx.c	Sun Apr 28 20:10:36 2002
+++ b/drivers/cdrom/mcdx.c	Sun Apr 28 20:10:36 2002
@@ -1182,7 +1182,7 @@
 		return 1;
 	}
 
-	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST,
+	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_mcdx_request,
 		       &mcdx_lock);
 	blksize_size[MAJOR_NR] = mcdx_blocksizes;
 
diff -Nru a/drivers/cdrom/optcd.c b/drivers/cdrom/optcd.c
--- a/drivers/cdrom/optcd.c	Sun Apr 28 20:10:32 2002
+++ b/drivers/cdrom/optcd.c	Sun Apr 28 20:10:32 2002
@@ -2060,7 +2060,7 @@
 	devfs_register (NULL, "optcd", DEVFS_FL_DEFAULT, MAJOR_NR, 0,
 			S_IFBLK | S_IRUGO | S_IWUGO, &opt_fops, NULL);
 	blksize_size[MAJOR_NR] = &blksize;
-	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST,
+	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_optcd_request,
 		       &optcd_lock);
 	request_region(optcd_port, 4, "optcd");
 	register_disk(NULL, mk_kdev(MAJOR_NR,0), 1, &opt_fops, 0);
diff -Nru a/drivers/cdrom/sbpcd.c b/drivers/cdrom/sbpcd.c
--- a/drivers/cdrom/sbpcd.c	Sun Apr 28 20:10:32 2002
+++ b/drivers/cdrom/sbpcd.c	Sun Apr 28 20:10:32 2002
@@ -5858,7 +5858,7 @@
 		goto init_done;
 #endif /* MODULE */
 	}
-	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
+	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DO_SBPCD_REQUEST, &sbpcd_lock);
 #ifdef DONT_MERGE_REQUESTS
 	(BLK_DEFAULT_QUEUE(MAJOR_NR))->back_merge_fn = dont_bh_merge_fn;
 	(BLK_DEFAULT_QUEUE(MAJOR_NR))->front_merge_fn = dont_bh_merge_fn;
diff -Nru a/drivers/cdrom/sjcd.c b/drivers/cdrom/sjcd.c
--- a/drivers/cdrom/sjcd.c	Sun Apr 28 20:10:37 2002
+++ b/drivers/cdrom/sjcd.c	Sun Apr 28 20:10:37 2002
@@ -1694,7 +1694,7 @@
 		return (-EIO);
 	}
 
-	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST,&sjcd_lock);
+	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_sjcd_request, &sjcd_lock);
 	register_disk(NULL, mk_kdev(MAJOR_NR, 0), 1, &sjcd_fops, 0);
 
 	if (check_region(sjcd_base, 4)) {
diff -Nru a/drivers/cdrom/sonycd535.c b/drivers/cdrom/sonycd535.c
--- a/drivers/cdrom/sonycd535.c	Sun Apr 28 20:10:35 2002
+++ b/drivers/cdrom/sonycd535.c	Sun Apr 28 20:10:35 2002
@@ -1596,7 +1596,9 @@
 							MAJOR_NR, CDU535_MESSAGE_NAME);
 					return -EIO;
 				}
-				blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST, &sonycd535_lock);
+				blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR),
+						do_cdu535_request,
+						&sonycd535_lock);
 				blksize_size[MAJOR_NR] = &sonycd535_block_size;
 
 				sony_toc = (struct s535_sony_toc *)
diff -Nru a/drivers/char/Config.help b/drivers/char/Config.help
--- a/drivers/char/Config.help	Sun Apr 28 20:10:35 2002
+++ b/drivers/char/Config.help	Sun Apr 28 20:10:35 2002
@@ -453,6 +453,17 @@
   The module will be called synclink.o.  If you want to do that, say M
   here.
 
+CONFIG_SYNCLINKMP
+  Enable support for the SyncLink Multiport (2 or 4 ports)
+  serial adapter, running asynchronous and HDLC communications up
+  to 2.048Mbps. Each ports is independently selectable for
+  RS-232, V.35, RS-449, RS-530, and X.21
+
+  This driver may be built as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called synclinkmp.o.  If you want to do that, say M
+  here.
+
 CONFIG_N_HDLC
   Allows synchronous HDLC communications with tty device drivers that
   support synchronous HDLC such as the Microgate SyncLink adapter.
diff -Nru a/drivers/char/Config.in b/drivers/char/Config.in
--- a/drivers/char/Config.in	Sun Apr 28 20:10:32 2002
+++ b/drivers/char/Config.in	Sun Apr 28 20:10:32 2002
@@ -46,6 +46,7 @@
       dep_tristate '  Multi-Tech multiport card support (EXPERIMENTAL)' CONFIG_ISI m
    fi
    tristate '  Microgate SyncLink card support' CONFIG_SYNCLINK
+   tristate '  SyncLink Multiport support' CONFIG_SYNCLINKMP
    tristate '  HDLC line discipline support' CONFIG_N_HDLC
    tristate '  SDL RISCom/8 card support' CONFIG_RISCOM8
    tristate '  Specialix IO8+ card support' CONFIG_SPECIALIX
diff -Nru a/drivers/char/Makefile b/drivers/char/Makefile
--- a/drivers/char/Makefile	Sun Apr 28 20:10:35 2002
+++ b/drivers/char/Makefile	Sun Apr 28 20:10:35 2002
@@ -143,6 +143,7 @@
 obj-$(CONFIG_ISI) += isicom.o
 obj-$(CONFIG_ESPSERIAL) += esp.o
 obj-$(CONFIG_SYNCLINK) += synclink.o
+obj-$(CONFIG_SYNCLINKMP) += synclinkmp.o
 obj-$(CONFIG_N_HDLC) += n_hdlc.o
 obj-$(CONFIG_SPECIALIX) += specialix.o
 obj-$(CONFIG_AMIGA_BUILTIN_SERIAL) += amiserial.o
diff -Nru a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h
--- a/drivers/char/agp/agp.h	Sun Apr 28 20:10:35 2002
+++ b/drivers/char/agp/agp.h	Sun Apr 28 20:10:35 2002
@@ -248,6 +248,9 @@
 #ifndef PCI_DEVICE_ID_AL_M1641_0
 #define PCI_DEVICE_ID_AL_M1641_0	0x1641
 #endif
+#ifndef PCI_DEVICE_ID_AL_M1644_0
+#define PCI_DEVICE_ID_AL_M1644_0	0x1644
+#endif
 #ifndef PCI_DEVICE_ID_AL_M1647_0
 #define PCI_DEVICE_ID_AL_M1647_0	0x1647
 #endif
diff -Nru a/drivers/char/agp/agpgart_be.c b/drivers/char/agp/agpgart_be.c
--- a/drivers/char/agp/agpgart_be.c	Sun Apr 28 20:10:31 2002
+++ b/drivers/char/agp/agpgart_be.c	Sun Apr 28 20:10:31 2002
@@ -3476,6 +3476,12 @@
 		"Ali",
 		"M1641",
 		ali_generic_setup },
+	{ PCI_DEVICE_ID_AL_M1644_0,
+		PCI_VENDOR_ID_AL,
+		ALI_M1644,
+		"Ali",
+		"M1644",
+		ali_generic_setup },
 	{ PCI_DEVICE_ID_AL_M1647_0,
 		PCI_VENDOR_ID_AL,
 		ALI_M1647,
@@ -3513,7 +3519,7 @@
 		PCI_VENDOR_ID_AMD,
 		AMD_762,
 		"AMD",
-		"AMD 760MP",
+		"760MP",
 		amd_irongate_setup },
 	{ 0,
 		PCI_VENDOR_ID_AMD,
@@ -3579,11 +3585,11 @@
 		"i845",
 		intel_845_setup },
 	{ PCI_DEVICE_ID_INTEL_850_0,
-	        PCI_VENDOR_ID_INTEL,
-	        INTEL_I850,
-	        "Intel",
-	        "i850",
-	        intel_850_setup },
+		PCI_VENDOR_ID_INTEL,
+		INTEL_I850,
+		"Intel",
+		"i850",
+intel_850_setup },
 	{ PCI_DEVICE_ID_INTEL_860_0,
 		PCI_VENDOR_ID_INTEL,
 		INTEL_I860,
diff -Nru a/drivers/char/drm/drm_agpsupport.h b/drivers/char/drm/drm_agpsupport.h
--- a/drivers/char/drm/drm_agpsupport.h	Sun Apr 28 20:10:30 2002
+++ b/drivers/char/drm/drm_agpsupport.h	Sun Apr 28 20:10:30 2002
@@ -303,6 +303,7 @@
 		case ALI_M1631: 	head->chipset = "ALi M1631";	 break;
 		case ALI_M1632: 	head->chipset = "ALi M1632";	 break;
 		case ALI_M1641: 	head->chipset = "ALi M1641";	 break;
+		case ALI_M1644: 	head->chipset = "ALi M1644";	 break;
 		case ALI_M1647: 	head->chipset = "ALi M1647";	 break;
 		case ALI_M1651: 	head->chipset = "ALi M1651";	 break;
 #endif
diff -Nru a/drivers/char/pcmcia/Config.help b/drivers/char/pcmcia/Config.help
--- a/drivers/char/pcmcia/Config.help	Sun Apr 28 20:10:38 2002
+++ b/drivers/char/pcmcia/Config.help	Sun Apr 28 20:10:38 2002
@@ -10,3 +10,12 @@
   a module, say M here and read <file:Documentation/modules.txt>.
   If unsure, say N.
 
+CONFIG_SYNCLINK_CS
+  Enable support for the SyncLink PC Card serial adapter, running
+  asynchronous and HDLC communications up to 512Kbps. The port is
+  selectable for RS-232, V.35, RS-449, RS-530, and X.21
+
+  This driver may be built as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called synclinkmp.o.  If you want to do that, say M
+  here.
diff -Nru a/drivers/char/pcmcia/Config.in b/drivers/char/pcmcia/Config.in
--- a/drivers/char/pcmcia/Config.in	Sun Apr 28 20:10:31 2002
+++ b/drivers/char/pcmcia/Config.in	Sun Apr 28 20:10:31 2002
@@ -10,5 +10,7 @@
    define_bool CONFIG_PCMCIA_CHRDEV y
 fi
 
+tristate 'SyncLink PC Card support' CONFIG_SYNCLINK_CS
+
 endmenu
 
diff -Nru a/drivers/char/pcmcia/Makefile b/drivers/char/pcmcia/Makefile
--- a/drivers/char/pcmcia/Makefile	Sun Apr 28 20:10:31 2002
+++ b/drivers/char/pcmcia/Makefile	Sun Apr 28 20:10:31 2002
@@ -16,5 +16,6 @@
 obj-		:=
 
 obj-$(CONFIG_PCMCIA_SERIAL_CS)	+= serial_cs.o
+obj-$(CONFIG_SYNCLINK_CS) += synclink_cs.o
 
 include $(TOPDIR)/Rules.make
diff -Nru a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/char/pcmcia/synclink_cs.c	Sun Apr 28 20:10:39 2002
@@ -0,0 +1,4562 @@
+/*
+ * linux/drivers/char/pcmcia/synclink_cs.c
+ *
+ * $Id: synclink_cs.c,v 4.2 2002/04/22 14:36:43 paulkf Exp $
+ *
+ * Device driver for Microgate SyncLink PC Card
+ * multiprotocol serial adapter.
+ *
+ * written by Paul Fulghum for Microgate Corporation
+ * paulkf@microgate.com
+ *
+ * Microgate and SyncLink are trademarks of Microgate Corporation
+ *
+ * This code is released under the GNU General Public License (GPL)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define VERSION(ver,rel,seq) (((ver)<<16) | ((rel)<<8) | (seq))
+#if defined(__i386__)
+#  define BREAKPOINT() asm("   int $3");
+#else
+#  define BREAKPOINT() { }
+#endif
+
+#define MAX_DEVICE_COUNT 4
+
+#include <linux/config.h>	
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+#include <asm/serial.h>
+#include <linux/delay.h>
+#include <linux/ioctl.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+#include <asm/bitops.h>
+#include <asm/types.h>
+#include <linux/termios.h>
+#include <linux/tqueue.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/bus_ops.h>
+
+#ifdef CONFIG_SYNCLINK_SYNCPPP_MODULE
+#define CONFIG_SYNCLINK_SYNCPPP 1
+#endif
+
+#ifdef CONFIG_SYNCLINK_SYNCPPP
+#include <net/syncppp.h>
+#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
+#define PUT_USER(error,value,addr) error = put_user(value,addr)
+#define COPY_TO_USER(error,dest,src,size) error = copy_to_user(dest,src,size) ? -EFAULT : 0
+
+#include <asm/uaccess.h>
+
+#include "linux/synclink.h"
+
+static MGSL_PARAMS default_params = {
+	MGSL_MODE_HDLC,			/* unsigned long mode */
+	0,				/* unsigned char loopback; */
+	HDLC_FLAG_UNDERRUN_ABORT15,	/* unsigned short flags; */
+	HDLC_ENCODING_NRZI_SPACE,	/* unsigned char encoding; */
+	0,				/* unsigned long clock_speed; */
+	0xff,				/* unsigned char addr_filter; */
+	HDLC_CRC_16_CCITT,		/* unsigned short crc_type; */
+	HDLC_PREAMBLE_LENGTH_8BITS,	/* unsigned char preamble_length; */
+	HDLC_PREAMBLE_PATTERN_NONE,	/* unsigned char preamble; */
+	9600,				/* unsigned long data_rate; */
+	8,				/* unsigned char data_bits; */
+	1,				/* unsigned char stop_bits; */
+	ASYNC_PARITY_NONE		/* unsigned char parity; */
+};
+
+typedef struct
+{
+	int count;
+	unsigned char status;
+	char data[1];
+} RXBUF;
+
+/* The queue of BH actions to be performed */
+
+#define BH_RECEIVE  1
+#define BH_TRANSMIT 2
+#define BH_STATUS   4
+
+#define IO_PIN_SHUTDOWN_LIMIT 100
+
+#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+
+struct _input_signal_events {
+	int	ri_up;	
+	int	ri_down;
+	int	dsr_up;
+	int	dsr_down;
+	int	dcd_up;
+	int	dcd_down;
+	int	cts_up;
+	int	cts_down;
+};
+
+
+/*
+ * Device instance data structure
+ */
+ 
+typedef struct _mgslpc_info {
+	void *if_ptr;	/* General purpose pointer (used by SPPP) */
+	int			magic;
+	int			flags;
+	int			count;		/* count of opens */
+	int			line;
+	unsigned short		close_delay;
+	unsigned short		closing_wait;	/* time to wait before closing */
+	
+	struct mgsl_icount	icount;
+	
+	struct termios		normal_termios;
+	struct termios		callout_termios;
+	
+	struct tty_struct 	*tty;
+	int			timeout;
+	int			x_char;		/* xon/xoff character */
+	int			blocked_open;	/* # of blocked opens */
+	long			session;	/* Session of opening process */
+	long			pgrp;		/* pgrp of opening process */
+	unsigned char		read_status_mask;
+	unsigned char		ignore_status_mask;	
+
+	unsigned char *tx_buf;
+	int            tx_put;
+	int            tx_get;
+	int            tx_count;
+
+	/* circular list of fixed length rx buffers */
+
+	unsigned char  *rx_buf;        /* memory allocated for all rx buffers */
+	int            rx_buf_total_size; /* size of memory allocated for rx buffers */
+	int            rx_put;         /* index of next empty rx buffer */
+	int            rx_get;         /* index of next full rx buffer */
+	int            rx_buf_size;    /* size in bytes of single rx buffer */
+	int            rx_buf_count;   /* total number of rx buffers */
+	int            rx_frame_count; /* number of full rx buffers */
+	
+	wait_queue_head_t	open_wait;
+	wait_queue_head_t	close_wait;
+	
+	wait_queue_head_t	status_event_wait_q;
+	wait_queue_head_t	event_wait_q;
+	struct timer_list	tx_timer;	/* HDLC transmit timeout timer */
+	struct _mgslpc_info	*next_device;	/* device list link */
+
+	unsigned short imra_value;
+	unsigned short imrb_value;
+	unsigned char  pim_value;
+
+	spinlock_t lock;
+	struct tq_struct task;		/* task structure for scheduling bh */
+
+	u32 max_frame_size;
+
+	u32 pending_bh;
+
+	int bh_running;
+	int bh_requested;
+	
+	int dcd_chkcount; /* check counts to prevent */
+	int cts_chkcount; /* too many IRQs if a signal */
+	int dsr_chkcount; /* is floating */
+	int ri_chkcount;
+
+	int rx_enabled;
+	int rx_overflow;
+
+	int tx_enabled;
+	int tx_active;
+	int tx_aborting;
+	u32 idle_mode;
+
+	int if_mode; /* serial interface selection (RS-232, v.35 etc) */
+
+	char device_name[25];		/* device instance name */
+
+	unsigned int io_base;	/* base I/O address of adapter */
+	unsigned int irq_level;
+	
+	MGSL_PARAMS params;		/* communications parameters */
+
+	unsigned char serial_signals;	/* current serial signal states */
+
+	char irq_occurred;		/* for diagnostics use */
+	char testing_irq;
+	unsigned int init_error;	/* startup error (DIAGS)	*/
+
+	char flag_buf[MAX_ASYNC_BUFFER_SIZE];
+	BOOLEAN drop_rts_on_tx_done;
+
+	struct	_input_signal_events	input_signal_events;
+
+	/* PCMCIA support */
+	dev_link_t	      link;
+	dev_node_t	      node;
+	int		      stop;
+	struct bus_operations *bus;
+
+	/* SPPP/Cisco HDLC device parts */
+	int netcount;
+	int dosyncppp;
+	spinlock_t netlock;
+#ifdef CONFIG_SYNCLINK_SYNCPPP
+	struct ppp_device pppdev;
+	char netname[10];
+	struct net_device *netdev;
+	struct net_device_stats netstats;
+	struct net_device netdevice;
+#endif
+} MGSLPC_INFO;
+
+#define MGSLPC_MAGIC 0x5402
+
+/*
+ * The size of the serial xmit buffer is 1 page, or 4096 bytes
+ */
+#define TXBUFSIZE 4096
+
+    
+#define CHA     0x00   /* channel A offset */
+#define CHB     0x40   /* channel B offset */
+    
+#define RXFIFO  0
+#define TXFIFO  0
+#define STAR    0x20
+#define CMDR    0x20
+#define RSTA    0x21
+#define PRE     0x21
+#define MODE    0x22
+#define TIMR    0x23
+#define XAD1    0x24
+#define XAD2    0x25
+#define RAH1    0x26
+#define RAH2    0x27
+#define DAFO    0x27
+#define RAL1    0x28
+#define RFC     0x28
+#define RHCR    0x29
+#define RAL2    0x29
+#define RBCL    0x2a
+#define XBCL    0x2a
+#define RBCH    0x2b
+#define XBCH    0x2b
+#define CCR0    0x2c
+#define CCR1    0x2d
+#define CCR2    0x2e
+#define CCR3    0x2f
+#define VSTR    0x34
+#define BGR     0x34
+#define RLCR    0x35
+#define AML     0x36
+#define AMH     0x37
+#define GIS     0x38
+#define IVA     0x38
+#define IPC     0x39
+#define ISR     0x3a
+#define IMR     0x3a
+#define PVR     0x3c
+#define PIS     0x3d
+#define PIM     0x3d
+#define PCR     0x3e
+#define CCR4    0x3f
+    
+// IMR/ISR
+    
+#define IRQ_BREAK_ON    BIT15   // rx break detected
+#define IRQ_DATAOVERRUN BIT14	// receive data overflow
+#define IRQ_ALLSENT     BIT13	// all sent
+#define IRQ_UNDERRUN    BIT12	// transmit data underrun
+#define IRQ_TIMER       BIT11	// timer interrupt
+#define IRQ_CTS         BIT10	// CTS status change
+#define IRQ_TXREPEAT    BIT9	// tx message repeat
+#define IRQ_TXFIFO      BIT8	// transmit pool ready
+#define IRQ_RXEOM       BIT7	// receive message end
+#define IRQ_EXITHUNT    BIT6	// receive frame start
+#define IRQ_RXTIME      BIT6    // rx char timeout
+#define IRQ_DCD         BIT2	// carrier detect status change
+#define IRQ_OVERRUN     BIT1	// receive frame overflow
+#define IRQ_RXFIFO      BIT0	// receive pool full
+    
+// STAR
+    
+#define XFW   BIT6		// transmit FIFO write enable
+#define CEC   BIT2		// command executing
+#define CTS   BIT1		// CTS state
+    
+#define PVR_DTR      BIT0
+#define PVR_DSR      BIT1
+#define PVR_RI       BIT2
+#define PVR_AUTOCTS  BIT3
+#define PVR_RS232    0x20   /* 0010b */
+#define PVR_V35      0xe0   /* 1110b */
+#define PVR_RS422    0x40   /* 0100b */
+    
+/* Register access functions */ 
+    
+#define write_reg(info, reg, val) outb((val),(info)->io_base + (reg))
+#define read_reg(info, reg) inb((info)->io_base + (reg))
+
+#define read_reg16(info, reg) inw((info)->io_base + (reg))  
+#define write_reg16(info, reg, val) outw((val), (info)->io_base + (reg))
+    
+#define set_reg_bits(info, reg, mask) \
+    write_reg(info, (reg), \
+		 (unsigned char) (read_reg(info, (reg)) | (mask)))  
+#define clear_reg_bits(info, reg, mask) \
+    write_reg(info, (reg), \
+		 (unsigned char) (read_reg(info, (reg)) & ~(mask)))  
+/*
+ * interrupt enable/disable routines
+ */ 
+static void irq_disable(MGSLPC_INFO *info, unsigned char channel, unsigned short mask) 
+{
+	if (channel == CHA) {
+		info->imra_value |= mask;
+		write_reg16(info, CHA + IMR, info->imra_value);
+	} else {
+		info->imrb_value |= mask;
+		write_reg16(info, CHB + IMR, info->imrb_value);
+	}
+}
+static void irq_enable(MGSLPC_INFO *info, unsigned char channel, unsigned short mask) 
+{
+	if (channel == CHA) {
+		info->imra_value &= ~mask;
+		write_reg16(info, CHA + IMR, info->imra_value);
+	} else {
+		info->imrb_value &= ~mask;
+		write_reg16(info, CHB + IMR, info->imrb_value);
+	}
+}
+
+#define port_irq_disable(info, mask) \
+  { info->pim_value |= (mask); write_reg(info, PIM, info->pim_value); }
+
+#define port_irq_enable(info, mask) \
+  { info->pim_value &= ~(mask); write_reg(info, PIM, info->pim_value); }
+
+static void rx_start(MGSLPC_INFO *info);
+static void rx_stop(MGSLPC_INFO *info);
+
+static void tx_start(MGSLPC_INFO *info);
+static void tx_stop(MGSLPC_INFO *info);
+static void tx_set_idle(MGSLPC_INFO *info);
+
+static void get_signals(MGSLPC_INFO *info);
+static void set_signals(MGSLPC_INFO *info);
+
+static void reset_device(MGSLPC_INFO *info);
+
+static void hdlc_mode(MGSLPC_INFO *info);
+static void async_mode(MGSLPC_INFO *info);
+
+static void tx_timeout(unsigned long context);
+
+static int ioctl_common(MGSLPC_INFO *info, unsigned int cmd, unsigned long arg);
+
+#ifdef CONFIG_SYNCLINK_SYNCPPP
+/* SPPP/HDLC stuff */
+static void mgslpc_sppp_init(MGSLPC_INFO *info);
+static void mgslpc_sppp_delete(MGSLPC_INFO *info);
+static int  mgslpc_sppp_open(struct net_device *d);
+static int  mgslpc_sppp_close(struct net_device *d);
+static void mgslpc_sppp_tx_timeout(struct net_device *d);
+static int  mgslpc_sppp_tx(struct sk_buff *skb, struct net_device *d);
+static void mgslpc_sppp_rx_done(MGSLPC_INFO *info, char *buf, int size);
+static void mgslpc_sppp_tx_done(MGSLPC_INFO *info);
+static int  mgslpc_sppp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
+struct net_device_stats *mgslpc_net_stats(struct net_device *dev);
+#endif
+
+static void trace_block(MGSLPC_INFO *info,const char* data, int count, int xmit);
+
+static BOOLEAN register_test(MGSLPC_INFO *info);
+static BOOLEAN irq_test(MGSLPC_INFO *info);
+static int adapter_test(MGSLPC_INFO *info);
+
+static int claim_resources(MGSLPC_INFO *info);
+static void release_resources(MGSLPC_INFO *info);
+static void mgslpc_add_device(MGSLPC_INFO *info);
+static void mgslpc_remove_device(MGSLPC_INFO *info);
+
+static int  rx_get_frame(MGSLPC_INFO *info);
+static void rx_reset_buffers(MGSLPC_INFO *info);
+static int  rx_alloc_buffers(MGSLPC_INFO *info);
+static void rx_free_buffers(MGSLPC_INFO *info);
+
+static void mgslpc_isr(int irq, void *dev_id, struct pt_regs * regs);
+
+/*
+ * Bottom half interrupt handlers
+ */
+static void bh_handler(void* Context);
+static void bh_transmit(MGSLPC_INFO *info);
+static void bh_status(MGSLPC_INFO *info);
+
+/*
+ * ioctl handlers
+ */
+static int set_modem_info(MGSLPC_INFO *info, unsigned int cmd,
+			  unsigned int *value);
+static int get_modem_info(MGSLPC_INFO *info, unsigned int *value);
+static int get_stats(MGSLPC_INFO *info, struct mgsl_icount *user_icount);
+static int get_params(MGSLPC_INFO *info, MGSL_PARAMS *user_params);
+static int set_params(MGSLPC_INFO *info, MGSL_PARAMS *new_params);
+static int get_txidle(MGSLPC_INFO *info, int*idle_mode);
+static int set_txidle(MGSLPC_INFO *info, int idle_mode);
+static int set_txenable(MGSLPC_INFO *info, int enable);
+static int tx_abort(MGSLPC_INFO *info);
+static int set_rxenable(MGSLPC_INFO *info, int enable);
+static int wait_events(MGSLPC_INFO *info, int *mask);
+
+#define jiffies_from_ms(a) ((((a) * HZ)/1000)+1)
+
+static MGSLPC_INFO *mgslpc_device_list = NULL;
+static int mgslpc_device_count = 0;
+
+/*
+ * Set this param to non-zero to load eax with the
+ * .text section address and breakpoint on module load.
+ * This is useful for use with gdb and add-symbol-file command.
+ */
+static int break_on_load=0;
+
+/*
+ * Driver major number, defaults to zero to get auto
+ * assigned major number. May be forced as module parameter.
+ */
+static int ttymajor=0;
+static int cuamajor=0;
+
+static int debug_level = 0;
+static int maxframe[MAX_DEVICE_COUNT] = {0,};
+
+/* The old way: bit map of interrupts to choose from */
+/* This means pick from 15, 14, 12, 11, 10, 9, 7, 5, 4, and 3 */
+static u_int irq_mask = 0xdeb8;
+
+/* Newer, simpler way of listing specific interrupts */
+static int irq_list[4] = { -1 };
+
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM(irq_list, "1-4i");
+
+MODULE_PARM(break_on_load,"i");
+MODULE_PARM(ttymajor,"i");
+MODULE_PARM(cuamajor,"i");
+MODULE_PARM(debug_level,"i");
+MODULE_PARM(maxframe,"1-" __MODULE_STRING(MAX_DEVICE_COUNT) "i");
+
+MODULE_LICENSE("GPL");
+
+static char *driver_name = "SyncLink PC Card driver";
+static char *driver_version = "$Revision: 4.2 $";
+
+static struct tty_driver serial_driver, callout_driver;
+static int serial_refcount;
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+static void mgslpc_change_params(MGSLPC_INFO *info);
+static void mgslpc_wait_until_sent(struct tty_struct *tty, int timeout);
+
+static struct tty_struct *serial_table[MAX_DEVICE_COUNT];
+static struct termios *serial_termios[MAX_DEVICE_COUNT];
+static struct termios *serial_termios_locked[MAX_DEVICE_COUNT];
+
+#ifndef MIN
+#define MIN(a,b)	((a) < (b) ? (a) : (b))
+#endif
+
+/* PCMCIA prototypes */
+
+static void mgslpc_config(dev_link_t *link);
+static void mgslpc_release(u_long arg);
+static int  mgslpc_event(event_t event, int priority,
+			 event_callback_args_t *args);
+static dev_link_t *mgslpc_attach(void);
+static void mgslpc_detach(dev_link_t *);
+
+static dev_info_t dev_info = "synclink_cs";
+static dev_link_t *dev_list = NULL;
+
+static void cs_error(client_handle_t handle, int func, int ret)
+{
+    error_info_t err = { func, ret };
+    CardServices(ReportError, handle, &err);
+}
+
+/*
+ * 1st function defined in .text section. Calling this function in
+ * init_module() followed by a breakpoint allows a remote debugger
+ * (gdb) to get the .text address for the add-symbol-file command.
+ * This allows remote debugging of dynamically loadable modules.
+ */
+static void* mgslpc_get_text_ptr(void);
+static void* mgslpc_get_text_ptr() {return mgslpc_get_text_ptr;}
+
+static dev_link_t *mgslpc_attach(void)
+{
+    MGSLPC_INFO *info;
+    dev_link_t *link;
+    client_reg_t client_reg;
+    int ret, i;
+    
+    if (debug_level >= DEBUG_LEVEL_INFO)
+	    printk("mgslpc_attach\n");
+	
+    info = (MGSLPC_INFO *)kmalloc(sizeof(MGSLPC_INFO), GFP_KERNEL);
+    if (!info) {
+	    printk("Error can't allocate device instance data\n");
+	    return NULL;
+    }
+
+    memset(info, 0, sizeof(MGSLPC_INFO));
+    info->magic = MGSLPC_MAGIC;
+    info->task.sync = 0;
+    info->task.routine = bh_handler;
+    info->task.data    = info;
+    info->max_frame_size = 4096;
+    info->close_delay = 5*HZ/10;
+    info->closing_wait = 30*HZ;
+    init_waitqueue_head(&info->open_wait);
+    init_waitqueue_head(&info->close_wait);
+    init_waitqueue_head(&info->status_event_wait_q);
+    init_waitqueue_head(&info->event_wait_q);
+    spin_lock_init(&info->lock);
+    spin_lock_init(&info->netlock);
+    memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS));
+    info->idle_mode = HDLC_TXIDLE_FLAGS;		
+    info->imra_value = 0xffff;
+    info->imrb_value = 0xffff;
+    info->pim_value = 0xff;
+
+    link = &info->link;
+    link->priv = info;
+    
+    /* Initialize the dev_link_t structure */
+    link->release.function = &mgslpc_release;
+    link->release.data = (u_long)link;
+
+    /* Interrupt setup */
+    link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+    link->irq.IRQInfo1   = IRQ_INFO2_VALID | IRQ_LEVEL_ID;
+    if (irq_list[0] == -1)
+	    link->irq.IRQInfo2 = irq_mask;
+    else
+	    for (i = 0; i < 4; i++)
+		    link->irq.IRQInfo2 |= 1 << irq_list[i];
+    link->irq.Handler = NULL;
+    
+    link->conf.Attributes = 0;
+    link->conf.Vcc = 50;
+    link->conf.IntType = INT_MEMORY_AND_IO;
+
+    /* Register with Card Services */
+    link->next = dev_list;
+    dev_list = link;
+
+    client_reg.dev_info = &dev_info;
+    client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
+    client_reg.EventMask =
+	    CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+	    CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+	    CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+    client_reg.event_handler = &mgslpc_event;
+    client_reg.Version = 0x0210;
+    client_reg.event_callback_args.client_data = link;
+
+    ret = CardServices(RegisterClient, &link->handle, &client_reg);
+    if (ret != CS_SUCCESS) {
+	    cs_error(link->handle, RegisterClient, ret);
+	    mgslpc_detach(link);
+	    return NULL;
+    }
+
+    mgslpc_add_device(info);
+
+    memset(serial_table,0,sizeof(struct tty_struct*)*MAX_DEVICE_COUNT);
+    memset(serial_termios,0,sizeof(struct termios*)*MAX_DEVICE_COUNT);
+    memset(serial_termios_locked,0,sizeof(struct termios*)*MAX_DEVICE_COUNT);
+		
+    info->callout_termios = callout_driver.init_termios;
+    info->normal_termios  = serial_driver.init_termios;
+
+    return link;
+}
+
+/* Card has been inserted.
+ */
+
+#define CS_CHECK(fn, args...) \
+while ((last_ret=CardServices(last_fn=(fn),args))!=0) goto cs_failed
+
+static void mgslpc_config(dev_link_t *link)
+{
+    client_handle_t handle = link->handle;
+    MGSLPC_INFO *info = link->priv;
+    tuple_t tuple;
+    cisparse_t parse;
+    int last_fn, last_ret;
+    u_char buf[64];
+    config_info_t conf;
+    cistpl_cftable_entry_t dflt = { 0 };
+    cistpl_cftable_entry_t *cfg;
+    
+    if (debug_level >= DEBUG_LEVEL_INFO)
+	    printk("mgslpc_config(0x%p)\n", link);
+
+    /* read CONFIG tuple to find its configuration registers */
+    tuple.DesiredTuple = CISTPL_CONFIG;
+    tuple.Attributes = 0;
+    tuple.TupleData = buf;
+    tuple.TupleDataMax = sizeof(buf);
+    tuple.TupleOffset = 0;
+    CS_CHECK(GetFirstTuple, handle, &tuple);
+    CS_CHECK(GetTupleData, handle, &tuple);
+    CS_CHECK(ParseTuple, handle, &tuple, &parse);
+    link->conf.ConfigBase = parse.config.base;
+    link->conf.Present = parse.config.rmask[0];
+    
+    /* Configure card */
+    link->state |= DEV_CONFIG;
+
+    /* Look up the current Vcc */
+    CS_CHECK(GetConfigurationInfo, handle, &conf);
+    link->conf.Vcc = conf.Vcc;
+
+    /* get CIS configuration entry */
+
+    tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+    CS_CHECK(GetFirstTuple, handle, &tuple);
+
+    cfg = &(parse.cftable_entry);
+    CS_CHECK(GetTupleData, handle, &tuple);
+    CS_CHECK(ParseTuple, handle, &tuple, &parse);
+
+    if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg;
+    if (cfg->index == 0)
+	    goto cs_failed;
+
+    link->conf.ConfigIndex = cfg->index;
+    link->conf.Attributes |= CONF_ENABLE_IRQ;
+	
+    /* IO window settings */
+    link->io.NumPorts1 = 0;
+    if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
+	    cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
+	    link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+	    if (!(io->flags & CISTPL_IO_8BIT))
+		    link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+	    if (!(io->flags & CISTPL_IO_16BIT))
+		    link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+	    link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
+	    link->io.BasePort1 = io->win[0].base;
+	    link->io.NumPorts1 = io->win[0].len;
+	    CS_CHECK(RequestIO, link->handle, &link->io);
+    }
+
+    link->conf.Attributes = CONF_ENABLE_IRQ;
+    link->conf.Vcc = 50;
+    link->conf.IntType = INT_MEMORY_AND_IO;
+    link->conf.ConfigIndex = 8;
+    link->conf.Present = PRESENT_OPTION;
+    
+    link->irq.Attributes |= IRQ_HANDLE_PRESENT;
+    link->irq.Handler     = mgslpc_isr;
+    link->irq.Instance    = info;
+    CS_CHECK(RequestIRQ, link->handle, &link->irq);
+
+    CS_CHECK(RequestConfiguration, link->handle, &link->conf);
+
+    info->io_base = link->io.BasePort1;
+    info->irq_level = link->irq.AssignedIRQ;
+
+    /* add to linked list of devices */
+    sprintf(info->node.dev_name, "mgslpc0");
+    info->node.major = info->node.minor = 0;
+    link->dev = &info->node;
+
+    printk(KERN_INFO "%s: index 0x%02x:",
+	   info->node.dev_name, link->conf.ConfigIndex);
+    if (link->conf.Attributes & CONF_ENABLE_IRQ)
+	    printk(", irq %d", link->irq.AssignedIRQ);
+    if (link->io.NumPorts1)
+	    printk(", io 0x%04x-0x%04x", link->io.BasePort1,
+		   link->io.BasePort1+link->io.NumPorts1-1);
+    printk("\n");
+    
+    link->state &= ~DEV_CONFIG_PENDING;
+    return;
+
+cs_failed:
+    cs_error(link->handle, last_fn, last_ret);
+    mgslpc_release((u_long)link);
+}
+
+/* Card has been removed.
+ * Unregister device and release PCMCIA configuration.
+ * If device is open, postpone until it is closed.
+ */
+static void mgslpc_release(u_long arg)
+{
+    dev_link_t *link = (dev_link_t *)arg;
+
+    if (debug_level >= DEBUG_LEVEL_INFO)
+	    printk("mgslpc_release(0x%p)\n", link);
+
+    if (link->open) {
+	    if (debug_level >= DEBUG_LEVEL_INFO)
+		    printk("synclink_cs: release postponed, '%s' still open\n",
+			   link->dev->dev_name);
+	    link->state |= DEV_STALE_CONFIG;
+	    return;
+    }
+
+    /* Unlink the device chain */
+    link->dev = NULL;
+    link->state &= ~DEV_CONFIG;
+
+    CardServices(ReleaseConfiguration, link->handle);
+    if (link->io.NumPorts1)
+	    CardServices(ReleaseIO, link->handle, &link->io);
+    if (link->irq.AssignedIRQ)
+	    CardServices(ReleaseIRQ, link->handle, &link->irq);
+    if (link->state & DEV_STALE_LINK)
+	    mgslpc_detach(link);
+}
+
+static void mgslpc_detach(dev_link_t *link)
+{
+    dev_link_t **linkp;
+
+    if (debug_level >= DEBUG_LEVEL_INFO)
+	    printk("mgslpc_detach(0x%p)\n", link);
+    
+    /* find device */
+    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+	    if (*linkp == link) break;
+    if (*linkp == NULL)
+	    return;
+
+    if (link->state & DEV_CONFIG) {
+	    /* device is configured/active, mark it so when
+	     * release() is called a proper detach() occurs.
+	     */
+	    if (debug_level >= DEBUG_LEVEL_INFO)
+		    printk(KERN_DEBUG "synclinkpc: detach postponed, '%s' "
+			   "still locked\n", link->dev->dev_name);
+	    link->state |= DEV_STALE_LINK;
+	    return;
+    }
+
+    /* Break the link with Card Services */
+    if (link->handle)
+	    CardServices(DeregisterClient, link->handle);
+    
+    /* Unlink device structure, and free it */
+    *linkp = link->next;
+    mgslpc_remove_device((MGSLPC_INFO *)link->priv);
+}
+
+static int mgslpc_event(event_t event, int priority,
+			event_callback_args_t *args)
+{
+    dev_link_t *link = args->client_data;
+    MGSLPC_INFO *info = link->priv;
+    
+    if (debug_level >= DEBUG_LEVEL_INFO)
+	    printk("mgslpc_event(0x%06x)\n", event);
+    
+    switch (event) {
+    case CS_EVENT_CARD_REMOVAL:
+	    link->state &= ~DEV_PRESENT;
+	    if (link->state & DEV_CONFIG) {
+		    ((MGSLPC_INFO *)link->priv)->stop = 1;
+		    mod_timer(&link->release, jiffies + HZ/20);
+	    }
+	    break;
+    case CS_EVENT_CARD_INSERTION:
+	    link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+	    info->bus = args->bus;
+	    mgslpc_config(link);
+	    break;
+    case CS_EVENT_PM_SUSPEND:
+	    link->state |= DEV_SUSPEND;
+	    /* Fall through... */
+    case CS_EVENT_RESET_PHYSICAL:
+	    /* Mark the device as stopped, to block IO until later */
+	    info->stop = 1;
+	    if (link->state & DEV_CONFIG)
+		    CardServices(ReleaseConfiguration, link->handle);
+	    break;
+    case CS_EVENT_PM_RESUME:
+	    link->state &= ~DEV_SUSPEND;
+	    /* Fall through... */
+    case CS_EVENT_CARD_RESET:
+	    if (link->state & DEV_CONFIG)
+		    CardServices(RequestConfiguration, link->handle, &link->conf);
+	    info->stop = 0;
+	    break;
+    }
+    return 0;
+}
+
+static inline int mgslpc_paranoia_check(MGSLPC_INFO *info,
+					kdev_t device, const char *routine)
+{
+#ifdef MGSLPC_PARANOIA_CHECK
+	static const char *badmagic =
+		"Warning: bad magic number for mgsl struct (%s) in %s\n";
+	static const char *badinfo =
+		"Warning: null mgslpc_info for (%s) in %s\n";
+
+	if (!info) {
+		printk(badinfo, kdevname(device), routine);
+		return 1;
+	}
+	if (info->magic != MGSLPC_MAGIC) {
+		printk(badmagic, kdevname(device), routine);
+		return 1;
+	}
+#endif
+	return 0;
+}
+
+
+#define CMD_RXFIFO      BIT7	// release current rx FIFO
+#define CMD_RXRESET     BIT6	// receiver reset
+#define CMD_RXFIFO_READ BIT5
+#define CMD_START_TIMER BIT4
+#define CMD_TXFIFO      BIT3	// release current tx FIFO
+#define CMD_TXEOM       BIT1	// transmit end message
+#define CMD_TXRESET     BIT0	// transmit reset
+
+static BOOLEAN wait_command_complete(MGSLPC_INFO *info, unsigned char channel) 
+{
+	int i = 0;
+	unsigned char status;
+	/* wait for command completion */ 
+	while ((status = read_reg(info, (unsigned char)(channel+STAR)) & BIT2)) {
+		udelay(1);
+		if (i++ == 1000)
+			return FALSE;
+	}
+	return TRUE;
+}
+
+static void issue_command(MGSLPC_INFO *info, unsigned char channel, unsigned char cmd) 
+{
+	wait_command_complete(info, channel);
+	write_reg(info, (unsigned char) (channel + CMDR), cmd);
+}
+
+static void tx_pause(struct tty_struct *tty)
+{
+	MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data;
+	unsigned long flags;
+	
+	if (mgslpc_paranoia_check(info, tty->device, "tx_pause"))
+		return;
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("tx_pause(%s)\n",info->device_name);	
+		
+	spin_lock_irqsave(&info->lock,flags);
+	if (info->tx_enabled)
+	 	tx_stop(info);
+	spin_unlock_irqrestore(&info->lock,flags);
+}
+
+static void tx_release(struct tty_struct *tty)
+{
+	MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data;
+	unsigned long flags;
+	
+	if (mgslpc_paranoia_check(info, tty->device, "tx_release"))
+		return;
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("tx_release(%s)\n",info->device_name);	
+		
+	spin_lock_irqsave(&info->lock,flags);
+	if (!info->tx_enabled)
+	 	tx_start(info);
+	spin_unlock_irqrestore(&info->lock,flags);
+}
+
+/* Return next bottom half action to perform.
+ * or 0 if nothing to do.
+ */
+int bh_action(MGSLPC_INFO *info)
+{
+	unsigned long flags;
+	int rc = 0;
+	
+	spin_lock_irqsave(&info->lock,flags);
+
+	if (info->pending_bh & BH_RECEIVE) {
+		info->pending_bh &= ~BH_RECEIVE;
+		rc = BH_RECEIVE;
+	} else if (info->pending_bh & BH_TRANSMIT) {
+		info->pending_bh &= ~BH_TRANSMIT;
+		rc = BH_TRANSMIT;
+	} else if (info->pending_bh & BH_STATUS) {
+		info->pending_bh &= ~BH_STATUS;
+		rc = BH_STATUS;
+	}
+
+	if (!rc) {
+		/* Mark BH routine as complete */
+		info->bh_running   = 0;
+		info->bh_requested = 0;
+	}
+	
+	spin_unlock_irqrestore(&info->lock,flags);
+	
+	return rc;
+}
+
+void bh_handler(void* Context)
+{
+	MGSLPC_INFO *info = (MGSLPC_INFO*)Context;
+	int action;
+
+	if (!info)
+		return;
+		
+	if (debug_level >= DEBUG_LEVEL_BH)
+		printk( "%s(%d):bh_handler(%s) entry\n",
+			__FILE__,__LINE__,info->device_name);
+	
+	info->bh_running = 1;
+
+	while((action = bh_action(info)) != 0) {
+	
+		/* Process work item */
+		if ( debug_level >= DEBUG_LEVEL_BH )
+			printk( "%s(%d):bh_handler() work item action=%d\n",
+				__FILE__,__LINE__,action);
+
+		switch (action) {
+		
+		case BH_RECEIVE:
+			while(rx_get_frame(info));
+			break;
+		case BH_TRANSMIT:
+			bh_transmit(info);
+			break;
+		case BH_STATUS:
+			bh_status(info);
+			break;
+		default:
+			/* unknown work item ID */
+			printk("Unknown work item ID=%08X!\n", action);
+			break;
+		}
+	}
+
+	if (debug_level >= DEBUG_LEVEL_BH)
+		printk( "%s(%d):bh_handler(%s) exit\n",
+			__FILE__,__LINE__,info->device_name);
+}
+
+void bh_transmit(MGSLPC_INFO *info)
+{
+	struct tty_struct *tty = info->tty;
+	if (debug_level >= DEBUG_LEVEL_BH)
+		printk("bh_transmit() entry on %s\n", info->device_name);
+
+	if (tty) {
+		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+		    tty->ldisc.write_wakeup) {
+			if ( debug_level >= DEBUG_LEVEL_BH )
+				printk( "%s(%d):calling ldisc.write_wakeup on %s\n",
+					__FILE__,__LINE__,info->device_name);
+			(tty->ldisc.write_wakeup)(tty);
+		}
+		wake_up_interruptible(&tty->write_wait);
+	}
+}
+
+void bh_status(MGSLPC_INFO *info)
+{
+	info->ri_chkcount = 0;
+	info->dsr_chkcount = 0;
+	info->dcd_chkcount = 0;
+	info->cts_chkcount = 0;
+}
+
+/* eom: non-zero = end of frame */ 
+void rx_ready_hdlc(MGSLPC_INFO *info, int eom) 
+{
+	unsigned char data[2];
+	unsigned char fifo_count, read_count, i;
+	RXBUF *buf = (RXBUF*)(info->rx_buf + (info->rx_put * info->rx_buf_size));
+
+	if (debug_level >= DEBUG_LEVEL_ISR)
+		printk("%s(%d):rx_ready_hdlc(eom=%d)\n",__FILE__,__LINE__,eom);
+	
+	if (!info->rx_enabled)
+		return;
+
+	if (info->rx_frame_count >= info->rx_buf_count) {
+		/* no more free buffers */
+		issue_command(info, CHA, CMD_RXRESET);
+		info->pending_bh |= BH_RECEIVE;
+		info->rx_overflow = 1;
+		info->icount.buf_overrun++;
+		return;
+	}
+
+	if (eom) {
+		/* end of frame, get FIFO count from RBCL register */ 
+		if (!(fifo_count = (unsigned char)(read_reg(info, CHA+RBCL) & 0x1f)))
+			fifo_count = 32;
+	} else
+		fifo_count = 32;
+	
+	do {
+		if (fifo_count == 1) {
+			read_count = 1;
+			data[0] = read_reg(info, CHA + RXFIFO);
+		} else {
+			read_count = 2;
+			*((unsigned short *) data) = read_reg16(info, CHA + RXFIFO);
+		}
+		fifo_count -= read_count;
+		if (!fifo_count && eom)
+			buf->status = data[--read_count];
+
+		for (i = 0; i < read_count; i++) {
+			if (buf->count >= info->max_frame_size) {
+				/* frame too large, reset receiver and reset current buffer */
+				issue_command(info, CHA, CMD_RXRESET);
+				buf->count = 0;
+				return;
+			}
+			*(buf->data + buf->count) = data[i];
+			buf->count++;
+		}
+	} while (fifo_count);
+
+	if (eom) {
+		info->pending_bh |= BH_RECEIVE;
+		info->rx_frame_count++;
+		info->rx_put++;
+		if (info->rx_put >= info->rx_buf_count)
+			info->rx_put = 0;
+	}
+	issue_command(info, CHA, CMD_RXFIFO);
+}
+
+void rx_ready_async(MGSLPC_INFO *info, int tcd) 
+{
+	unsigned char data, status;
+	int fifo_count;
+ 	struct tty_struct *tty = info->tty;
+ 	struct mgsl_icount *icount = &info->icount;
+
+	if (tcd) {
+		/* early termination, get FIFO count from RBCL register */ 
+		fifo_count = (unsigned char)(read_reg(info, CHA+RBCL) & 0x1f);
+
+		/* Zero fifo count could mean 0 or 32 bytes available.
+		 * If BIT5 of STAR is set then at least 1 byte is available.
+		 */
+		if (!fifo_count && (read_reg(info,CHA+STAR) & BIT5))
+			fifo_count = 32;
+	} else
+		fifo_count = 32;
+	
+	/* Flush received async data to receive data buffer. */ 
+	while (fifo_count) {
+		data   = read_reg(info, CHA + RXFIFO);
+		status = read_reg(info, CHA + RXFIFO);
+		fifo_count -= 2;
+
+		if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+			break;
+			
+		*tty->flip.char_buf_ptr = data;
+		icount->rx++;
+		
+		*tty->flip.flag_buf_ptr = 0;
+
+		// if no frameing/crc error then save data
+		// BIT7:parity error
+		// BIT6:framing error
+
+		if (status & (BIT7 + BIT6)) {
+			if (status & BIT7) 
+				icount->parity++;
+			else
+				icount->frame++;
+
+			/* discard char if tty control flags say so */
+			if (status & info->ignore_status_mask)
+				continue;
+				
+			status &= info->read_status_mask;
+
+			if (status & BIT7)
+				*tty->flip.flag_buf_ptr = TTY_PARITY;
+			else if (status & BIT6)
+				*tty->flip.flag_buf_ptr = TTY_FRAME;
+		}
+		
+		tty->flip.flag_buf_ptr++;
+		tty->flip.char_buf_ptr++;
+		tty->flip.count++;
+	}
+	issue_command(info, CHA, CMD_RXFIFO);
+
+	if (debug_level >= DEBUG_LEVEL_ISR) {
+		printk("%s(%d):rx_ready_async count=%d\n",
+			__FILE__,__LINE__,tty->flip.count);
+		printk("%s(%d):rx=%d brk=%d parity=%d frame=%d overrun=%d\n",
+			__FILE__,__LINE__,icount->rx,icount->brk,
+			icount->parity,icount->frame,icount->overrun);
+	}
+			
+	if (tty->flip.count)
+		tty_flip_buffer_push(tty);
+}
+
+
+void tx_done(MGSLPC_INFO *info) 
+{
+	if (!info->tx_active)
+		return;
+			
+	info->tx_active = 0;
+	info->tx_aborting = 0;
+
+	if (info->params.mode == MGSL_MODE_ASYNC)
+		return;
+
+	info->tx_count = info->tx_put = info->tx_get = 0;
+	del_timer(&info->tx_timer);	
+	
+	if (info->drop_rts_on_tx_done) {
+		get_signals(info);
+		if (info->serial_signals & SerialSignal_RTS) {
+			info->serial_signals &= ~SerialSignal_RTS;
+			set_signals(info);
+		}
+		info->drop_rts_on_tx_done = 0;
+	}
+
+#ifdef CONFIG_SYNCLINK_SYNCPPP	
+	if (info->netcount)
+		mgslpc_sppp_tx_done(info);
+	else 
+#endif
+	{
+		if (info->tty->stopped || info->tty->hw_stopped) {
+			tx_stop(info);
+			return;
+		}
+		info->pending_bh |= BH_TRANSMIT;
+	}
+}
+
+void tx_ready(MGSLPC_INFO *info) 
+{
+	unsigned char fifo_count = 32;
+	int c;
+
+	if (debug_level >= DEBUG_LEVEL_ISR)
+		printk("%s(%d):tx_ready(%s)\n", __FILE__,__LINE__,info->device_name);
+
+	if (info->params.mode == MGSL_MODE_HDLC) {
+		if (!info->tx_active)
+			return;
+	} else {
+		if (info->tty->stopped || info->tty->hw_stopped) {
+			tx_stop(info);
+			return;
+		}
+		if (!info->tx_count)
+			info->tx_active = 0;
+	}
+
+	if (!info->tx_count)
+		return;
+
+	while (info->tx_count && fifo_count) {
+		c = MIN(2, MIN(fifo_count, MIN(info->tx_count, TXBUFSIZE - info->tx_get)));
+		
+		if (c == 1) {
+			write_reg(info, CHA + TXFIFO, *(info->tx_buf + info->tx_get));
+		} else {
+			write_reg16(info, CHA + TXFIFO,
+					  *((unsigned short*)(info->tx_buf + info->tx_get)));
+		}
+		info->tx_count -= c;
+		info->tx_get = (info->tx_get + c) & (TXBUFSIZE - 1);
+		fifo_count -= c;
+	}
+
+	if (info->params.mode == MGSL_MODE_ASYNC) {
+		if (info->tx_count < WAKEUP_CHARS)
+			info->pending_bh |= BH_TRANSMIT;
+		issue_command(info, CHA, CMD_TXFIFO);
+	} else {
+		if (info->tx_count)
+			issue_command(info, CHA, CMD_TXFIFO);
+		else
+			issue_command(info, CHA, CMD_TXFIFO + CMD_TXEOM);
+	}
+}
+
+void cts_change(MGSLPC_INFO *info) 
+{
+	get_signals(info);
+	if ((info->cts_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT)
+		irq_disable(info, CHB, IRQ_CTS);
+	info->icount.cts++;
+	if (info->serial_signals & SerialSignal_CTS)
+		info->input_signal_events.cts_up++;
+	else
+		info->input_signal_events.cts_down++;
+	wake_up_interruptible(&info->status_event_wait_q);
+	wake_up_interruptible(&info->event_wait_q);
+
+	if (info->flags & ASYNC_CTS_FLOW) {
+		if (info->tty->hw_stopped) {
+			if (info->serial_signals & SerialSignal_CTS) {
+				if (debug_level >= DEBUG_LEVEL_ISR)
+					printk("CTS tx start...");
+				if (info->tty)
+					info->tty->hw_stopped = 0;
+				tx_start(info);
+				info->pending_bh |= BH_TRANSMIT;
+				return;
+			}
+		} else {
+			if (!(info->serial_signals & SerialSignal_CTS)) {
+				if (debug_level >= DEBUG_LEVEL_ISR)
+					printk("CTS tx stop...");
+				if (info->tty)
+					info->tty->hw_stopped = 1;
+				tx_stop(info);
+			}
+		}
+	}
+	info->pending_bh |= BH_STATUS;
+}
+
+void dcd_change(MGSLPC_INFO *info) 
+{
+	get_signals(info);
+	if ((info->dcd_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT)
+		irq_disable(info, CHB, IRQ_DCD);
+	info->icount.dcd++;
+	if (info->serial_signals & SerialSignal_DCD) {
+		info->input_signal_events.dcd_up++;
+#ifdef CONFIG_SYNCLINK_SYNCPPP	
+		if (info->netcount)
+			sppp_reopen(info->netdev);
+#endif
+	}
+	else
+		info->input_signal_events.dcd_down++;
+	wake_up_interruptible(&info->status_event_wait_q);
+	wake_up_interruptible(&info->event_wait_q);
+
+	if (info->flags & ASYNC_CHECK_CD) {
+		if (debug_level >= DEBUG_LEVEL_ISR)
+			printk("%s CD now %s...", info->device_name,
+			       (info->serial_signals & SerialSignal_DCD) ? "on" : "off");
+		if (info->serial_signals & SerialSignal_DCD)
+			wake_up_interruptible(&info->open_wait);
+		else if (!(info->flags & (ASYNC_CALLOUT_ACTIVE | ASYNC_CALLOUT_NOHUP))) {
+			if (debug_level >= DEBUG_LEVEL_ISR)
+				printk("doing serial hangup...");
+			if (info->tty)
+				tty_hangup(info->tty);
+		}
+	}
+	info->pending_bh |= BH_STATUS;
+}
+
+void dsr_change(MGSLPC_INFO *info) 
+{
+	get_signals(info);
+	if ((info->dsr_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT)
+		port_irq_disable(info, PVR_DSR);
+	info->icount.dsr++;
+	if (info->serial_signals & SerialSignal_DSR)
+		info->input_signal_events.dsr_up++;
+	else
+		info->input_signal_events.dsr_down++;
+	wake_up_interruptible(&info->status_event_wait_q);
+	wake_up_interruptible(&info->event_wait_q);
+	info->pending_bh |= BH_STATUS;
+}
+
+void ri_change(MGSLPC_INFO *info) 
+{
+	get_signals(info);
+	if ((info->ri_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT)
+		port_irq_disable(info, PVR_RI);
+	info->icount.rng++;
+	if (info->serial_signals & SerialSignal_RI)
+		info->input_signal_events.ri_up++;
+	else
+		info->input_signal_events.ri_down++;
+	wake_up_interruptible(&info->status_event_wait_q);
+	wake_up_interruptible(&info->event_wait_q);
+	info->pending_bh |= BH_STATUS;
+}
+
+/* Interrupt service routine entry point.
+ * 	
+ * Arguments:
+ * 
+ * irq     interrupt number that caused interrupt
+ * dev_id  device ID supplied during interrupt registration
+ * regs    interrupted processor context
+ */
+static void mgslpc_isr(int irq, void *dev_id, struct pt_regs * regs)
+{
+	MGSLPC_INFO * info = (MGSLPC_INFO *)dev_id;
+	unsigned short isr;
+	unsigned char gis, pis;
+	int count=0;
+
+	if (debug_level >= DEBUG_LEVEL_ISR)	
+		printk("mgslpc_isr(%d) entry.\n", irq);
+	if (!info)
+		return;
+		
+	if (!(info->link.state & DEV_CONFIG))
+		return;
+
+	spin_lock(&info->lock);
+
+	while ((gis = read_reg(info, CHA + GIS))) {
+		if (debug_level >= DEBUG_LEVEL_ISR)	
+			printk("mgslpc_isr %s gis=%04X\n", info->device_name,gis);
+
+		if ((gis & 0x70) || count > 1000) {
+			printk("synclink_cs:hardware failed or ejected\n");
+			break;
+		}
+		count++;
+
+		if (gis & (BIT1 + BIT0)) {
+			isr = read_reg16(info, CHB + ISR);
+			if (isr & IRQ_DCD)
+				dcd_change(info);
+			if (isr & IRQ_CTS)
+				cts_change(info);
+		}
+		if (gis & (BIT3 + BIT2))
+		{
+			isr = read_reg16(info, CHA + ISR);
+			if (isr & IRQ_TIMER) {
+				info->irq_occurred = 1;
+				irq_disable(info, CHA, IRQ_TIMER);
+			}
+
+			/* receive IRQs */ 
+			if (isr & IRQ_EXITHUNT) {
+				info->icount.exithunt++;
+				wake_up_interruptible(&info->event_wait_q);
+			}
+			if (isr & IRQ_BREAK_ON) {
+				info->icount.brk++;
+				if (info->flags & ASYNC_SAK)
+					do_SAK(info->tty);
+			}
+			if (isr & IRQ_RXTIME) {
+				issue_command(info, CHA, CMD_RXFIFO_READ);
+			}
+			if (isr & (IRQ_RXEOM + IRQ_RXFIFO)) {
+				if (info->params.mode == MGSL_MODE_HDLC)
+					rx_ready_hdlc(info, isr & IRQ_RXEOM); 
+				else
+					rx_ready_async(info, isr & IRQ_RXEOM);
+			}
+
+			/* transmit IRQs */ 
+			if (isr & IRQ_UNDERRUN) {
+				if (info->tx_aborting)
+					info->icount.txabort++;
+				else
+					info->icount.txunder++;
+				tx_done(info);
+			}
+			else if (isr & IRQ_ALLSENT) {
+				info->icount.txok++;
+				tx_done(info);
+			}
+			else if (isr & IRQ_TXFIFO)
+				tx_ready(info);
+		}
+		if (gis & BIT7) {
+			pis = read_reg(info, CHA + PIS);
+			if (pis & BIT1)
+				dsr_change(info);
+			if (pis & BIT2)
+				ri_change(info);
+		}
+	}
+	
+	/* Request bottom half processing if there's something 
+	 * for it to do and the bh is not already running
+	 */
+
+	if (info->pending_bh && !info->bh_running && !info->bh_requested) {
+		if ( debug_level >= DEBUG_LEVEL_ISR )	
+			printk("%s(%d):%s queueing bh task.\n",
+				__FILE__,__LINE__,info->device_name);
+		queue_task(&info->task, &tq_immediate);
+		mark_bh(IMMEDIATE_BH);
+		info->bh_requested = 1;
+	}
+
+	spin_unlock(&info->lock);
+	
+	if (debug_level >= DEBUG_LEVEL_ISR)	
+		printk("%s(%d):mgslpc_isr(%d)exit.\n",
+		       __FILE__,__LINE__,irq);
+}
+
+/* Initialize and start device.
+ */
+static int startup(MGSLPC_INFO * info)
+{
+	int retval = 0;
+	
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):startup(%s)\n",__FILE__,__LINE__,info->device_name);
+		
+	if (info->flags & ASYNC_INITIALIZED)
+		return 0;
+	
+	if (!info->tx_buf) {
+		/* allocate a page of memory for a transmit buffer */
+		info->tx_buf = (unsigned char *)get_free_page(GFP_KERNEL);
+		if (!info->tx_buf) {
+			printk(KERN_ERR"%s(%d):%s can't allocate transmit buffer\n",
+				__FILE__,__LINE__,info->device_name);
+			return -ENOMEM;
+		}
+	}
+
+	info->pending_bh = 0;
+	
+	init_timer(&info->tx_timer);
+	info->tx_timer.data = (unsigned long)info;
+	info->tx_timer.function = tx_timeout;
+
+	/* Allocate and claim adapter resources */
+	retval = claim_resources(info);
+	
+	/* perform existance check and diagnostics */
+	if ( !retval )
+		retval = adapter_test(info);
+		
+	if ( retval ) {
+  		if (capable(CAP_SYS_ADMIN) && info->tty)
+			set_bit(TTY_IO_ERROR, &info->tty->flags);
+		release_resources(info);
+  		return retval;
+  	}
+
+	/* program hardware for current parameters */
+	mgslpc_change_params(info);
+	
+	if (info->tty)
+		clear_bit(TTY_IO_ERROR, &info->tty->flags);
+
+	info->flags |= ASYNC_INITIALIZED;
+	
+	return 0;
+}
+
+/* Called by mgslpc_close() and mgslpc_hangup() to shutdown hardware
+ */
+static void shutdown(MGSLPC_INFO * info)
+{
+	unsigned long flags;
+	
+	if (!(info->flags & ASYNC_INITIALIZED))
+		return;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgslpc_shutdown(%s)\n",
+			 __FILE__,__LINE__, info->device_name );
+
+	/* clear status wait queue because status changes */
+	/* can't happen after shutting down the hardware */
+	wake_up_interruptible(&info->status_event_wait_q);
+	wake_up_interruptible(&info->event_wait_q);
+
+	del_timer(&info->tx_timer);	
+
+	if (info->tx_buf) {
+		free_page((unsigned long) info->tx_buf);
+		info->tx_buf = 0;
+	}
+
+	spin_lock_irqsave(&info->lock,flags);
+
+	rx_stop(info);
+	tx_stop(info);
+
+	/* TODO:disable interrupts instead of reset to preserve signal states */
+	reset_device(info);
+	
+ 	if (!info->tty || info->tty->termios->c_cflag & HUPCL) {
+ 		info->serial_signals &= ~(SerialSignal_DTR + SerialSignal_RTS);
+		set_signals(info);
+	}
+	
+	spin_unlock_irqrestore(&info->lock,flags);
+
+	release_resources(info);	
+	
+	if (info->tty)
+		set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+	info->flags &= ~ASYNC_INITIALIZED;
+}
+
+static void mgslpc_program_hw(MGSLPC_INFO *info)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&info->lock,flags);
+	
+	rx_stop(info);
+	tx_stop(info);
+	info->tx_count = info->tx_put = info->tx_get = 0;
+	
+	if (info->params.mode == MGSL_MODE_HDLC || info->netcount)
+		hdlc_mode(info);
+	else
+		async_mode(info);
+		
+	set_signals(info);
+	
+	info->dcd_chkcount = 0;
+	info->cts_chkcount = 0;
+	info->ri_chkcount = 0;
+	info->dsr_chkcount = 0;
+
+	irq_enable(info, CHB, IRQ_DCD | IRQ_CTS);
+	port_irq_enable(info, (unsigned char) PVR_DSR | PVR_RI);
+	get_signals(info);
+		
+	if (info->netcount || info->tty->termios->c_cflag & CREAD)
+		rx_start(info);
+		
+	spin_unlock_irqrestore(&info->lock,flags);
+}
+
+/* Reconfigure adapter based on new parameters
+ */
+static void mgslpc_change_params(MGSLPC_INFO *info)
+{
+	unsigned cflag;
+	int bits_per_char;
+
+	if (!info->tty || !info->tty->termios)
+		return;
+		
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgslpc_change_params(%s)\n",
+			 __FILE__,__LINE__, info->device_name );
+			 
+	cflag = info->tty->termios->c_cflag;
+
+	/* if B0 rate (hangup) specified then negate DTR and RTS */
+	/* otherwise assert DTR and RTS */
+ 	if (cflag & CBAUD)
+		info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR;
+	else
+		info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
+	
+	/* byte size and parity */
+	
+	switch (cflag & CSIZE) {
+	case CS5: info->params.data_bits = 5; break;
+	case CS6: info->params.data_bits = 6; break;
+	case CS7: info->params.data_bits = 7; break;
+	case CS8: info->params.data_bits = 8; break;
+	default:  info->params.data_bits = 7; break;
+	}
+	      
+	if (cflag & CSTOPB)
+		info->params.stop_bits = 2;
+	else
+		info->params.stop_bits = 1;
+
+	info->params.parity = ASYNC_PARITY_NONE;
+	if (cflag & PARENB) {
+		if (cflag & PARODD)
+			info->params.parity = ASYNC_PARITY_ODD;
+		else
+			info->params.parity = ASYNC_PARITY_EVEN;
+#ifdef CMSPAR
+		if (cflag & CMSPAR)
+			info->params.parity = ASYNC_PARITY_SPACE;
+#endif
+	}
+
+	/* calculate number of jiffies to transmit a full
+	 * FIFO (32 bytes) at specified data rate
+	 */
+	bits_per_char = info->params.data_bits + 
+			info->params.stop_bits + 1;
+
+	/* if port data rate is set to 460800 or less then
+	 * allow tty settings to override, otherwise keep the
+	 * current data rate.
+	 */
+	if (info->params.data_rate <= 460800) {
+		info->params.data_rate = tty_get_baud_rate(info->tty);
+	}
+	
+	if ( info->params.data_rate ) {
+		info->timeout = (32*HZ*bits_per_char) / 
+				info->params.data_rate;
+	}
+	info->timeout += HZ/50;		/* Add .02 seconds of slop */
+
+	if (cflag & CRTSCTS)
+		info->flags |= ASYNC_CTS_FLOW;
+	else
+		info->flags &= ~ASYNC_CTS_FLOW;
+		
+	if (cflag & CLOCAL)
+		info->flags &= ~ASYNC_CHECK_CD;
+	else
+		info->flags |= ASYNC_CHECK_CD;
+
+	/* process tty input control flags */
+	
+	info->read_status_mask = 0;
+	if (I_INPCK(info->tty))
+		info->read_status_mask |= BIT7 | BIT6;
+	if (I_IGNPAR(info->tty))
+		info->ignore_status_mask |= BIT7 | BIT6;
+
+	mgslpc_program_hw(info);
+}
+
+/* Add a character to the transmit buffer
+ */
+static void mgslpc_put_char(struct tty_struct *tty, unsigned char ch)
+{
+	MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data;
+	unsigned long flags;
+
+	if (debug_level >= DEBUG_LEVEL_INFO) {
+		printk( "%s(%d):mgslpc_put_char(%d) on %s\n",
+			__FILE__,__LINE__,ch,info->device_name);
+	}
+
+	if (mgslpc_paranoia_check(info, tty->device, "mgslpc_put_char"))
+		return;
+
+	if (!tty || !info->tx_buf)
+		return;
+
+	spin_lock_irqsave(&info->lock,flags);
+	
+	if (info->params.mode == MGSL_MODE_ASYNC || !info->tx_active) {
+		if (info->tx_count < TXBUFSIZE - 1) {
+			info->tx_buf[info->tx_put++] = ch;
+			info->tx_put &= TXBUFSIZE-1;
+			info->tx_count++;
+		}
+	}
+	
+	spin_unlock_irqrestore(&info->lock,flags);
+}
+
+/* Enable transmitter so remaining characters in the
+ * transmit buffer are sent.
+ */
+static void mgslpc_flush_chars(struct tty_struct *tty)
+{
+	MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data;
+	unsigned long flags;
+				
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk( "%s(%d):mgslpc_flush_chars() entry on %s tx_count=%d\n",
+			__FILE__,__LINE__,info->device_name,info->tx_count);
+	
+	if (mgslpc_paranoia_check(info, tty->device, "mgslpc_flush_chars"))
+		return;
+
+	if (info->tx_count <= 0 || tty->stopped ||
+	    tty->hw_stopped || !info->tx_buf)
+		return;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk( "%s(%d):mgslpc_flush_chars() entry on %s starting transmitter\n",
+			__FILE__,__LINE__,info->device_name);
+
+	spin_lock_irqsave(&info->lock,flags);
+	if (!info->tx_active)
+	 	tx_start(info);
+	spin_unlock_irqrestore(&info->lock,flags);
+}
+
+/* Send a block of data
+ * 	
+ * Arguments:
+ * 
+ * tty        pointer to tty information structure
+ * from_user  flag: 1 = from user process
+ * buf	      pointer to buffer containing send data
+ * count      size of send data in bytes
+ * 	
+ * Returns: number of characters written
+ */
+static int mgslpc_write(struct tty_struct * tty, int from_user,
+			const unsigned char *buf, int count)
+{
+	int c, ret = 0, err;
+	MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data;
+	unsigned long flags;
+	
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk( "%s(%d):mgslpc_write(%s) count=%d\n",
+			__FILE__,__LINE__,info->device_name,count);
+	
+	if (mgslpc_paranoia_check(info, tty->device, "mgslpc_write") ||
+	    !tty || !info->tx_buf)
+		goto cleanup;
+
+	if (info->params.mode == MGSL_MODE_HDLC) {
+		if (count > TXBUFSIZE) {
+			ret = -EIO;
+			goto cleanup;
+		}
+		if (info->tx_active)
+			goto cleanup;
+		else if (info->tx_count)
+			goto start;
+	}
+
+	for (;;) {
+		c = MIN(count,
+			MIN(TXBUFSIZE - info->tx_count - 1,
+			    TXBUFSIZE - info->tx_put));
+		if (c <= 0)
+			break;
+			
+		if (from_user) {
+			COPY_FROM_USER(err, info->tx_buf + info->tx_put, buf, c);
+			if (err) {
+				if (!ret)
+					ret = -EFAULT;
+				break;
+			}
+		} else
+			memcpy(info->tx_buf + info->tx_put, buf, c);
+
+		spin_lock_irqsave(&info->lock,flags);
+		info->tx_put = (info->tx_put + c) & (TXBUFSIZE-1);
+		info->tx_count += c;
+		spin_unlock_irqrestore(&info->lock,flags);
+
+		buf += c;
+		count -= c;
+		ret += c;
+	}
+start:
+ 	if (info->tx_count && !tty->stopped && !tty->hw_stopped) {
+		spin_lock_irqsave(&info->lock,flags);
+		if (!info->tx_active)
+		 	tx_start(info);
+		spin_unlock_irqrestore(&info->lock,flags);
+ 	}
+cleanup:	
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk( "%s(%d):mgslpc_write(%s) returning=%d\n",
+			__FILE__,__LINE__,info->device_name,ret);
+	return ret;
+}
+
+/* Return the count of free bytes in transmit buffer
+ */
+static int mgslpc_write_room(struct tty_struct *tty)
+{
+	MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data;
+	int ret;
+				
+	if (mgslpc_paranoia_check(info, tty->device, "mgslpc_write_room"))
+		return 0;
+
+	if (info->params.mode == MGSL_MODE_HDLC) {
+		/* HDLC (frame oriented) mode */
+		if (info->tx_active)
+			return 0;
+		else
+			return HDLC_MAX_FRAME_SIZE;
+	} else {
+		ret = TXBUFSIZE - info->tx_count - 1;
+		if (ret < 0)
+			ret = 0;
+	}
+	
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgslpc_write_room(%s)=%d\n",
+			 __FILE__,__LINE__, info->device_name, ret);
+	return ret;
+}
+
+/* Return the count of bytes in transmit buffer
+ */
+static int mgslpc_chars_in_buffer(struct tty_struct *tty)
+{
+	MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data;
+	int rc;
+		 
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgslpc_chars_in_buffer(%s)\n",
+			 __FILE__,__LINE__, info->device_name );
+			 
+	if (mgslpc_paranoia_check(info, tty->device, "mgslpc_chars_in_buffer"))
+		return 0;
+		
+	if (info->params.mode == MGSL_MODE_HDLC)
+		rc = info->tx_active ? info->max_frame_size : 0;
+	else
+		rc = info->tx_count;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgslpc_chars_in_buffer(%s)=%d\n",
+			 __FILE__,__LINE__, info->device_name, rc);
+			 
+	return rc;
+}
+
+/* Discard all data in the send buffer
+ */
+static void mgslpc_flush_buffer(struct tty_struct *tty)
+{
+	MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data;
+	unsigned long flags;
+	
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgslpc_flush_buffer(%s) entry\n",
+			 __FILE__,__LINE__, info->device_name );
+	
+	if (mgslpc_paranoia_check(info, tty->device, "mgslpc_flush_buffer"))
+		return;
+		
+	spin_lock_irqsave(&info->lock,flags); 
+	info->tx_count = info->tx_put = info->tx_get = 0;
+	del_timer(&info->tx_timer);	
+	spin_unlock_irqrestore(&info->lock,flags);
+	
+	wake_up_interruptible(&tty->write_wait);
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+	    tty->ldisc.write_wakeup)
+		(tty->ldisc.write_wakeup)(tty);
+}
+
+/* Send a high-priority XON/XOFF character
+ */
+static void mgslpc_send_xchar(struct tty_struct *tty, char ch)
+{
+	MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data;
+	unsigned long flags;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgslpc_send_xchar(%s,%d)\n",
+			 __FILE__,__LINE__, info->device_name, ch );
+			 
+	if (mgslpc_paranoia_check(info, tty->device, "mgslpc_send_xchar"))
+		return;
+
+	info->x_char = ch;
+	if (ch) {
+		spin_lock_irqsave(&info->lock,flags);
+		if (!info->tx_enabled)
+		 	tx_start(info);
+		spin_unlock_irqrestore(&info->lock,flags);
+	}
+}
+
+/* Signal remote device to throttle send data (our receive data)
+ */
+static void mgslpc_throttle(struct tty_struct * tty)
+{
+	MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data;
+	unsigned long flags;
+	
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgslpc_throttle(%s) entry\n",
+			 __FILE__,__LINE__, info->device_name );
+
+	if (mgslpc_paranoia_check(info, tty->device, "mgslpc_throttle"))
+		return;
+	
+	if (I_IXOFF(tty))
+		mgslpc_send_xchar(tty, STOP_CHAR(tty));
+ 
+ 	if (tty->termios->c_cflag & CRTSCTS) {
+		spin_lock_irqsave(&info->lock,flags);
+		info->serial_signals &= ~SerialSignal_RTS;
+	 	set_signals(info);
+		spin_unlock_irqrestore(&info->lock,flags);
+	}
+}
+
+/* Signal remote device to stop throttling send data (our receive data)
+ */
+static void mgslpc_unthrottle(struct tty_struct * tty)
+{
+	MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data;
+	unsigned long flags;
+	
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgslpc_unthrottle(%s) entry\n",
+			 __FILE__,__LINE__, info->device_name );
+
+	if (mgslpc_paranoia_check(info, tty->device, "mgslpc_unthrottle"))
+		return;
+	
+	if (I_IXOFF(tty)) {
+		if (info->x_char)
+			info->x_char = 0;
+		else
+			mgslpc_send_xchar(tty, START_CHAR(tty));
+	}
+	
+ 	if (tty->termios->c_cflag & CRTSCTS) {
+		spin_lock_irqsave(&info->lock,flags);
+		info->serial_signals |= SerialSignal_RTS;
+	 	set_signals(info);
+		spin_unlock_irqrestore(&info->lock,flags);
+	}
+}
+
+/* get the current serial statistics
+ */
+static int get_stats(MGSLPC_INFO * info, struct mgsl_icount *user_icount)
+{
+	int err;
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("get_params(%s)\n", info->device_name);
+	COPY_TO_USER(err,user_icount, &info->icount, sizeof(struct mgsl_icount));
+	if (err)
+		return -EFAULT;
+	return 0;
+}
+
+/* get the current serial parameters
+ */
+static int get_params(MGSLPC_INFO * info, MGSL_PARAMS *user_params)
+{
+	int err;
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("get_params(%s)\n", info->device_name);
+	COPY_TO_USER(err,user_params, &info->params, sizeof(MGSL_PARAMS));
+	if (err)
+		return -EFAULT;
+	return 0;
+}
+
+/* set the serial parameters
+ * 	
+ * Arguments:
+ * 
+ * 	info		pointer to device instance data
+ * 	new_params	user buffer containing new serial params
+ *
+ * Returns:	0 if success, otherwise error code
+ */
+static int set_params(MGSLPC_INFO * info, MGSL_PARAMS *new_params)
+{
+ 	unsigned long flags;
+	MGSL_PARAMS tmp_params;
+	int err;
+ 
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):set_params %s\n", __FILE__,__LINE__,
+			info->device_name );
+	COPY_FROM_USER(err,&tmp_params, new_params, sizeof(MGSL_PARAMS));
+	if (err) {
+		if ( debug_level >= DEBUG_LEVEL_INFO )
+			printk( "%s(%d):set_params(%s) user buffer copy failed\n",
+				__FILE__,__LINE__,info->device_name);
+		return -EFAULT;
+	}
+	
+	spin_lock_irqsave(&info->lock,flags);
+	memcpy(&info->params,&tmp_params,sizeof(MGSL_PARAMS));
+	spin_unlock_irqrestore(&info->lock,flags);
+	
+ 	mgslpc_change_params(info);
+	
+	return 0;
+}
+
+static int get_txidle(MGSLPC_INFO * info, int*idle_mode)
+{
+	int err;
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("get_txidle(%s)=%d\n", info->device_name, info->idle_mode);
+	COPY_TO_USER(err,idle_mode, &info->idle_mode, sizeof(int));
+	if (err)
+		return -EFAULT;
+	return 0;
+}
+
+static int set_txidle(MGSLPC_INFO * info, int idle_mode)
+{
+ 	unsigned long flags;
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("set_txidle(%s,%d)\n", info->device_name, idle_mode);
+	spin_lock_irqsave(&info->lock,flags);
+	info->idle_mode = idle_mode;
+	tx_set_idle(info);
+	spin_unlock_irqrestore(&info->lock,flags);
+	return 0;
+}
+
+static int get_interface(MGSLPC_INFO * info, int*if_mode)
+{
+	int err;
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("get_interface(%s)=%d\n", info->device_name, info->if_mode);
+	COPY_TO_USER(err,if_mode, &info->if_mode, sizeof(int));
+	if (err)
+		return -EFAULT;
+	return 0;
+}
+
+static int set_interface(MGSLPC_INFO * info, int if_mode)
+{
+ 	unsigned long flags;
+	unsigned char val;
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("set_interface(%s,%d)\n", info->device_name, if_mode);
+	spin_lock_irqsave(&info->lock,flags);
+	info->if_mode = if_mode;
+
+	val = read_reg(info, PVR) & 0x0f;
+	switch (info->if_mode)
+	{
+	case MGSL_INTERFACE_RS232: val |= PVR_RS232; break;
+	case MGSL_INTERFACE_V35:   val |= PVR_V35;   break;
+	case MGSL_INTERFACE_RS422: val |= PVR_RS422; break;
+	}
+	write_reg(info, PVR, val);
+
+	spin_unlock_irqrestore(&info->lock,flags);
+	return 0;
+}
+
+static int set_txenable(MGSLPC_INFO * info, int enable)
+{
+ 	unsigned long flags;
+ 
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("set_txenable(%s,%d)\n", info->device_name, enable);
+			
+	spin_lock_irqsave(&info->lock,flags);
+	if (enable) {
+		if (!info->tx_enabled)
+			tx_start(info);
+	} else {
+		if (info->tx_enabled)
+			tx_stop(info);
+	}
+	spin_unlock_irqrestore(&info->lock,flags);
+	return 0;
+}
+
+static int tx_abort(MGSLPC_INFO * info)
+{
+ 	unsigned long flags;
+ 
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("tx_abort(%s)\n", info->device_name);
+			
+	spin_lock_irqsave(&info->lock,flags);
+	if (info->tx_active && info->tx_count &&
+	    info->params.mode == MGSL_MODE_HDLC) {
+		/* clear data count so FIFO is not filled on next IRQ.
+		 * This results in underrun and abort transmission.
+		 */
+		info->tx_count = info->tx_put = info->tx_get = 0;
+		info->tx_aborting = TRUE;
+	}
+	spin_unlock_irqrestore(&info->lock,flags);
+	return 0;
+}
+
+static int set_rxenable(MGSLPC_INFO * info, int enable)
+{
+ 	unsigned long flags;
+ 
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("set_rxenable(%s,%d)\n", info->device_name, enable);
+			
+	spin_lock_irqsave(&info->lock,flags);
+	if (enable) {
+		if (!info->rx_enabled)
+			rx_start(info);
+	} else {
+		if (info->rx_enabled)
+			rx_stop(info);
+	}
+	spin_unlock_irqrestore(&info->lock,flags);
+	return 0;
+}
+
+/* wait for specified event to occur
+ * 	
+ * Arguments:	 	info	pointer to device instance data
+ * 			mask	pointer to bitmask of events to wait for
+ * Return Value:	0 	if successful and bit mask updated with
+ *				of events triggerred,
+ * 			otherwise error code
+ */
+static int wait_events(MGSLPC_INFO * info, int * mask_ptr)
+{
+ 	unsigned long flags;
+	int s;
+	int rc=0;
+	struct mgsl_icount cprev, cnow;
+	int events;
+	int mask;
+	struct	_input_signal_events oldsigs, newsigs;
+	DECLARE_WAITQUEUE(wait, current);
+
+	COPY_FROM_USER(rc,&mask, mask_ptr, sizeof(int));
+	if (rc)
+		return  -EFAULT;
+		 
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("wait_events(%s,%d)\n", info->device_name, mask);
+
+	spin_lock_irqsave(&info->lock,flags);
+
+	/* return immediately if state matches requested events */
+	get_signals(info);
+	s = info->serial_signals;
+	events = mask &
+		( ((s & SerialSignal_DSR) ? MgslEvent_DsrActive:MgslEvent_DsrInactive) +
+ 		  ((s & SerialSignal_DCD) ? MgslEvent_DcdActive:MgslEvent_DcdInactive) +
+		  ((s & SerialSignal_CTS) ? MgslEvent_CtsActive:MgslEvent_CtsInactive) +
+		  ((s & SerialSignal_RI)  ? MgslEvent_RiActive :MgslEvent_RiInactive) );
+	if (events) {
+		spin_unlock_irqrestore(&info->lock,flags);
+		goto exit;
+	}
+
+	/* save current irq counts */
+	cprev = info->icount;
+	oldsigs = info->input_signal_events;
+	
+	if ((info->params.mode == MGSL_MODE_HDLC) &&
+	    (mask & MgslEvent_ExitHuntMode))
+		irq_enable(info, CHA, IRQ_EXITHUNT);
+	
+	set_current_state(TASK_INTERRUPTIBLE);
+	add_wait_queue(&info->event_wait_q, &wait);
+	
+	spin_unlock_irqrestore(&info->lock,flags);
+	
+	
+	for(;;) {
+		schedule();
+		if (signal_pending(current)) {
+			rc = -ERESTARTSYS;
+			break;
+		}
+			
+		/* get current irq counts */
+		spin_lock_irqsave(&info->lock,flags);
+		cnow = info->icount;
+		newsigs = info->input_signal_events;
+		set_current_state(TASK_INTERRUPTIBLE);
+		spin_unlock_irqrestore(&info->lock,flags);
+
+		/* if no change, wait aborted for some reason */
+		if (newsigs.dsr_up   == oldsigs.dsr_up   &&
+		    newsigs.dsr_down == oldsigs.dsr_down &&
+		    newsigs.dcd_up   == oldsigs.dcd_up   &&
+		    newsigs.dcd_down == oldsigs.dcd_down &&
+		    newsigs.cts_up   == oldsigs.cts_up   &&
+		    newsigs.cts_down == oldsigs.cts_down &&
+		    newsigs.ri_up    == oldsigs.ri_up    &&
+		    newsigs.ri_down  == oldsigs.ri_down  &&
+		    cnow.exithunt    == cprev.exithunt   &&
+		    cnow.rxidle      == cprev.rxidle) {
+			rc = -EIO;
+			break;
+		}
+
+		events = mask &
+			( (newsigs.dsr_up   != oldsigs.dsr_up   ? MgslEvent_DsrActive:0)   +
+			  (newsigs.dsr_down != oldsigs.dsr_down ? MgslEvent_DsrInactive:0) +
+			  (newsigs.dcd_up   != oldsigs.dcd_up   ? MgslEvent_DcdActive:0)   +
+			  (newsigs.dcd_down != oldsigs.dcd_down ? MgslEvent_DcdInactive:0) +
+			  (newsigs.cts_up   != oldsigs.cts_up   ? MgslEvent_CtsActive:0)   +
+			  (newsigs.cts_down != oldsigs.cts_down ? MgslEvent_CtsInactive:0) +
+			  (newsigs.ri_up    != oldsigs.ri_up    ? MgslEvent_RiActive:0)    +
+			  (newsigs.ri_down  != oldsigs.ri_down  ? MgslEvent_RiInactive:0)  +
+			  (cnow.exithunt    != cprev.exithunt   ? MgslEvent_ExitHuntMode:0) +
+			  (cnow.rxidle      != cprev.rxidle     ? MgslEvent_IdleReceived:0) );
+		if (events)
+			break;
+		
+		cprev = cnow;
+		oldsigs = newsigs;
+	}
+	
+	remove_wait_queue(&info->event_wait_q, &wait);
+	set_current_state(TASK_RUNNING);
+
+	if (mask & MgslEvent_ExitHuntMode) {
+		spin_lock_irqsave(&info->lock,flags);
+		if (!waitqueue_active(&info->event_wait_q))
+			irq_disable(info, CHA, IRQ_EXITHUNT);
+		spin_unlock_irqrestore(&info->lock,flags);
+	}
+exit:
+	if (rc == 0)
+		PUT_USER(rc, events, mask_ptr);
+	return rc;
+}
+
+static int modem_input_wait(MGSLPC_INFO *info,int arg)
+{
+ 	unsigned long flags;
+	int rc;
+	struct mgsl_icount cprev, cnow;
+	DECLARE_WAITQUEUE(wait, current);
+
+	/* save current irq counts */
+	spin_lock_irqsave(&info->lock,flags);
+	cprev = info->icount;
+	add_wait_queue(&info->status_event_wait_q, &wait);
+	set_current_state(TASK_INTERRUPTIBLE);
+	spin_unlock_irqrestore(&info->lock,flags);
+
+	for(;;) {
+		schedule();
+		if (signal_pending(current)) {
+			rc = -ERESTARTSYS;
+			break;
+		}
+
+		/* get new irq counts */
+		spin_lock_irqsave(&info->lock,flags);
+		cnow = info->icount;
+		set_current_state(TASK_INTERRUPTIBLE);
+		spin_unlock_irqrestore(&info->lock,flags);
+
+		/* if no change, wait aborted for some reason */
+		if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
+		    cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) {
+			rc = -EIO;
+			break;
+		}
+
+		/* check for change in caller specified modem input */
+		if ((arg & TIOCM_RNG && cnow.rng != cprev.rng) ||
+		    (arg & TIOCM_DSR && cnow.dsr != cprev.dsr) ||
+		    (arg & TIOCM_CD  && cnow.dcd != cprev.dcd) ||
+		    (arg & TIOCM_CTS && cnow.cts != cprev.cts)) {
+			rc = 0;
+			break;
+		}
+
+		cprev = cnow;
+	}
+	remove_wait_queue(&info->status_event_wait_q, &wait);
+	set_current_state(TASK_RUNNING);
+	return rc;
+}
+
+/* Return state of the serial control/status signals
+ * 	
+ * Arguments:	 	info	pointer to device instance data
+ * 			value	pointer to int to hold returned info
+ * 	
+ * Return Value:	0 if success, otherwise error code
+ */
+static int get_modem_info(MGSLPC_INFO * info, unsigned int *value)
+{
+	unsigned int result;
+ 	unsigned long flags;
+	int err;
+ 
+	spin_lock_irqsave(&info->lock,flags);
+ 	get_signals(info);
+	spin_unlock_irqrestore(&info->lock,flags);
+
+	result = ((info->serial_signals & SerialSignal_RTS) ? TIOCM_RTS:0) |
+		 ((info->serial_signals & SerialSignal_DTR) ? TIOCM_DTR:0) |
+		 ((info->serial_signals & SerialSignal_DCD) ? TIOCM_CAR:0) |
+		 ((info->serial_signals & SerialSignal_RI)  ? TIOCM_RNG:0) |
+		 ((info->serial_signals & SerialSignal_DSR) ? TIOCM_DSR:0) |
+		 ((info->serial_signals & SerialSignal_CTS) ? TIOCM_CTS:0);
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("mgslpc_get_modem_info %s value=%08X\n", info->device_name, result);
+			
+	PUT_USER(err,result,value);
+	return err ? -EFAULT : 0;
+}
+
+/* Set the state of the modem control signals (DTR/RTS)
+ * 	
+ * Arguments:
+ * 
+ * 	info	pointer to device instance data
+ * 	cmd	signal command: TIOCMBIS = set bit TIOCMBIC = clear bit
+ *		TIOCMSET = set/clear signal values
+ * 	value	bit mask for command
+ * 	
+ * Return Value:	0 if success, otherwise error code
+ */
+static int set_modem_info(MGSLPC_INFO * info, unsigned int cmd,
+			  unsigned int *value)
+{
+ 	int error;
+ 	unsigned int arg;
+ 	unsigned long flags;
+ 
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("mgslpc_set_modem_info %s\n", info->device_name);
+			
+ 	GET_USER(error,arg,value);
+ 	if (error)
+ 		return error;
+		
+ 	switch (cmd) {
+ 	case TIOCMBIS: 
+ 		if (arg & TIOCM_RTS)
+ 			info->serial_signals |= SerialSignal_RTS;
+ 		if (arg & TIOCM_DTR)
+ 			info->serial_signals |= SerialSignal_DTR;
+ 		break;
+ 	case TIOCMBIC:
+ 		if (arg & TIOCM_RTS)
+ 			info->serial_signals &= ~SerialSignal_RTS;
+ 		if (arg & TIOCM_DTR)
+ 			info->serial_signals &= ~SerialSignal_DTR;
+ 		break;
+ 	case TIOCMSET:
+ 		if (arg & TIOCM_RTS)
+ 			info->serial_signals |= SerialSignal_RTS;
+		else
+ 			info->serial_signals &= ~SerialSignal_RTS;
+		
+ 		if (arg & TIOCM_DTR)
+ 			info->serial_signals |= SerialSignal_DTR;
+		else
+ 			info->serial_signals &= ~SerialSignal_DTR;
+ 		break;
+ 	default:
+ 		return -EINVAL;
+ 	}
+	
+	spin_lock_irqsave(&info->lock,flags);
+ 	set_signals(info);
+	spin_unlock_irqrestore(&info->lock,flags);
+	
+	return 0;
+}
+
+/* Set or clear transmit break condition
+ *
+ * Arguments:		tty		pointer to tty instance data
+ *			break_state	-1=set break condition, 0=clear
+ */
+static void mgslpc_break(struct tty_struct *tty, int break_state)
+{
+	MGSLPC_INFO * info = (MGSLPC_INFO *)tty->driver_data;
+	unsigned long flags;
+	
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgslpc_break(%s,%d)\n",
+			 __FILE__,__LINE__, info->device_name, break_state);
+			 
+	if (mgslpc_paranoia_check(info, tty->device, "mgslpc_break"))
+		return;
+
+	spin_lock_irqsave(&info->lock,flags);
+ 	if (break_state == -1)
+		set_reg_bits(info, CHA+DAFO, BIT6);
+	else 
+		clear_reg_bits(info, CHA+DAFO, BIT6);
+	spin_unlock_irqrestore(&info->lock,flags);
+}
+
+/* Service an IOCTL request
+ * 	
+ * Arguments:
+ * 
+ * 	tty	pointer to tty instance data
+ * 	file	pointer to associated file object for device
+ * 	cmd	IOCTL command code
+ * 	arg	command argument/context
+ * 	
+ * Return Value:	0 if success, otherwise error code
+ */
+static int mgslpc_ioctl(struct tty_struct *tty, struct file * file,
+			unsigned int cmd, unsigned long arg)
+{
+	MGSLPC_INFO * info = (MGSLPC_INFO *)tty->driver_data;
+	
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgslpc_ioctl %s cmd=%08X\n", __FILE__,__LINE__,
+			info->device_name, cmd );
+	
+	if (mgslpc_paranoia_check(info, tty->device, "mgslpc_ioctl"))
+		return -ENODEV;
+
+	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+	    (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
+		if (tty->flags & (1 << TTY_IO_ERROR))
+		    return -EIO;
+	}
+
+	return ioctl_common(info, cmd, arg);
+}
+
+int ioctl_common(MGSLPC_INFO *info, unsigned int cmd, unsigned long arg)
+{
+	int error;
+	struct mgsl_icount cnow;	/* kernel counter temps */
+	struct serial_icounter_struct *p_cuser;	/* user space */
+	unsigned long flags;
+	
+	switch (cmd) {
+	case TIOCMGET:
+		return get_modem_info(info, (unsigned int *) arg);
+	case TIOCMBIS:
+	case TIOCMBIC:
+	case TIOCMSET:
+		return set_modem_info(info, cmd, (unsigned int *) arg);
+	case MGSL_IOCGPARAMS:
+		return get_params(info,(MGSL_PARAMS *)arg);
+	case MGSL_IOCSPARAMS:
+		return set_params(info,(MGSL_PARAMS *)arg);
+	case MGSL_IOCGTXIDLE:
+		return get_txidle(info,(int*)arg);
+	case MGSL_IOCSTXIDLE:
+		return set_txidle(info,(int)arg);
+	case MGSL_IOCGIF:
+		return get_interface(info,(int*)arg);
+	case MGSL_IOCSIF:
+		return set_interface(info,(int)arg);
+	case MGSL_IOCTXENABLE:
+		return set_txenable(info,(int)arg);
+	case MGSL_IOCRXENABLE:
+		return set_rxenable(info,(int)arg);
+	case MGSL_IOCTXABORT:
+		return tx_abort(info);
+	case MGSL_IOCGSTATS:
+		return get_stats(info,(struct mgsl_icount*)arg);
+	case MGSL_IOCWAITEVENT:
+		return wait_events(info,(int*)arg);
+	case MGSL_IOCCLRMODCOUNT:
+		while(MOD_IN_USE)
+			MOD_DEC_USE_COUNT;
+		return 0;
+	case TIOCMIWAIT:
+		return modem_input_wait(info,(int)arg);
+	case TIOCGICOUNT:
+		spin_lock_irqsave(&info->lock,flags);
+		cnow = info->icount;
+		spin_unlock_irqrestore(&info->lock,flags);
+		p_cuser = (struct serial_icounter_struct *) arg;
+		PUT_USER(error,cnow.cts, &p_cuser->cts);
+		if (error) return error;
+		PUT_USER(error,cnow.dsr, &p_cuser->dsr);
+		if (error) return error;
+		PUT_USER(error,cnow.rng, &p_cuser->rng);
+		if (error) return error;
+		PUT_USER(error,cnow.dcd, &p_cuser->dcd);
+		if (error) return error;
+		PUT_USER(error,cnow.rx, &p_cuser->rx);
+		if (error) return error;
+		PUT_USER(error,cnow.tx, &p_cuser->tx);
+		if (error) return error;
+		PUT_USER(error,cnow.frame, &p_cuser->frame);
+		if (error) return error;
+		PUT_USER(error,cnow.overrun, &p_cuser->overrun);
+		if (error) return error;
+		PUT_USER(error,cnow.parity, &p_cuser->parity);
+		if (error) return error;
+		PUT_USER(error,cnow.brk, &p_cuser->brk);
+		if (error) return error;
+		PUT_USER(error,cnow.buf_overrun, &p_cuser->buf_overrun);
+		if (error) return error;
+		return 0;
+	default:
+		return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+/* Set new termios settings
+ * 	
+ * Arguments:
+ * 
+ * 	tty		pointer to tty structure
+ * 	termios		pointer to buffer to hold returned old termios
+ */
+static void mgslpc_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+	MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data;
+	unsigned long flags;
+	
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgslpc_set_termios %s\n", __FILE__,__LINE__,
+			tty->driver.name );
+	
+	/* just return if nothing has changed */
+	if ((tty->termios->c_cflag == old_termios->c_cflag)
+	    && (RELEVANT_IFLAG(tty->termios->c_iflag) 
+		== RELEVANT_IFLAG(old_termios->c_iflag)))
+	  return;
+
+	mgslpc_change_params(info);
+
+	/* Handle transition to B0 status */
+	if (old_termios->c_cflag & CBAUD &&
+	    !(tty->termios->c_cflag & CBAUD)) {
+		info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
+		spin_lock_irqsave(&info->lock,flags);
+	 	set_signals(info);
+		spin_unlock_irqrestore(&info->lock,flags);
+	}
+	
+	/* Handle transition away from B0 status */
+	if (!(old_termios->c_cflag & CBAUD) &&
+	    tty->termios->c_cflag & CBAUD) {
+		info->serial_signals |= SerialSignal_DTR;
+ 		if (!(tty->termios->c_cflag & CRTSCTS) || 
+ 		    !test_bit(TTY_THROTTLED, &tty->flags)) {
+			info->serial_signals |= SerialSignal_RTS;
+ 		}
+		spin_lock_irqsave(&info->lock,flags);
+	 	set_signals(info);
+		spin_unlock_irqrestore(&info->lock,flags);
+	}
+	
+	/* Handle turning off CRTSCTS */
+	if (old_termios->c_cflag & CRTSCTS &&
+	    !(tty->termios->c_cflag & CRTSCTS)) {
+		tty->hw_stopped = 0;
+		tx_release(tty);
+	}
+}
+
+static void mgslpc_close(struct tty_struct *tty, struct file * filp)
+{
+	MGSLPC_INFO * info = (MGSLPC_INFO *)tty->driver_data;
+
+	if (!info || mgslpc_paranoia_check(info, tty->device, "mgslpc_close"))
+		return;
+	
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgslpc_close(%s) entry, count=%d\n",
+			 __FILE__,__LINE__, info->device_name, info->count);
+			 
+	if (!info->count || tty_hung_up_p(filp))
+		goto cleanup;
+			
+	if ((tty->count == 1) && (info->count != 1)) {
+		/*
+		 * tty->count is 1 and the tty structure will be freed.
+		 * info->count should be one in this case.
+		 * if it's not, correct it so that the port is shutdown.
+		 */
+		printk("mgslpc_close: bad refcount; tty->count is 1, "
+		       "info->count is %d\n", info->count);
+		info->count = 1;
+	}
+	
+	info->count--;
+	
+	/* if at least one open remaining, leave hardware active */
+	if (info->count)
+		goto cleanup;
+	
+	info->flags |= ASYNC_CLOSING;
+	
+	/* Save the termios structure, since this port may have
+	 * separate termios for callout and dialin.
+	 */
+	if (info->flags & ASYNC_NORMAL_ACTIVE)
+		info->normal_termios = *tty->termios;
+	if (info->flags & ASYNC_CALLOUT_ACTIVE)
+		info->callout_termios = *tty->termios;
+		
+	/* set tty->closing to notify line discipline to 
+	 * only process XON/XOFF characters. Only the N_TTY
+	 * discipline appears to use this (ppp does not).
+	 */
+	tty->closing = 1;
+	
+	/* wait for transmit data to clear all layers */
+	
+	if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) {
+		if (debug_level >= DEBUG_LEVEL_INFO)
+			printk("%s(%d):mgslpc_close(%s) calling tty_wait_until_sent\n",
+				 __FILE__,__LINE__, info->device_name );
+		tty_wait_until_sent(tty, info->closing_wait);
+	}
+		
+ 	if (info->flags & ASYNC_INITIALIZED)
+ 		mgslpc_wait_until_sent(tty, info->timeout);
+
+	if (tty->driver.flush_buffer)
+		tty->driver.flush_buffer(tty);
+		
+	if (tty->ldisc.flush_buffer)
+		tty->ldisc.flush_buffer(tty);
+		
+	shutdown(info);
+	
+	tty->closing = 0;
+	info->tty = 0;
+	
+	if (info->blocked_open) {
+		if (info->close_delay) {
+			set_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);
+	
+cleanup:			
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgslpc_close(%s) exit, count=%d\n", __FILE__,__LINE__,
+			tty->driver.name, info->count);
+	if(MOD_IN_USE)
+		MOD_DEC_USE_COUNT;
+}
+
+/* Wait until the transmitter is empty.
+ */
+static void mgslpc_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+	MGSLPC_INFO * info = (MGSLPC_INFO *)tty->driver_data;
+	unsigned long orig_jiffies, char_time;
+
+	if (!info )
+		return;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgslpc_wait_until_sent(%s) entry\n",
+			 __FILE__,__LINE__, info->device_name );
+      
+	if (mgslpc_paranoia_check(info, tty->device, "mgslpc_wait_until_sent"))
+		return;
+
+	if (!(info->flags & ASYNC_INITIALIZED))
+		goto exit;
+	 
+	orig_jiffies = jiffies;
+      
+	/* Set check interval to 1/5 of estimated time to
+	 * send a character, and make it at least 1. The check
+	 * interval should also be less than the timeout.
+	 * Note: use tight timings here to satisfy the NIST-PCTS.
+	 */ 
+       
+	if ( info->params.data_rate ) {
+	       	char_time = info->timeout/(32 * 5);
+		if (!char_time)
+			char_time++;
+	} else
+		char_time = 1;
+		
+	if (timeout)
+		char_time = MIN(char_time, timeout);
+		
+	if (info->params.mode == MGSL_MODE_HDLC) {
+		while (info->tx_active) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule_timeout(char_time);
+			if (signal_pending(current))
+				break;
+			if (timeout && ((orig_jiffies + timeout) < jiffies))
+				break;
+		}
+	} else {
+		while ((info->tx_count || info->tx_active) &&
+			info->tx_enabled) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule_timeout(char_time);
+			if (signal_pending(current))
+				break;
+			if (timeout && ((orig_jiffies + timeout) < jiffies))
+				break;
+		}
+	}
+      
+exit:
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgslpc_wait_until_sent(%s) exit\n",
+			 __FILE__,__LINE__, info->device_name );
+}
+
+/* Called by tty_hangup() when a hangup is signaled.
+ * This is the same as closing all open files for the port.
+ */
+static void mgslpc_hangup(struct tty_struct *tty)
+{
+	MGSLPC_INFO * info = (MGSLPC_INFO *)tty->driver_data;
+	
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgslpc_hangup(%s)\n",
+			 __FILE__,__LINE__, info->device_name );
+			 
+	if (mgslpc_paranoia_check(info, tty->device, "mgslpc_hangup"))
+		return;
+
+	mgslpc_flush_buffer(tty);
+	shutdown(info);
+	
+	info->count = 0;	
+	info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
+	info->tty = 0;
+
+	wake_up_interruptible(&info->open_wait);
+}
+
+/* Block the current process until the specified port
+ * is ready to be opened.
+ */
+static int block_til_ready(struct tty_struct *tty, struct file *filp,
+			   MGSLPC_INFO *info)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	int		retval;
+	int		do_clocal = 0, extra_count = 0;
+	unsigned long	flags;
+	
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):block_til_ready on %s\n",
+			 __FILE__,__LINE__, tty->driver.name );
+
+	if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
+		/* this is a callout device */
+		/* just verify that normal device is not in use */
+		if (info->flags & ASYNC_NORMAL_ACTIVE)
+			return -EBUSY;
+		if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+		    (info->flags & ASYNC_SESSION_LOCKOUT) &&
+		    (info->session != current->session))
+		    return -EBUSY;
+		if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+		    (info->flags & ASYNC_PGRP_LOCKOUT) &&
+		    (info->pgrp != current->pgrp))
+		    return -EBUSY;
+		info->flags |= ASYNC_CALLOUT_ACTIVE;
+		return 0;
+	}
+	
+	if (filp->f_flags & O_NONBLOCK || tty->flags & (1 << TTY_IO_ERROR)){
+		/* nonblock mode is set or port is not enabled */
+		/* just verify that callout device is not active */
+		if (info->flags & ASYNC_CALLOUT_ACTIVE)
+			return -EBUSY;
+		info->flags |= ASYNC_NORMAL_ACTIVE;
+		return 0;
+	}
+
+	if (info->flags & ASYNC_CALLOUT_ACTIVE) {
+		if (info->normal_termios.c_cflag & CLOCAL)
+			do_clocal = 1;
+	} else {
+		if (tty->termios->c_cflag & CLOCAL)
+			do_clocal = 1;
+	}
+	
+	/* Wait for 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
+	 * mgslpc_close() knows when to free things.  We restore it upon
+	 * exit, either normal or abnormal.
+	 */
+	 
+	retval = 0;
+	add_wait_queue(&info->open_wait, &wait);
+	
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):block_til_ready before block on %s count=%d\n",
+			 __FILE__,__LINE__, tty->driver.name, info->count );
+
+	save_flags(flags); cli();
+	if (!tty_hung_up_p(filp)) {
+		extra_count = 1;
+		info->count--;
+	}
+	restore_flags(flags);
+	info->blocked_open++;
+	
+	while (1) {
+		if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ 		    (tty->termios->c_cflag & CBAUD)) {
+			spin_lock_irqsave(&info->lock,flags);
+			info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR;
+		 	set_signals(info);
+			spin_unlock_irqrestore(&info->lock,flags);
+		}
+		
+		set_current_state(TASK_INTERRUPTIBLE);
+		
+		if (tty_hung_up_p(filp) || !(info->flags & ASYNC_INITIALIZED)){
+			retval = (info->flags & ASYNC_HUP_NOTIFY) ?
+					-EAGAIN : -ERESTARTSYS;
+			break;
+		}
+		
+		spin_lock_irqsave(&info->lock,flags);
+	 	get_signals(info);
+		spin_unlock_irqrestore(&info->lock,flags);
+		
+ 		if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ 		    !(info->flags & ASYNC_CLOSING) &&
+ 		    (do_clocal || (info->serial_signals & SerialSignal_DCD)) ) {
+ 			break;
+		}
+			
+		if (signal_pending(current)) {
+			retval = -ERESTARTSYS;
+			break;
+		}
+		
+		if (debug_level >= DEBUG_LEVEL_INFO)
+			printk("%s(%d):block_til_ready blocking on %s count=%d\n",
+				 __FILE__,__LINE__, tty->driver.name, info->count );
+				 
+		schedule();
+	}
+	
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&info->open_wait, &wait);
+	
+	if (extra_count)
+		info->count++;
+	info->blocked_open--;
+	
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):block_til_ready after blocking on %s count=%d\n",
+			 __FILE__,__LINE__, tty->driver.name, info->count );
+			 
+	if (!retval)
+		info->flags |= ASYNC_NORMAL_ACTIVE;
+		
+	return retval;
+}
+
+static int mgslpc_open(struct tty_struct *tty, struct file * filp)
+{
+	MGSLPC_INFO	*info;
+	int 			retval, line;
+	unsigned long flags;
+
+	/* verify range of specified line number */	
+	line = minor(tty->device) - tty->driver.minor_start;
+	if ((line < 0) || (line >= mgslpc_device_count)) {
+		printk("%s(%d):mgslpc_open with illegal line #%d.\n",
+			__FILE__,__LINE__,line);
+		return -ENODEV;
+	}
+
+	/* find the info structure for the specified line */
+	info = mgslpc_device_list;
+	while(info && info->line != line)
+		info = info->next_device;
+	if ( !info ){
+		printk("%s(%d):Can't find specified device on open (line=%d)\n",
+			__FILE__,__LINE__,line);
+		return -ENODEV;
+	}
+	
+	tty->driver_data = info;
+	info->tty = tty;
+	if (mgslpc_paranoia_check(info, tty->device, "mgslpc_open"))
+		return -ENODEV;
+		
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgslpc_open(%s), old ref count = %d\n",
+			 __FILE__,__LINE__,tty->driver.name, info->count);
+
+	MOD_INC_USE_COUNT;
+	
+	/* If port is closing, signal caller to try again */
+	if (tty_hung_up_p(filp) || info->flags & ASYNC_CLOSING){
+		if (info->flags & ASYNC_CLOSING)
+			interruptible_sleep_on(&info->close_wait);
+		retval = ((info->flags & ASYNC_HUP_NOTIFY) ?
+			-EAGAIN : -ERESTARTSYS);
+		goto cleanup;
+	}
+	
+	info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+
+	spin_lock_irqsave(&info->netlock, flags);
+	if (info->netcount) {
+		retval = -EBUSY;
+		spin_unlock_irqrestore(&info->netlock, flags);
+		goto cleanup;
+	}
+	info->count++;
+	spin_unlock_irqrestore(&info->netlock, flags);
+
+	if (info->count == 1) {
+		/* 1st open on this device, init hardware */
+		retval = startup(info);
+		if (retval < 0)
+			goto cleanup;
+	}
+
+	retval = block_til_ready(tty, filp, info);
+	if (retval) {
+		if (debug_level >= DEBUG_LEVEL_INFO)
+			printk("%s(%d):block_til_ready(%s) returned %d\n",
+				 __FILE__,__LINE__, info->device_name, retval);
+		goto cleanup;
+	}
+
+	if ((info->count == 1) &&
+	    info->flags & ASYNC_SPLIT_TERMIOS) {
+		if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+			*tty->termios = info->normal_termios;
+		else 
+			*tty->termios = info->callout_termios;
+		mgslpc_change_params(info);
+	}
+	
+	info->session = current->session;
+	info->pgrp    = current->pgrp;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgslpc_open(%s) success\n",
+			 __FILE__,__LINE__, info->device_name);
+	retval = 0;
+	
+cleanup:			
+	if (retval) {
+		if(MOD_IN_USE)
+			MOD_DEC_USE_COUNT;
+		if(info->count)
+			info->count--;
+	}
+	
+	return retval;
+}
+
+/*
+ * /proc fs routines....
+ */
+
+static inline int line_info(char *buf, MGSLPC_INFO *info)
+{
+	char	stat_buf[30];
+	int	ret;
+	unsigned long flags;
+
+	ret = sprintf(buf, "%s:io:%04X irq:%d",
+		      info->device_name, info->io_base, info->irq_level);
+
+	/* output current serial signal states */
+	spin_lock_irqsave(&info->lock,flags);
+ 	get_signals(info);
+	spin_unlock_irqrestore(&info->lock,flags);
+	
+	stat_buf[0] = 0;
+	stat_buf[1] = 0;
+	if (info->serial_signals & SerialSignal_RTS)
+		strcat(stat_buf, "|RTS");
+	if (info->serial_signals & SerialSignal_CTS)
+		strcat(stat_buf, "|CTS");
+	if (info->serial_signals & SerialSignal_DTR)
+		strcat(stat_buf, "|DTR");
+	if (info->serial_signals & SerialSignal_DSR)
+		strcat(stat_buf, "|DSR");
+	if (info->serial_signals & SerialSignal_DCD)
+		strcat(stat_buf, "|CD");
+	if (info->serial_signals & SerialSignal_RI)
+		strcat(stat_buf, "|RI");
+
+	if (info->params.mode == MGSL_MODE_HDLC) {
+		ret += sprintf(buf+ret, " HDLC txok:%d rxok:%d",
+			      info->icount.txok, info->icount.rxok);
+		if (info->icount.txunder)
+			ret += sprintf(buf+ret, " txunder:%d", info->icount.txunder);
+		if (info->icount.txabort)
+			ret += sprintf(buf+ret, " txabort:%d", info->icount.txabort);
+		if (info->icount.rxshort)
+			ret += sprintf(buf+ret, " rxshort:%d", info->icount.rxshort);	
+		if (info->icount.rxlong)
+			ret += sprintf(buf+ret, " rxlong:%d", info->icount.rxlong);
+		if (info->icount.rxover)
+			ret += sprintf(buf+ret, " rxover:%d", info->icount.rxover);
+		if (info->icount.rxcrc)
+			ret += sprintf(buf+ret, " rxlong:%d", info->icount.rxcrc);
+	} else {
+		ret += sprintf(buf+ret, " ASYNC tx:%d rx:%d",
+			      info->icount.tx, info->icount.rx);
+		if (info->icount.frame)
+			ret += sprintf(buf+ret, " fe:%d", info->icount.frame);
+		if (info->icount.parity)
+			ret += sprintf(buf+ret, " pe:%d", info->icount.parity);
+		if (info->icount.brk)
+			ret += sprintf(buf+ret, " brk:%d", info->icount.brk);	
+		if (info->icount.overrun)
+			ret += sprintf(buf+ret, " oe:%d", info->icount.overrun);
+	}
+	
+	/* Append serial signal status to end */
+	ret += sprintf(buf+ret, " %s\n", stat_buf+1);
+	
+	ret += sprintf(buf+ret, "txactive=%d bh_req=%d bh_run=%d pending_bh=%x\n",
+		       info->tx_active,info->bh_requested,info->bh_running,
+		       info->pending_bh);
+	
+	return ret;
+}
+
+/* Called to print information about devices
+ */
+int mgslpc_read_proc(char *page, char **start, off_t off, int count,
+		 int *eof, void *data)
+{
+	int len = 0, l;
+	off_t	begin = 0;
+	MGSLPC_INFO *info;
+	
+	len += sprintf(page, "synclink driver:%s\n", driver_version);
+	
+	info = mgslpc_device_list;
+	while( info ) {
+		l = line_info(page + len, info);
+		len += l;
+		if (len+begin > off+count)
+			goto done;
+		if (len+begin < off) {
+			begin += len;
+			len = 0;
+		}
+		info = info->next_device;
+	}
+
+	*eof = 1;
+done:
+	if (off >= len+begin)
+		return 0;
+	*start = page + (off-begin);
+	return ((count < begin+len-off) ? count : begin+len-off);
+}
+
+int rx_alloc_buffers(MGSLPC_INFO *info)
+{
+	/* each buffer has header and data */
+	info->rx_buf_size = sizeof(RXBUF) + info->max_frame_size;
+
+	/* calculate total allocation size for 8 buffers */
+	info->rx_buf_total_size = info->rx_buf_size * 8;
+
+	/* limit total allocated memory */
+	if (info->rx_buf_total_size > 0x10000)
+		info->rx_buf_total_size = 0x10000;
+
+	/* calculate number of buffers */
+	info->rx_buf_count = info->rx_buf_total_size / info->rx_buf_size;
+
+	info->rx_buf = kmalloc(info->rx_buf_total_size, GFP_KERNEL);
+	if (info->rx_buf == NULL)
+		return -ENOMEM;
+
+	rx_reset_buffers(info);
+	return 0;
+}
+
+void rx_free_buffers(MGSLPC_INFO *info)
+{
+	if (info->rx_buf)
+		kfree(info->rx_buf);
+	info->rx_buf = NULL;
+}
+
+int claim_resources(MGSLPC_INFO *info)
+{
+	if (rx_alloc_buffers(info) < 0 ) {
+		printk( "Cant allocate rx buffer %s\n", info->device_name);
+		release_resources(info);
+		return -ENODEV;
+	}	
+	return 0;
+}
+
+void release_resources(MGSLPC_INFO *info)
+{
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("release_resources(%s)\n", info->device_name);
+	rx_free_buffers(info);
+}
+
+/* Add the specified device instance data structure to the
+ * global linked list of devices and increment the device count.
+ * 	
+ * Arguments:		info	pointer to device instance data
+ */
+void mgslpc_add_device(MGSLPC_INFO *info)
+{
+	info->next_device = NULL;
+	info->line = mgslpc_device_count;
+	sprintf(info->device_name,"ttySLP%d",info->line);
+	
+	if (info->line < MAX_DEVICE_COUNT) {
+		if (maxframe[info->line])
+			info->max_frame_size = maxframe[info->line];
+//		info->dosyncppp = dosyncppp[info->line];
+		info->dosyncppp = 1;
+	}
+
+	mgslpc_device_count++;
+	
+	if (!mgslpc_device_list)
+		mgslpc_device_list = info;
+	else {	
+		MGSLPC_INFO *current_dev = mgslpc_device_list;
+		while( current_dev->next_device )
+			current_dev = current_dev->next_device;
+		current_dev->next_device = info;
+	}
+	
+	if (info->max_frame_size < 4096)
+		info->max_frame_size = 4096;
+	else if (info->max_frame_size > 65535)
+		info->max_frame_size = 65535;
+	
+	printk( "SyncLink PC Card %s:IO=%04X IRQ=%d\n",
+		info->device_name, info->io_base, info->irq_level);
+
+
+#ifdef CONFIG_SYNCLINK_SYNCPPP
+#ifdef MODULE
+	if (info->dosyncppp)
+#endif
+		mgslpc_sppp_init(info);
+#endif
+}
+
+void mgslpc_remove_device(MGSLPC_INFO *remove_info)
+{
+	MGSLPC_INFO *info = mgslpc_device_list;
+	MGSLPC_INFO *last = NULL;
+
+	while(info) {
+		if (info == remove_info) {
+			if (last)
+				last->next_device = info->next_device;
+			else
+				mgslpc_device_list = info->next_device;
+#ifdef CONFIG_SYNCLINK_SYNCPPP
+			if (info->dosyncppp)
+				mgslpc_sppp_delete(info);
+#endif
+			release_resources(info);
+			kfree(info);
+			mgslpc_device_count--;
+			return;
+		}
+		last = info;
+		info = info->next_device;
+	}
+}
+
+static int __init synclink_cs_init(void)
+{
+    servinfo_t serv;
+
+    EXPORT_NO_SYMBOLS;
+	
+    if (break_on_load) {
+	    mgslpc_get_text_ptr();
+	    BREAKPOINT();
+    }
+
+    printk("%s %s\n", driver_name, driver_version);
+
+    CardServices(GetCardServicesInfo, &serv);
+    if (serv.Revision != CS_RELEASE_CODE) {
+	    printk(KERN_NOTICE "synclink_cs: Card Services release "
+		   "does not match!\n");
+	    return -1;
+    }
+    register_pccard_driver(&dev_info, &mgslpc_attach, &mgslpc_detach);
+
+    /* Initialize the tty_driver structure */
+	
+    memset(&serial_driver, 0, sizeof(struct tty_driver));
+    serial_driver.magic = TTY_DRIVER_MAGIC;
+    serial_driver.driver_name = "synclink_cs";
+    serial_driver.name = "ttySLP";
+    serial_driver.major = ttymajor;
+    serial_driver.minor_start = 64;
+    serial_driver.num = MAX_DEVICE_COUNT;
+    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 = mgslpc_open;
+    serial_driver.close = mgslpc_close;
+    serial_driver.write = mgslpc_write;
+    serial_driver.put_char = mgslpc_put_char;
+    serial_driver.flush_chars = mgslpc_flush_chars;
+    serial_driver.write_room = mgslpc_write_room;
+    serial_driver.chars_in_buffer = mgslpc_chars_in_buffer;
+    serial_driver.flush_buffer = mgslpc_flush_buffer;
+    serial_driver.ioctl = mgslpc_ioctl;
+    serial_driver.throttle = mgslpc_throttle;
+    serial_driver.unthrottle = mgslpc_unthrottle;
+    serial_driver.send_xchar = mgslpc_send_xchar;
+    serial_driver.break_ctl = mgslpc_break;
+    serial_driver.wait_until_sent = mgslpc_wait_until_sent;
+    serial_driver.read_proc = mgslpc_read_proc;
+    serial_driver.set_termios = mgslpc_set_termios;
+    serial_driver.stop = tx_pause;
+    serial_driver.start = tx_release;
+    serial_driver.hangup = mgslpc_hangup;
+	
+    /*
+     * The callout device is just like normal device except for
+     * major number and the subtype code.
+     */
+    callout_driver = serial_driver;
+    callout_driver.name = "cuaSLP";
+    callout_driver.major = cuamajor;
+    callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+    callout_driver.read_proc = 0;
+    callout_driver.proc_entry = 0;
+
+    if (tty_register_driver(&serial_driver) < 0)
+	    printk("%s(%d):Couldn't register serial driver\n",
+		   __FILE__,__LINE__);
+			
+    if (tty_register_driver(&callout_driver) < 0)
+	    printk("%s(%d):Couldn't register callout driver\n",
+		   __FILE__,__LINE__);
+
+    printk("%s %s, tty major#%d callout major#%d\n",
+	   driver_name, driver_version,
+	   serial_driver.major, callout_driver.major);
+	
+    return 0;
+}
+
+static void __exit synclink_cs_exit(void) 
+{
+	unsigned long flags;
+	int rc;
+
+	printk("Unloading %s: version %s\n", driver_name, driver_version);
+
+	while(mgslpc_device_list)
+		mgslpc_remove_device(mgslpc_device_list);
+
+	save_flags(flags);
+	cli();
+	if ((rc = tty_unregister_driver(&serial_driver)))
+		printk("%s(%d) failed to unregister tty driver err=%d\n",
+		       __FILE__,__LINE__,rc);
+	if ((rc = tty_unregister_driver(&callout_driver)))
+		printk("%s(%d) failed to unregister callout driver err=%d\n",
+		       __FILE__,__LINE__,rc);
+	restore_flags(flags);
+
+	unregister_pccard_driver(&dev_info);
+	while (dev_list != NULL) {
+		del_timer(&dev_list->release);
+		if (dev_list->state & DEV_CONFIG)
+			mgslpc_release((u_long)dev_list);
+		mgslpc_detach(dev_list);
+	}
+}
+
+module_init(synclink_cs_init);
+module_exit(synclink_cs_exit);
+
+void mgslpc_set_rate(MGSLPC_INFO *info, unsigned char channel, unsigned int rate) 
+{
+	unsigned int M, N;
+	unsigned char val;
+
+	/* note:standard BRG mode is broken in V3.2 chip 
+	 * so enhanced mode is always used 
+	 */
+
+	if (rate) {
+		N = 3686400 / rate;
+		if (!N)
+			N = 1;
+		N >>= 1;
+		for (M = 1; N > 64 && M < 16; M++)
+			N >>= 1;
+		N--;
+
+		/* BGR[5..0] = N
+		 * BGR[9..6] = M
+		 * BGR[7..0] contained in BGR register
+		 * BGR[9..8] contained in CCR2[7..6]
+		 * divisor = (N+1)*2^M
+		 *
+		 * Note: M *must* not be zero (causes asymetric duty cycle)
+		 */ 
+		write_reg(info, (unsigned char) (channel + BGR),
+				  (unsigned char) ((M << 6) + N));
+		val = read_reg(info, (unsigned char) (channel + CCR2)) & 0x3f;
+		val |= ((M << 4) & 0xc0);
+		write_reg(info, (unsigned char) (channel + CCR2), val);
+	}
+}
+
+/* Enabled the AUX clock output at the specified frequency.
+ */
+void enable_auxclk(MGSLPC_INFO *info)
+{
+	unsigned char val;
+	
+	/* MODE
+	 *
+	 * 07..06  MDS[1..0] 10 = transparent HDLC mode
+	 * 05      ADM Address Mode, 0 = no addr recognition
+	 * 04      TMD Timer Mode, 0 = external
+	 * 03      RAC Receiver Active, 0 = inactive
+	 * 02      RTS 0=RTS active during xmit, 1=RTS always active
+	 * 01      TRS Timer Resolution, 1=512
+	 * 00      TLP Test Loop, 0 = no loop
+	 *
+	 * 1000 0010
+	 */ 
+	val = 0x82;
+	
+	/* channel B RTS is used to enable AUXCLK driver on SP505 */ 
+	if (info->params.mode == MGSL_MODE_HDLC && info->params.clock_speed)
+		val |= BIT2;
+	write_reg(info, CHB + MODE, val);
+	
+	/* CCR0
+	 *
+	 * 07      PU Power Up, 1=active, 0=power down
+	 * 06      MCE Master Clock Enable, 1=enabled
+	 * 05      Reserved, 0
+	 * 04..02  SC[2..0] Encoding
+	 * 01..00  SM[1..0] Serial Mode, 00=HDLC
+	 *
+	 * 11000000
+	 */ 
+	write_reg(info, CHB + CCR0, 0xc0);
+	
+	/* CCR1
+	 *
+	 * 07      SFLG Shared Flag, 0 = disable shared flags
+	 * 06      GALP Go Active On Loop, 0 = not used
+	 * 05      GLP Go On Loop, 0 = not used
+	 * 04      ODS Output Driver Select, 1=TxD is push-pull output
+	 * 03      ITF Interframe Time Fill, 0=mark, 1=flag
+	 * 02..00  CM[2..0] Clock Mode
+	 *
+	 * 0001 0111
+	 */ 
+	write_reg(info, CHB + CCR1, 0x17);
+	
+	/* CCR2 (Channel B)
+	 *
+	 * 07..06  BGR[9..8] Baud rate bits 9..8
+	 * 05      BDF Baud rate divisor factor, 0=1, 1=BGR value
+	 * 04      SSEL Clock source select, 1=submode b
+	 * 03      TOE 0=TxCLK is input, 1=TxCLK is output
+	 * 02      RWX Read/Write Exchange 0=disabled
+	 * 01      C32, CRC select, 0=CRC-16, 1=CRC-32
+	 * 00      DIV, data inversion 0=disabled, 1=enabled
+	 *
+	 * 0011 1000
+	 */ 
+	if (info->params.mode == MGSL_MODE_HDLC && info->params.clock_speed)
+		write_reg(info, CHB + CCR2, 0x38);
+	else
+		write_reg(info, CHB + CCR2, 0x30);
+	
+	/* CCR4
+	 *
+	 * 07      MCK4 Master Clock Divide by 4, 1=enabled
+	 * 06      EBRG Enhanced Baud Rate Generator Mode, 1=enabled
+	 * 05      TST1 Test Pin, 0=normal operation
+	 * 04      ICD Ivert Carrier Detect, 1=enabled (active low)
+	 * 03..02  Reserved, must be 0
+	 * 01..00  RFT[1..0] RxFIFO Threshold 00=32 bytes
+	 *
+	 * 0101 0000
+	 */ 
+	write_reg(info, CHB + CCR4, 0x50);
+	
+	/* if auxclk not enabled, set internal BRG so
+	 * CTS transitions can be detected (requires TxC)
+	 */ 
+	if (info->params.mode == MGSL_MODE_HDLC && info->params.clock_speed)
+		mgslpc_set_rate(info, CHB, info->params.clock_speed);
+	else
+		mgslpc_set_rate(info, CHB, 921600);
+}
+
+static void loopback_enable(MGSLPC_INFO *info) 
+{
+	unsigned char val;
+	
+	/* CCR1:02..00  CM[2..0] Clock Mode = 111 (clock mode 7) */ 
+	val = read_reg(info, CHA + CCR1) | (BIT2 + BIT1 + BIT0);
+	write_reg(info, CHA + CCR1, val);
+	
+	/* CCR2:04 SSEL Clock source select, 1=submode b */ 
+	val = read_reg(info, CHA + CCR2) | (BIT4 + BIT5);
+	write_reg(info, CHA + CCR2, val);
+	
+	/* set LinkSpeed if available, otherwise default to 2Mbps */ 
+	if (info->params.clock_speed)
+		mgslpc_set_rate(info, CHA, info->params.clock_speed);
+	else
+		mgslpc_set_rate(info, CHA, 1843200);
+	
+	/* MODE:00 TLP Test Loop, 1=loopback enabled */ 
+	val = read_reg(info, CHA + MODE) | BIT0;
+	write_reg(info, CHA + MODE, val);
+}
+
+void hdlc_mode(MGSLPC_INFO *info)
+{
+	unsigned char val;
+	unsigned char clkmode, clksubmode;
+
+	/* disable all interrupts */ 
+	irq_disable(info, CHA, 0xffff);
+	irq_disable(info, CHB, 0xffff);
+	port_irq_disable(info, 0xff);
+	
+	/* assume clock mode 0a, rcv=RxC xmt=TxC */ 
+	clkmode = clksubmode = 0;
+	if (info->params.flags & HDLC_FLAG_RXC_DPLL
+	    && info->params.flags & HDLC_FLAG_TXC_DPLL) {
+		/* clock mode 7a, rcv = DPLL, xmt = DPLL */ 
+		clkmode = 7;
+	} else if (info->params.flags & HDLC_FLAG_RXC_BRG
+		 && info->params.flags & HDLC_FLAG_TXC_BRG) {
+		/* clock mode 7b, rcv = BRG, xmt = BRG */ 
+		clkmode = 7;
+		clksubmode = 1;
+	} else if (info->params.flags & HDLC_FLAG_RXC_DPLL) {
+		if (info->params.flags & HDLC_FLAG_TXC_BRG) {
+			/* clock mode 6b, rcv = DPLL, xmt = BRG/16 */ 
+			clkmode = 6;
+			clksubmode = 1;
+		} else {
+			/* clock mode 6a, rcv = DPLL, xmt = TxC */ 
+			clkmode = 6;
+		}
+	} else if (info->params.flags & HDLC_FLAG_TXC_BRG) {
+		/* clock mode 0b, rcv = RxC, xmt = BRG */ 
+		clksubmode = 1;
+	}
+	
+	/* MODE
+	 *
+	 * 07..06  MDS[1..0] 10 = transparent HDLC mode
+	 * 05      ADM Address Mode, 0 = no addr recognition
+	 * 04      TMD Timer Mode, 0 = external
+	 * 03      RAC Receiver Active, 0 = inactive
+	 * 02      RTS 0=RTS active during xmit, 1=RTS always active
+	 * 01      TRS Timer Resolution, 1=512
+	 * 00      TLP Test Loop, 0 = no loop
+	 *
+	 * 1000 0010
+	 */ 
+	val = 0x82;
+	if (info->params.loopback)
+		val |= BIT0;
+	
+	/* preserve RTS state */ 
+	if (info->serial_signals & SerialSignal_RTS)
+		val |= BIT2;
+	write_reg(info, CHA + MODE, val);
+	
+	/* CCR0
+	 *
+	 * 07      PU Power Up, 1=active, 0=power down
+	 * 06      MCE Master Clock Enable, 1=enabled
+	 * 05      Reserved, 0
+	 * 04..02  SC[2..0] Encoding
+	 * 01..00  SM[1..0] Serial Mode, 00=HDLC
+	 *
+	 * 11000000
+	 */ 
+	val = 0xc0;
+	switch (info->params.encoding)
+	{
+	case HDLC_ENCODING_NRZI:
+		val |= BIT3;
+		break;
+	case HDLC_ENCODING_BIPHASE_SPACE:
+		val |= BIT4;
+		break;		// FM0
+	case HDLC_ENCODING_BIPHASE_MARK:
+		val |= BIT4 + BIT2;
+		break;		// FM1
+	case HDLC_ENCODING_BIPHASE_LEVEL:
+		val |= BIT4 + BIT3;
+		break;		// Manchester
+	}
+	write_reg(info, CHA + CCR0, val);
+	
+	/* CCR1
+	 *
+	 * 07      SFLG Shared Flag, 0 = disable shared flags
+	 * 06      GALP Go Active On Loop, 0 = not used
+	 * 05      GLP Go On Loop, 0 = not used
+	 * 04      ODS Output Driver Select, 1=TxD is push-pull output
+	 * 03      ITF Interframe Time Fill, 0=mark, 1=flag
+	 * 02..00  CM[2..0] Clock Mode
+	 *
+	 * 0001 0000
+	 */ 
+	val = 0x10 + clkmode;
+	write_reg(info, CHA + CCR1, val);
+	
+	/* CCR2
+	 *
+	 * 07..06  BGR[9..8] Baud rate bits 9..8
+	 * 05      BDF Baud rate divisor factor, 0=1, 1=BGR value
+	 * 04      SSEL Clock source select, 1=submode b
+	 * 03      TOE 0=TxCLK is input, 0=TxCLK is input
+	 * 02      RWX Read/Write Exchange 0=disabled
+	 * 01      C32, CRC select, 0=CRC-16, 1=CRC-32
+	 * 00      DIV, data inversion 0=disabled, 1=enabled
+	 *
+	 * 0000 0000
+	 */ 
+	val = 0x00;
+	if (clkmode == 2 || clkmode == 3 || clkmode == 6
+	    || clkmode == 7 || (clkmode == 0 && clksubmode == 1))
+		val |= BIT5;
+	if (clksubmode)
+		val |= BIT4;
+	if (info->params.crc_type == HDLC_CRC_32_CCITT)
+		val |= BIT1;
+	if (info->params.encoding == HDLC_ENCODING_NRZB)
+		val |= BIT0;
+	write_reg(info, CHA + CCR2, val);
+	
+	/* CCR3
+	 *
+	 * 07..06  PRE[1..0] Preamble count 00=1, 01=2, 10=4, 11=8
+	 * 05      EPT Enable preamble transmission, 1=enabled
+	 * 04      RADD Receive address pushed to FIFO, 0=disabled
+	 * 03      CRL CRC Reset Level, 0=FFFF
+	 * 02      RCRC Rx CRC 0=On 1=Off
+	 * 01      TCRC Tx CRC 0=On 1=Off
+	 * 00      PSD DPLL Phase Shift Disable
+	 *
+	 * 0000 0000
+	 */ 
+	val = 0x00;
+	if (info->params.crc_type == HDLC_CRC_NONE)
+		val |= BIT2 + BIT1;
+	if (info->params.preamble != HDLC_PREAMBLE_PATTERN_NONE)
+		val |= BIT5;
+	switch (info->params.preamble_length)
+	{
+	case HDLC_PREAMBLE_LENGTH_16BITS:
+		val |= BIT6;
+		break;
+	case HDLC_PREAMBLE_LENGTH_32BITS:
+		val |= BIT6;
+		break;
+	case HDLC_PREAMBLE_LENGTH_64BITS:
+		val |= BIT7 + BIT6;
+		break;
+	}
+	write_reg(info, CHA + CCR3, val);
+	
+	/* PRE - Preamble pattern */ 
+	val = 0;
+	switch (info->params.preamble)
+	{
+	case HDLC_PREAMBLE_PATTERN_FLAGS: val = 0x7e; break;
+	case HDLC_PREAMBLE_PATTERN_10:    val = 0xaa; break;
+	case HDLC_PREAMBLE_PATTERN_01:    val = 0x55; break;
+	case HDLC_PREAMBLE_PATTERN_ONES:  val = 0xff; break;
+	}
+	write_reg(info, CHA + PRE, val);
+	
+	/* CCR4
+	 *
+	 * 07      MCK4 Master Clock Divide by 4, 1=enabled
+	 * 06      EBRG Enhanced Baud Rate Generator Mode, 1=enabled
+	 * 05      TST1 Test Pin, 0=normal operation
+	 * 04      ICD Ivert Carrier Detect, 1=enabled (active low)
+	 * 03..02  Reserved, must be 0
+	 * 01..00  RFT[1..0] RxFIFO Threshold 00=32 bytes
+	 *
+	 * 0101 0000
+	 */ 
+	val = 0x50;
+	write_reg(info, CHA + CCR4, val);
+	if (info->params.flags & HDLC_FLAG_RXC_DPLL)
+		mgslpc_set_rate(info, CHA, info->params.clock_speed * 16);
+	else
+		mgslpc_set_rate(info, CHA, info->params.clock_speed);
+	
+	/* RLCR Receive length check register
+	 *
+	 * 7     1=enable receive length check
+	 * 6..0  Max frame length = (RL + 1) * 32
+	 */ 
+	write_reg(info, CHA + RLCR, 0);
+	
+	/* XBCH Transmit Byte Count High
+	 *
+	 * 07      DMA mode, 0 = interrupt driven
+	 * 06      NRM, 0=ABM (ignored)
+	 * 05      CAS Carrier Auto Start
+	 * 04      XC Transmit Continuously (ignored)
+	 * 03..00  XBC[10..8] Transmit byte count bits 10..8
+	 *
+	 * 0000 0000
+	 */ 
+	val = 0x00;
+	if (info->params.flags & HDLC_FLAG_AUTO_DCD)
+		val |= BIT5;
+	write_reg(info, CHA + XBCH, val);
+	enable_auxclk(info);
+	if (info->params.loopback || info->testing_irq)
+		loopback_enable(info);
+	if (info->params.flags & HDLC_FLAG_AUTO_CTS)
+	{
+		irq_enable(info, CHB, IRQ_CTS);
+		/* PVR[3] 1=AUTO CTS active */ 
+		set_reg_bits(info, CHA + PVR, BIT3);
+	} else
+		clear_reg_bits(info, CHA + PVR, BIT3);
+
+	irq_enable(info, CHA,
+			 IRQ_RXEOM + IRQ_RXFIFO + IRQ_ALLSENT +
+			 IRQ_UNDERRUN + IRQ_TXFIFO);
+	issue_command(info, CHA, CMD_TXRESET + CMD_RXRESET);
+	wait_command_complete(info, CHA);
+	read_reg16(info, CHA + ISR);	/* clear pending IRQs */
+	
+	/* Master clock mode enabled above to allow reset commands
+	 * to complete even if no data clocks are present.
+	 *
+	 * Disable master clock mode for normal communications because
+	 * V3.2 of the ESCC2 has a bug that prevents the transmit all sent
+	 * IRQ when in master clock mode.
+	 *
+	 * Leave master clock mode enabled for IRQ test because the
+	 * timer IRQ used by the test can only happen in master clock mode.
+	 */ 
+	if (!info->testing_irq)
+		clear_reg_bits(info, CHA + CCR0, BIT6);
+
+	tx_set_idle(info);
+
+	tx_stop(info);
+	rx_stop(info);
+}
+
+void rx_stop(MGSLPC_INFO *info)
+{
+	if (debug_level >= DEBUG_LEVEL_ISR)
+		printk("%s(%d):rx_stop(%s)\n",
+			 __FILE__,__LINE__, info->device_name );
+			 
+	/* MODE:03 RAC Receiver Active, 0=inactive */ 
+	clear_reg_bits(info, CHA + MODE, BIT3);
+
+	info->rx_enabled = 0;
+	info->rx_overflow = 0;
+}
+
+void rx_start(MGSLPC_INFO *info)
+{
+	if (debug_level >= DEBUG_LEVEL_ISR)
+		printk("%s(%d):rx_start(%s)\n",
+			 __FILE__,__LINE__, info->device_name );
+
+	rx_reset_buffers(info);
+	info->rx_enabled = 0;
+	info->rx_overflow = 0;
+
+	/* MODE:03 RAC Receiver Active, 1=active */ 
+	set_reg_bits(info, CHA + MODE, BIT3);
+
+	info->rx_enabled = 1;
+}
+
+void tx_start(MGSLPC_INFO *info)
+{
+	if (debug_level >= DEBUG_LEVEL_ISR)
+		printk("%s(%d):tx_start(%s)\n",
+			 __FILE__,__LINE__, info->device_name );
+			 
+	if (info->tx_count) {
+		/* If auto RTS enabled and RTS is inactive, then assert */
+		/* RTS and set a flag indicating that the driver should */
+		/* negate RTS when the transmission completes. */
+		info->drop_rts_on_tx_done = 0;
+
+		if (info->params.flags & HDLC_FLAG_AUTO_RTS) {
+			get_signals(info);
+			if (!(info->serial_signals & SerialSignal_RTS)) {
+				info->serial_signals |= SerialSignal_RTS;
+				set_signals(info);
+				info->drop_rts_on_tx_done = 1;
+			}
+		}
+
+		if (info->params.mode == MGSL_MODE_ASYNC) {
+			if (!info->tx_active) {
+				info->tx_active = 1;
+				tx_ready(info);
+			}
+		} else {
+			info->tx_active = 1;
+			tx_ready(info);
+			info->tx_timer.expires = jiffies + jiffies_from_ms(5000);
+			add_timer(&info->tx_timer);	
+		}
+	}
+
+	if (!info->tx_enabled)
+		info->tx_enabled = 1;
+}
+
+void tx_stop(MGSLPC_INFO *info)
+{
+	if (debug_level >= DEBUG_LEVEL_ISR)
+		printk("%s(%d):tx_stop(%s)\n",
+			 __FILE__,__LINE__, info->device_name );
+			 
+	del_timer(&info->tx_timer);	
+
+	info->tx_enabled = 0;
+	info->tx_active  = 0;
+}
+
+/* Reset the adapter to a known state and prepare it for further use.
+ */
+void reset_device(MGSLPC_INFO *info)
+{
+	/* power up both channels (set BIT7) */ 
+	write_reg(info, CHA + CCR0, 0x80);
+	write_reg(info, CHB + CCR0, 0x80);
+	write_reg(info, CHA + MODE, 0);
+	write_reg(info, CHB + MODE, 0);
+	
+	/* disable all interrupts */ 
+	irq_disable(info, CHA, 0xffff);
+	irq_disable(info, CHB, 0xffff);
+	port_irq_disable(info, 0xff);
+	
+	/* PCR Port Configuration Register
+	 *
+	 * 07..04  DEC[3..0] Serial I/F select outputs
+	 * 03      output, 1=AUTO CTS control enabled
+	 * 02      RI Ring Indicator input 0=active
+	 * 01      DSR input 0=active
+	 * 00      DTR output 0=active
+	 *
+	 * 0000 0110
+	 */ 
+	write_reg(info, PCR, 0x06);
+	
+	/* PVR Port Value Register
+	 *
+	 * 07..04  DEC[3..0] Serial I/F select (0000=disabled)
+	 * 03      AUTO CTS output 1=enabled
+	 * 02      RI Ring Indicator input
+	 * 01      DSR input
+	 * 00      DTR output (1=inactive)
+	 *
+	 * 0000 0001
+	 */
+//	write_reg(info, PVR, PVR_DTR);
+	
+	/* IPC Interrupt Port Configuration
+	 *
+	 * 07      VIS 1=Masked interrupts visible
+	 * 06..05  Reserved, 0
+	 * 04..03  SLA Slave address, 00 ignored
+	 * 02      CASM Cascading Mode, 1=daisy chain
+	 * 01..00  IC[1..0] Interrupt Config, 01=push-pull output, active low
+	 *
+	 * 0000 0101
+	 */ 
+	write_reg(info, IPC, 0x05);
+}
+
+void async_mode(MGSLPC_INFO *info)
+{
+	unsigned char val;
+
+	/* disable all interrupts */ 
+	irq_disable(info, CHA, 0xffff);
+	irq_disable(info, CHB, 0xffff);
+	port_irq_disable(info, 0xff);
+	
+	/* MODE
+	 *
+	 * 07      Reserved, 0
+	 * 06      FRTS RTS State, 0=active
+	 * 05      FCTS Flow Control on CTS
+	 * 04      FLON Flow Control Enable
+	 * 03      RAC Receiver Active, 0 = inactive
+	 * 02      RTS 0=Auto RTS, 1=manual RTS
+	 * 01      TRS Timer Resolution, 1=512
+	 * 00      TLP Test Loop, 0 = no loop
+	 *
+	 * 0000 0110
+	 */ 
+	val = 0x06;
+	if (info->params.loopback)
+		val |= BIT0;
+	
+	/* preserve RTS state */ 
+	if (!(info->serial_signals & SerialSignal_RTS))
+		val |= BIT6;
+	write_reg(info, CHA + MODE, val);
+	
+	/* CCR0
+	 *
+	 * 07      PU Power Up, 1=active, 0=power down
+	 * 06      MCE Master Clock Enable, 1=enabled
+	 * 05      Reserved, 0
+	 * 04..02  SC[2..0] Encoding, 000=NRZ
+	 * 01..00  SM[1..0] Serial Mode, 11=Async
+	 *
+	 * 1000 0011
+	 */ 
+	write_reg(info, CHA + CCR0, 0x83);
+	
+	/* CCR1
+	 *
+	 * 07..05  Reserved, 0
+	 * 04      ODS Output Driver Select, 1=TxD is push-pull output
+	 * 03      BCR Bit Clock Rate, 1=16x
+	 * 02..00  CM[2..0] Clock Mode, 111=BRG
+	 *
+	 * 0001 1111
+	 */ 
+	write_reg(info, CHA + CCR1, 0x1f);
+	
+	/* CCR2 (channel A)
+	 *
+	 * 07..06  BGR[9..8] Baud rate bits 9..8
+	 * 05      BDF Baud rate divisor factor, 0=1, 1=BGR value
+	 * 04      SSEL Clock source select, 1=submode b
+	 * 03      TOE 0=TxCLK is input, 0=TxCLK is input
+	 * 02      RWX Read/Write Exchange 0=disabled
+	 * 01      Reserved, 0
+	 * 00      DIV, data inversion 0=disabled, 1=enabled
+	 *
+	 * 0001 0000
+	 */ 
+	write_reg(info, CHA + CCR2, 0x10);
+	
+	/* CCR3
+	 *
+	 * 07..01  Reserved, 0
+	 * 00      PSD DPLL Phase Shift Disable
+	 *
+	 * 0000 0000
+	 */ 
+	write_reg(info, CHA + CCR3, 0);
+	
+	/* CCR4
+	 *
+	 * 07      MCK4 Master Clock Divide by 4, 1=enabled
+	 * 06      EBRG Enhanced Baud Rate Generator Mode, 1=enabled
+	 * 05      TST1 Test Pin, 0=normal operation
+	 * 04      ICD Ivert Carrier Detect, 1=enabled (active low)
+	 * 03..00  Reserved, must be 0
+	 *
+	 * 0101 0000
+	 */ 
+	write_reg(info, CHA + CCR4, 0x50);
+	mgslpc_set_rate(info, CHA, info->params.data_rate * 16);
+	
+	/* DAFO Data Format
+	 *
+	 * 07      Reserved, 0
+	 * 06      XBRK transmit break, 0=normal operation
+	 * 05      Stop bits (0=1, 1=2)
+	 * 04..03  PAR[1..0] Parity (01=odd, 10=even)
+	 * 02      PAREN Parity Enable
+	 * 01..00  CHL[1..0] Character Length (00=8, 01=7)
+	 *
+	 */ 
+	val = 0x00;
+	if (info->params.data_bits != 8)
+		val |= BIT0;	/* 7 bits */
+	if (info->params.stop_bits != 1)
+		val |= BIT5;
+	if (info->params.parity != ASYNC_PARITY_NONE)
+	{
+		val |= BIT2;	/* Parity enable */
+		if (info->params.parity == ASYNC_PARITY_ODD)
+			val |= BIT3;
+		else
+			val |= BIT4;
+	}
+	write_reg(info, CHA + DAFO, val);
+	
+	/* RFC Rx FIFO Control
+	 *
+	 * 07      Reserved, 0
+	 * 06      DPS, 1=parity bit not stored in data byte
+	 * 05      DXS, 0=all data stored in FIFO (including XON/XOFF)
+	 * 04      RFDF Rx FIFO Data Format, 1=status byte stored in FIFO
+	 * 03..02  RFTH[1..0], rx threshold, 11=16 status + 16 data byte
+	 * 01      Reserved, 0
+	 * 00      TCDE Terminate Char Detect Enable, 0=disabled
+	 *
+	 * 0101 1100
+	 */ 
+	write_reg(info, CHA + RFC, 0x5c);
+	
+	/* RLCR Receive length check register
+	 *
+	 * Max frame length = (RL + 1) * 32
+	 */ 
+	write_reg(info, CHA + RLCR, 0);
+	
+	/* XBCH Transmit Byte Count High
+	 *
+	 * 07      DMA mode, 0 = interrupt driven
+	 * 06      NRM, 0=ABM (ignored)
+	 * 05      CAS Carrier Auto Start
+	 * 04      XC Transmit Continuously (ignored)
+	 * 03..00  XBC[10..8] Transmit byte count bits 10..8
+	 *
+	 * 0000 0000
+	 */ 
+	val = 0x00;
+	if (info->params.flags & HDLC_FLAG_AUTO_DCD)
+		val |= BIT5;
+	write_reg(info, CHA + XBCH, val);
+	if (info->params.flags & HDLC_FLAG_AUTO_CTS)
+		irq_enable(info, CHA, IRQ_CTS);
+	
+	/* MODE:03 RAC Receiver Active, 1=active */ 
+	set_reg_bits(info, CHA + MODE, BIT3);
+	enable_auxclk(info);
+	if (info->params.flags & HDLC_FLAG_AUTO_CTS) {
+		irq_enable(info, CHB, IRQ_CTS);
+		/* PVR[3] 1=AUTO CTS active */ 
+		set_reg_bits(info, CHA + PVR, BIT3);
+	} else
+		clear_reg_bits(info, CHA + PVR, BIT3);
+	irq_enable(info, CHA,
+			  IRQ_RXEOM + IRQ_RXFIFO + IRQ_BREAK_ON + IRQ_RXTIME +
+			  IRQ_ALLSENT + IRQ_TXFIFO);
+	issue_command(info, CHA, CMD_TXRESET + CMD_RXRESET);
+	wait_command_complete(info, CHA);
+	read_reg16(info, CHA + ISR);	/* clear pending IRQs */
+}
+
+/* Set the HDLC idle mode for the transmitter.
+ */
+void tx_set_idle(MGSLPC_INFO *info)
+{
+	/* Note: ESCC2 only supports flags and one idle modes */ 
+	if (info->idle_mode == HDLC_TXIDLE_FLAGS)
+		set_reg_bits(info, CHA + CCR1, BIT3);
+	else
+		clear_reg_bits(info, CHA + CCR1, BIT3);
+}
+
+/* get state of the V24 status (input) signals.
+ */
+void get_signals(MGSLPC_INFO *info)
+{
+	unsigned char status = 0;
+	
+	/* preserve DTR and RTS */ 
+	info->serial_signals &= SerialSignal_DTR + SerialSignal_RTS;
+
+	if (read_reg(info, CHB + VSTR) & BIT7)
+		info->serial_signals |= SerialSignal_DCD;
+	if (read_reg(info, CHB + STAR) & BIT1)
+		info->serial_signals |= SerialSignal_CTS;
+
+	status = read_reg(info, CHA + PVR);
+	if (!(status & PVR_RI))
+		info->serial_signals |= SerialSignal_RI;
+	if (!(status & PVR_DSR))
+		info->serial_signals |= SerialSignal_DSR;
+}
+
+/* Set the state of DTR and RTS based on contents of
+ * serial_signals member of device extension.
+ */
+void set_signals(MGSLPC_INFO *info)
+{
+	unsigned char val;
+
+	val = read_reg(info, CHA + MODE);
+	if (info->params.mode == MGSL_MODE_ASYNC) {
+		if (info->serial_signals & SerialSignal_RTS)
+			val &= ~BIT6;
+		else
+			val |= BIT6;
+	} else {
+		if (info->serial_signals & SerialSignal_RTS)
+			val |= BIT2;
+		else
+			val &= ~BIT2;
+	}
+	write_reg(info, CHA + MODE, val);
+
+	if (info->serial_signals & SerialSignal_DTR)
+		clear_reg_bits(info, CHA + PVR, PVR_DTR);
+	else
+		set_reg_bits(info, CHA + PVR, PVR_DTR);
+}
+
+void rx_reset_buffers(MGSLPC_INFO *info)
+{
+	RXBUF *buf;
+	int i;
+
+	info->rx_put = 0;
+	info->rx_get = 0;
+	info->rx_frame_count = 0;
+	for (i=0 ; i < info->rx_buf_count ; i++) {
+		buf = (RXBUF*)(info->rx_buf + (i * info->rx_buf_size));
+		buf->status = buf->count = 0;
+	}
+}
+
+/* Attempt to return a received HDLC frame
+ * Only frames received without errors are returned.
+ *
+ * Returns 1 if frame returned, otherwise 0
+ */
+int rx_get_frame(MGSLPC_INFO *info)
+{
+	unsigned short status;
+	RXBUF *buf;
+	unsigned int framesize = 0;
+	unsigned long flags;
+	struct tty_struct *tty = info->tty;
+	int return_frame = 0;
+	
+	if (info->rx_frame_count == 0)
+		return 0;
+
+	buf = (RXBUF*)(info->rx_buf + (info->rx_get * info->rx_buf_size));
+
+	status = buf->status;
+
+	/* 07  VFR  1=valid frame
+	 * 06  RDO  1=data overrun
+	 * 05  CRC  1=OK, 0=error
+	 * 04  RAB  1=frame aborted
+	 */
+	if ((status & 0xf0) != 0xA0) {
+		if (!(status & BIT7) || (status & BIT4))
+			info->icount.rxabort++;
+		else if (status & BIT6)
+			info->icount.rxover++;
+		else if (!(status & BIT5)) {
+			info->icount.rxcrc++;
+			if (info->params.crc_type & HDLC_CRC_RETURN_EX)
+				return_frame = 1;
+		}
+		framesize = 0;
+#ifdef CONFIG_SYNCLINK_SYNCPPP
+		info->netstats.rx_errors++;
+		info->netstats.rx_frame_errors++;
+#endif
+	} else
+		return_frame = 1;
+
+	if (return_frame)
+		framesize = buf->count;
+
+	if (debug_level >= DEBUG_LEVEL_BH)
+		printk("%s(%d):rx_get_frame(%s) status=%04X size=%d\n",
+			__FILE__,__LINE__,info->device_name,status,framesize);
+			
+	if (debug_level >= DEBUG_LEVEL_DATA)
+		trace_block(info, buf->data, framesize, 0);	
+		
+	if (framesize) {
+		if ((info->params.crc_type & HDLC_CRC_RETURN_EX &&
+		      framesize+1 > info->max_frame_size) ||
+		    framesize > info->max_frame_size)
+			info->icount.rxlong++;
+		else {
+			if (status & BIT5)
+				info->icount.rxok++;
+
+			if (info->params.crc_type & HDLC_CRC_RETURN_EX) {
+				*(buf->data + framesize) = status & BIT5 ? RX_OK:RX_CRC_ERROR;
+				++framesize;
+			}
+
+#ifdef CONFIG_SYNCLINK_SYNCPPP
+			if (info->netcount) {
+				/* pass frame to syncppp device */
+				mgslpc_sppp_rx_done(info, buf->data, framesize);
+			} 
+			else
+#endif
+			{
+				/* Call the line discipline receive callback directly. */
+				if (tty && tty->ldisc.receive_buf)
+					tty->ldisc.receive_buf(tty, buf->data, info->flag_buf, framesize);
+			}
+		}
+	}
+
+	spin_lock_irqsave(&info->lock,flags);
+	buf->status = buf->count = 0;
+	info->rx_frame_count--;
+	info->rx_get++;
+	if (info->rx_get >= info->rx_buf_count)
+		info->rx_get = 0;
+	spin_unlock_irqrestore(&info->lock,flags);
+
+	return 1;
+}
+
+BOOLEAN register_test(MGSLPC_INFO *info)
+{
+	static unsigned char patterns[] = 
+	    { 0x00, 0xff, 0xaa, 0x55, 0x69, 0x96, 0x0f };
+	static unsigned int count = sizeof(patterns) / sizeof(patterns[0]);
+	unsigned int i;
+	BOOLEAN rc = TRUE;
+	unsigned long flags;
+
+	spin_lock_irqsave(&info->lock,flags);
+	reset_device(info);
+
+	for (i = 0; i < count; i++) {
+		write_reg(info, XAD1, patterns[i]);
+		write_reg(info, XAD2, patterns[(i + 1) % count]);
+		if ((read_reg(info, XAD1) != patterns[i]) || 
+		    (read_reg(info, XAD2) != patterns[(i + 1) % count])) {
+			rc = FALSE;
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&info->lock,flags);
+	return rc;
+}
+
+BOOLEAN irq_test(MGSLPC_INFO *info)
+{
+	unsigned long end_time;
+	unsigned long flags;
+
+	spin_lock_irqsave(&info->lock,flags);
+	reset_device(info);
+
+	info->testing_irq = TRUE;
+	hdlc_mode(info);
+
+	info->irq_occurred = FALSE;
+
+	/* init hdlc mode */
+
+	irq_enable(info, CHA, IRQ_TIMER);
+	write_reg(info, CHA + TIMR, 0);	/* 512 cycles */
+	issue_command(info, CHA, CMD_START_TIMER);
+
+	spin_unlock_irqrestore(&info->lock,flags);
+
+	end_time=100;
+	while(end_time-- && !info->irq_occurred) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(jiffies_from_ms(10));
+	}
+	
+	info->testing_irq = FALSE;
+
+	spin_lock_irqsave(&info->lock,flags);
+	reset_device(info);
+	spin_unlock_irqrestore(&info->lock,flags);
+	
+	return info->irq_occurred ? TRUE : FALSE;
+}
+
+int adapter_test(MGSLPC_INFO *info)
+{
+	if (!register_test(info)) {
+		info->init_error = DiagStatus_AddressFailure;
+		printk( "%s(%d):Register test failure for device %s Addr=%04X\n",
+			__FILE__,__LINE__,info->device_name, (unsigned short)(info->io_base) );
+		return -ENODEV;
+	}
+
+	if (!irq_test(info)) {
+		info->init_error = DiagStatus_IrqFailure;
+		printk( "%s(%d):Interrupt test failure for device %s IRQ=%d\n",
+			__FILE__,__LINE__,info->device_name, (unsigned short)(info->irq_level) );
+		return -ENODEV;
+	}
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):device %s passed diagnostics\n",
+			__FILE__,__LINE__,info->device_name);
+	return 0;
+}
+
+void trace_block(MGSLPC_INFO *info,const char* data, int count, int xmit)
+{
+	int i;
+	int linecount;
+	if (xmit)
+		printk("%s tx data:\n",info->device_name);
+	else
+		printk("%s rx data:\n",info->device_name);
+		
+	while(count) {
+		if (count > 16)
+			linecount = 16;
+		else
+			linecount = count;
+			
+		for(i=0;i<linecount;i++)
+			printk("%02X ",(unsigned char)data[i]);
+		for(;i<17;i++)
+			printk("   ");
+		for(i=0;i<linecount;i++) {
+			if (data[i]>=040 && data[i]<=0176)
+				printk("%c",data[i]);
+			else
+				printk(".");
+		}
+		printk("\n");
+		
+		data  += linecount;
+		count -= linecount;
+	}
+}
+
+/* HDLC frame time out
+ * update stats and do tx completion processing
+ */
+void tx_timeout(unsigned long context)
+{
+	MGSLPC_INFO *info = (MGSLPC_INFO*)context;
+	unsigned long flags;
+	
+	if ( debug_level >= DEBUG_LEVEL_INFO )
+		printk( "%s(%d):tx_timeout(%s)\n",
+			__FILE__,__LINE__,info->device_name);
+	if(info->tx_active &&
+	   info->params.mode == MGSL_MODE_HDLC) {
+		info->icount.txtimeout++;
+	}
+	spin_lock_irqsave(&info->lock,flags);
+	info->tx_active = 0;
+	info->tx_count = info->tx_put = info->tx_get = 0;
+
+	spin_unlock_irqrestore(&info->lock,flags);
+	
+#ifdef CONFIG_SYNCLINK_SYNCPPP
+	if (info->netcount)
+		mgslpc_sppp_tx_done(info);
+	else
+#endif
+		bh_transmit(info);
+}
+
+#ifdef CONFIG_SYNCLINK_SYNCPPP
+/* syncppp net device routines
+ */
+
+void mgslpc_sppp_init(MGSLPC_INFO *info)
+{
+	struct net_device *d;
+
+	sprintf(info->netname,"mgslp%d",info->line);
+
+	info->if_ptr = &info->pppdev;
+	info->netdev = info->pppdev.dev = &info->netdevice;
+
+	sppp_attach(&info->pppdev);
+
+	d = info->netdev;
+	strcpy(d->name,info->netname);
+	d->base_addr = info->io_base;
+	d->irq = info->irq_level;
+	d->priv = info;
+	d->init = NULL;
+	d->open = mgslpc_sppp_open;
+	d->stop = mgslpc_sppp_close;
+	d->hard_start_xmit = mgslpc_sppp_tx;
+	d->do_ioctl = mgslpc_sppp_ioctl;
+	d->get_stats = mgslpc_net_stats;
+	d->tx_timeout = mgslpc_sppp_tx_timeout;
+	d->watchdog_timeo = 10*HZ;
+
+	if (register_netdev(d) == -1) {
+		printk(KERN_WARNING "%s: register_netdev failed.\n", d->name);
+		sppp_detach(info->netdev);
+		return;
+	}
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("mgslpc_sppp_init()\n");	
+}
+
+void mgslpc_sppp_delete(MGSLPC_INFO *info)
+{
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("mgslpc_sppp_delete(%s)\n",info->netname);	
+	sppp_detach(info->netdev);
+	unregister_netdev(info->netdev);
+}
+
+int mgslpc_sppp_open(struct net_device *d)
+{
+	MGSLPC_INFO *info = d->priv;
+	int err;
+	long flags;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("mgslpc_sppp_open(%s)\n",info->netname);	
+
+	spin_lock_irqsave(&info->netlock, flags);
+	if (info->count != 0 || info->netcount != 0) {
+		printk(KERN_WARNING "%s: sppp_open returning busy\n", info->netname);
+		spin_unlock_irqrestore(&info->netlock, flags);
+		return -EBUSY;
+	}
+	info->netcount=1;
+	MOD_INC_USE_COUNT;
+	spin_unlock_irqrestore(&info->netlock, flags);
+
+	/* claim resources and init adapter */
+	if ((err = startup(info)) != 0)
+		goto open_fail;
+
+	/* allow syncppp module to do open processing */
+	if ((err = sppp_open(d)) != 0) {
+		shutdown(info);
+		goto open_fail;
+	}
+
+	info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR;
+	mgslpc_program_hw(info);
+
+	d->trans_start = jiffies;
+	netif_start_queue(d);
+	return 0;
+
+open_fail:
+	spin_lock_irqsave(&info->netlock, flags);
+	info->netcount=0;
+	MOD_DEC_USE_COUNT;
+	spin_unlock_irqrestore(&info->netlock, flags);
+	return err;
+}
+
+void mgslpc_sppp_tx_timeout(struct net_device *dev)
+{
+	MGSLPC_INFO *info = dev->priv;
+	long flags;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("mgslpc_sppp_tx_timeout(%s)\n",info->netname);	
+
+	info->netstats.tx_errors++;
+	info->netstats.tx_aborted_errors++;
+
+	spin_lock_irqsave(&info->lock,flags);
+	tx_stop(info);
+	spin_unlock_irqrestore(&info->lock,flags);
+
+	netif_wake_queue(dev);
+}
+
+int mgslpc_sppp_tx(struct sk_buff *skb, struct net_device *dev)
+{
+	MGSLPC_INFO *info = dev->priv;
+	unsigned long flags;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("mgslpc_sppp_tx(%s)\n",info->netname);	
+
+	netif_stop_queue(dev);
+
+	info->tx_count = skb->len;
+
+	memcpy(info->tx_buf, skb->data, skb->len);
+	info->tx_get = 0;
+	info->tx_put = info->tx_count = skb->len;
+
+	info->netstats.tx_packets++;
+	info->netstats.tx_bytes += skb->len;
+	dev_kfree_skb(skb);
+
+	dev->trans_start = jiffies;
+
+	spin_lock_irqsave(&info->lock,flags);
+	if (!info->tx_active)
+	 	tx_start(info);
+	spin_unlock_irqrestore(&info->lock,flags);
+
+	return 0;
+}
+
+int mgslpc_sppp_close(struct net_device *d)
+{
+	MGSLPC_INFO *info = d->priv;
+	unsigned long flags;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("mgslpc_sppp_close(%s)\n",info->netname);	
+
+	/* shutdown adapter and release resources */
+	shutdown(info);
+
+	/* allow syncppp to do close processing */
+	sppp_close(d);
+	netif_stop_queue(d);
+
+	spin_lock_irqsave(&info->netlock, flags);
+	info->netcount=0;
+	MOD_DEC_USE_COUNT;
+	spin_unlock_irqrestore(&info->netlock, flags);
+	return 0;
+}
+
+void mgslpc_sppp_rx_done(MGSLPC_INFO *info, char *buf, int size)
+{
+	struct sk_buff *skb = dev_alloc_skb(size);
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("mgslpc_sppp_rx_done(%s)\n",info->netname);	
+	if (skb == NULL) {
+		printk(KERN_NOTICE "%s: cant alloc skb, dropping packet\n",
+			info->netname);
+		info->netstats.rx_dropped++;
+		return;
+	}
+
+	memcpy(skb_put(skb, size),buf,size);
+
+	skb->protocol = htons(ETH_P_WAN_PPP);
+	skb->dev = info->netdev;
+	skb->mac.raw = skb->data;
+	info->netstats.rx_packets++;
+	info->netstats.rx_bytes += size;
+	netif_rx(skb);
+	info->netdev->trans_start = jiffies;
+}
+
+void mgslpc_sppp_tx_done(MGSLPC_INFO *info)
+{
+	if (netif_queue_stopped(info->netdev))
+	    netif_wake_queue(info->netdev);
+}
+
+struct net_device_stats *mgslpc_net_stats(struct net_device *dev)
+{
+	MGSLPC_INFO *info = dev->priv;
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("mgslpc_net_stats(%s)\n",info->netname);	
+	return &info->netstats;
+}
+
+int mgslpc_sppp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	MGSLPC_INFO *info = (MGSLPC_INFO *)dev->priv;
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):mgslpc_ioctl %s cmd=%08X\n", __FILE__,__LINE__,
+			info->netname, cmd );
+	return sppp_do_ioctl(dev, ifr, cmd);
+}
+
+#endif /* ifdef CONFIG_SYNCLINK_SYNCPPP */
diff -Nru a/drivers/char/raw.c b/drivers/char/raw.c
--- a/drivers/char/raw.c	Sun Apr 28 20:10:31 2002
+++ b/drivers/char/raw.c	Sun Apr 28 20:10:31 2002
@@ -69,7 +69,6 @@
 {
 	int minor;
 	struct block_device * bdev;
-	kdev_t rdev;	/* it should eventually go away */
 	int err;
 	int sector_size;
 	int sector_bits;
@@ -104,7 +103,6 @@
 		goto out;
 
 	atomic_inc(&bdev->bd_count);
-	rdev = to_kdev_t(bdev->bd_dev);
 	err = blkdev_get(bdev, filp->f_mode, 0, BDEV_RAW);
 	if (err)
 		goto out;
@@ -117,7 +115,7 @@
 	if (raw_devices[minor].inuse++)
 		goto out;
 
-	sector_size = get_hardsect_size(rdev);
+	sector_size = bdev_hardsect_size(bdev);
 	raw_devices[minor].sector_size = sector_size;
 	for (sector_bits = 0; !(sector_size & 1); )
 		sector_size>>=1, sector_bits++;
diff -Nru a/drivers/char/sonypi.c b/drivers/char/sonypi.c
--- a/drivers/char/sonypi.c	Sun Apr 28 20:10:32 2002
+++ b/drivers/char/sonypi.c	Sun Apr 28 20:10:32 2002
@@ -50,6 +50,7 @@
 static int fnkeyinit; /* = 0 */
 static int camera; /* = 0 */
 static int compat; /* = 0 */
+static int nojogdial; /* = 0 */
 
 /* Inits the queue */
 static inline void sonypi_initq(void) {
@@ -310,24 +311,28 @@
 	int i;
 	u8 sonypi_jogger_ev, sonypi_fnkey_ev;
 	u8 sonypi_capture_ev, sonypi_bluetooth_ev;
+	u8 sonypi_pkey_ev;
 
 	if (sonypi_device.model == SONYPI_DEVICE_MODEL_TYPE2) {
 		sonypi_jogger_ev = SONYPI_TYPE2_JOGGER_EV;
 		sonypi_fnkey_ev = SONYPI_TYPE2_FNKEY_EV;
 		sonypi_capture_ev = SONYPI_TYPE2_CAPTURE_EV;
 		sonypi_bluetooth_ev = SONYPI_TYPE2_BLUETOOTH_EV;
+		sonypi_pkey_ev = nojogdial ? SONYPI_TYPE2_PKEY_EV 
+					   : SONYPI_TYPE1_PKEY_EV;
 	}
 	else {
 		sonypi_jogger_ev = SONYPI_TYPE1_JOGGER_EV;
 		sonypi_fnkey_ev = SONYPI_TYPE1_FNKEY_EV;
 		sonypi_capture_ev = SONYPI_TYPE1_CAPTURE_EV;
 		sonypi_bluetooth_ev = SONYPI_TYPE1_BLUETOOTH_EV;
+		sonypi_pkey_ev = SONYPI_TYPE1_PKEY_EV;
 	}
 
 	v1 = inb_p(sonypi_device.ioport1);
 	v2 = inb_p(sonypi_device.ioport2);
 
-	if ((v2 & SONYPI_TYPE1_PKEY_EV) == SONYPI_TYPE1_PKEY_EV) {
+	if ((v2 & sonypi_pkey_ev) == sonypi_pkey_ev) {
 		for (i = 0; sonypi_pkeyev[i].event; i++)
 			if (sonypi_pkeyev[i].data == v1) {
 				event = sonypi_pkeyev[i].event;
@@ -713,11 +718,12 @@
 	       SONYPI_DRIVER_MAJORVERSION,
 	       SONYPI_DRIVER_MINORVERSION);
 	printk(KERN_INFO "sonypi: detected %s model, "
-	       "camera = %s, compat = %s\n",
+	       "camera = %s, compat = %s, nojogdial = %s\n",
 	       (sonypi_device.model == SONYPI_DEVICE_MODEL_TYPE1) ?
 			"type1" : "type2",
 	       camera ? "on" : "off",
-	       compat ? "on" : "off");
+	       compat ? "on" : "off",
+	       nojogdial ? "on" : "off");
 	printk(KERN_INFO "sonypi: enabled at irq=%d, port1=0x%x, port2=0x%x\n",
 	       sonypi_device.irq, 
 	       sonypi_device.ioport1, sonypi_device.ioport2);
@@ -791,6 +797,9 @@
 	if (ints[0] == 4)
 		goto out;
 	compat = ints[5];
+	if (ints[0] == 5)
+		goto out;
+	nojogdial = ints[6];
 out:
 	return 1;
 }
@@ -817,5 +826,7 @@
 MODULE_PARM_DESC(camera, "set this if you have a MotionEye camera (PictureBook series)");
 MODULE_PARM(compat,"i");
 MODULE_PARM_DESC(compat, "set this if you want to enable backward compatibility mode");
+MODULE_PARM(nojogdial, "i");
+MODULE_PARM_DESC(nojogdial, "set this if you have a Vaio without a jogdial (like the fx series)");
 
 EXPORT_SYMBOL(sonypi_camera_command);
diff -Nru a/drivers/char/sonypi.h b/drivers/char/sonypi.h
--- a/drivers/char/sonypi.h	Sun Apr 28 20:10:30 2002
+++ b/drivers/char/sonypi.h	Sun Apr 28 20:10:30 2002
@@ -35,7 +35,7 @@
 #ifdef __KERNEL__
 
 #define SONYPI_DRIVER_MAJORVERSION	 1
-#define SONYPI_DRIVER_MINORVERSION	10
+#define SONYPI_DRIVER_MINORVERSION	11
 
 #include <linux/types.h>
 #include <linux/pci.h>
@@ -141,6 +141,7 @@
 #define SONYPI_TYPE1_BLUETOOTH_EV	0x30
 #define SONYPI_TYPE2_BLUETOOTH_EV	0x08
 #define SONYPI_TYPE1_PKEY_EV		0x40
+#define SONYPI_TYPE2_PKEY_EV		0x08
 #define SONYPI_BACK_EV			0x08
 #define SONYPI_LID_EV			0x38
 
diff -Nru a/drivers/char/synclinkmp.c b/drivers/char/synclinkmp.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/char/synclinkmp.c	Sun Apr 28 20:10:39 2002
@@ -0,0 +1,5609 @@
+/*
+ * $Id: synclinkmp.c,v 4.4 2002/04/22 16:05:41 paulkf Exp $
+ *
+ * Device driver for Microgate SyncLink Multiport
+ * high speed multiprotocol serial adapter.
+ *
+ * written by Paul Fulghum for Microgate Corporation
+ * paulkf@microgate.com
+ *
+ * Microgate and SyncLink are trademarks of Microgate Corporation
+ *
+ * Derived from serial.c written by Theodore Ts'o and Linus Torvalds
+ * This code is released under the GNU General Public License (GPL)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define VERSION(ver,rel,seq) (((ver)<<16) | ((rel)<<8) | (seq))
+#if defined(__i386__)
+#  define BREAKPOINT() asm("   int $3");
+#else
+#  define BREAKPOINT() { }
+#endif
+
+#define MAX_DEVICES 12
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+#include <asm/serial.h>
+#include <linux/delay.h>
+#include <linux/ioctl.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+#include <asm/bitops.h>
+#include <asm/types.h>
+#include <linux/termios.h>
+#include <linux/tqueue.h>
+
+#ifdef CONFIG_SYNCLINK_SYNCPPP_MODULE
+#define CONFIG_SYNCLINK_SYNCPPP 1
+#endif
+
+#ifdef CONFIG_SYNCLINK_SYNCPPP
+#include <net/syncppp.h>
+#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
+#define PUT_USER(error,value,addr) error = put_user(value,addr)
+#define COPY_TO_USER(error,dest,src,size) error = copy_to_user(dest,src,size) ? -EFAULT : 0
+
+#include <asm/uaccess.h>
+
+#include "linux/synclink.h"
+
+static MGSL_PARAMS default_params = {
+	MGSL_MODE_HDLC,			/* unsigned long mode */
+	0,				/* unsigned char loopback; */
+	HDLC_FLAG_UNDERRUN_ABORT15,	/* unsigned short flags; */
+	HDLC_ENCODING_NRZI_SPACE,	/* unsigned char encoding; */
+	0,				/* unsigned long clock_speed; */
+	0xff,				/* unsigned char addr_filter; */
+	HDLC_CRC_16_CCITT,		/* unsigned short crc_type; */
+	HDLC_PREAMBLE_LENGTH_8BITS,	/* unsigned char preamble_length; */
+	HDLC_PREAMBLE_PATTERN_NONE,	/* unsigned char preamble; */
+	9600,				/* unsigned long data_rate; */
+	8,				/* unsigned char data_bits; */
+	1,				/* unsigned char stop_bits; */
+	ASYNC_PARITY_NONE		/* unsigned char parity; */
+};
+
+/* size in bytes of DMA data buffers */
+#define SCABUFSIZE 	1024
+#define SCA_MEM_SIZE	0x40000
+#define SCA_BASE_SIZE   512
+#define SCA_REG_SIZE    16
+#define SCA_MAX_PORTS   4
+#define SCAMAXDESC 	128
+
+#define	BUFFERLISTSIZE	4096
+
+/* SCA-I style DMA buffer descriptor */
+typedef struct _SCADESC
+{
+	u16	next;		/* lower l6 bits of next descriptor addr */
+	u16	buf_ptr;	/* lower 16 bits of buffer addr */
+	u8	buf_base;	/* upper 8 bits of buffer addr */
+	u8	pad1;
+	u16	length;		/* length of buffer */
+	u8	status;		/* status of buffer */
+	u8	pad2;
+} SCADESC, *PSCADESC;
+
+typedef struct _SCADESC_EX
+{
+	/* device driver bookkeeping section */
+	char 	*virt_addr;    	/* virtual address of data buffer */
+	u16	phys_entry;	/* lower 16-bits of physical address of this descriptor */
+} SCADESC_EX, *PSCADESC_EX;
+
+/* The queue of BH actions to be performed */
+
+#define BH_RECEIVE  1
+#define BH_TRANSMIT 2
+#define BH_STATUS   4
+
+#define IO_PIN_SHUTDOWN_LIMIT 100
+
+#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+
+struct	_input_signal_events {
+	int	ri_up;
+	int	ri_down;
+	int	dsr_up;
+	int	dsr_down;
+	int	dcd_up;
+	int	dcd_down;
+	int	cts_up;
+	int	cts_down;
+};
+
+/*
+ * Device instance data structure
+ */
+typedef struct _synclinkmp_info {
+	void *if_ptr;				/* General purpose pointer (used by SPPP) */
+	int			magic;
+	int			flags;
+	int			count;		/* count of opens */
+	int			line;
+	unsigned short		close_delay;
+	unsigned short		closing_wait;	/* time to wait before closing */
+
+	struct mgsl_icount	icount;
+
+	struct termios		normal_termios;
+	struct termios		callout_termios;
+
+	struct tty_struct 	*tty;
+	int			timeout;
+	int			x_char;		/* xon/xoff character */
+	int			blocked_open;	/* # of blocked opens */
+	long			session;	/* Session of opening process */
+	long			pgrp;		/* pgrp of opening process */
+	u16			read_status_mask1;  /* break detection (SR1 indications) */
+	u16			read_status_mask2;  /* parity/framing/overun (SR2 indications) */
+	unsigned char 		ignore_status_mask1;  /* break detection (SR1 indications) */
+	unsigned char		ignore_status_mask2;  /* parity/framing/overun (SR2 indications) */
+	unsigned char 		*tx_buf;
+	int			tx_put;
+	int			tx_get;
+	int			tx_count;
+
+	wait_queue_head_t	open_wait;
+	wait_queue_head_t	close_wait;
+
+	wait_queue_head_t	status_event_wait_q;
+	wait_queue_head_t	event_wait_q;
+	struct timer_list	tx_timer;	/* HDLC transmit timeout timer */
+	struct _synclinkmp_info	*next_device;	/* device list link */
+	struct timer_list	status_timer;	/* input signal status check timer */
+
+	spinlock_t lock;		/* spinlock for synchronizing with ISR */
+	struct tq_struct task;	 		/* task structure for scheduling bh */
+
+	u32 max_frame_size;			/* as set by device config */
+
+	u32 pending_bh;
+
+	int bh_running;				/* Protection from multiple */
+	int isr_overflow;
+	int bh_requested;
+
+	int dcd_chkcount;			/* check counts to prevent */
+	int cts_chkcount;			/* too many IRQs if a signal */
+	int dsr_chkcount;			/* is floating */
+	int ri_chkcount;
+
+	char *buffer_list;			/* virtual address of Rx & Tx buffer lists */
+	unsigned long buffer_list_phys;
+
+	unsigned int rx_buf_count;		/* count of total allocated Rx buffers */
+	SCADESC *rx_buf_list;   		/* list of receive buffer entries */
+	SCADESC_EX rx_buf_list_ex[SCAMAXDESC]; /* list of receive buffer entries */
+	unsigned int current_rx_buf;
+
+	unsigned int tx_buf_count;		/* count of total allocated Tx buffers */
+	SCADESC *tx_buf_list;		/* list of transmit buffer entries */
+	SCADESC_EX tx_buf_list_ex[SCAMAXDESC]; /* list of transmit buffer entries */
+	unsigned int last_tx_buf;
+
+	unsigned char *tmp_rx_buf;
+	unsigned int tmp_rx_buf_count;
+
+	int rx_enabled;
+	int rx_overflow;
+
+	int tx_enabled;
+	int tx_active;
+	u32 idle_mode;
+
+	unsigned char ie0_value;
+	unsigned char ie1_value;
+	unsigned char ie2_value;
+	unsigned char ctrlreg_value;
+	unsigned char old_signals;
+
+	char device_name[25];			/* device instance name */
+
+	int port_count;
+	int adapter_num;
+	int port_num;
+
+	struct _synclinkmp_info *port_array[SCA_MAX_PORTS];
+
+	unsigned int bus_type;			/* expansion bus type (ISA,EISA,PCI) */
+
+	unsigned int irq_level;			/* interrupt level */
+	unsigned long irq_flags;
+	int irq_requested;			/* nonzero if IRQ requested */
+
+	MGSL_PARAMS params;			/* communications parameters */
+
+	unsigned char serial_signals;		/* current serial signal states */
+
+	int irq_occurred;			/* for diagnostics use */
+	unsigned int init_error;		/* Initialization startup error */
+
+	u32 last_mem_alloc;
+	unsigned char* memory_base;		/* shared memory address (PCI only) */
+	u32 phys_memory_base;
+    	int shared_mem_requested;
+
+	unsigned char* sca_base;		/* HD64570 SCA Memory address */
+	u32 phys_sca_base;
+	u32 sca_offset;
+	int sca_base_requested;
+
+	unsigned char* lcr_base;		/* local config registers (PCI only) */
+	u32 phys_lcr_base;
+	u32 lcr_offset;
+	int lcr_mem_requested;
+
+	unsigned char* statctrl_base;		/* status/control register memory */
+	u32 phys_statctrl_base;
+	u32 statctrl_offset;
+	int sca_statctrl_requested;
+
+	u32 misc_ctrl_value;
+	char flag_buf[MAX_ASYNC_BUFFER_SIZE];
+	char char_buf[MAX_ASYNC_BUFFER_SIZE];
+	BOOLEAN drop_rts_on_tx_done;
+
+	struct	_input_signal_events	input_signal_events;
+
+	/* SPPP/Cisco HDLC device parts */
+	int netcount;
+	int dosyncppp;
+	spinlock_t netlock;
+#ifdef CONFIG_SYNCLINK_SYNCPPP
+	struct ppp_device pppdev;
+	char netname[10];
+	struct net_device *netdev;
+	struct net_device_stats netstats;
+	struct net_device netdevice;
+#endif
+} SLMP_INFO;
+
+#define MGSL_MAGIC 0x5401
+
+/*
+ * define serial signal status change macros
+ */
+#define	MISCSTATUS_DCD_LATCHED	(SerialSignal_DCD<<8)	/* indicates change in DCD */
+#define MISCSTATUS_RI_LATCHED	(SerialSignal_RI<<8)	/* indicates change in RI */
+#define MISCSTATUS_CTS_LATCHED	(SerialSignal_CTS<<8)	/* indicates change in CTS */
+#define MISCSTATUS_DSR_LATCHED	(SerialSignal_DSR<<8)	/* change in DSR */
+
+/* Common Register macros */
+#define LPR	0x00
+#define PABR0	0x02
+#define PABR1	0x03
+#define WCRL	0x04
+#define WCRM	0x05
+#define WCRH	0x06
+#define DPCR	0x08
+#define DMER	0x09
+#define ISR0	0x10
+#define ISR1	0x11
+#define ISR2	0x12
+#define IER0	0x14
+#define IER1	0x15
+#define IER2	0x16
+#define ITCR	0x18
+#define INTVR 	0x1a
+#define IMVR	0x1c
+
+/* MSCI Register macros */
+#define TRB	0x20
+#define TRBL	0x20
+#define TRBH	0x21
+#define SR0	0x22
+#define SR1	0x23
+#define SR2	0x24
+#define SR3	0x25
+#define FST	0x26
+#define IE0	0x28
+#define IE1	0x29
+#define IE2	0x2a
+#define FIE	0x2b
+#define CMD	0x2c
+#define MD0	0x2e
+#define MD1	0x2f
+#define MD2	0x30
+#define CTL	0x31
+#define SA0	0x32
+#define SA1	0x33
+#define IDL	0x34
+#define TMC	0x35
+#define RXS	0x36
+#define TXS	0x37
+#define TRC0	0x38
+#define TRC1	0x39
+#define RRC	0x3a
+#define CST0	0x3c
+#define CST1	0x3d
+
+/* Timer Register Macros */
+#define TCNT	0x60
+#define TCNTL	0x60
+#define TCNTH	0x61
+#define TCONR	0x62
+#define TCONRL	0x62
+#define TCONRH	0x63
+#define TMCS	0x64
+#define TEPR	0x65
+
+/* DMA Controller Register macros */
+#define DAR	0x80
+#define DARL	0x80
+#define DARH	0x81
+#define DARB	0x82
+#define BAR	0x80
+#define BARL	0x80
+#define BARH	0x81
+#define BARB	0x82
+#define SAR	0x84
+#define SARL	0x84
+#define SARH	0x85
+#define SARB	0x86
+#define CPB	0x86
+#define CDA	0x88
+#define CDAL	0x88
+#define CDAH	0x89
+#define EDA	0x8a
+#define EDAL	0x8a
+#define EDAH	0x8b
+#define BFL	0x8c
+#define BFLL	0x8c
+#define BFLH	0x8d
+#define BCR	0x8e
+#define BCRL	0x8e
+#define BCRH	0x8f
+#define DSR	0x90
+#define DMR	0x91
+#define FCT	0x93
+#define DIR	0x94
+#define DCMD	0x95
+
+/* combine with timer or DMA register address */
+#define TIMER0	0x00
+#define TIMER1	0x08
+#define TIMER2	0x10
+#define TIMER3	0x18
+#define RXDMA 	0x00
+#define TXDMA 	0x20
+
+/* SCA Command Codes */
+#define NOOP		0x00
+#define TXRESET		0x01
+#define TXENABLE	0x02
+#define TXDISABLE	0x03
+#define TXCRCINIT	0x04
+#define TXCRCEXCL	0x05
+#define TXEOM		0x06
+#define TXABORT		0x07
+#define MPON		0x08
+#define TXBUFCLR	0x09
+#define RXRESET		0x11
+#define RXENABLE	0x12
+#define RXDISABLE	0x13
+#define RXCRCINIT	0x14
+#define RXREJECT	0x15
+#define SEARCHMP	0x16
+#define RXCRCEXCL	0x17
+#define RXCRCCALC	0x18
+#define CHRESET		0x21
+#define HUNT		0x31
+
+/* DMA command codes */
+#define SWABORT		0x01
+#define FEICLEAR	0x02
+
+/* IE0 */
+#define TXINTE 		BIT7
+#define RXINTE 		BIT6
+#define TXRDYE 		BIT1
+#define RXRDYE 		BIT0
+
+/* IE1 & SR1 */
+#define UDRN   	BIT7
+#define IDLE   	BIT6
+#define SYNCD  	BIT4
+#define FLGD   	BIT4
+#define CCTS   	BIT3
+#define CDCD   	BIT2
+#define BRKD   	BIT1
+#define ABTD   	BIT1
+#define GAPD   	BIT1
+#define BRKE   	BIT0
+#define IDLD	BIT0
+
+/* IE2 & SR2 */
+#define EOM	BIT7
+#define PMP	BIT6
+#define SHRT	BIT6
+#define PE	BIT5
+#define ABT	BIT5
+#define FRME	BIT4
+#define RBIT	BIT4
+#define OVRN	BIT3
+#define CRCE	BIT2
+
+
+#define jiffies_from_ms(a) ((((a) * HZ)/1000)+1)
+
+/*
+ * Global linked list of SyncLink devices
+ */
+static SLMP_INFO *synclinkmp_device_list = NULL;
+static int synclinkmp_adapter_count = -1;
+static int synclinkmp_device_count = 0;
+
+/*
+ * Set this param to non-zero to load eax with the
+ * .text section address and breakpoint on module load.
+ * This is useful for use with gdb and add-symbol-file command.
+ */
+static int break_on_load=0;
+
+/*
+ * Driver major number, defaults to zero to get auto
+ * assigned major number. May be forced as module parameter.
+ */
+static int ttymajor=0;
+static int cuamajor=0;
+
+/*
+ * Array of user specified options for ISA adapters.
+ */
+static int debug_level = 0;
+static int maxframe[MAX_DEVICES] = {0,};
+static int dosyncppp[MAX_DEVICES] = {0,};
+
+MODULE_PARM(break_on_load,"i");
+MODULE_PARM(ttymajor,"i");
+MODULE_PARM(cuamajor,"i");
+MODULE_PARM(debug_level,"i");
+MODULE_PARM(maxframe,"1-" __MODULE_STRING(MAX_DEVICES) "i");
+MODULE_PARM(dosyncppp,"1-" __MODULE_STRING(MAX_DEVICES) "i");
+
+static char *driver_name = "SyncLink MultiPort driver";
+static char *driver_version = "$Revision: 4.4 $";
+
+static int synclinkmp_init_one(struct pci_dev *dev,const struct pci_device_id *ent);
+static void synclinkmp_remove_one(struct pci_dev *dev);
+
+static struct pci_device_id synclinkmp_pci_tbl[] __devinitdata = {
+	{ PCI_VENDOR_ID_MICROGATE, PCI_DEVICE_ID_MICROGATE_SCA, PCI_ANY_ID, PCI_ANY_ID, },
+	{ 0, }, /* terminate list */
+};
+MODULE_DEVICE_TABLE(pci, synclinkmp_pci_tbl);
+
+MODULE_LICENSE("GPL");
+
+static struct pci_driver synclinkmp_pci_driver = {
+	name:		"synclinkmp",
+	id_table:	synclinkmp_pci_tbl,
+	probe:		synclinkmp_init_one,
+	remove:		__devexit_p(synclinkmp_remove_one),
+};
+
+
+static struct tty_driver serial_driver, callout_driver;
+static int serial_refcount;
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+static struct tty_struct *serial_table[MAX_DEVICES];
+static struct termios *serial_termios[MAX_DEVICES];
+static struct termios *serial_termios_locked[MAX_DEVICES];
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+
+/* tty callbacks */
+
+static int  open(struct tty_struct *tty, struct file * filp);
+static void close(struct tty_struct *tty, struct file * filp);
+static void hangup(struct tty_struct *tty);
+static void set_termios(struct tty_struct *tty, struct termios *old_termios);
+
+static int  write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count);
+static void put_char(struct tty_struct *tty, unsigned char ch);
+static void send_xchar(struct tty_struct *tty, char ch);
+static void wait_until_sent(struct tty_struct *tty, int timeout);
+static int  write_room(struct tty_struct *tty);
+static void flush_chars(struct tty_struct *tty);
+static void flush_buffer(struct tty_struct *tty);
+static void tx_hold(struct tty_struct *tty);
+static void tx_release(struct tty_struct *tty);
+
+static int  ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg);
+static int  read_proc(char *page, char **start, off_t off, int count,int *eof, void *data);
+static int  chars_in_buffer(struct tty_struct *tty);
+static void throttle(struct tty_struct * tty);
+static void unthrottle(struct tty_struct * tty);
+static void set_break(struct tty_struct *tty, int break_state);
+
+/* sppp support and callbacks */
+
+#ifdef CONFIG_SYNCLINK_SYNCPPP
+static void sppp_init(SLMP_INFO *info);
+static void sppp_delete(SLMP_INFO *info);
+static void sppp_rx_done(SLMP_INFO *info, char *buf, int size);
+static void sppp_tx_done(SLMP_INFO *info);
+
+static int  sppp_cb_open(struct net_device *d);
+static int  sppp_cb_close(struct net_device *d);
+static int  sppp_cb_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
+static int  sppp_cb_tx(struct sk_buff *skb, struct net_device *dev);
+static void sppp_cb_tx_timeout(struct net_device *dev);
+static struct net_device_stats *sppp_cb_net_stats(struct net_device *dev);
+#endif
+
+/* ioctl handlers */
+
+static int  get_stats(SLMP_INFO *info, struct mgsl_icount *user_icount);
+static int  get_params(SLMP_INFO *info, MGSL_PARAMS *params);
+static int  set_params(SLMP_INFO *info, MGSL_PARAMS *params);
+static int  get_txidle(SLMP_INFO *info, int*idle_mode);
+static int  set_txidle(SLMP_INFO *info, int idle_mode);
+static int  tx_enable(SLMP_INFO *info, int enable);
+static int  tx_abort(SLMP_INFO *info);
+static int  rx_enable(SLMP_INFO *info, int enable);
+static int  map_status(int signals);
+static int  modem_input_wait(SLMP_INFO *info,int arg);
+static int  wait_mgsl_event(SLMP_INFO *info, int *mask_ptr);
+static int  get_modem_info(SLMP_INFO *info, unsigned int *value);
+static int  set_modem_info(SLMP_INFO *info, unsigned int cmd,unsigned int *value);
+static void set_break(struct tty_struct *tty, int break_state);
+
+static void add_device(SLMP_INFO *info);
+static void device_init(int adapter_num, struct pci_dev *pdev);
+static int  claim_resources(SLMP_INFO *info);
+static void release_resources(SLMP_INFO *info);
+
+static int  startup(SLMP_INFO *info);
+static int  block_til_ready(struct tty_struct *tty, struct file * filp,SLMP_INFO *info);
+static void shutdown(SLMP_INFO *info);
+static void program_hw(SLMP_INFO *info);
+static void change_params(SLMP_INFO *info);
+
+static int  init_adapter(SLMP_INFO *info);
+static int  register_test(SLMP_INFO *info);
+static int  irq_test(SLMP_INFO *info);
+static int  loopback_test(SLMP_INFO *info);
+static int  adapter_test(SLMP_INFO *info);
+static int  memory_test(SLMP_INFO *info);
+
+static void reset_adapter(SLMP_INFO *info);
+static void reset_port(SLMP_INFO *info);
+static void async_mode(SLMP_INFO *info);
+static void hdlc_mode(SLMP_INFO *info);
+
+static void rx_stop(SLMP_INFO *info);
+static void rx_start(SLMP_INFO *info);
+static void rx_reset_buffers(SLMP_INFO *info);
+static void rx_free_frame_buffers(SLMP_INFO *info, unsigned int first, unsigned int last);
+static int  rx_get_frame(SLMP_INFO *info);
+
+static void tx_start(SLMP_INFO *info);
+static void tx_stop(SLMP_INFO *info);
+static void tx_load_fifo(SLMP_INFO *info);
+static void tx_set_idle(SLMP_INFO *info);
+static void tx_load_dma_buffer(SLMP_INFO *info, const char *buf, unsigned int count);
+
+static void get_signals(SLMP_INFO *info);
+static void set_signals(SLMP_INFO *info);
+static void enable_loopback(SLMP_INFO *info, int enable);
+static void set_rate(SLMP_INFO *info, u32 data_rate);
+
+static int  bh_action(SLMP_INFO *info);
+static void bh_handler(void* Context);
+static void bh_receive(SLMP_INFO *info);
+static void bh_transmit(SLMP_INFO *info);
+static void bh_status(SLMP_INFO *info);
+static void isr_timer(SLMP_INFO *info);
+static void isr_rxint(SLMP_INFO *info);
+static void isr_rxrdy(SLMP_INFO *info);
+static void isr_txint(SLMP_INFO *info);
+static void isr_txrdy(SLMP_INFO *info);
+static void isr_rxdmaok(SLMP_INFO *info);
+static void isr_rxdmaerror(SLMP_INFO *info);
+static void isr_txdmaok(SLMP_INFO *info);
+static void isr_txdmaerror(SLMP_INFO *info);
+static void isr_io_pin(SLMP_INFO *info, u16 status);
+static void synclinkmp_interrupt(int irq, void *dev_id, struct pt_regs * regs);
+
+static int  alloc_dma_bufs(SLMP_INFO *info);
+static void free_dma_bufs(SLMP_INFO *info);
+static int  alloc_buf_list(SLMP_INFO *info);
+static int  alloc_frame_bufs(SLMP_INFO *info, SCADESC *list, SCADESC_EX *list_ex,int count);
+static int  alloc_tmp_rx_buf(SLMP_INFO *info);
+static void free_tmp_rx_buf(SLMP_INFO *info);
+
+static void load_pci_memory(SLMP_INFO *info, char* dest, const char* src, unsigned short count);
+static void trace_block(SLMP_INFO *info, const char* data, int count, int xmit);
+static void tx_timeout(unsigned long context);
+static void status_timeout(unsigned long context);
+
+static unsigned char read_reg(SLMP_INFO *info, unsigned char addr);
+static void write_reg(SLMP_INFO *info, unsigned char addr, unsigned char val);
+static u16 read_reg16(SLMP_INFO *info, unsigned char addr);
+static void write_reg16(SLMP_INFO *info, unsigned char addr, u16 val);
+static unsigned char read_status_reg(SLMP_INFO * info);
+static void write_control_reg(SLMP_INFO * info);
+
+
+static unsigned char rx_active_fifo_level = 16;	// rx request FIFO activation level in bytes
+static unsigned char tx_active_fifo_level = 16;	// tx request FIFO activation level in bytes
+static unsigned char tx_negate_fifo_level = 32;	// tx request FIFO negation level in bytes
+
+static u32 misc_ctrl_value = 0x007e4040;
+static u32 lcr1_brdr_value = 0x0080002d;
+
+static u32 read_ahead_count = 8;
+
+/* DPCR, DMA Priority Control
+ *
+ * 07..05  Not used, must be 0
+ * 04      BRC, bus release condition: 0=all transfers complete
+ *              1=release after 1 xfer on all channels
+ * 03      CCC, channel change condition: 0=every cycle
+ *              1=after each channel completes all xfers
+ * 02..00  PR<2..0>, priority 100=round robin
+ *
+ * 00000100 = 0x00
+ */
+static unsigned char dma_priority = 0x04;
+
+// Number of bytes that can be written to shared RAM
+// in a single write operation
+static u32 sca_pci_load_interval = 64;
+
+/*
+ * 1st function defined in .text section. Calling this function in
+ * init_module() followed by a breakpoint allows a remote debugger
+ * (gdb) to get the .text address for the add-symbol-file command.
+ * This allows remote debugging of dynamically loadable modules.
+ */
+static void* synclinkmp_get_text_ptr(void);
+static void* synclinkmp_get_text_ptr() {return synclinkmp_get_text_ptr;}
+
+static inline int sanity_check(SLMP_INFO *info,
+			       kdev_t device, const char *routine)
+{
+#ifdef SANITY_CHECK
+	static const char *badmagic =
+		"Warning: bad magic number for synclinkmp_struct (%s) in %s\n";
+	static const char *badinfo =
+		"Warning: null synclinkmp_struct for (%s) in %s\n";
+
+	if (!info) {
+		printk(badinfo, kdevname(device), routine);
+		return 1;
+	}
+	if (info->magic != MGSL_MAGIC) {
+		printk(badmagic, kdevname(device), routine);
+		return 1;
+	}
+#endif
+	return 0;
+}
+
+/* tty callbacks */
+
+/* Called when a port is opened.  Init and enable port.
+ */
+static int open(struct tty_struct *tty, struct file *filp)
+{
+	SLMP_INFO *info;
+	int retval, line;
+	unsigned long flags;
+
+	line = minor(tty->device) - tty->driver.minor_start;
+	if ((line < 0) || (line >= synclinkmp_device_count)) {
+		printk("%s(%d): open with illegal line #%d.\n",
+			__FILE__,__LINE__,line);
+		return -ENODEV;
+	}
+
+	info = synclinkmp_device_list;
+	while(info && info->line != line)
+		info = info->next_device;
+	if ( !info ){
+		printk("%s(%d):%s Can't find specified device on open (line=%d)\n",
+			__FILE__,__LINE__,info->device_name,line);
+		return -ENODEV;
+	}
+
+	if ( info->init_error ) {
+		printk("%s(%d):%s device is not allocated, init error=%d\n",
+			__FILE__,__LINE__,info->device_name,info->init_error);
+		return -ENODEV;
+	}
+
+	tty->driver_data = info;
+	info->tty = tty;
+	if (sanity_check(info, tty->device, "open"))
+		return -ENODEV;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):%s open(), old ref count = %d\n",
+			 __FILE__,__LINE__,tty->driver.name, info->count);
+
+	MOD_INC_USE_COUNT;
+
+	/* If port is closing, signal caller to try again */
+	if (tty_hung_up_p(filp) || info->flags & ASYNC_CLOSING){
+		if (info->flags & ASYNC_CLOSING)
+			interruptible_sleep_on(&info->close_wait);
+		retval = ((info->flags & ASYNC_HUP_NOTIFY) ?
+			-EAGAIN : -ERESTARTSYS);
+		goto cleanup;
+	}
+
+	info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+
+	spin_lock_irqsave(&info->netlock, flags);
+	if (info->netcount) {
+		retval = -EBUSY;
+		spin_unlock_irqrestore(&info->netlock, flags);
+		goto cleanup;
+	}
+	info->count++;
+	spin_unlock_irqrestore(&info->netlock, flags);
+
+	if (info->count == 1) {
+		/* 1st open on this device, init hardware */
+		retval = startup(info);
+		if (retval < 0)
+			goto cleanup;
+	}
+
+	retval = block_til_ready(tty, filp, info);
+	if (retval) {
+		if (debug_level >= DEBUG_LEVEL_INFO)
+			printk("%s(%d):%s block_til_ready() returned %d\n",
+				 __FILE__,__LINE__, info->device_name, retval);
+		goto cleanup;
+	}
+
+	if ((info->count == 1) &&
+	    info->flags & ASYNC_SPLIT_TERMIOS) {
+		if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+			*tty->termios = info->normal_termios;
+		else
+			*tty->termios = info->callout_termios;
+		change_params(info);
+	}
+
+	info->session = current->session;
+	info->pgrp    = current->pgrp;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):%s open() success\n",
+			 __FILE__,__LINE__, info->device_name);
+	retval = 0;
+
+cleanup:
+	if (retval) {
+		if(MOD_IN_USE)
+			MOD_DEC_USE_COUNT;
+		if(info->count)
+			info->count--;
+	}
+
+	return retval;
+}
+
+/* Called when port is closed. Wait for remaining data to be
+ * sent. Disable port and free resources.
+ */
+static void close(struct tty_struct *tty, struct file *filp)
+{
+	SLMP_INFO * info = (SLMP_INFO *)tty->driver_data;
+
+	if (!info || sanity_check(info, tty->device, "close"))
+		return;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):%s close() entry, count=%d\n",
+			 __FILE__,__LINE__, info->device_name, info->count);
+
+	if (!info->count || tty_hung_up_p(filp))
+		goto cleanup;
+
+	if ((tty->count == 1) && (info->count != 1)) {
+		/*
+		 * tty->count is 1 and the tty structure will be freed.
+		 * info->count should be one in this case.
+		 * if it's not, correct it so that the port is shutdown.
+		 */
+		printk("%s(%d):%s close: bad refcount; tty->count is 1, "
+		       "info->count is %d\n",
+			 __FILE__,__LINE__, info->device_name, info->count);
+		info->count = 1;
+	}
+
+	info->count--;
+
+	/* if at least one open remaining, leave hardware active */
+	if (info->count)
+		goto cleanup;
+
+	info->flags |= ASYNC_CLOSING;
+
+	/* Save the termios structure, since this port may have
+	 * separate termios for callout and dialin.
+	 */
+	if (info->flags & ASYNC_NORMAL_ACTIVE)
+		info->normal_termios = *tty->termios;
+	if (info->flags & ASYNC_CALLOUT_ACTIVE)
+		info->callout_termios = *tty->termios;
+
+	/* set tty->closing to notify line discipline to
+	 * only process XON/XOFF characters. Only the N_TTY
+	 * discipline appears to use this (ppp does not).
+	 */
+	tty->closing = 1;
+
+	/* wait for transmit data to clear all layers */
+
+	if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) {
+		if (debug_level >= DEBUG_LEVEL_INFO)
+			printk("%s(%d):%s close() calling tty_wait_until_sent\n",
+				 __FILE__,__LINE__, info->device_name );
+		tty_wait_until_sent(tty, info->closing_wait);
+	}
+
+ 	if (info->flags & ASYNC_INITIALIZED)
+ 		wait_until_sent(tty, info->timeout);
+
+	if (tty->driver.flush_buffer)
+		tty->driver.flush_buffer(tty);
+
+	if (tty->ldisc.flush_buffer)
+		tty->ldisc.flush_buffer(tty);
+
+	shutdown(info);
+
+	tty->closing = 0;
+	info->tty = 0;
+
+	if (info->blocked_open) {
+		if (info->close_delay) {
+			set_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);
+
+cleanup:
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):%s close() exit, count=%d\n", __FILE__,__LINE__,
+			tty->driver.name, info->count);
+	if(MOD_IN_USE)
+		MOD_DEC_USE_COUNT;
+}
+
+/* Called by tty_hangup() when a hangup is signaled.
+ * This is the same as closing all open descriptors for the port.
+ */
+static void hangup(struct tty_struct *tty)
+{
+	SLMP_INFO *info = (SLMP_INFO *)tty->driver_data;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):%s hangup()\n",
+			 __FILE__,__LINE__, info->device_name );
+
+	if (sanity_check(info, tty->device, "hangup"))
+		return;
+
+	flush_buffer(tty);
+	shutdown(info);
+
+	info->count = 0;
+	info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
+	info->tty = 0;
+
+	wake_up_interruptible(&info->open_wait);
+}
+
+/* Set new termios settings
+ */
+static void set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+	SLMP_INFO *info = (SLMP_INFO *)tty->driver_data;
+	unsigned long flags;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):%s set_termios()\n", __FILE__,__LINE__,
+			tty->driver.name );
+
+	/* just return if nothing has changed */
+	if ((tty->termios->c_cflag == old_termios->c_cflag)
+	    && (RELEVANT_IFLAG(tty->termios->c_iflag)
+		== RELEVANT_IFLAG(old_termios->c_iflag)))
+	  return;
+
+	change_params(info);
+
+	/* Handle transition to B0 status */
+	if (old_termios->c_cflag & CBAUD &&
+	    !(tty->termios->c_cflag & CBAUD)) {
+		info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
+		spin_lock_irqsave(&info->lock,flags);
+	 	set_signals(info);
+		spin_unlock_irqrestore(&info->lock,flags);
+	}
+
+	/* Handle transition away from B0 status */
+	if (!(old_termios->c_cflag & CBAUD) &&
+	    tty->termios->c_cflag & CBAUD) {
+		info->serial_signals |= SerialSignal_DTR;
+ 		if (!(tty->termios->c_cflag & CRTSCTS) ||
+ 		    !test_bit(TTY_THROTTLED, &tty->flags)) {
+			info->serial_signals |= SerialSignal_RTS;
+ 		}
+		spin_lock_irqsave(&info->lock,flags);
+	 	set_signals(info);
+		spin_unlock_irqrestore(&info->lock,flags);
+	}
+
+	/* Handle turning off CRTSCTS */
+	if (old_termios->c_cflag & CRTSCTS &&
+	    !(tty->termios->c_cflag & CRTSCTS)) {
+		tty->hw_stopped = 0;
+		tx_release(tty);
+	}
+}
+
+/* Send a block of data
+ *
+ * Arguments:
+ *
+ * 	tty		pointer to tty information structure
+ * 	from_user	flag: 1 = from user process
+ * 	buf		pointer to buffer containing send data
+ * 	count		size of send data in bytes
+ *
+ * Return Value:	number of characters written
+ */
+static int write(struct tty_struct *tty, int from_user,
+		 const unsigned char *buf, int count)
+{
+	int	c, ret = 0, err;
+	SLMP_INFO *info = (SLMP_INFO *)tty->driver_data;
+	unsigned long flags;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):%s write() count=%d\n",
+		       __FILE__,__LINE__,info->device_name,count);
+
+	if (sanity_check(info, tty->device, "write"))
+		goto cleanup;
+
+	if (!tty || !info->tx_buf)
+		goto cleanup;
+
+	if (info->params.mode == MGSL_MODE_HDLC) {
+		if (count > info->max_frame_size) {
+			ret = -EIO;
+			goto cleanup;
+		}
+		if (info->tx_active)
+			goto cleanup;
+		if (info->tx_count) {
+			/* send accumulated data from send_char() calls */
+			/* as frame and wait before accepting more data. */
+			tx_load_dma_buffer(info, info->tx_buf, info->tx_count);
+			goto start;
+		}
+		if (!from_user) {
+			ret = info->tx_count = count;
+			tx_load_dma_buffer(info, buf, count);
+			goto start;
+		}
+	}
+
+	for (;;) {
+		c = MIN(count,
+			MIN(info->max_frame_size - info->tx_count - 1,
+			    info->max_frame_size - info->tx_put));
+		if (c <= 0)
+			break;
+			
+		if (from_user) {
+			COPY_FROM_USER(err, info->tx_buf + info->tx_put, buf, c);
+			if (err) {
+				if (!ret)
+					ret = -EFAULT;
+				break;
+			}
+		} else
+			memcpy(info->tx_buf + info->tx_put, buf, c);
+
+		spin_lock_irqsave(&info->lock,flags);
+		info->tx_put += c;
+		if (info->tx_put >= info->max_frame_size)
+			info->tx_put -= info->max_frame_size;
+		info->tx_count += c;
+		spin_unlock_irqrestore(&info->lock,flags);
+
+		buf += c;
+		count -= c;
+		ret += c;
+	}
+
+	if (info->params.mode == MGSL_MODE_HDLC) {
+		if (count) {
+			ret = info->tx_count = 0;
+			goto cleanup;
+		}
+		tx_load_dma_buffer(info, info->tx_buf, info->tx_count);
+	}
+start:
+ 	if (info->tx_count && !tty->stopped && !tty->hw_stopped) {
+		spin_lock_irqsave(&info->lock,flags);
+		if (!info->tx_active)
+		 	tx_start(info);
+		spin_unlock_irqrestore(&info->lock,flags);
+ 	}
+
+cleanup:
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk( "%s(%d):%s write() returning=%d\n",
+			__FILE__,__LINE__,info->device_name,ret);
+	return ret;
+}
+
+/* Add a character to the transmit buffer.
+ */
+static void put_char(struct tty_struct *tty, unsigned char ch)
+{
+	SLMP_INFO *info = (SLMP_INFO *)tty->driver_data;
+	unsigned long flags;
+
+	if ( debug_level >= DEBUG_LEVEL_INFO ) {
+		printk( "%s(%d):%s put_char(%d)\n",
+			__FILE__,__LINE__,info->device_name,ch);
+	}
+
+	if (sanity_check(info, tty->device, "put_char"))
+		return;
+
+	if (!tty || !info->tx_buf)
+		return;
+
+	spin_lock_irqsave(&info->lock,flags);
+
+	if ( (info->params.mode != MGSL_MODE_HDLC) ||
+	     !info->tx_active ) {
+
+		if (info->tx_count < info->max_frame_size - 1) {
+			info->tx_buf[info->tx_put++] = ch;
+			if (info->tx_put >= info->max_frame_size)
+				info->tx_put -= info->max_frame_size;
+			info->tx_count++;
+		}
+	}
+
+	spin_unlock_irqrestore(&info->lock,flags);
+}
+
+/* Send a high-priority XON/XOFF character
+ */
+static void send_xchar(struct tty_struct *tty, char ch)
+{
+	SLMP_INFO *info = (SLMP_INFO *)tty->driver_data;
+	unsigned long flags;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):%s send_xchar(%d)\n",
+			 __FILE__,__LINE__, info->device_name, ch );
+
+	if (sanity_check(info, tty->device, "send_xchar"))
+		return;
+
+	info->x_char = ch;
+	if (ch) {
+		/* Make sure transmit interrupts are on */
+		spin_lock_irqsave(&info->lock,flags);
+		if (!info->tx_enabled)
+		 	tx_start(info);
+		spin_unlock_irqrestore(&info->lock,flags);
+	}
+}
+
+/* Wait until the transmitter is empty.
+ */
+static void wait_until_sent(struct tty_struct *tty, int timeout)
+{
+	SLMP_INFO * info = (SLMP_INFO *)tty->driver_data;
+	unsigned long orig_jiffies, char_time;
+
+	if (!info )
+		return;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):%s wait_until_sent() entry\n",
+			 __FILE__,__LINE__, info->device_name );
+
+	if (sanity_check(info, tty->device, "wait_until_sent"))
+		return;
+
+	if (!(info->flags & ASYNC_INITIALIZED))
+		goto exit;
+
+	orig_jiffies = jiffies;
+
+	/* Set check interval to 1/5 of estimated time to
+	 * send a character, and make it at least 1. The check
+	 * interval should also be less than the timeout.
+	 * Note: use tight timings here to satisfy the NIST-PCTS.
+	 */
+
+	if ( info->params.data_rate ) {
+	       	char_time = info->timeout/(32 * 5);
+		if (!char_time)
+			char_time++;
+	} else
+		char_time = 1;
+
+	if (timeout)
+		char_time = MIN(char_time, timeout);
+
+	if ( info->params.mode == MGSL_MODE_HDLC ) {
+		while (info->tx_active) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule_timeout(char_time);
+			if (signal_pending(current))
+				break;
+			if (timeout && ((orig_jiffies + timeout) < jiffies))
+				break;
+		}
+	} else {
+		//TODO: determine if there is something similar to USC16C32
+		// 	TXSTATUS_ALL_SENT status
+		while ( info->tx_active && info->tx_enabled) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule_timeout(char_time);
+			if (signal_pending(current))
+				break;
+			if (timeout && ((orig_jiffies + timeout) < jiffies))
+				break;
+		}
+	}
+
+exit:
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):%s wait_until_sent() exit\n",
+			 __FILE__,__LINE__, info->device_name );
+}
+
+/* Return the count of free bytes in transmit buffer
+ */
+static int write_room(struct tty_struct *tty)
+{
+	SLMP_INFO *info = (SLMP_INFO *)tty->driver_data;
+	int ret;
+
+	if (sanity_check(info, tty->device, "write_room"))
+		return 0;
+
+	if (info->params.mode == MGSL_MODE_HDLC) {
+		ret = (info->tx_active) ? 0 : HDLC_MAX_FRAME_SIZE;
+	} else {
+		ret = info->max_frame_size - info->tx_count - 1;
+		if (ret < 0)
+			ret = 0;
+	}
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):%s write_room()=%d\n",
+		       __FILE__, __LINE__, info->device_name, ret);
+
+	return ret;
+}
+
+/* enable transmitter and send remaining buffered characters
+ */
+static void flush_chars(struct tty_struct *tty)
+{
+	SLMP_INFO *info = (SLMP_INFO *)tty->driver_data;
+	unsigned long flags;
+
+	if ( debug_level >= DEBUG_LEVEL_INFO )
+		printk( "%s(%d):%s flush_chars() entry tx_count=%d\n",
+			__FILE__,__LINE__,info->device_name,info->tx_count);
+
+	if (sanity_check(info, tty->device, "flush_chars"))
+		return;
+
+	if (info->tx_count <= 0 || tty->stopped || tty->hw_stopped ||
+	    !info->tx_buf)
+		return;
+
+	if ( debug_level >= DEBUG_LEVEL_INFO )
+		printk( "%s(%d):%s flush_chars() entry, starting transmitter\n",
+			__FILE__,__LINE__,info->device_name );
+
+	spin_lock_irqsave(&info->lock,flags);
+
+	if (!info->tx_active) {
+		if ( (info->params.mode == MGSL_MODE_HDLC) &&
+			info->tx_count ) {
+			/* operating in synchronous (frame oriented) mode */
+			/* copy data from circular tx_buf to */
+			/* transmit DMA buffer. */
+			tx_load_dma_buffer(info,
+				 info->tx_buf,info->tx_count);
+		}
+	 	tx_start(info);
+	}
+
+	spin_unlock_irqrestore(&info->lock,flags);
+}
+
+/* Discard all data in the send buffer
+ */
+static void flush_buffer(struct tty_struct *tty)
+{
+	SLMP_INFO *info = (SLMP_INFO *)tty->driver_data;
+	unsigned long flags;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):%s flush_buffer() entry\n",
+			 __FILE__,__LINE__, info->device_name );
+
+	if (sanity_check(info, tty->device, "flush_buffer"))
+		return;
+
+	spin_lock_irqsave(&info->lock,flags);
+	info->tx_count = info->tx_put = info->tx_get = 0;
+	del_timer(&info->tx_timer);
+	spin_unlock_irqrestore(&info->lock,flags);
+
+	wake_up_interruptible(&tty->write_wait);
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+	    tty->ldisc.write_wakeup)
+		(tty->ldisc.write_wakeup)(tty);
+}
+
+/* throttle (stop) transmitter
+ */
+static void tx_hold(struct tty_struct *tty)
+{
+	SLMP_INFO *info = (SLMP_INFO *)tty->driver_data;
+	unsigned long flags;
+
+	if (sanity_check(info, tty->device, "tx_hold"))
+		return;
+
+	if ( debug_level >= DEBUG_LEVEL_INFO )
+		printk("%s(%d):%s tx_hold()\n",
+			__FILE__,__LINE__,info->device_name);
+
+	spin_lock_irqsave(&info->lock,flags);
+	if (info->tx_enabled)
+	 	tx_stop(info);
+	spin_unlock_irqrestore(&info->lock,flags);
+}
+
+/* release (start) transmitter
+ */
+static void tx_release(struct tty_struct *tty)
+{
+	SLMP_INFO *info = (SLMP_INFO *)tty->driver_data;
+	unsigned long flags;
+
+	if (sanity_check(info, tty->device, "tx_release"))
+		return;
+
+	if ( debug_level >= DEBUG_LEVEL_INFO )
+		printk("%s(%d):%s tx_release()\n",
+			__FILE__,__LINE__,info->device_name);
+
+	spin_lock_irqsave(&info->lock,flags);
+	if (!info->tx_enabled)
+	 	tx_start(info);
+	spin_unlock_irqrestore(&info->lock,flags);
+}
+
+/* Service an IOCTL request
+ *
+ * Arguments:
+ *
+ * 	tty	pointer to tty instance data
+ * 	file	pointer to associated file object for device
+ * 	cmd	IOCTL command code
+ * 	arg	command argument/context
+ *
+ * Return Value:	0 if success, otherwise error code
+ */
+static int ioctl(struct tty_struct *tty, struct file *file,
+		 unsigned int cmd, unsigned long arg)
+{
+	SLMP_INFO *info = (SLMP_INFO *)tty->driver_data;
+	int error;
+	struct mgsl_icount cnow;	/* kernel counter temps */
+	struct serial_icounter_struct *p_cuser;	/* user space */
+	unsigned long flags;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):%s ioctl() cmd=%08X\n", __FILE__,__LINE__,
+			info->device_name, cmd );
+
+	if (sanity_check(info, tty->device, "ioctl"))
+		return -ENODEV;
+
+	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+	    (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
+		if (tty->flags & (1 << TTY_IO_ERROR))
+		    return -EIO;
+	}
+
+	switch (cmd) {
+	case TIOCMGET:
+		return get_modem_info(info, (unsigned int *) arg);
+	case TIOCMBIS:
+	case TIOCMBIC:
+	case TIOCMSET:
+		return set_modem_info(info, cmd, (unsigned int *) arg);
+	case MGSL_IOCGPARAMS:
+		return get_params(info,(MGSL_PARAMS *)arg);
+	case MGSL_IOCSPARAMS:
+		return set_params(info,(MGSL_PARAMS *)arg);
+	case MGSL_IOCGTXIDLE:
+		return get_txidle(info,(int*)arg);
+	case MGSL_IOCSTXIDLE:
+		return set_txidle(info,(int)arg);
+	case MGSL_IOCTXENABLE:
+		return tx_enable(info,(int)arg);
+	case MGSL_IOCRXENABLE:
+		return rx_enable(info,(int)arg);
+	case MGSL_IOCTXABORT:
+		return tx_abort(info);
+	case MGSL_IOCGSTATS:
+		return get_stats(info,(struct mgsl_icount*)arg);
+	case MGSL_IOCWAITEVENT:
+		return wait_mgsl_event(info,(int*)arg);
+	case MGSL_IOCLOOPTXDONE:
+		return 0; // TODO: Not supported, need to document
+	case MGSL_IOCCLRMODCOUNT:
+		while(MOD_IN_USE)
+			MOD_DEC_USE_COUNT;
+		return 0;
+		/* Wait for modem input (DCD,RI,DSR,CTS) change
+		 * as specified by mask in arg (TIOCM_RNG/DSR/CD/CTS)
+		 */
+	case TIOCMIWAIT:
+		return modem_input_wait(info,(int)arg);
+		
+		/*
+		 * 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:
+		spin_lock_irqsave(&info->lock,flags);
+		cnow = info->icount;
+		spin_unlock_irqrestore(&info->lock,flags);
+		p_cuser = (struct serial_icounter_struct *) arg;
+		PUT_USER(error,cnow.cts, &p_cuser->cts);
+		if (error) return error;
+		PUT_USER(error,cnow.dsr, &p_cuser->dsr);
+		if (error) return error;
+		PUT_USER(error,cnow.rng, &p_cuser->rng);
+		if (error) return error;
+		PUT_USER(error,cnow.dcd, &p_cuser->dcd);
+		if (error) return error;
+		PUT_USER(error,cnow.rx, &p_cuser->rx);
+		if (error) return error;
+		PUT_USER(error,cnow.tx, &p_cuser->tx);
+		if (error) return error;
+		PUT_USER(error,cnow.frame, &p_cuser->frame);
+		if (error) return error;
+		PUT_USER(error,cnow.overrun, &p_cuser->overrun);
+		if (error) return error;
+		PUT_USER(error,cnow.parity, &p_cuser->parity);
+		if (error) return error;
+		PUT_USER(error,cnow.brk, &p_cuser->brk);
+		if (error) return error;
+		PUT_USER(error,cnow.buf_overrun, &p_cuser->buf_overrun);
+		if (error) return error;
+		return 0;
+	default:
+		return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+/*
+ * /proc fs routines....
+ */
+
+static inline int line_info(char *buf, SLMP_INFO *info)
+{
+	char	stat_buf[30];
+	int	ret;
+	unsigned long flags;
+
+	ret = sprintf(buf, "%s: SCABase=%08x Mem=%08X StatusControl=%08x LCR=%08X\n"
+		       "\tIRQ=%d MaxFrameSize=%u\n",
+		info->device_name,
+		info->phys_sca_base,
+		info->phys_memory_base,
+		info->phys_statctrl_base,
+		info->phys_lcr_base,
+		info->irq_level,
+		info->max_frame_size );
+
+	/* output current serial signal states */
+	spin_lock_irqsave(&info->lock,flags);
+ 	get_signals(info);
+	spin_unlock_irqrestore(&info->lock,flags);
+
+	stat_buf[0] = 0;
+	stat_buf[1] = 0;
+	if (info->serial_signals & SerialSignal_RTS)
+		strcat(stat_buf, "|RTS");
+	if (info->serial_signals & SerialSignal_CTS)
+		strcat(stat_buf, "|CTS");
+	if (info->serial_signals & SerialSignal_DTR)
+		strcat(stat_buf, "|DTR");
+	if (info->serial_signals & SerialSignal_DSR)
+		strcat(stat_buf, "|DSR");
+	if (info->serial_signals & SerialSignal_DCD)
+		strcat(stat_buf, "|CD");
+	if (info->serial_signals & SerialSignal_RI)
+		strcat(stat_buf, "|RI");
+
+	if (info->params.mode == MGSL_MODE_HDLC) {
+		ret += sprintf(buf+ret, "\tHDLC txok:%d rxok:%d",
+			      info->icount.txok, info->icount.rxok);
+		if (info->icount.txunder)
+			ret += sprintf(buf+ret, " txunder:%d", info->icount.txunder);
+		if (info->icount.txabort)
+			ret += sprintf(buf+ret, " txabort:%d", info->icount.txabort);
+		if (info->icount.rxshort)
+			ret += sprintf(buf+ret, " rxshort:%d", info->icount.rxshort);
+		if (info->icount.rxlong)
+			ret += sprintf(buf+ret, " rxlong:%d", info->icount.rxlong);
+		if (info->icount.rxover)
+			ret += sprintf(buf+ret, " rxover:%d", info->icount.rxover);
+		if (info->icount.rxcrc)
+			ret += sprintf(buf+ret, " rxlong:%d", info->icount.rxcrc);
+	} else {
+		ret += sprintf(buf+ret, "\tASYNC tx:%d rx:%d",
+			      info->icount.tx, info->icount.rx);
+		if (info->icount.frame)
+			ret += sprintf(buf+ret, " fe:%d", info->icount.frame);
+		if (info->icount.parity)
+			ret += sprintf(buf+ret, " pe:%d", info->icount.parity);
+		if (info->icount.brk)
+			ret += sprintf(buf+ret, " brk:%d", info->icount.brk);
+		if (info->icount.overrun)
+			ret += sprintf(buf+ret, " oe:%d", info->icount.overrun);
+	}
+
+	/* Append serial signal status to end */
+	ret += sprintf(buf+ret, " %s\n", stat_buf+1);
+
+	ret += sprintf(buf+ret, "\ttxactive=%d bh_req=%d bh_run=%d pending_bh=%x\n",
+	 info->tx_active,info->bh_requested,info->bh_running,
+	 info->pending_bh);
+
+	return ret;
+}
+
+/* Called to print information about devices
+ */
+int read_proc(char *page, char **start, off_t off, int count,
+	      int *eof, void *data)
+{
+	int len = 0, l;
+	off_t	begin = 0;
+	SLMP_INFO *info;
+
+	len += sprintf(page, "synclinkmp driver:%s\n", driver_version);
+
+	info = synclinkmp_device_list;
+	while( info ) {
+		l = line_info(page + len, info);
+		len += l;
+		if (len+begin > off+count)
+			goto done;
+		if (len+begin < off) {
+			begin += len;
+			len = 0;
+		}
+		info = info->next_device;
+	}
+
+	*eof = 1;
+done:
+	if (off >= len+begin)
+		return 0;
+	*start = page + (off-begin);
+	return ((count < begin+len-off) ? count : begin+len-off);
+}
+
+/* Return the count of bytes in transmit buffer
+ */
+static int chars_in_buffer(struct tty_struct *tty)
+{
+	SLMP_INFO *info = (SLMP_INFO *)tty->driver_data;
+
+	if (sanity_check(info, tty->device, "chars_in_buffer"))
+		return 0;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):%s chars_in_buffer()=%d\n",
+		       __FILE__, __LINE__, info->device_name, info->tx_count);
+
+	return info->tx_count;
+}
+
+/* Signal remote device to throttle send data (our receive data)
+ */
+static void throttle(struct tty_struct * tty)
+{
+	SLMP_INFO *info = (SLMP_INFO *)tty->driver_data;
+	unsigned long flags;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):%s throttle() entry\n",
+			 __FILE__,__LINE__, info->device_name );
+
+	if (sanity_check(info, tty->device, "throttle"))
+		return;
+
+	if (I_IXOFF(tty))
+		send_xchar(tty, STOP_CHAR(tty));
+
+ 	if (tty->termios->c_cflag & CRTSCTS) {
+		spin_lock_irqsave(&info->lock,flags);
+		info->serial_signals &= ~SerialSignal_RTS;
+	 	set_signals(info);
+		spin_unlock_irqrestore(&info->lock,flags);
+	}
+}
+
+/* Signal remote device to stop throttling send data (our receive data)
+ */
+static void unthrottle(struct tty_struct * tty)
+{
+	SLMP_INFO *info = (SLMP_INFO *)tty->driver_data;
+	unsigned long flags;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):%s unthrottle() entry\n",
+			 __FILE__,__LINE__, info->device_name );
+
+	if (sanity_check(info, tty->device, "unthrottle"))
+		return;
+
+	if (I_IXOFF(tty)) {
+		if (info->x_char)
+			info->x_char = 0;
+		else
+			send_xchar(tty, START_CHAR(tty));
+	}
+
+ 	if (tty->termios->c_cflag & CRTSCTS) {
+		spin_lock_irqsave(&info->lock,flags);
+		info->serial_signals |= SerialSignal_RTS;
+	 	set_signals(info);
+		spin_unlock_irqrestore(&info->lock,flags);
+	}
+}
+
+/* set or clear transmit break condition
+ * break_state	-1=set break condition, 0=clear
+ */
+static void set_break(struct tty_struct *tty, int break_state)
+{
+	unsigned char RegValue;
+	SLMP_INFO * info = (SLMP_INFO *)tty->driver_data;
+	unsigned long flags;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):%s set_break(%d)\n",
+			 __FILE__,__LINE__, info->device_name, break_state);
+
+	if (sanity_check(info, tty->device, "set_break"))
+		return;
+
+	spin_lock_irqsave(&info->lock,flags);
+	RegValue = read_reg(info, CTL);
+ 	if (break_state == -1)
+		RegValue |= BIT3;
+	else
+		RegValue &= ~BIT3;
+	write_reg(info, CTL, RegValue);
+	spin_unlock_irqrestore(&info->lock,flags);
+}
+
+#ifdef CONFIG_SYNCLINK_SYNCPPP
+
+/* syncppp support and callbacks */
+
+static void sppp_init(SLMP_INFO *info)
+{
+	struct net_device *d;
+
+	sprintf(info->netname,"mgslm%dp%d",info->adapter_num,info->port_num);
+
+	info->if_ptr = &info->pppdev;
+	info->netdev = info->pppdev.dev = &info->netdevice;
+
+	sppp_attach(&info->pppdev);
+
+	d = info->netdev;
+	strcpy(d->name,info->netname);
+	d->base_addr = 0;
+	d->irq = info->irq_level;
+	d->dma = 0;
+	d->priv = info;
+	d->init = NULL;
+	d->open = sppp_cb_open;
+	d->stop = sppp_cb_close;
+	d->hard_start_xmit = sppp_cb_tx;
+	d->do_ioctl = sppp_cb_ioctl;
+	d->get_stats = sppp_cb_net_stats;
+	d->tx_timeout = sppp_cb_tx_timeout;
+	d->watchdog_timeo = 10*HZ;
+
+	if (register_netdev(d) == -1) {
+		printk(KERN_WARNING "%s: register_netdev failed.\n", d->name);
+		sppp_detach(info->netdev);
+		return;
+	}
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("sppp_init(%s)\n",info->netname);
+}
+
+static void sppp_delete(SLMP_INFO *info)
+{
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("sppp_delete(%s)\n",info->netname);
+	sppp_detach(info->netdev);
+	unregister_netdev(info->netdev);
+}
+
+static int sppp_cb_open(struct net_device *d)
+{
+	SLMP_INFO *info = d->priv;
+	int err;
+	long flags;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("sppp_cb_open(%s)\n",info->netname);
+
+	spin_lock_irqsave(&info->netlock, flags);
+	if (info->count != 0 || info->netcount != 0) {
+		printk(KERN_WARNING "%s: sppp_cb_open returning busy\n", info->netname);
+		spin_unlock_irqrestore(&info->netlock, flags);
+		return -EBUSY;
+	}
+	info->netcount=1;
+	MOD_INC_USE_COUNT;
+	spin_unlock_irqrestore(&info->netlock, flags);
+
+	/* claim resources and init adapter */
+	if ((err = startup(info)) != 0)
+		goto open_fail;
+
+	/* allow syncppp module to do open processing */
+	if ((err = sppp_open(d)) != 0) {
+		shutdown(info);
+		goto open_fail;
+	}
+
+	info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR;
+	program_hw(info);
+
+	d->trans_start = jiffies;
+	netif_start_queue(d);
+	return 0;
+
+open_fail:
+	spin_lock_irqsave(&info->netlock, flags);
+	info->netcount=0;
+	MOD_DEC_USE_COUNT;
+	spin_unlock_irqrestore(&info->netlock, flags);
+	return err;
+}
+
+static void sppp_cb_tx_timeout(struct net_device *dev)
+{
+	SLMP_INFO *info = dev->priv;
+	long flags;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("sppp_tx_timeout(%s)\n",info->netname);
+
+	info->netstats.tx_errors++;
+	info->netstats.tx_aborted_errors++;
+
+	spin_lock_irqsave(&info->lock,flags);
+	tx_stop(info);
+	spin_unlock_irqrestore(&info->lock,flags);
+
+	netif_wake_queue(dev);
+}
+
+static int sppp_cb_tx(struct sk_buff *skb, struct net_device *dev)
+{
+	SLMP_INFO *info = dev->priv;
+	unsigned long flags;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("sppp_tx(%s)\n",info->netname);
+
+	netif_stop_queue(dev);
+
+	info->tx_count = skb->len;
+	tx_load_dma_buffer(info, skb->data, skb->len);
+	info->netstats.tx_packets++;
+	info->netstats.tx_bytes += skb->len;
+	dev_kfree_skb(skb);
+
+	dev->trans_start = jiffies;
+
+	spin_lock_irqsave(&info->lock,flags);
+	if (!info->tx_active)
+	 	tx_start(info);
+	spin_unlock_irqrestore(&info->lock,flags);
+
+	return 0;
+}
+
+static int sppp_cb_close(struct net_device *d)
+{
+	SLMP_INFO *info = d->priv;
+	unsigned long flags;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("sppp_cb_close(%s)\n",info->netname);
+
+	/* shutdown adapter and release resources */
+	shutdown(info);
+
+	/* allow syncppp to do close processing */
+	sppp_close(d);
+	netif_stop_queue(d);
+
+	spin_lock_irqsave(&info->netlock, flags);
+	info->netcount=0;
+	MOD_DEC_USE_COUNT;
+	spin_unlock_irqrestore(&info->netlock, flags);
+	return 0;
+}
+
+static void sppp_rx_done(SLMP_INFO *info, char *buf, int size)
+{
+	struct sk_buff *skb = dev_alloc_skb(size);
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("sppp_rx_done(%s)\n",info->netname);
+	if (skb == NULL) {
+		printk(KERN_NOTICE "%s: cant alloc skb, dropping packet\n",
+			info->netname);
+		info->netstats.rx_dropped++;
+		return;
+	}
+
+	memcpy(skb_put(skb, size),buf,size);
+
+	skb->protocol = htons(ETH_P_WAN_PPP);
+	skb->dev = info->netdev;
+	skb->mac.raw = skb->data;
+	info->netstats.rx_packets++;
+	info->netstats.rx_bytes += size;
+	netif_rx(skb);
+	info->netdev->trans_start = jiffies;
+}
+
+static void sppp_tx_done(SLMP_INFO *info)
+{
+	if (netif_queue_stopped(info->netdev))
+	    netif_wake_queue(info->netdev);
+}
+
+static struct net_device_stats *sppp_cb_net_stats(struct net_device *dev)
+{
+	SLMP_INFO *info = dev->priv;
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("net_stats(%s)\n",info->netname);
+	return &info->netstats;
+}
+
+static int sppp_cb_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	SLMP_INFO *info = (SLMP_INFO *)dev->priv;
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):ioctl %s cmd=%08X\n", __FILE__,__LINE__,
+			info->netname, cmd );
+	return sppp_do_ioctl(dev, ifr, cmd);
+}
+
+#endif /* ifdef CONFIG_SYNCLINK_SYNCPPP */
+
+
+/* Return next bottom half action to perform.
+ * Return Value:	BH action code or 0 if nothing to do.
+ */
+int bh_action(SLMP_INFO *info)
+{
+	unsigned long flags;
+	int rc = 0;
+
+	spin_lock_irqsave(&info->lock,flags);
+
+	if (info->pending_bh & BH_RECEIVE) {
+		info->pending_bh &= ~BH_RECEIVE;
+		rc = BH_RECEIVE;
+	} else if (info->pending_bh & BH_TRANSMIT) {
+		info->pending_bh &= ~BH_TRANSMIT;
+		rc = BH_TRANSMIT;
+	} else if (info->pending_bh & BH_STATUS) {
+		info->pending_bh &= ~BH_STATUS;
+		rc = BH_STATUS;
+	}
+
+	if (!rc) {
+		/* Mark BH routine as complete */
+		info->bh_running   = 0;
+		info->bh_requested = 0;
+	}
+
+	spin_unlock_irqrestore(&info->lock,flags);
+
+	return rc;
+}
+
+/* Perform bottom half processing of work items queued by ISR.
+ */
+void bh_handler(void* Context)
+{
+	SLMP_INFO *info = (SLMP_INFO*)Context;
+	int action;
+
+	if (!info)
+		return;
+
+	if ( debug_level >= DEBUG_LEVEL_BH )
+		printk( "%s(%d):%s bh_handler() entry\n",
+			__FILE__,__LINE__,info->device_name);
+
+	info->bh_running = 1;
+
+	while((action = bh_action(info)) != 0) {
+
+		/* Process work item */
+		if ( debug_level >= DEBUG_LEVEL_BH )
+			printk( "%s(%d):%s bh_handler() work item action=%d\n",
+				__FILE__,__LINE__,info->device_name, action);
+
+		switch (action) {
+
+		case BH_RECEIVE:
+			bh_receive(info);
+			break;
+		case BH_TRANSMIT:
+			bh_transmit(info);
+			break;
+		case BH_STATUS:
+			bh_status(info);
+			break;
+		default:
+			/* unknown work item ID */
+			printk("%s(%d):%s Unknown work item ID=%08X!\n",
+				__FILE__,__LINE__,info->device_name,action);
+			break;
+		}
+	}
+
+	if ( debug_level >= DEBUG_LEVEL_BH )
+		printk( "%s(%d):%s bh_handler() exit\n",
+			__FILE__,__LINE__,info->device_name);
+}
+
+void bh_receive(SLMP_INFO *info)
+{
+	if ( debug_level >= DEBUG_LEVEL_BH )
+		printk( "%s(%d):%s bh_receive()\n",
+			__FILE__,__LINE__,info->device_name);
+
+	while( rx_get_frame(info) );
+}
+
+void bh_transmit(SLMP_INFO *info)
+{
+	struct tty_struct *tty = info->tty;
+
+	if ( debug_level >= DEBUG_LEVEL_BH )
+		printk( "%s(%d):%s bh_transmit() entry\n",
+			__FILE__,__LINE__,info->device_name);
+
+	if (tty) {
+		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+		    tty->ldisc.write_wakeup) {
+			if ( debug_level >= DEBUG_LEVEL_BH )
+				printk( "%s(%d):%s calling ldisc.write_wakeup\n",
+					__FILE__,__LINE__,info->device_name);
+			(tty->ldisc.write_wakeup)(tty);
+		}
+		wake_up_interruptible(&tty->write_wait);
+	}
+}
+
+void bh_status(SLMP_INFO *info)
+{
+	if ( debug_level >= DEBUG_LEVEL_BH )
+		printk( "%s(%d):%s bh_status() entry\n",
+			__FILE__,__LINE__,info->device_name);
+
+	info->ri_chkcount = 0;
+	info->dsr_chkcount = 0;
+	info->dcd_chkcount = 0;
+	info->cts_chkcount = 0;
+}
+
+void isr_timer(SLMP_INFO * info)
+{
+	unsigned char timer = (info->port_num & 1) ? TIMER2 : TIMER0;
+
+	/* IER2<7..4> = timer<3..0> interrupt enables (0=disabled) */
+	write_reg(info, IER2, 0);
+
+	/* TMCS, Timer Control/Status Register
+	 *
+	 * 07      CMF, Compare match flag (read only) 1=match
+	 * 06      ECMI, CMF Interrupt Enable: 0=disabled
+	 * 05      Reserved, must be 0
+	 * 04      TME, Timer Enable
+	 * 03..00  Reserved, must be 0
+	 *
+	 * 0000 0000
+	 */
+	write_reg(info, (unsigned char)(timer + TMCS), 0);
+
+	info->irq_occurred = TRUE;
+
+	if ( debug_level >= DEBUG_LEVEL_ISR )
+		printk("%s(%d):%s isr_timer()\n",
+			__FILE__,__LINE__,info->device_name);
+}
+
+void isr_rxint(SLMP_INFO * info)
+{
+ 	struct tty_struct *tty = info->tty;
+ 	struct	mgsl_icount *icount = &info->icount;
+	unsigned char status = read_reg(info, SR1);
+	unsigned char status2 = read_reg(info, SR2);
+
+	/* clear status bits */
+	if ( status & (FLGD + IDLD + CDCD + BRKD) )
+		write_reg(info, SR1, 
+				(unsigned char)(status & (FLGD + IDLD + CDCD + BRKD)));
+
+	if ( status2 & OVRN )
+		write_reg(info, SR2, (unsigned char)(status2 & OVRN));
+	
+	if ( debug_level >= DEBUG_LEVEL_ISR )
+		printk("%s(%d):%s isr_rxint status=%02X %02x\n",
+			__FILE__,__LINE__,info->device_name,status,status2);
+
+	if (info->params.mode == MGSL_MODE_ASYNC) {
+		if (status & BRKD) {
+			icount->brk++;
+
+			/* process break detection if tty control
+			 * is not set to ignore it
+			 */
+			if ( tty ) {
+				if (!(status & info->ignore_status_mask1)) {
+					if (info->read_status_mask1 & BRKD) {
+						*tty->flip.flag_buf_ptr = TTY_BREAK;
+						if (info->flags & ASYNC_SAK)
+							do_SAK(tty);
+					}
+				}
+			}
+		}
+	}
+	else {
+		if (status & (FLGD|IDLD)) {
+			if (status & FLGD)
+				info->icount.exithunt++;
+			else if (status & IDLD)
+				info->icount.rxidle++;
+			wake_up_interruptible(&info->event_wait_q);
+		}
+	}
+
+	if (status & CDCD) {
+		/* simulate a common modem status change interrupt
+		 * for our handler
+		 */
+		get_signals( info );
+		isr_io_pin(info,
+			MISCSTATUS_DCD_LATCHED|(info->serial_signals&SerialSignal_DCD));
+	}
+}
+
+/*
+ * handle async rx data interrupts
+ */
+void isr_rxrdy(SLMP_INFO * info)
+{
+	u16 status;
+	unsigned char DataByte;
+ 	struct tty_struct *tty = info->tty;
+ 	struct	mgsl_icount *icount = &info->icount;
+
+	if ( debug_level >= DEBUG_LEVEL_ISR )
+		printk("%s(%d):%s isr_rxrdy\n",
+			__FILE__,__LINE__,info->device_name);
+
+	while((status = read_reg(info,CST0)) & BIT0)
+	{
+		DataByte = read_reg(info,TRB);
+
+		if ( tty ) {
+			if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+				continue;
+
+			*tty->flip.char_buf_ptr = DataByte;
+			*tty->flip.flag_buf_ptr = 0;
+		}
+
+		icount->rx++;
+
+		if ( status & (PE + FRME + OVRN) ) {
+			printk("%s(%d):%s rxerr=%04X\n",
+				__FILE__,__LINE__,info->device_name,status);
+
+			/* update error statistics */
+			if (status & PE)
+				icount->parity++;
+			else if (status & FRME)
+				icount->frame++;
+			else if (status & OVRN)
+				icount->overrun++;
+
+			/* discard char if tty control flags say so */
+			if (status & info->ignore_status_mask2)
+				continue;
+
+			status &= info->read_status_mask2;
+
+			if ( tty ) {
+				if (status & PE)
+					*tty->flip.flag_buf_ptr = TTY_PARITY;
+				else if (status & FRME)
+					*tty->flip.flag_buf_ptr = TTY_FRAME;
+				if (status & OVRN) {
+					/* Overrun is special, since it's
+					 * reported immediately, and doesn't
+					 * affect the current character
+					 */
+					if (tty->flip.count < TTY_FLIPBUF_SIZE) {
+						tty->flip.count++;
+						tty->flip.flag_buf_ptr++;
+						tty->flip.char_buf_ptr++;
+						*tty->flip.flag_buf_ptr = TTY_OVERRUN;
+					}
+				}
+			}
+		}	/* end of if (error) */
+
+		if ( tty ) {
+			tty->flip.flag_buf_ptr++;
+			tty->flip.char_buf_ptr++;
+			tty->flip.count++;
+		}
+	}
+
+	if ( debug_level >= DEBUG_LEVEL_ISR ) {
+		printk("%s(%d):%s isr_rxrdy() flip count=%d\n",
+			__FILE__,__LINE__,info->device_name,
+			tty ? tty->flip.count : 0);
+		printk("%s(%d):%s rx=%d brk=%d parity=%d frame=%d overrun=%d\n",
+			__FILE__,__LINE__,info->device_name,
+			icount->rx,icount->brk,icount->parity,
+			icount->frame,icount->overrun);
+	}
+
+	if ( tty && tty->flip.count )
+		tty_flip_buffer_push(tty);
+}
+
+void isr_txeom(SLMP_INFO * info, unsigned char status)
+{
+	if ( debug_level >= DEBUG_LEVEL_ISR )
+		printk("%s(%d):%s isr_txeom status=%02x\n",
+			__FILE__,__LINE__,info->device_name,status);
+
+	/* disable and clear MSCI interrupts */
+	info->ie1_value &= ~(IDLE + UDRN);
+	write_reg(info, IE1, info->ie1_value);
+	write_reg(info, SR1, (unsigned char)(UDRN + IDLE));
+
+	write_reg(info, TXDMA + DIR, 0x00); /* disable Tx DMA IRQs */
+	write_reg(info, TXDMA + DSR, 0xc0); /* clear IRQs and disable DMA */
+	write_reg(info, TXDMA + DCMD, SWABORT);	/* reset/init DMA channel */
+
+	if ( info->tx_active ) {
+		if (info->params.mode != MGSL_MODE_ASYNC) {
+			if (status & UDRN)
+				info->icount.txunder++;
+			else if (status & IDLE)
+				info->icount.txok++;
+		}
+
+		info->tx_active = 0;
+		info->tx_count = info->tx_put = info->tx_get = 0;
+
+		del_timer(&info->tx_timer);
+
+		if (info->params.mode != MGSL_MODE_ASYNC && info->drop_rts_on_tx_done ) {
+			info->serial_signals &= ~SerialSignal_RTS;
+			info->drop_rts_on_tx_done = 0;
+			set_signals(info);
+		}
+
+#ifdef CONFIG_SYNCLINK_SYNCPPP
+		if (info->netcount)
+			sppp_tx_done(info);
+		else
+#endif
+		{
+			if (info->tty && (info->tty->stopped || info->tty->hw_stopped)) {
+				tx_stop(info);
+				return;
+			}
+			info->pending_bh |= BH_TRANSMIT;
+		}
+	}
+}
+
+
+/*
+ * handle tx status interrupts
+ */
+void isr_txint(SLMP_INFO * info)
+{
+	unsigned char status = read_reg(info, SR1);
+
+	/* clear status bits */
+	write_reg(info, SR1, (unsigned char)(status & (UDRN + IDLE + CCTS)));
+
+	if ( debug_level >= DEBUG_LEVEL_ISR )
+		printk("%s(%d):%s isr_txint status=%02x\n",
+			__FILE__,__LINE__,info->device_name,status);
+
+	if (status & (UDRN + IDLE))
+		isr_txeom(info, status);
+
+	if (status & CCTS) {
+		/* simulate a common modem status change interrupt
+		 * for our handler
+		 */
+		get_signals( info );
+		isr_io_pin(info,
+			MISCSTATUS_CTS_LATCHED|(info->serial_signals&SerialSignal_CTS));
+
+	}
+}
+
+/*
+ * handle async tx data interrupts
+ */
+void isr_txrdy(SLMP_INFO * info)
+{
+	if ( debug_level >= DEBUG_LEVEL_ISR )
+		printk("%s(%d):%s isr_txrdy() tx_count=%d\n",
+			__FILE__,__LINE__,info->device_name,info->tx_count);
+
+	if (info->tty && (info->tty->stopped || info->tty->hw_stopped)) {
+		tx_stop(info);
+		return;
+	}
+
+	if ( info->tx_count )
+		tx_load_fifo( info );
+	else {
+		info->tx_active = 0;
+		info->ie0_value &= ~TXRDYE;
+		write_reg(info, IE0, info->ie0_value);
+	}
+
+	if (info->tx_count < WAKEUP_CHARS)
+		info->pending_bh |= BH_TRANSMIT;
+}
+
+void isr_rxdmaok(SLMP_INFO * info)
+{
+	/* BIT7 = EOT (end of transfer)
+	 * BIT6 = EOM (end of message/frame)
+	 */
+	unsigned char status = read_reg(info,RXDMA + DSR) & 0xc0;
+
+	/* clear IRQ (BIT0 must be 1 to prevent clearing DE bit) */
+	write_reg(info, RXDMA + DSR, (unsigned char)(status | 1));
+
+	if ( debug_level >= DEBUG_LEVEL_ISR )
+		printk("%s(%d):%s isr_rxdmaok(), status=%02x\n",
+			__FILE__,__LINE__,info->device_name,status);
+
+	info->pending_bh |= BH_RECEIVE;
+}
+
+void isr_rxdmaerror(SLMP_INFO * info)
+{
+	/* BIT5 = BOF (buffer overflow)
+	 * BIT4 = COF (counter overflow)
+	 */
+	unsigned char status = read_reg(info,RXDMA + DSR) & 0x30;
+
+	/* clear IRQ (BIT0 must be 1 to prevent clearing DE bit) */
+	write_reg(info, RXDMA + DSR, (unsigned char)(status | 1));
+
+	if ( debug_level >= DEBUG_LEVEL_ISR )
+		printk("%s(%d):%s isr_rxdmaerror(), status=%02x\n",
+			__FILE__,__LINE__,info->device_name,status);
+
+	info->rx_overflow = TRUE;
+	info->pending_bh |= BH_RECEIVE;
+}
+
+void isr_txdmaok(SLMP_INFO * info)
+{
+	/* BIT7 = EOT (end of transfer, used for async mode)
+	 * BIT6 = EOM (end of message/frame, used for sync mode)
+	 *
+	 * We don't look at DMA status because only EOT is enabled
+	 * and we always clear and disable all tx DMA IRQs.
+	 */
+//	unsigned char dma_status = read_reg(info,TXDMA + DSR) & 0xc0;
+	unsigned char status_reg1 = read_reg(info, SR1);
+
+	write_reg(info, TXDMA + DIR, 0x00);	/* disable Tx DMA IRQs */
+	write_reg(info, TXDMA + DSR, 0xc0); /* clear IRQs and disable DMA */
+	write_reg(info, TXDMA + DCMD, SWABORT);	/* reset/init DMA channel */
+
+	if ( debug_level >= DEBUG_LEVEL_ISR )
+		printk("%s(%d):%s isr_txdmaok(), status=%02x\n",
+			__FILE__,__LINE__,info->device_name,status_reg1);
+
+	/* If transmitter already idle, do end of frame processing,
+	 * otherwise enable interrupt for tx IDLE.
+	 */
+	if (status_reg1 & IDLE)
+		isr_txeom(info, IDLE);
+	else {
+		/* disable and clear underrun IRQ, enable IDLE interrupt */
+		info->ie1_value |= IDLE;
+		info->ie1_value &= ~UDRN;
+		write_reg(info, IE1, info->ie1_value);
+
+		write_reg(info, SR1, UDRN);
+	}
+}
+
+void isr_txdmaerror(SLMP_INFO * info)
+{
+	/* BIT5 = BOF (buffer overflow)
+	 * BIT4 = COF (counter overflow)
+	 */
+	unsigned char status = read_reg(info,TXDMA + DSR) & 0x30;
+
+	/* clear IRQ (BIT0 must be 1 to prevent clearing DE bit) */
+	write_reg(info, TXDMA + DSR, (unsigned char)(status | 1));
+
+	if ( debug_level >= DEBUG_LEVEL_ISR )
+		printk("%s(%d):%s isr_txdmaerror(), status=%02x\n",
+			__FILE__,__LINE__,info->device_name,status);
+}
+
+/* handle input serial signal changes
+ */
+void isr_io_pin( SLMP_INFO *info, u16 status )
+{
+ 	struct	mgsl_icount *icount;
+
+	if ( debug_level >= DEBUG_LEVEL_ISR )
+		printk("%s(%d):isr_io_pin status=%04X\n",
+			__FILE__,__LINE__,status);
+
+	if (status & (MISCSTATUS_CTS_LATCHED | MISCSTATUS_DCD_LATCHED |
+	              MISCSTATUS_DSR_LATCHED | MISCSTATUS_RI_LATCHED) ) {
+		icount = &info->icount;
+		/* update input line counters */
+		if (status & MISCSTATUS_RI_LATCHED) {
+			icount->rng++;
+			if ( status & SerialSignal_RI )
+				info->input_signal_events.ri_up++;
+			else
+				info->input_signal_events.ri_down++;
+		}
+		if (status & MISCSTATUS_DSR_LATCHED) {
+			icount->dsr++;
+			if ( status & SerialSignal_DSR )
+				info->input_signal_events.dsr_up++;
+			else
+				info->input_signal_events.dsr_down++;
+		}
+		if (status & MISCSTATUS_DCD_LATCHED) {
+			if ((info->dcd_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT) {
+				info->ie1_value &= ~CDCD;
+				write_reg(info, IE1, info->ie1_value);
+			}
+			icount->dcd++;
+			if (status & SerialSignal_DCD) {
+				info->input_signal_events.dcd_up++;
+#ifdef CONFIG_SYNCLINK_SYNCPPP
+				if (info->netcount)
+					sppp_reopen(info->netdev);
+#endif
+			} else
+				info->input_signal_events.dcd_down++;
+		}
+		if (status & MISCSTATUS_CTS_LATCHED)
+		{
+			if ((info->cts_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT) {
+				info->ie1_value &= ~CCTS;
+				write_reg(info, IE1, info->ie1_value);
+			}
+			icount->cts++;
+			if ( status & SerialSignal_CTS )
+				info->input_signal_events.cts_up++;
+			else
+				info->input_signal_events.cts_down++;
+		}
+		wake_up_interruptible(&info->status_event_wait_q);
+		wake_up_interruptible(&info->event_wait_q);
+
+		if ( (info->flags & ASYNC_CHECK_CD) &&
+		     (status & MISCSTATUS_DCD_LATCHED) ) {
+			if ( debug_level >= DEBUG_LEVEL_ISR )
+				printk("%s CD now %s...", info->device_name,
+				       (status & SerialSignal_DCD) ? "on" : "off");
+			if (status & SerialSignal_DCD)
+				wake_up_interruptible(&info->open_wait);
+			else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+				   (info->flags & ASYNC_CALLOUT_NOHUP))) {
+				if ( debug_level >= DEBUG_LEVEL_ISR )
+					printk("doing serial hangup...");
+				if (info->tty)
+					tty_hangup(info->tty);
+			}
+		}
+
+		if ( (info->flags & ASYNC_CTS_FLOW) &&
+		     (status & MISCSTATUS_CTS_LATCHED) ) {
+			if ( info->tty ) {
+				if (info->tty->hw_stopped) {
+					if (status & SerialSignal_CTS) {
+						if ( debug_level >= DEBUG_LEVEL_ISR )
+							printk("CTS tx start...");
+			 			info->tty->hw_stopped = 0;
+						tx_start(info);
+						info->pending_bh |= BH_TRANSMIT;
+						return;
+					}
+				} else {
+					if (!(status & SerialSignal_CTS)) {
+						if ( debug_level >= DEBUG_LEVEL_ISR )
+							printk("CTS tx stop...");
+			 			info->tty->hw_stopped = 1;
+						tx_stop(info);
+					}
+				}
+			}
+		}
+	}
+
+	info->pending_bh |= BH_STATUS;
+}
+
+/* Interrupt service routine entry point.
+ *
+ * Arguments:
+ * 	irq		interrupt number that caused interrupt
+ * 	dev_id		device ID supplied during interrupt registration
+ * 	regs		interrupted processor context
+ */
+static void synclinkmp_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+	SLMP_INFO * info;
+	unsigned char status, status0, status1=0;
+	unsigned char dmastatus, dmastatus0, dmastatus1=0;
+	unsigned char timerstatus0, timerstatus1=0;
+	unsigned char shift;
+	unsigned int i;
+	unsigned short tmp;
+
+	if ( debug_level >= DEBUG_LEVEL_ISR )
+		printk("%s(%d): synclinkmp_interrupt(%d)entry.\n",
+			__FILE__,__LINE__,irq);
+
+	info = (SLMP_INFO *)dev_id;
+	if (!info)
+		return;
+
+	spin_lock(&info->lock);
+
+	for(;;) {
+
+		/* get status for SCA0 (ports 0-1) */
+		tmp = read_reg16(info, ISR0);	/* get ISR0 and ISR1 in one read */
+		status0 = (unsigned char)tmp;
+		dmastatus0 = (unsigned char)(tmp>>8);
+		timerstatus0 = read_reg(info, ISR2);
+
+		if ( debug_level >= DEBUG_LEVEL_ISR )
+			printk("%s(%d):%s status0=%02x, dmastatus0=%02x, timerstatus0=%02x\n",
+				__FILE__,__LINE__,info->device_name,
+				status0,dmastatus0,timerstatus0);
+
+		if (info->port_count == 4) {
+			/* get status for SCA1 (ports 2-3) */
+			tmp = read_reg16(info->port_array[2], ISR0);
+			status1 = (unsigned char)tmp;
+			dmastatus1 = (unsigned char)(tmp>>8);
+			timerstatus1 = read_reg(info->port_array[2], ISR2);
+
+			if ( debug_level >= DEBUG_LEVEL_ISR )
+				printk("%s(%d):%s status1=%02x, dmastatus1=%02x, timerstatus1=%02x\n",
+					__FILE__,__LINE__,info->device_name,
+					status1,dmastatus1,timerstatus1);
+		}
+
+		if (!status0 && !dmastatus0 && !timerstatus0 &&
+			 !status1 && !dmastatus1 && !timerstatus1)
+			break;
+
+		for(i=0; i < info->port_count ; i++) {
+			if (info->port_array[i] == NULL)
+				continue;
+			if (i < 2) {
+				status = status0;
+				dmastatus = dmastatus0;
+			} else {
+				status = status1;
+				dmastatus = dmastatus1;
+			}
+
+			shift = i & 1 ? 4 :0;
+
+			if (status & BIT0 << shift)
+				isr_rxrdy(info->port_array[i]);
+			if (status & BIT1 << shift)
+				isr_txrdy(info->port_array[i]);
+			if (status & BIT2 << shift)
+				isr_rxint(info->port_array[i]);
+			if (status & BIT3 << shift)
+				isr_txint(info->port_array[i]);
+
+			if (dmastatus & BIT0 << shift)
+				isr_rxdmaerror(info->port_array[i]);
+			if (dmastatus & BIT1 << shift)
+				isr_rxdmaok(info->port_array[i]);
+			if (dmastatus & BIT2 << shift)
+				isr_txdmaerror(info->port_array[i]);
+			if (dmastatus & BIT3 << shift)
+				isr_txdmaok(info->port_array[i]);
+		}
+
+		if (timerstatus0 & (BIT5 | BIT4))
+			isr_timer(info->port_array[0]);
+		if (timerstatus0 & (BIT7 | BIT6))
+			isr_timer(info->port_array[1]);
+		if (timerstatus1 & (BIT5 | BIT4))
+			isr_timer(info->port_array[2]);
+		if (timerstatus1 & (BIT7 | BIT6))
+			isr_timer(info->port_array[3]);
+	}
+
+	for(i=0; i < info->port_count ; i++) {
+		SLMP_INFO * port = info->port_array[i];
+
+		/* Request bottom half processing if there's something
+		 * for it to do and the bh is not already running.
+		 *
+		 * Note: startup adapter diags require interrupts.
+		 * do not request bottom half processing if the
+		 * device is not open in a normal mode.
+		 */
+		if ( port && (port->count || port->netcount) &&
+		     port->pending_bh && !port->bh_running &&
+		     !port->bh_requested ) {
+			if ( debug_level >= DEBUG_LEVEL_ISR )
+				printk("%s(%d):%s queueing bh task.\n",
+					__FILE__,__LINE__,port->device_name);
+			queue_task(&port->task, &tq_immediate);
+			mark_bh(IMMEDIATE_BH);
+			port->bh_requested = 1;
+		}
+	}
+
+	spin_unlock(&info->lock);
+
+	if ( debug_level >= DEBUG_LEVEL_ISR )
+		printk("%s(%d):synclinkmp_interrupt(%d)exit.\n",
+			__FILE__,__LINE__,irq);
+}
+
+/* Initialize and start device.
+ */
+static int startup(SLMP_INFO * info)
+{
+	if ( debug_level >= DEBUG_LEVEL_INFO )
+		printk("%s(%d):%s tx_releaseup()\n",__FILE__,__LINE__,info->device_name);
+
+	if (info->flags & ASYNC_INITIALIZED)
+		return 0;
+
+	if (!info->tx_buf) {
+		info->tx_buf = (unsigned char *)kmalloc(info->max_frame_size, GFP_KERNEL);
+		if (!info->tx_buf) {
+			printk(KERN_ERR"%s(%d):%s can't allocate transmit buffer\n",
+				__FILE__,__LINE__,info->device_name);
+			return -ENOMEM;
+		}
+	}
+
+	info->pending_bh = 0;
+
+	init_timer(&info->tx_timer);
+	info->tx_timer.data = (unsigned long)info;
+	info->tx_timer.function = tx_timeout;
+
+	/* program hardware for current parameters */
+	reset_port(info);
+
+	change_params(info);
+
+	init_timer(&info->status_timer);
+	info->status_timer.data = (unsigned long)info;
+	info->status_timer.function = status_timeout;
+	info->status_timer.expires = jiffies + jiffies_from_ms(10);
+	add_timer(&info->status_timer);
+
+	if (info->tty)
+		clear_bit(TTY_IO_ERROR, &info->tty->flags);
+
+	info->flags |= ASYNC_INITIALIZED;
+
+	return 0;
+}
+
+/* Called by close() and hangup() to shutdown hardware
+ */
+static void shutdown(SLMP_INFO * info)
+{
+	unsigned long flags;
+
+	if (!(info->flags & ASYNC_INITIALIZED))
+		return;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):%s synclinkmp_shutdown()\n",
+			 __FILE__,__LINE__, info->device_name );
+
+	/* clear status wait queue because status changes */
+	/* can't happen after shutting down the hardware */
+	wake_up_interruptible(&info->status_event_wait_q);
+	wake_up_interruptible(&info->event_wait_q);
+
+	del_timer(&info->tx_timer);
+	del_timer(&info->status_timer);
+
+	if (info->tx_buf) {
+		free_page((unsigned long) info->tx_buf);
+		info->tx_buf = 0;
+	}
+
+	spin_lock_irqsave(&info->lock,flags);
+
+	reset_port(info);
+
+ 	if (!info->tty || info->tty->termios->c_cflag & HUPCL) {
+ 		info->serial_signals &= ~(SerialSignal_DTR + SerialSignal_RTS);
+		set_signals(info);
+	}
+
+	spin_unlock_irqrestore(&info->lock,flags);
+
+	if (info->tty)
+		set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+	info->flags &= ~ASYNC_INITIALIZED;
+}
+
+static void program_hw(SLMP_INFO *info)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&info->lock,flags);
+
+	rx_stop(info);
+	tx_stop(info);
+
+	info->tx_count = info->tx_put = info->tx_get = 0;
+
+	if (info->params.mode == MGSL_MODE_HDLC || info->netcount)
+		hdlc_mode(info);
+	else
+		async_mode(info);
+
+	set_signals(info);
+
+	info->dcd_chkcount = 0;
+	info->cts_chkcount = 0;
+	info->ri_chkcount = 0;
+	info->dsr_chkcount = 0;
+
+	info->ie1_value |= (CDCD|CCTS);
+	write_reg(info, IE1, info->ie1_value);
+
+	get_signals(info);
+
+	if (info->netcount || (info->tty && info->tty->termios->c_cflag & CREAD) )
+		rx_start(info);
+
+	spin_unlock_irqrestore(&info->lock,flags);
+}
+
+/* Reconfigure adapter based on new parameters
+ */
+static void change_params(SLMP_INFO *info)
+{
+	unsigned cflag;
+	int bits_per_char;
+
+	if (!info->tty || !info->tty->termios)
+		return;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):%s change_params()\n",
+			 __FILE__,__LINE__, info->device_name );
+
+	cflag = info->tty->termios->c_cflag;
+
+	/* if B0 rate (hangup) specified then negate DTR and RTS */
+	/* otherwise assert DTR and RTS */
+ 	if (cflag & CBAUD)
+		info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR;
+	else
+		info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
+
+	/* byte size and parity */
+
+	switch (cflag & CSIZE) {
+	      case CS5: info->params.data_bits = 5; break;
+	      case CS6: info->params.data_bits = 6; break;
+	      case CS7: info->params.data_bits = 7; break;
+	      case CS8: info->params.data_bits = 8; break;
+	      /* Never happens, but GCC is too dumb to figure it out */
+	      default:  info->params.data_bits = 7; break;
+	      }
+
+	if (cflag & CSTOPB)
+		info->params.stop_bits = 2;
+	else
+		info->params.stop_bits = 1;
+
+	info->params.parity = ASYNC_PARITY_NONE;
+	if (cflag & PARENB) {
+		if (cflag & PARODD)
+			info->params.parity = ASYNC_PARITY_ODD;
+		else
+			info->params.parity = ASYNC_PARITY_EVEN;
+#ifdef CMSPAR
+		if (cflag & CMSPAR)
+			info->params.parity = ASYNC_PARITY_SPACE;
+#endif
+	}
+
+	/* calculate number of jiffies to transmit a full
+	 * FIFO (32 bytes) at specified data rate
+	 */
+	bits_per_char = info->params.data_bits +
+			info->params.stop_bits + 1;
+
+	/* if port data rate is set to 460800 or less then
+	 * allow tty settings to override, otherwise keep the
+	 * current data rate.
+	 */
+	if (info->params.data_rate <= 460800) {
+		info->params.data_rate = tty_get_baud_rate(info->tty);
+	}
+
+	if ( info->params.data_rate ) {
+		info->timeout = (32*HZ*bits_per_char) /
+				info->params.data_rate;
+	}
+	info->timeout += HZ/50;		/* Add .02 seconds of slop */
+
+	if (cflag & CRTSCTS)
+		info->flags |= ASYNC_CTS_FLOW;
+	else
+		info->flags &= ~ASYNC_CTS_FLOW;
+
+	if (cflag & CLOCAL)
+		info->flags &= ~ASYNC_CHECK_CD;
+	else
+		info->flags |= ASYNC_CHECK_CD;
+
+	/* process tty input control flags */
+
+	info->read_status_mask2 = OVRN;
+	if (I_INPCK(info->tty))
+		info->read_status_mask2 |= PE | FRME;
+ 	if (I_BRKINT(info->tty) || I_PARMRK(info->tty))
+ 		info->read_status_mask1 |= BRKD;
+	if (I_IGNPAR(info->tty))
+		info->ignore_status_mask2 |= PE | FRME;
+	if (I_IGNBRK(info->tty)) {
+		info->ignore_status_mask1 |= BRKD;
+		/* If ignoring parity and break indicators, ignore
+		 * overruns too.  (For real raw support).
+		 */
+		if (I_IGNPAR(info->tty))
+			info->ignore_status_mask2 |= OVRN;
+	}
+
+	program_hw(info);
+}
+
+static int get_stats(SLMP_INFO * info, struct mgsl_icount *user_icount)
+{
+	int err;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):%s get_params()\n",
+			 __FILE__,__LINE__, info->device_name);
+
+	COPY_TO_USER(err,user_icount, &info->icount, sizeof(struct mgsl_icount));
+	if (err) {
+		if ( debug_level >= DEBUG_LEVEL_INFO )
+			printk( "%s(%d):%s get_stats() user buffer copy failed\n",
+				__FILE__,__LINE__,info->device_name);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int get_params(SLMP_INFO * info, MGSL_PARAMS *user_params)
+{
+	int err;
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):%s get_params()\n",
+			 __FILE__,__LINE__, info->device_name);
+
+	COPY_TO_USER(err,user_params, &info->params, sizeof(MGSL_PARAMS));
+	if (err) {
+		if ( debug_level >= DEBUG_LEVEL_INFO )
+			printk( "%s(%d):%s get_params() user buffer copy failed\n",
+				__FILE__,__LINE__,info->device_name);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int set_params(SLMP_INFO * info, MGSL_PARAMS *new_params)
+{
+ 	unsigned long flags;
+	MGSL_PARAMS tmp_params;
+	int err;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):%s set_params\n",
+			__FILE__,__LINE__,info->device_name );
+	COPY_FROM_USER(err,&tmp_params, new_params, sizeof(MGSL_PARAMS));
+	if (err) {
+		if ( debug_level >= DEBUG_LEVEL_INFO )
+			printk( "%s(%d):%s set_params() user buffer copy failed\n",
+				__FILE__,__LINE__,info->device_name);
+		return -EFAULT;
+	}
+
+	spin_lock_irqsave(&info->lock,flags);
+	memcpy(&info->params,&tmp_params,sizeof(MGSL_PARAMS));
+	spin_unlock_irqrestore(&info->lock,flags);
+
+ 	change_params(info);
+
+	return 0;
+}
+
+static int get_txidle(SLMP_INFO * info, int*idle_mode)
+{
+	int err;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):%s get_txidle()=%d\n",
+			 __FILE__,__LINE__, info->device_name, info->idle_mode);
+
+	COPY_TO_USER(err,idle_mode, &info->idle_mode, sizeof(int));
+	if (err) {
+		if ( debug_level >= DEBUG_LEVEL_INFO )
+			printk( "%s(%d):%s get_txidle() user buffer copy failed\n",
+				__FILE__,__LINE__,info->device_name);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int set_txidle(SLMP_INFO * info, int idle_mode)
+{
+ 	unsigned long flags;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):%s set_txidle(%d)\n",
+			__FILE__,__LINE__,info->device_name, idle_mode );
+
+	spin_lock_irqsave(&info->lock,flags);
+	info->idle_mode = idle_mode;
+	tx_set_idle( info );
+	spin_unlock_irqrestore(&info->lock,flags);
+	return 0;
+}
+
+static int tx_enable(SLMP_INFO * info, int enable)
+{
+ 	unsigned long flags;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):%s tx_enable(%d)\n",
+			__FILE__,__LINE__,info->device_name, enable);
+
+	spin_lock_irqsave(&info->lock,flags);
+	if ( enable ) {
+		if ( !info->tx_enabled ) {
+			tx_start(info);
+		}
+	} else {
+		if ( info->tx_enabled )
+			tx_stop(info);
+	}
+	spin_unlock_irqrestore(&info->lock,flags);
+	return 0;
+}
+
+/* abort send HDLC frame
+ */
+static int tx_abort(SLMP_INFO * info)
+{
+ 	unsigned long flags;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):%s tx_abort()\n",
+			__FILE__,__LINE__,info->device_name);
+
+	spin_lock_irqsave(&info->lock,flags);
+	if ( info->tx_active && info->params.mode == MGSL_MODE_HDLC ) {
+		info->ie1_value &= ~UDRN;
+		info->ie1_value |= IDLE;
+		write_reg(info, IE1, info->ie1_value);	/* disable tx status interrupts */
+		write_reg(info, SR1, (unsigned char)(IDLE + UDRN));	/* clear pending */
+
+		write_reg(info, TXDMA + DSR, 0);		/* disable DMA channel */
+		write_reg(info, TXDMA + DCMD, SWABORT);	/* reset/init DMA channel */
+
+   		write_reg(info, CMD, TXABORT);
+	}
+	spin_unlock_irqrestore(&info->lock,flags);
+	return 0;
+}
+
+static int rx_enable(SLMP_INFO * info, int enable)
+{
+ 	unsigned long flags;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):%s rx_enable(%d)\n",
+			__FILE__,__LINE__,info->device_name,enable);
+
+	spin_lock_irqsave(&info->lock,flags);
+	if ( enable ) {
+		if ( !info->rx_enabled )
+			rx_start(info);
+	} else {
+		if ( info->rx_enabled )
+			rx_stop(info);
+	}
+	spin_unlock_irqrestore(&info->lock,flags);
+	return 0;
+}
+
+static int map_status(int signals)
+{
+	/* Map status bits to API event bits */
+
+	return ((signals & SerialSignal_DSR) ? MgslEvent_DsrActive : MgslEvent_DsrInactive) +
+	       ((signals & SerialSignal_CTS) ? MgslEvent_CtsActive : MgslEvent_CtsInactive) +
+	       ((signals & SerialSignal_DCD) ? MgslEvent_DcdActive : MgslEvent_DcdInactive) +
+	       ((signals & SerialSignal_RI)  ? MgslEvent_RiActive : MgslEvent_RiInactive);
+}
+
+/* wait for specified event to occur
+ */
+static int wait_mgsl_event(SLMP_INFO * info, int * mask_ptr)
+{
+ 	unsigned long flags;
+	int s;
+	int rc=0;
+	struct mgsl_icount cprev, cnow;
+	int events;
+	int mask;
+	struct	_input_signal_events oldsigs, newsigs;
+	DECLARE_WAITQUEUE(wait, current);
+
+	COPY_FROM_USER(rc,&mask, mask_ptr, sizeof(int));
+	if (rc) {
+		return  -EFAULT;
+	}
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):%s wait_mgsl_event(%d)\n",
+			__FILE__,__LINE__,info->device_name,mask);
+
+	spin_lock_irqsave(&info->lock,flags);
+
+	/* return immediately if state matches requested events */
+	get_signals(info);
+	s = map_status(info->serial_signals);
+
+	events = mask &
+		( ((s & SerialSignal_DSR) ? MgslEvent_DsrActive:MgslEvent_DsrInactive) +
+ 		  ((s & SerialSignal_DCD) ? MgslEvent_DcdActive:MgslEvent_DcdInactive) +
+		  ((s & SerialSignal_CTS) ? MgslEvent_CtsActive:MgslEvent_CtsInactive) +
+		  ((s & SerialSignal_RI)  ? MgslEvent_RiActive :MgslEvent_RiInactive) );
+	if (events) {
+		spin_unlock_irqrestore(&info->lock,flags);
+		goto exit;
+	}
+
+	/* save current irq counts */
+	cprev = info->icount;
+	oldsigs = info->input_signal_events;
+
+	/* enable hunt and idle irqs if needed */
+	if (mask & (MgslEvent_ExitHuntMode+MgslEvent_IdleReceived)) {
+		unsigned char oldval = info->ie1_value;
+		unsigned char newval = oldval +
+			 (mask & MgslEvent_ExitHuntMode ? FLGD:0) +
+			 (mask & MgslEvent_IdleReceived ? IDLE:0);
+		if ( oldval != newval ) {
+			info->ie1_value = newval;
+			write_reg(info, IE1, info->ie1_value);
+		}
+	}
+
+	set_current_state(TASK_INTERRUPTIBLE);
+	add_wait_queue(&info->event_wait_q, &wait);
+
+	spin_unlock_irqrestore(&info->lock,flags);
+
+	for(;;) {
+		schedule();
+		if (signal_pending(current)) {
+			rc = -ERESTARTSYS;
+			break;
+		}
+
+		/* get current irq counts */
+		spin_lock_irqsave(&info->lock,flags);
+		cnow = info->icount;
+		newsigs = info->input_signal_events;
+		set_current_state(TASK_INTERRUPTIBLE);
+		spin_unlock_irqrestore(&info->lock,flags);
+
+		/* if no change, wait aborted for some reason */
+		if (newsigs.dsr_up   == oldsigs.dsr_up   &&
+		    newsigs.dsr_down == oldsigs.dsr_down &&
+		    newsigs.dcd_up   == oldsigs.dcd_up   &&
+		    newsigs.dcd_down == oldsigs.dcd_down &&
+		    newsigs.cts_up   == oldsigs.cts_up   &&
+		    newsigs.cts_down == oldsigs.cts_down &&
+		    newsigs.ri_up    == oldsigs.ri_up    &&
+		    newsigs.ri_down  == oldsigs.ri_down  &&
+		    cnow.exithunt    == cprev.exithunt   &&
+		    cnow.rxidle      == cprev.rxidle) {
+			rc = -EIO;
+			break;
+		}
+
+		events = mask &
+			( (newsigs.dsr_up   != oldsigs.dsr_up   ? MgslEvent_DsrActive:0)   +
+			  (newsigs.dsr_down != oldsigs.dsr_down ? MgslEvent_DsrInactive:0) +
+			  (newsigs.dcd_up   != oldsigs.dcd_up   ? MgslEvent_DcdActive:0)   +
+			  (newsigs.dcd_down != oldsigs.dcd_down ? MgslEvent_DcdInactive:0) +
+			  (newsigs.cts_up   != oldsigs.cts_up   ? MgslEvent_CtsActive:0)   +
+			  (newsigs.cts_down != oldsigs.cts_down ? MgslEvent_CtsInactive:0) +
+			  (newsigs.ri_up    != oldsigs.ri_up    ? MgslEvent_RiActive:0)    +
+			  (newsigs.ri_down  != oldsigs.ri_down  ? MgslEvent_RiInactive:0)  +
+			  (cnow.exithunt    != cprev.exithunt   ? MgslEvent_ExitHuntMode:0) +
+			  (cnow.rxidle      != cprev.rxidle     ? MgslEvent_IdleReceived:0) );
+		if (events)
+			break;
+
+		cprev = cnow;
+		oldsigs = newsigs;
+	}
+
+	remove_wait_queue(&info->event_wait_q, &wait);
+	set_current_state(TASK_RUNNING);
+
+
+	if (mask & (MgslEvent_ExitHuntMode + MgslEvent_IdleReceived)) {
+		spin_lock_irqsave(&info->lock,flags);
+		if (!waitqueue_active(&info->event_wait_q)) {
+			/* disable enable exit hunt mode/idle rcvd IRQs */
+			info->ie1_value &= ~(FLGD|IDLE);
+			write_reg(info, IE1, info->ie1_value);
+		}
+		spin_unlock_irqrestore(&info->lock,flags);
+	}
+exit:
+	if ( rc == 0 )
+		PUT_USER(rc, events, mask_ptr);
+
+	return rc;
+}
+
+static int modem_input_wait(SLMP_INFO *info,int arg)
+{
+ 	unsigned long flags;
+	int rc;
+	struct mgsl_icount cprev, cnow;
+	DECLARE_WAITQUEUE(wait, current);
+
+	/* save current irq counts */
+	spin_lock_irqsave(&info->lock,flags);
+	cprev = info->icount;
+	add_wait_queue(&info->status_event_wait_q, &wait);
+	set_current_state(TASK_INTERRUPTIBLE);
+	spin_unlock_irqrestore(&info->lock,flags);
+
+	for(;;) {
+		schedule();
+		if (signal_pending(current)) {
+			rc = -ERESTARTSYS;
+			break;
+		}
+
+		/* get new irq counts */
+		spin_lock_irqsave(&info->lock,flags);
+		cnow = info->icount;
+		set_current_state(TASK_INTERRUPTIBLE);
+		spin_unlock_irqrestore(&info->lock,flags);
+
+		/* if no change, wait aborted for some reason */
+		if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
+		    cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) {
+			rc = -EIO;
+			break;
+		}
+
+		/* check for change in caller specified modem input */
+		if ((arg & TIOCM_RNG && cnow.rng != cprev.rng) ||
+		    (arg & TIOCM_DSR && cnow.dsr != cprev.dsr) ||
+		    (arg & TIOCM_CD  && cnow.dcd != cprev.dcd) ||
+		    (arg & TIOCM_CTS && cnow.cts != cprev.cts)) {
+			rc = 0;
+			break;
+		}
+
+		cprev = cnow;
+	}
+	remove_wait_queue(&info->status_event_wait_q, &wait);
+	set_current_state(TASK_RUNNING);
+	return rc;
+}
+
+/* return the state of the serial control and status signals
+ */
+static int get_modem_info(SLMP_INFO * info, unsigned int *value)
+{
+	unsigned int result;
+ 	unsigned long flags;
+	int err;
+
+	spin_lock_irqsave(&info->lock,flags);
+ 	get_signals(info);
+	spin_unlock_irqrestore(&info->lock,flags);
+
+	result = ((info->serial_signals & SerialSignal_RTS) ? TIOCM_RTS:0) +
+		((info->serial_signals & SerialSignal_DTR) ? TIOCM_DTR:0) +
+		((info->serial_signals & SerialSignal_DCD) ? TIOCM_CAR:0) +
+		((info->serial_signals & SerialSignal_RI)  ? TIOCM_RNG:0) +
+		((info->serial_signals & SerialSignal_DSR) ? TIOCM_DSR:0) +
+		((info->serial_signals & SerialSignal_CTS) ? TIOCM_CTS:0);
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):%s synclinkmp_get_modem_info() value=%08X\n",
+			 __FILE__,__LINE__, info->device_name, result );
+
+	PUT_USER(err,result,value);
+	return err;
+}
+
+/* set modem control signals (DTR/RTS)
+ *
+ * 	cmd	signal command: TIOCMBIS = set bit TIOCMBIC = clear bit
+ *		TIOCMSET = set/clear signal values
+ * 	value	bit mask for command
+ */
+static int set_modem_info(SLMP_INFO * info, unsigned int cmd,
+			  unsigned int *value)
+{
+ 	int error;
+ 	unsigned int arg;
+ 	unsigned long flags;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):%s synclinkmp_set_modem_info()\n",
+			__FILE__,__LINE__,info->device_name );
+
+ 	GET_USER(error,arg,value);
+ 	if (error)
+ 		return error;
+
+ 	switch (cmd) {
+ 	case TIOCMBIS:
+ 		if (arg & TIOCM_RTS)
+ 			info->serial_signals |= SerialSignal_RTS;
+ 		if (arg & TIOCM_DTR)
+ 			info->serial_signals |= SerialSignal_DTR;
+ 		break;
+ 	case TIOCMBIC:
+ 		if (arg & TIOCM_RTS)
+ 			info->serial_signals &= ~SerialSignal_RTS;
+ 		if (arg & TIOCM_DTR)
+ 			info->serial_signals &= ~SerialSignal_DTR;
+ 		break;
+ 	case TIOCMSET:
+ 		if (arg & TIOCM_RTS)
+ 			info->serial_signals |= SerialSignal_RTS;
+		else
+ 			info->serial_signals &= ~SerialSignal_RTS;
+
+ 		if (arg & TIOCM_DTR)
+ 			info->serial_signals |= SerialSignal_DTR;
+		else
+ 			info->serial_signals &= ~SerialSignal_DTR;
+ 		break;
+ 	default:
+ 		return -EINVAL;
+ 	}
+
+	spin_lock_irqsave(&info->lock,flags);
+ 	set_signals(info);
+	spin_unlock_irqrestore(&info->lock,flags);
+
+	return 0;
+}
+
+
+
+/* Block the current process until the specified port is ready to open.
+ */
+static int block_til_ready(struct tty_struct *tty, struct file *filp,
+			   SLMP_INFO *info)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	int		retval;
+	int		do_clocal = 0, extra_count = 0;
+	unsigned long	flags;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):%s block_til_ready()\n",
+			 __FILE__,__LINE__, tty->driver.name );
+
+	if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
+		/* this is a callout device */
+		/* just verify that normal device is not in use */
+		if (info->flags & ASYNC_NORMAL_ACTIVE)
+			return -EBUSY;
+		if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+		    (info->flags & ASYNC_SESSION_LOCKOUT) &&
+		    (info->session != current->session))
+		    return -EBUSY;
+		if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+		    (info->flags & ASYNC_PGRP_LOCKOUT) &&
+		    (info->pgrp != current->pgrp))
+		    return -EBUSY;
+		info->flags |= ASYNC_CALLOUT_ACTIVE;
+		return 0;
+	}
+
+	if (filp->f_flags & O_NONBLOCK || tty->flags & (1 << TTY_IO_ERROR)){
+		/* nonblock mode is set or port is not enabled */
+		/* just verify that callout device is not active */
+		if (info->flags & ASYNC_CALLOUT_ACTIVE)
+			return -EBUSY;
+		info->flags |= ASYNC_NORMAL_ACTIVE;
+		return 0;
+	}
+
+	if (info->flags & ASYNC_CALLOUT_ACTIVE) {
+		if (info->normal_termios.c_cflag & CLOCAL)
+			do_clocal = 1;
+	} else {
+		if (tty->termios->c_cflag & CLOCAL)
+			do_clocal = 1;
+	}
+
+	/* Wait for 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
+	 * close() knows when to free things.  We restore it upon
+	 * exit, either normal or abnormal.
+	 */
+
+	retval = 0;
+	add_wait_queue(&info->open_wait, &wait);
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):%s block_til_ready() before block, count=%d\n",
+			 __FILE__,__LINE__, tty->driver.name, info->count );
+
+	save_flags(flags); cli();
+	if (!tty_hung_up_p(filp)) {
+		extra_count = 1;
+		info->count--;
+	}
+	restore_flags(flags);
+	info->blocked_open++;
+
+	while (1) {
+		if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ 		    (tty->termios->c_cflag & CBAUD)) {
+			spin_lock_irqsave(&info->lock,flags);
+			info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR;
+		 	set_signals(info);
+			spin_unlock_irqrestore(&info->lock,flags);
+		}
+
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		if (tty_hung_up_p(filp) || !(info->flags & ASYNC_INITIALIZED)){
+			retval = (info->flags & ASYNC_HUP_NOTIFY) ?
+					-EAGAIN : -ERESTARTSYS;
+			break;
+		}
+
+		spin_lock_irqsave(&info->lock,flags);
+	 	get_signals(info);
+		spin_unlock_irqrestore(&info->lock,flags);
+
+ 		if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ 		    !(info->flags & ASYNC_CLOSING) &&
+ 		    (do_clocal || (info->serial_signals & SerialSignal_DCD)) ) {
+ 			break;
+		}
+
+		if (signal_pending(current)) {
+			retval = -ERESTARTSYS;
+			break;
+		}
+
+		if (debug_level >= DEBUG_LEVEL_INFO)
+			printk("%s(%d):%s block_til_ready() count=%d\n",
+				 __FILE__,__LINE__, tty->driver.name, info->count );
+
+		schedule();
+	}
+
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&info->open_wait, &wait);
+
+	if (extra_count)
+		info->count++;
+	info->blocked_open--;
+
+	if (debug_level >= DEBUG_LEVEL_INFO)
+		printk("%s(%d):%s block_til_ready() after, count=%d\n",
+			 __FILE__,__LINE__, tty->driver.name, info->count );
+
+	if (!retval)
+		info->flags |= ASYNC_NORMAL_ACTIVE;
+
+	return retval;
+}
+
+int alloc_dma_bufs(SLMP_INFO *info)
+{
+	unsigned short BuffersPerFrame;
+	unsigned short BufferCount;
+
+	// Force allocation to start at 64K boundary for each port.
+	// This is necessary because *all* buffer descriptors for a port
+	// *must* be in the same 64K block. All descriptors on a port
+	// share a common 'base' address (upper 8 bits of 24 bits) programmed
+	// into the CBP register.
+	info->port_array[0]->last_mem_alloc = (SCA_MEM_SIZE/4) * info->port_num;
+
+	/* Calculate the number of DMA buffers necessary to hold the */
+	/* largest allowable frame size. Note: If the max frame size is */
+	/* not an even multiple of the DMA buffer size then we need to */
+	/* round the buffer count per frame up one. */
+
+	BuffersPerFrame = (unsigned short)(info->max_frame_size/SCABUFSIZE);
+	if ( info->max_frame_size % SCABUFSIZE )
+		BuffersPerFrame++;
+
+	/* calculate total number of data buffers (SCABUFSIZE) possible
+	 * in one ports memory (SCA_MEM_SIZE/4) after allocating memory
+	 * for the descriptor list (BUFFERLISTSIZE).
+	 */
+	BufferCount = (SCA_MEM_SIZE/4 - BUFFERLISTSIZE)/SCABUFSIZE;
+
+	/* limit number of buffers to maximum amount of descriptors */
+	if (BufferCount > BUFFERLISTSIZE/sizeof(SCADESC))
+		BufferCount = BUFFERLISTSIZE/sizeof(SCADESC);
+
+	/* use enough buffers to transmit one max size frame */
+	info->tx_buf_count = BuffersPerFrame + 1;
+
+	/* never use more than half the available buffers for transmit */
+	if (info->tx_buf_count > (BufferCount/2))
+		info->tx_buf_count = BufferCount/2;
+
+	if (info->tx_buf_count > SCAMAXDESC)
+		info->tx_buf_count = SCAMAXDESC;
+
+	/* use remaining buffers for receive */
+	info->rx_buf_count = BufferCount - info->tx_buf_count;
+
+	if (info->rx_buf_count > SCAMAXDESC)
+		info->rx_buf_count = SCAMAXDESC;
+
+	if ( debug_level >= DEBUG_LEVEL_INFO )
+		printk("%s(%d):%s Allocating %d TX and %d RX DMA buffers.\n",
+			__FILE__,__LINE__, info->device_name,
+			info->tx_buf_count,info->rx_buf_count);
+
+	if ( alloc_buf_list( info ) < 0 ||
+		alloc_frame_bufs(info,
+		  			info->rx_buf_list,
+		  			info->rx_buf_list_ex,
+					info->rx_buf_count) < 0 ||
+		alloc_frame_bufs(info,
+					info->tx_buf_list,
+					info->tx_buf_list_ex,
+					info->tx_buf_count) < 0 ||
+		alloc_tmp_rx_buf(info) < 0 ) {
+		printk("%s(%d):%s Can't allocate DMA buffer memory\n",
+			__FILE__,__LINE__, info->device_name);
+		return -ENOMEM;
+	}
+
+	rx_reset_buffers( info );
+
+	return 0;
+}
+
+/* Allocate DMA buffers for the transmit and receive descriptor lists.
+ */
+int alloc_buf_list(SLMP_INFO *info)
+{
+	unsigned int i;
+
+	/* build list in adapter shared memory */
+	info->buffer_list = info->memory_base + info->port_array[0]->last_mem_alloc;
+	info->buffer_list_phys = info->port_array[0]->last_mem_alloc;
+	info->port_array[0]->last_mem_alloc += BUFFERLISTSIZE;
+
+	memset(info->buffer_list, 0, BUFFERLISTSIZE);
+
+	/* Save virtual address pointers to the receive and */
+	/* transmit buffer lists. (Receive 1st). These pointers will */
+	/* be used by the processor to access the lists. */
+	info->rx_buf_list = (SCADESC *)info->buffer_list;
+
+	info->tx_buf_list = (SCADESC *)info->buffer_list;
+	info->tx_buf_list += info->rx_buf_count;
+
+	/* Build links for circular buffer entry lists (tx and rx)
+	 *
+	 * Note: links are physical addresses read by the SCA device
+	 * to determine the next buffer entry to use.
+	 */
+
+	for ( i = 0; i < info->rx_buf_count; i++ ) {
+		/* calculate and store physical address of this buffer entry */
+		info->rx_buf_list_ex[i].phys_entry =
+			info->buffer_list_phys + (i * sizeof(SCABUFSIZE));
+
+		/* calculate and store physical address of */
+		/* next entry in cirular list of entries */
+		info->rx_buf_list[i].next = info->buffer_list_phys;
+		if ( i < info->rx_buf_count - 1 )
+			info->rx_buf_list[i].next += (i + 1) * sizeof(SCADESC);
+
+		info->rx_buf_list[i].length = SCABUFSIZE;
+	}
+
+	for ( i = 0; i < info->tx_buf_count; i++ ) {
+		/* calculate and store physical address of this buffer entry */
+		info->tx_buf_list_ex[i].phys_entry = info->buffer_list_phys +
+			((info->rx_buf_count + i) * sizeof(SCADESC));
+
+		/* calculate and store physical address of */
+		/* next entry in cirular list of entries */
+
+		info->tx_buf_list[i].next = info->buffer_list_phys +
+			info->rx_buf_count * sizeof(SCADESC);
+
+		if ( i < info->tx_buf_count - 1 )
+			info->tx_buf_list[i].next += (i + 1) * sizeof(SCADESC);
+	}
+
+	return 0;
+}
+
+/* Allocate the frame DMA buffers used by the specified buffer list.
+ */
+int alloc_frame_bufs(SLMP_INFO *info, SCADESC *buf_list,SCADESC_EX *buf_list_ex,int count)
+{
+	int i;
+	unsigned long phys_addr;
+
+	for ( i = 0; i < count; i++ ) {
+		buf_list_ex[i].virt_addr = info->memory_base + info->port_array[0]->last_mem_alloc;
+		phys_addr = info->port_array[0]->last_mem_alloc;
+		info->port_array[0]->last_mem_alloc += SCABUFSIZE;
+
+		buf_list[i].buf_ptr  = (unsigned short)phys_addr;
+		buf_list[i].buf_base = (unsigned char)(phys_addr >> 16);
+	}
+
+	return 0;
+}
+
+void free_dma_bufs(SLMP_INFO *info)
+{
+	info->buffer_list = NULL;
+	info->rx_buf_list = NULL;
+	info->tx_buf_list = NULL;
+}
+
+/* allocate buffer large enough to hold max_frame_size.
+ * This buffer is used to pass an assembled frame to the line discipline.
+ */
+int alloc_tmp_rx_buf(SLMP_INFO *info)
+{
+	info->tmp_rx_buf = kmalloc(info->max_frame_size, GFP_KERNEL);
+	if (info->tmp_rx_buf == NULL)
+		return -ENOMEM;
+	return 0;
+}
+
+void free_tmp_rx_buf(SLMP_INFO *info)
+{
+	if (info->tmp_rx_buf)
+		kfree(info->tmp_rx_buf);
+	info->tmp_rx_buf = NULL;
+}
+
+int claim_resources(SLMP_INFO *info)
+{
+	if (request_mem_region(info->phys_memory_base,0x40000,"synclinkmp") == NULL) {
+		printk( "%s(%d):%s mem addr conflict, Addr=%08X\n",
+			__FILE__,__LINE__,info->device_name, info->phys_memory_base);
+		goto errout;
+	}
+	else
+		info->shared_mem_requested = 1;
+
+	if (request_mem_region(info->phys_lcr_base + info->lcr_offset,128,"synclinkmp") == NULL) {
+		printk( "%s(%d):%s lcr mem addr conflict, Addr=%08X\n",
+			__FILE__,__LINE__,info->device_name, info->phys_lcr_base);
+		goto errout;
+	}
+	else
+		info->lcr_mem_requested = 1;
+
+	if (request_mem_region(info->phys_sca_base + info->sca_offset,512,"synclinkmp") == NULL) {
+		printk( "%s(%d):%s sca mem addr conflict, Addr=%08X\n",
+			__FILE__,__LINE__,info->device_name, info->phys_sca_base);
+		goto errout;
+	}
+	else
+		info->sca_base_requested = 1;
+
+	if (request_mem_region(info->phys_statctrl_base + info->statctrl_offset,16,"synclinkmp") == NULL) {
+		printk( "%s(%d):%s stat/ctrl mem addr conflict, Addr=%08X\n",
+			__FILE__,__LINE__,info->device_name, info->phys_statctrl_base);
+		goto errout;
+	}
+	else
+		info->sca_statctrl_requested = 1;
+
+	info->memory_base = ioremap(info->phys_memory_base,SCA_MEM_SIZE);
+	if (!info->memory_base) {
+		printk( "%s(%d):%s Cant map shared memory, MemAddr=%08X\n",
+			__FILE__,__LINE__,info->device_name, info->phys_memory_base );
+		goto errout;
+	}
+
+	if ( !memory_test(info) ) {
+		printk( "%s(%d):Shared Memory Test failed for device %s MemAddr=%08X\n",
+			__FILE__,__LINE__,info->device_name, info->phys_memory_base );
+		goto errout;
+	}
+
+	info->lcr_base = ioremap(info->phys_lcr_base,PAGE_SIZE) + info->lcr_offset;
+	if (!info->lcr_base) {
+		printk( "%s(%d):%s Cant map LCR memory, MemAddr=%08X\n",
+			__FILE__,__LINE__,info->device_name, info->phys_lcr_base );
+		goto errout;
+	}
+
+	info->sca_base = ioremap(info->phys_sca_base,PAGE_SIZE) + info->sca_offset;
+	if (!info->sca_base) {
+		printk( "%s(%d):%s Cant map SCA memory, MemAddr=%08X\n",
+			__FILE__,__LINE__,info->device_name, info->phys_sca_base );
+		goto errout;
+	}
+
+	info->statctrl_base = ioremap(info->phys_statctrl_base,PAGE_SIZE) + info->statctrl_offset;
+	if (!info->statctrl_base) {
+		printk( "%s(%d):%s Cant map SCA Status/Control memory, MemAddr=%08X\n",
+			__FILE__,__LINE__,info->device_name, info->phys_statctrl_base );
+		goto errout;
+	}
+
+	return 0;
+
+errout:
+	release_resources( info );
+	return -ENODEV;
+}
+
+void release_resources(SLMP_INFO *info)
+{
+	if ( debug_level >= DEBUG_LEVEL_INFO )
+		printk( "%s(%d):%s release_resources() entry\n",
+			__FILE__,__LINE__,info->device_name );
+
+	if ( info->irq_requested ) {
+		free_irq(info->irq_level, info);
+		info->irq_requested = 0;
+	}
+
+	if ( info->shared_mem_requested ) {
+		release_mem_region(info->phys_memory_base,0x40000);
+		info->shared_mem_requested = 0;
+	}
+	if ( info->lcr_mem_requested ) {
+		release_mem_region(info->phys_lcr_base + info->lcr_offset,128);
+		info->lcr_mem_requested = 0;
+	}
+	if ( info->sca_base_requested ) {
+		release_mem_region(info->phys_sca_base + info->sca_offset,512);
+		info->sca_base_requested = 0;
+	}
+	if ( info->sca_statctrl_requested ) {
+		release_mem_region(info->phys_statctrl_base + info->statctrl_offset,16);
+		info->sca_statctrl_requested = 0;
+	}
+
+	if (info->memory_base){
+		iounmap(info->memory_base);
+		info->memory_base = 0;
+	}
+
+	if (info->sca_base) {
+		iounmap(info->sca_base - info->sca_offset);
+		info->sca_base=0;
+	}
+
+	if (info->statctrl_base) {
+		iounmap(info->statctrl_base - info->statctrl_offset);
+		info->statctrl_base=0;
+	}
+
+	if (info->lcr_base){
+		iounmap(info->lcr_base - info->lcr_offset);
+		info->lcr_base = 0;
+	}
+
+	if ( debug_level >= DEBUG_LEVEL_INFO )
+		printk( "%s(%d):%s release_resources() exit\n",
+			__FILE__,__LINE__,info->device_name );
+}
+
+/* Add the specified device instance data structure to the
+ * global linked list of devices and increment the device count.
+ */
+void add_device(SLMP_INFO *info)
+{
+	info->next_device = NULL;
+	info->line = synclinkmp_device_count;
+	sprintf(info->device_name,"ttySLM%dp%d",info->adapter_num,info->port_num);
+
+	if (info->line < MAX_DEVICES) {
+		if (maxframe[info->line])
+			info->max_frame_size = maxframe[info->line];
+		info->dosyncppp = dosyncppp[info->line];
+	}
+
+	synclinkmp_device_count++;
+
+	if ( !synclinkmp_device_list )
+		synclinkmp_device_list = info;
+	else {
+		SLMP_INFO *current_dev = synclinkmp_device_list;
+		while( current_dev->next_device )
+			current_dev = current_dev->next_device;
+		current_dev->next_device = info;
+	}
+
+	if ( info->max_frame_size < 4096 )
+		info->max_frame_size = 4096;
+	else if ( info->max_frame_size > 65535 )
+		info->max_frame_size = 65535;
+
+	printk( "SyncLink MultiPort %s: "
+		"Mem=(%08x %08X %08x %08X) IRQ=%d MaxFrameSize=%u\n",
+		info->device_name,
+		info->phys_sca_base,
+		info->phys_memory_base,
+		info->phys_statctrl_base,
+		info->phys_lcr_base,
+		info->irq_level,
+		info->max_frame_size );
+
+#ifdef CONFIG_SYNCLINK_SYNCPPP
+	if (info->dosyncppp)
+		sppp_init(info);
+#endif
+}
+
+/* Allocate and initialize a device instance structure
+ *
+ * Return Value:	pointer to SLMP_INFO if success, otherwise NULL
+ */
+SLMP_INFO *alloc_dev(int adapter_num, int port_num, struct pci_dev *pdev)
+{
+	SLMP_INFO *info;
+
+	info = (SLMP_INFO *)kmalloc(sizeof(SLMP_INFO),
+		 GFP_KERNEL);
+
+	if (!info) {
+		printk("%s(%d) Error can't allocate device instance data for adapter %d, port %d\n",
+			__FILE__,__LINE__, adapter_num, port_num);
+	} else {
+		memset(info, 0, sizeof(SLMP_INFO));
+		info->magic = MGSL_MAGIC;
+		info->task.sync = 0;
+		info->task.routine = bh_handler;
+		info->task.data    = info;
+		info->max_frame_size = 4096;
+		info->close_delay = 5*HZ/10;
+		info->closing_wait = 30*HZ;
+		init_waitqueue_head(&info->open_wait);
+		init_waitqueue_head(&info->close_wait);
+		init_waitqueue_head(&info->status_event_wait_q);
+		init_waitqueue_head(&info->event_wait_q);
+		spin_lock_init(&info->netlock);
+		memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS));
+		info->idle_mode = HDLC_TXIDLE_FLAGS;
+		info->adapter_num = adapter_num;
+		info->port_num = port_num;
+
+		/* Copy configuration info to device instance data */
+		info->irq_level = pdev->irq;
+		info->phys_lcr_base = pci_resource_start(pdev,0);
+		info->phys_sca_base = pci_resource_start(pdev,2);
+		info->phys_memory_base = pci_resource_start(pdev,3);
+		info->phys_statctrl_base = pci_resource_start(pdev,4);
+
+		/* Because veremap only works on page boundaries we must map
+		 * a larger area than is actually implemented for the LCR
+		 * memory range. We map a full page starting at the page boundary.
+		 */
+		info->lcr_offset    = info->phys_lcr_base & (PAGE_SIZE-1);
+		info->phys_lcr_base &= ~(PAGE_SIZE-1);
+
+		info->sca_offset    = info->phys_sca_base & (PAGE_SIZE-1);
+		info->phys_sca_base &= ~(PAGE_SIZE-1);
+
+		info->statctrl_offset    = info->phys_statctrl_base & (PAGE_SIZE-1);
+		info->phys_statctrl_base &= ~(PAGE_SIZE-1);
+
+		info->bus_type = MGSL_BUS_TYPE_PCI;
+		info->irq_flags = SA_SHIRQ;
+
+		/* Store the PCI9050 misc control register value because a flaw
+		 * in the PCI9050 prevents LCR registers from being read if
+		 * BIOS assigns an LCR base address with bit 7 set.
+		 *
+		 * Only the misc control register is accessed for which only
+		 * write access is needed, so set an initial value and change
+		 * bits to the device instance data as we write the value
+		 * to the actual misc control register.
+		 */
+		info->misc_ctrl_value = 0x087e4546;
+
+		/* initial port state is unknown - if startup errors
+		 * occur, init_error will be set to indicate the
+		 * problem. Once the port is fully initialized,
+		 * this value will be set to 0 to indicate the
+		 * port is available.
+		 */
+		info->init_error = -1;
+	}
+
+	return info;
+}
+
+void device_init(int adapter_num, struct pci_dev *pdev)
+{
+	SLMP_INFO *port_array[SCA_MAX_PORTS];
+	int port;
+
+	/* allocate device instances for up to SCA_MAX_PORTS devices */
+	for ( port = 0; port < SCA_MAX_PORTS; ++port ) {
+		port_array[port] = alloc_dev(adapter_num,port,pdev);
+		if( port_array[port] == NULL ) {
+			for ( --port; port >= 0; --port )
+				kfree(port_array[port]);
+			return;
+		}
+	}
+
+	/* give copy of port_array to all ports and add to device list  */
+	for ( port = 0; port < SCA_MAX_PORTS; ++port ) {
+		memcpy(port_array[port]->port_array,port_array,sizeof(port_array));
+		add_device( port_array[port] );
+		spin_lock_init(&port_array[port]->lock);
+	}
+
+	/* Allocate and claim adapter resources */
+	if ( !claim_resources(port_array[0]) ) {
+
+		alloc_dma_bufs(port_array[0]);
+
+		/* copy resource information from first port to others */
+		for ( port = 1; port < SCA_MAX_PORTS; ++port ) {
+			port_array[port]->lock  = port_array[0]->lock;
+			port_array[port]->irq_level     = port_array[0]->irq_level;
+			port_array[port]->memory_base   = port_array[0]->memory_base;
+			port_array[port]->sca_base      = port_array[0]->sca_base;
+			port_array[port]->statctrl_base = port_array[0]->statctrl_base;
+			port_array[port]->lcr_base      = port_array[0]->lcr_base;
+			alloc_dma_bufs(port_array[port]);
+		}
+
+		if ( request_irq(port_array[0]->irq_level,
+					synclinkmp_interrupt,
+					port_array[0]->irq_flags,
+					port_array[0]->device_name,
+					port_array[0]) < 0 ) {
+			printk( "%s(%d):%s Cant request interrupt, IRQ=%d\n",
+				__FILE__,__LINE__,
+				port_array[0]->device_name,
+				port_array[0]->irq_level );
+		}
+		else {
+			port_array[0]->irq_requested = 1;
+			adapter_test(port_array[0]);
+		}
+	}
+}
+
+/* Driver initialization entry point.
+ */
+
+static int __init synclinkmp_init(void)
+{
+	SLMP_INFO *info;
+
+	EXPORT_NO_SYMBOLS;
+
+	if (break_on_load) {
+	 	synclinkmp_get_text_ptr();
+  		BREAKPOINT();
+	}
+
+ 	printk("%s %s\n", driver_name, driver_version);
+
+	synclinkmp_adapter_count = -1;
+	pci_register_driver(&synclinkmp_pci_driver);
+
+	if ( !synclinkmp_device_list ) {
+		printk("%s(%d):No SyncLink devices found.\n",__FILE__,__LINE__);
+		return -ENODEV;
+	}
+
+	memset(serial_table,0,sizeof(struct tty_struct*)*MAX_DEVICES);
+	memset(serial_termios,0,sizeof(struct termios*)*MAX_DEVICES);
+	memset(serial_termios_locked,0,sizeof(struct termios*)*MAX_DEVICES);
+
+	/* Initialize the tty_driver structure */
+
+	memset(&serial_driver, 0, sizeof(struct tty_driver));
+	serial_driver.magic = TTY_DRIVER_MAGIC;
+	serial_driver.driver_name = "synclinkmp";
+	serial_driver.name = "ttySLM";
+	serial_driver.major = ttymajor;
+	serial_driver.minor_start = 64;
+	serial_driver.num = synclinkmp_device_count;
+	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 = open;
+	serial_driver.close = close;
+	serial_driver.write = write;
+	serial_driver.put_char = put_char;
+	serial_driver.flush_chars = flush_chars;
+	serial_driver.write_room = write_room;
+	serial_driver.chars_in_buffer = chars_in_buffer;
+	serial_driver.flush_buffer = flush_buffer;
+	serial_driver.ioctl = ioctl;
+	serial_driver.throttle = throttle;
+	serial_driver.unthrottle = unthrottle;
+	serial_driver.send_xchar = send_xchar;
+	serial_driver.break_ctl = set_break;
+	serial_driver.wait_until_sent = wait_until_sent;
+ 	serial_driver.read_proc = read_proc;
+	serial_driver.set_termios = set_termios;
+	serial_driver.stop = tx_hold;
+	serial_driver.start = tx_release;
+	serial_driver.hangup = hangup;
+
+	/*
+	 * The callout device is just like normal device except for
+	 * major number and the subtype code.
+	 */
+	callout_driver = serial_driver;
+	callout_driver.name = "cuaSLM";
+	callout_driver.major = cuamajor;
+	callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+	callout_driver.read_proc = 0;
+	callout_driver.proc_entry = 0;
+
+	if (tty_register_driver(&serial_driver) < 0)
+		printk("%s(%d):Couldn't register serial driver\n",
+			__FILE__,__LINE__);
+
+	if (tty_register_driver(&callout_driver) < 0)
+		printk("%s(%d):Couldn't register callout driver\n",
+			__FILE__,__LINE__);
+
+ 	printk("%s %s, tty major#%d callout major#%d\n",
+		driver_name, driver_version,
+		serial_driver.major, callout_driver.major);
+
+	/* Propagate these values to all device instances */
+
+	info = synclinkmp_device_list;
+	while(info){
+		info->callout_termios = callout_driver.init_termios;
+		info->normal_termios  = serial_driver.init_termios;
+		info = info->next_device;
+	}
+
+	return 0;
+}
+
+static void __exit synclinkmp_exit(void)
+{
+	unsigned long flags;
+	int rc;
+	SLMP_INFO *info;
+	SLMP_INFO *tmp;
+
+	printk("Unloading %s %s\n", driver_name, driver_version);
+	save_flags(flags);
+	cli();
+	if ((rc = tty_unregister_driver(&serial_driver)))
+		printk("%s(%d) failed to unregister tty driver err=%d\n",
+		       __FILE__,__LINE__,rc);
+	if ((rc = tty_unregister_driver(&callout_driver)))
+		printk("%s(%d) failed to unregister callout driver err=%d\n",
+		       __FILE__,__LINE__,rc);
+	restore_flags(flags);
+
+	info = synclinkmp_device_list;
+	while(info) {
+#ifdef CONFIG_SYNCLINK_SYNCPPP
+		if (info->dosyncppp)
+			sppp_delete(info);
+#endif
+		reset_port(info);
+		if ( info->port_num == 0 ) {
+			if ( info->irq_requested ) {
+				free_irq(info->irq_level, info);
+				info->irq_requested = 0;
+			}
+		}
+		info = info->next_device;
+	}
+
+	/* port 0 of each adapter originally claimed
+	 * all resources, release those now
+	 */
+	info = synclinkmp_device_list;
+	while(info) {
+		free_dma_bufs(info);
+		free_tmp_rx_buf(info);
+		if ( info->port_num == 0 ) {
+			spin_lock_irqsave(&info->lock,flags);
+			reset_adapter(info);
+			write_reg(info, LPR, 1);		/* set low power mode */
+			spin_unlock_irqrestore(&info->lock,flags);
+			release_resources(info);
+		}
+		tmp = info;
+		info = info->next_device;
+		kfree(tmp);
+	}
+
+	pci_unregister_driver(&synclinkmp_pci_driver);
+}
+
+module_init(synclinkmp_init);
+module_exit(synclinkmp_exit);
+
+/* Set the port for internal loopback mode.
+ * The TxCLK and RxCLK signals are generated from the BRG and
+ * the TxD is looped back to the RxD internally.
+ */
+void enable_loopback(SLMP_INFO *info, int enable)
+{
+	if (enable) {
+		/* MD2 (Mode Register 2)
+		 * 01..00  CNCT<1..0> Channel Connection 11=Local Loopback
+		 */
+		write_reg(info, MD2, (unsigned char)(read_reg(info, MD2) | (BIT1 + BIT0)));
+
+		/* degate external TxC clock source */
+		info->port_array[0]->ctrlreg_value |= (BIT0 << (info->port_num * 2));
+		write_control_reg(info);
+
+		/* RXS/TXS (Rx/Tx clock source)
+		 * 07      Reserved, must be 0
+		 * 06..04  Clock Source, 100=BRG
+		 * 03..00  Clock Divisor, 0000=1
+		 */
+		write_reg(info, RXS, 0x40);
+		write_reg(info, TXS, 0x40);
+
+	} else {
+		/* MD2 (Mode Register 2)
+	 	 * 01..00  CNCT<1..0> Channel connection, 0=normal
+		 */
+		write_reg(info, MD2, (unsigned char)(read_reg(info, MD2) & ~(BIT1 + BIT0)));
+
+		/* RXS/TXS (Rx/Tx clock source)
+		 * 07      Reserved, must be 0
+		 * 06..04  Clock Source, 000=RxC/TxC Pin
+		 * 03..00  Clock Divisor, 0000=1
+		 */
+		write_reg(info, RXS, 0x00);
+		write_reg(info, TXS, 0x00);
+	}
+
+	/* set LinkSpeed if available, otherwise default to 2Mbps */
+	if (info->params.clock_speed)
+		set_rate(info, info->params.clock_speed);
+	else
+		set_rate(info, 3686400);
+}
+
+/* Set the baud rate register to the desired speed
+ *
+ *	data_rate	data rate of clock in bits per second
+ *			A data rate of 0 disables the AUX clock.
+ */
+void set_rate( SLMP_INFO *info, u32 data_rate )
+{
+       	u32 TMCValue;
+       	unsigned char BRValue;
+	u32 Divisor=0;
+
+	/* fBRG = fCLK/(TMC * 2^BR)
+	 */
+	if (data_rate != 0) {
+		Divisor = 14745600/data_rate;
+		if (!Divisor)
+			Divisor = 1;
+
+		TMCValue = Divisor;
+
+		BRValue = 0;
+		if (TMCValue != 1 && TMCValue != 2) {
+			/* BRValue of 0 provides 50/50 duty cycle *only* when
+			 * TMCValue is 1 or 2. BRValue of 1 to 9 always provides
+			 * 50/50 duty cycle.
+			 */
+			BRValue = 1;
+			TMCValue >>= 1;
+		}
+
+		/* while TMCValue is too big for TMC register, divide
+		 * by 2 and increment BR exponent.
+		 */
+		for(; TMCValue > 256 && BRValue < 10; BRValue++)
+			TMCValue >>= 1;
+
+		write_reg(info, TXS,
+			(unsigned char)((read_reg(info, TXS) & 0xf0) | BRValue));
+		write_reg(info, RXS,
+			(unsigned char)((read_reg(info, RXS) & 0xf0) | BRValue));
+		write_reg(info, TMC, (unsigned char)TMCValue);
+	}
+	else {
+		write_reg(info, TXS,0);
+		write_reg(info, RXS,0);
+		write_reg(info, TMC, 0);
+	}
+}
+
+/* Disable receiver
+ */
+void rx_stop(SLMP_INFO *info)
+{
+	if (debug_level >= DEBUG_LEVEL_ISR)
+		printk("%s(%d):%s rx_stop()\n",
+			 __FILE__,__LINE__, info->device_name );
+
+	write_reg(info, CMD, RXRESET);
+
+	info->ie0_value &= ~RXRDYE;
+	write_reg(info, IE0, info->ie0_value);	/* disable Rx data interrupts */
+
+	write_reg(info, RXDMA + DSR, 0);	/* disable Rx DMA */
+	write_reg(info, RXDMA + DCMD, SWABORT);	/* reset/init Rx DMA */
+	write_reg(info, RXDMA + DIR, 0);	/* disable Rx DMA interrupts */
+
+	info->rx_enabled = 0;
+	info->rx_overflow = 0;
+}
+
+/* enable the receiver
+ */
+void rx_start(SLMP_INFO *info)
+{
+	int i;
+
+	if (debug_level >= DEBUG_LEVEL_ISR)
+		printk("%s(%d):%s rx_start()\n",
+			 __FILE__,__LINE__, info->device_name );
+
+	write_reg(info, CMD, RXRESET);
+
+	if ( info->params.mode == MGSL_MODE_HDLC ) {
+		/* HDLC, disabe IRQ on rxdata */
+		info->ie0_value &= ~RXRDYE;
+		write_reg(info, IE0, info->ie0_value);
+
+		/* Reset all Rx DMA buffers and program rx dma */
+		write_reg(info, RXDMA + DSR, 0);		/* disable Rx DMA */
+		write_reg(info, RXDMA + DCMD, SWABORT);	/* reset/init Rx DMA */
+
+		for (i = 0; i < info->rx_buf_count; i++) {
+			info->rx_buf_list[i].status = 0xff;
+
+			// throttle to 4 shared memory writes at a time to prevent
+			// hogging local bus (keep latency time for DMA requests low).
+			if (!(i % 4))
+				read_status_reg(info);
+		}
+		info->current_rx_buf = 0;
+
+		/* set current/1st descriptor address */
+		write_reg16(info, RXDMA + CDA,
+			info->rx_buf_list_ex[0].phys_entry);
+
+		/* set new last rx descriptor address */
+		write_reg16(info, RXDMA + EDA,
+			info->rx_buf_list_ex[info->rx_buf_count - 1].phys_entry);
+
+		/* set buffer length (shared by all rx dma data buffers) */
+		write_reg16(info, RXDMA + BFL, SCABUFSIZE);
+
+		write_reg(info, RXDMA + DIR, 0x60);	/* enable Rx DMA interrupts (EOM/BOF) */
+		write_reg(info, RXDMA + DSR, 0xf2);	/* clear Rx DMA IRQs, enable Rx DMA */
+	} else {
+		/* async, enable IRQ on rxdata */
+		info->ie0_value |= RXRDYE;
+		write_reg(info, IE0, info->ie0_value);
+	}
+
+	write_reg(info, CMD, RXENABLE);
+
+	info->rx_overflow = FALSE;
+	info->rx_enabled = 1;
+}
+
+/* Enable the transmitter and send a transmit frame if
+ * one is loaded in the DMA buffers.
+ */
+void tx_start(SLMP_INFO *info)
+{
+	if (debug_level >= DEBUG_LEVEL_ISR)
+		printk("%s(%d):%s tx_start() tx_count=%d\n",
+			 __FILE__,__LINE__, info->device_name,info->tx_count );
+
+	if (!info->tx_enabled ) {
+		write_reg(info, CMD, TXRESET);
+		write_reg(info, CMD, TXENABLE);
+		info->tx_enabled = TRUE;
+	}
+
+	if ( info->tx_count ) {
+
+		/* If auto RTS enabled and RTS is inactive, then assert */
+		/* RTS and set a flag indicating that the driver should */
+		/* negate RTS when the transmission completes. */
+
+		info->drop_rts_on_tx_done = 0;
+
+		if (info->params.mode != MGSL_MODE_ASYNC) {
+
+			if ( info->params.flags & HDLC_FLAG_AUTO_RTS ) {
+				get_signals( info );
+				if ( !(info->serial_signals & SerialSignal_RTS) ) {
+					info->serial_signals |= SerialSignal_RTS;
+					set_signals( info );
+					info->drop_rts_on_tx_done = 1;
+				}
+			}
+
+			write_reg(info, TXDMA + DSR, 0); 		/* disable DMA channel */
+			write_reg(info, TXDMA + DCMD, SWABORT);	/* reset/init DMA channel */
+	
+			/* set TX CDA (current descriptor address) */
+			write_reg16(info, TXDMA + CDA,
+				info->tx_buf_list_ex[0].phys_entry);
+	
+			/* set TX EDA (last descriptor address) */
+			write_reg16(info, TXDMA + EDA,
+				info->tx_buf_list_ex[info->last_tx_buf].phys_entry);
+	
+			/* clear IDLE and UDRN status bit */
+			info->ie1_value &= ~(IDLE + UDRN);
+			if (info->params.mode != MGSL_MODE_ASYNC)
+				info->ie1_value |= UDRN;     		/* HDLC, IRQ on underrun */
+			write_reg(info, IE1, info->ie1_value);	/* enable MSCI interrupts */
+			write_reg(info, SR1, (unsigned char)(IDLE + UDRN));
+	
+			write_reg(info, TXDMA + DIR, 0x40);		/* enable Tx DMA interrupts (EOM) */
+			write_reg(info, TXDMA + DSR, 0xf2);		/* clear Tx DMA IRQs, enable Tx DMA */
+	
+			info->tx_timer.expires = jiffies + jiffies_from_ms(5000);
+			add_timer(&info->tx_timer);
+		}
+		else {
+			tx_load_fifo(info);
+			/* async, enable IRQ on txdata */
+			info->ie0_value |= TXRDYE;
+			write_reg(info, IE0, info->ie0_value);
+		}
+
+		info->tx_active = 1;
+	}
+}
+
+/* stop the transmitter and DMA
+ */
+void tx_stop( SLMP_INFO *info )
+{
+	if (debug_level >= DEBUG_LEVEL_ISR)
+		printk("%s(%d):%s tx_stop()\n",
+			 __FILE__,__LINE__, info->device_name );
+
+	del_timer(&info->tx_timer);
+
+	write_reg(info, TXDMA + DSR, 0);		/* disable DMA channel */
+	write_reg(info, TXDMA + DCMD, SWABORT);	/* reset/init DMA channel */
+
+	write_reg(info, CMD, TXRESET);
+
+	info->ie1_value &= ~(UDRN + IDLE);
+	write_reg(info, IE1, info->ie1_value);	/* disable tx status interrupts */
+	write_reg(info, SR1, (unsigned char)(IDLE + UDRN));	/* clear pending */
+
+	info->ie0_value &= ~TXRDYE;
+	write_reg(info, IE0, info->ie0_value);	/* disable tx data interrupts */
+
+	info->tx_enabled = 0;
+	info->tx_active  = 0;
+}
+
+/* Fill the transmit FIFO until the FIFO is full or
+ * there is no more data to load.
+ */
+void tx_load_fifo(SLMP_INFO *info)
+{
+	u8 TwoBytes[2];
+
+	/* do nothing is now tx data available and no XON/XOFF pending */
+
+	if ( !info->tx_count && !info->x_char )
+		return;
+
+	/* load the Transmit FIFO until FIFOs full or all data sent */
+
+	while( info->tx_count && (read_reg(info,SR0) & BIT1) ) {
+
+		/* there is more space in the transmit FIFO and */
+		/* there is more data in transmit buffer */
+
+		if ( (info->tx_count > 1) && !info->x_char ) {
+ 			/* write 16-bits */
+			TwoBytes[0] = info->tx_buf[info->tx_get++];
+			if (info->tx_get >= info->max_frame_size)
+				info->tx_get -= info->max_frame_size;
+			TwoBytes[1] = info->tx_buf[info->tx_get++];
+			if (info->tx_get >= info->max_frame_size)
+				info->tx_get -= info->max_frame_size;
+
+			write_reg16(info, TRB, *((u16 *)TwoBytes));
+
+			info->tx_count -= 2;
+			info->icount.tx += 2;
+		} else {
+			/* only 1 byte left to transmit or 1 FIFO slot left */
+
+			if (info->x_char) {
+				/* transmit pending high priority char */
+				write_reg(info, TRB, info->x_char);
+				info->x_char = 0;
+			} else {
+				write_reg(info, TRB, info->tx_buf[info->tx_get++]);
+				if (info->tx_get >= info->max_frame_size)
+					info->tx_get -= info->max_frame_size;
+				info->tx_count--;
+			}
+			info->icount.tx++;
+		}
+	}
+}
+
+/* Reset a port to a known state
+ */
+void reset_port(SLMP_INFO *info)
+{
+	if (info->sca_base) {
+
+		tx_stop(info);
+		rx_stop(info);
+
+		info->serial_signals &= ~(SerialSignal_DTR + SerialSignal_RTS);
+		set_signals(info);
+
+		/* disable all port interrupts */
+		info->ie0_value = 0;
+		info->ie1_value = 0;
+		info->ie2_value = 0;
+		write_reg(info, IE0, info->ie0_value);
+		write_reg(info, IE1, info->ie1_value);
+		write_reg(info, IE2, info->ie2_value);
+
+		write_reg(info, CMD, CHRESET);
+	}
+}
+
+/* Reset all the ports to a known state.
+ */
+void reset_adapter(SLMP_INFO *info)
+{
+	int i;
+
+	for ( i=0; i < SCA_MAX_PORTS; ++i) {
+		if (info->port_array[i])
+			reset_port(info->port_array[i]);
+	}
+}
+
+/* Program port for asynchronous communications.
+ */
+void async_mode(SLMP_INFO *info)
+{
+
+  	unsigned char RegValue;
+
+	tx_stop(info);
+	rx_stop(info);
+
+	/* MD0, Mode Register 0
+	 *
+	 * 07..05  PRCTL<2..0>, Protocol Mode, 000=async
+	 * 04      AUTO, Auto-enable (RTS/CTS/DCD)
+	 * 03      Reserved, must be 0
+	 * 02      CRCCC, CRC Calculation, 0=disabled
+	 * 01..00  STOP<1..0> Stop bits (00=1,10=2)
+	 *
+	 * 0000 0000
+	 */
+	RegValue = 0x00;
+	if (info->params.stop_bits != 1)
+		RegValue |= BIT1;
+	write_reg(info, MD0, RegValue);
+
+	/* MD1, Mode Register 1
+	 *
+	 * 07..06  BRATE<1..0>, bit rate, 00=1/1 01=1/16 10=1/32 11=1/64
+	 * 05..04  TXCHR<1..0>, tx char size, 00=8 bits,01=7,10=6,11=5
+	 * 03..02  RXCHR<1..0>, rx char size
+	 * 01..00  PMPM<1..0>, Parity mode, 00=none 10=even 11=odd
+	 *
+	 * 0100 0000
+	 */
+	RegValue = 0x40;
+	switch (info->params.data_bits) {
+	case 7: RegValue |= BIT4 + BIT2; break;
+	case 6: RegValue |= BIT5 + BIT3; break;
+	case 5: RegValue |= BIT5 + BIT4 + BIT3 + BIT2; break;
+	}
+	if (info->params.parity != ASYNC_PARITY_NONE) {
+		RegValue |= BIT1;
+		if (info->params.parity == ASYNC_PARITY_ODD)
+			RegValue |= BIT0;
+	}
+	write_reg(info, MD1, RegValue);
+
+	/* MD2, Mode Register 2
+	 *
+	 * 07..02  Reserved, must be 0
+	 * 01..00  CNCT<1..0> Channel connection, 0=normal
+	 *
+	 * 0000 0000
+	 */
+	RegValue = 0x00;
+	write_reg(info, MD2, RegValue);
+
+	/* RXS, Receive clock source
+	 *
+	 * 07      Reserved, must be 0
+	 * 06..04  RXCS<2..0>, clock source, 000=RxC Pin, 100=BRG, 110=DPLL
+	 * 03..00  RXBR<3..0>, rate divisor, 0000=1
+	 */
+	RegValue=BIT6;
+	write_reg(info, RXS, RegValue);
+
+	/* TXS, Transmit clock source
+	 *
+	 * 07      Reserved, must be 0
+	 * 06..04  RXCS<2..0>, clock source, 000=TxC Pin, 100=BRG, 110=Receive Clock
+	 * 03..00  RXBR<3..0>, rate divisor, 0000=1
+	 */
+	RegValue=BIT6;
+	write_reg(info, TXS, RegValue);
+
+	/* Control Register
+	 *
+	 * 6,4,2,0  CLKSEL<3..0>, 0 = TcCLK in, 1 = Auxclk out
+	 */
+	info->port_array[0]->ctrlreg_value |= (BIT0 << (info->port_num * 2));
+	write_control_reg(info);
+
+	tx_set_idle(info);
+
+	/* RRC Receive Ready Control 0
+	 *
+	 * 07..05  Reserved, must be 0
+	 * 04..00  RRC<4..0> Rx FIFO trigger active 0x00 = 1 byte
+	 */
+	write_reg(info, TRC0, 0x00);
+
+	/* TRC0 Transmit Ready Control 0
+	 *
+	 * 07..05  Reserved, must be 0
+	 * 04..00  TRC<4..0> Tx FIFO trigger active 0x10 = 16 bytes
+	 */
+	write_reg(info, TRC0, 0x10);
+
+	/* TRC1 Transmit Ready Control 1
+	 *
+	 * 07..05  Reserved, must be 0
+	 * 04..00  TRC<4..0> Tx FIFO trigger inactive 0x1e = 31 bytes (full-1)
+	 */
+	write_reg(info, TRC1, 0x1e);
+
+	/* CTL, MSCI control register
+	 *
+	 * 07..06  Reserved, set to 0
+	 * 05      UDRNC, underrun control, 0=abort 1=CRC+flag (HDLC/BSC)
+	 * 04      IDLC, idle control, 0=mark 1=idle register
+	 * 03      BRK, break, 0=off 1 =on (async)
+	 * 02      SYNCLD, sync char load enable (BSC) 1=enabled
+	 * 01      GOP, go active on poll (LOOP mode) 1=enabled
+	 * 00      RTS, RTS output control, 0=active 1=inactive
+	 *
+	 * 0001 0001
+	 */
+	RegValue = 0x10;
+	if (!(info->serial_signals & SerialSignal_RTS))
+		RegValue |= 0x01;
+	write_reg(info, CTL, RegValue);
+
+	/* enable status interrupts */
+	info->ie0_value |= TXINTE + RXINTE;
+	write_reg(info, IE0, info->ie0_value);
+
+	/* enable break detect interrupt */
+	info->ie1_value = BRKD;
+	write_reg(info, IE1, info->ie1_value);
+
+	/* enable rx overrun interrupt */
+	info->ie2_value = OVRN;
+	write_reg(info, IE2, info->ie2_value);
+
+	set_rate( info, info->params.data_rate * 16 );
+
+	if (info->params.loopback)
+		enable_loopback(info,1);
+}
+
+/* Program the SCA for HDLC communications.
+ */
+void hdlc_mode(SLMP_INFO *info)
+{
+	unsigned char RegValue;
+	u32 DpllDivisor;
+
+	// Can't use DPLL because SCA outputs recovered clock on RxC when
+	// DPLL mode selected. This causes output contention with RxC receiver.
+	// Use of DPLL would require external hardware to disable RxC receiver
+	// when DPLL mode selected.
+	info->params.flags &= ~(HDLC_FLAG_TXC_DPLL + HDLC_FLAG_RXC_DPLL);
+
+	/* disable DMA interrupts */
+	write_reg(info, TXDMA + DIR, 0);
+	write_reg(info, RXDMA + DIR, 0);
+
+	/* MD0, Mode Register 0
+	 *
+	 * 07..05  PRCTL<2..0>, Protocol Mode, 100=HDLC
+	 * 04      AUTO, Auto-enable (RTS/CTS/DCD)
+	 * 03      Reserved, must be 0
+	 * 02      CRCCC, CRC Calculation, 1=enabled
+	 * 01      CRC1, CRC selection, 0=CRC-16,1=CRC-CCITT-16
+	 * 00      CRC0, CRC initial value, 1 = all 1s
+	 *
+	 * 1000 0001
+	 */
+	RegValue = 0x81;
+	if (info->params.flags & HDLC_FLAG_AUTO_CTS)
+		RegValue |= BIT4;
+	if (info->params.flags & HDLC_FLAG_AUTO_DCD)
+		RegValue |= BIT4;
+	if (info->params.crc_type == HDLC_CRC_16_CCITT)
+		RegValue |= BIT2 + BIT1;
+	write_reg(info, MD0, RegValue);
+
+	/* MD1, Mode Register 1
+	 *
+	 * 07..06  ADDRS<1..0>, Address detect, 00=no addr check
+	 * 05..04  TXCHR<1..0>, tx char size, 00=8 bits
+	 * 03..02  RXCHR<1..0>, rx char size, 00=8 bits
+	 * 01..00  PMPM<1..0>, Parity mode, 00=no parity
+	 *
+	 * 0000 0000
+	 */
+	RegValue = 0x00;
+	write_reg(info, MD1, RegValue);
+
+	/* MD2, Mode Register 2
+	 *
+	 * 07      NRZFM, 0=NRZ, 1=FM
+	 * 06..05  CODE<1..0> Encoding, 00=NRZ
+	 * 04..03  DRATE<1..0> DPLL Divisor, 00=8
+	 * 02      Reserved, must be 0
+	 * 01..00  CNCT<1..0> Channel connection, 0=normal
+	 *
+	 * 0000 0000
+	 */
+	RegValue = 0x00;
+	switch(info->params.encoding) {
+	case HDLC_ENCODING_NRZI:	  RegValue |= BIT5; break;
+	case HDLC_ENCODING_BIPHASE_MARK:  RegValue |= BIT7 + BIT5; break; /* aka FM1 */
+	case HDLC_ENCODING_BIPHASE_SPACE: RegValue |= BIT7 + BIT6; break; /* aka FM0 */
+	case HDLC_ENCODING_BIPHASE_LEVEL: RegValue |= BIT7; break; 	/* aka Manchester */
+#if 0
+	case HDLC_ENCODING_NRZB:	       				/* not supported */
+	case HDLC_ENCODING_NRZI_MARK:          				/* not supported */
+	case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: 				/* not supported */
+#endif
+	}
+	if ( info->params.flags & HDLC_FLAG_DPLL_DIV16 ) {
+		DpllDivisor = 16;
+		RegValue |= BIT3;
+	} else if ( info->params.flags & HDLC_FLAG_DPLL_DIV8 ) {
+		DpllDivisor = 8;
+	} else {
+		DpllDivisor = 32;
+		RegValue |= BIT4;
+	}
+	write_reg(info, MD2, RegValue);
+
+
+	/* RXS, Receive clock source
+	 *
+	 * 07      Reserved, must be 0
+	 * 06..04  RXCS<2..0>, clock source, 000=RxC Pin, 100=BRG, 110=DPLL
+	 * 03..00  RXBR<3..0>, rate divisor, 0000=1
+	 */
+	RegValue=0;
+	if (info->params.flags & HDLC_FLAG_RXC_BRG)
+		RegValue |= BIT6;
+	if (info->params.flags & HDLC_FLAG_RXC_DPLL)
+		RegValue |= BIT6 + BIT5;
+	write_reg(info, RXS, RegValue);
+
+	/* TXS, Transmit clock source
+	 *
+	 * 07      Reserved, must be 0
+	 * 06..04  RXCS<2..0>, clock source, 000=TxC Pin, 100=BRG, 110=Receive Clock
+	 * 03..00  RXBR<3..0>, rate divisor, 0000=1
+	 */
+	RegValue=0;
+	if (info->params.flags & HDLC_FLAG_TXC_BRG)
+		RegValue |= BIT6;
+	if (info->params.flags & HDLC_FLAG_TXC_DPLL)
+		RegValue |= BIT6 + BIT5;
+	write_reg(info, TXS, RegValue);
+
+	if (info->params.flags & HDLC_FLAG_RXC_DPLL)
+		set_rate(info, info->params.clock_speed * DpllDivisor);
+	else
+		set_rate(info, info->params.clock_speed);
+
+	/* GPDATA (General Purpose I/O Data Register)
+	 *
+	 * 6,4,2,0  CLKSEL<3..0>, 0 = TcCLK in, 1 = Auxclk out
+	 */
+	if (info->params.flags & HDLC_FLAG_TXC_BRG)
+		info->port_array[0]->ctrlreg_value |= (BIT0 << (info->port_num * 2));
+	else
+		info->port_array[0]->ctrlreg_value &= ~(BIT0 << (info->port_num * 2));
+	write_control_reg(info);
+
+	/* RRC Receive Ready Control 0
+	 *
+	 * 07..05  Reserved, must be 0
+	 * 04..00  RRC<4..0> Rx FIFO trigger active
+	 */
+	write_reg(info, RRC, rx_active_fifo_level);
+
+	/* TRC0 Transmit Ready Control 0
+	 *
+	 * 07..05  Reserved, must be 0
+	 * 04..00  TRC<4..0> Tx FIFO trigger active
+	 */
+	write_reg(info, TRC0, tx_active_fifo_level);
+
+	/* TRC1 Transmit Ready Control 1
+	 *
+	 * 07..05  Reserved, must be 0
+	 * 04..00  TRC<4..0> Tx FIFO trigger inactive 0x1f = 32 bytes (full)
+	 */
+	write_reg(info, TRC1, (unsigned char)(tx_negate_fifo_level - 1));
+
+	/* DMR, DMA Mode Register
+	 *
+	 * 07..05  Reserved, must be 0
+	 * 04      TMOD, Transfer Mode: 1=chained-block
+	 * 03      Reserved, must be 0
+	 * 02      NF, Number of Frames: 1=multi-frame
+	 * 01      CNTE, Frame End IRQ Counter enable: 0=disabled
+	 * 00      Reserved, must be 0
+	 *
+	 * 0001 0100
+	 */
+	write_reg(info, TXDMA + DMR, 0x14);
+	write_reg(info, RXDMA + DMR, 0x14);
+
+	/* Set chain pointer base (upper 8 bits of 24 bit addr) */
+	write_reg(info, RXDMA + CPB,
+		(unsigned char)(info->buffer_list_phys >> 16));
+
+	/* Set chain pointer base (upper 8 bits of 24 bit addr) */
+	write_reg(info, TXDMA + CPB,
+		(unsigned char)(info->buffer_list_phys >> 16));
+
+	/* enable status interrupts. other code enables/disables
+	 * the individual sources for these two interrupt classes.
+	 */
+	info->ie0_value |= TXINTE + RXINTE;
+	write_reg(info, IE0, info->ie0_value);
+
+	/* CTL, MSCI control register
+	 *
+	 * 07..06  Reserved, set to 0
+	 * 05      UDRNC, underrun control, 0=abort 1=CRC+flag (HDLC/BSC)
+	 * 04      IDLC, idle control, 0=mark 1=idle register
+	 * 03      BRK, break, 0=off 1 =on (async)
+	 * 02      SYNCLD, sync char load enable (BSC) 1=enabled
+	 * 01      GOP, go active on poll (LOOP mode) 1=enabled
+	 * 00      RTS, RTS output control, 0=active 1=inactive
+	 *
+	 * 0001 0001
+	 */
+	RegValue = 0x10;
+	if (!(info->serial_signals & SerialSignal_RTS))
+		RegValue |= 0x01;
+	write_reg(info, CTL, RegValue);
+
+	/* preamble not supported ! */
+
+	tx_set_idle(info);
+	tx_stop(info);
+	rx_stop(info);
+
+	set_rate(info, info->params.clock_speed);
+
+	if (info->params.loopback)
+		enable_loopback(info,1);
+}
+
+/* Set the transmit HDLC idle mode
+ */
+void tx_set_idle(SLMP_INFO *info)
+{
+	unsigned char RegValue = 0xff;
+
+	/* Map API idle mode to SCA register bits */
+	switch(info->idle_mode) {
+	case HDLC_TXIDLE_FLAGS:			RegValue = 0x7e; break;
+	case HDLC_TXIDLE_ALT_ZEROS_ONES:	RegValue = 0xaa; break;
+	case HDLC_TXIDLE_ZEROS:			RegValue = 0x00; break;
+	case HDLC_TXIDLE_ONES:			RegValue = 0xff; break;
+	case HDLC_TXIDLE_ALT_MARK_SPACE:	RegValue = 0xaa; break;
+	case HDLC_TXIDLE_SPACE:			RegValue = 0x00; break;
+	case HDLC_TXIDLE_MARK:			RegValue = 0xff; break;
+	}
+
+	write_reg(info, IDL, RegValue);
+}
+
+/* Query the adapter for the state of the V24 status (input) signals.
+ */
+void get_signals(SLMP_INFO *info)
+{
+	u16 status = read_reg(info, SR3);
+	u16 gpstatus = read_status_reg(info);
+	u16 testbit;
+
+	/* clear all serial signals except DTR and RTS */
+	info->serial_signals &= SerialSignal_DTR + SerialSignal_RTS;
+
+	/* set serial signal bits to reflect MISR */
+
+	if (!(status & BIT3))
+		info->serial_signals |= SerialSignal_CTS;
+
+	if ( !(status & BIT2))
+		info->serial_signals |= SerialSignal_DCD;
+
+	testbit = BIT1 << (info->port_num * 2); // Port 0..3 RI is GPDATA<1,3,5,7>
+	if (!(gpstatus & testbit))
+		info->serial_signals |= SerialSignal_RI;
+
+	testbit = BIT0 << (info->port_num * 2); // Port 0..3 DSR is GPDATA<0,2,4,6>
+	if (!(gpstatus & testbit))
+		info->serial_signals |= SerialSignal_DSR;
+}
+
+/* Set the state of DTR and RTS based on contents of
+ * serial_signals member of device context.
+ */
+void set_signals(SLMP_INFO *info)
+{
+	unsigned char RegValue;
+	u16 EnableBit;
+
+	RegValue = read_reg(info, CTL);
+	if (info->serial_signals & SerialSignal_RTS)
+		RegValue &= ~BIT0;
+	else
+		RegValue |= BIT0;
+	write_reg(info, CTL, RegValue);
+
+	// Port 0..3 DTR is ctrl reg <1,3,5,7>
+	EnableBit = BIT1 << (info->port_num*2);
+	if (info->serial_signals & SerialSignal_DTR)
+		info->port_array[0]->ctrlreg_value &= ~EnableBit;
+	else
+		info->port_array[0]->ctrlreg_value |= EnableBit;
+	write_control_reg(info);
+}
+
+/*******************/
+/* DMA Buffer Code */
+/*******************/
+
+/* Set the count for all receive buffers to SCABUFSIZE
+ * and set the current buffer to the first buffer. This effectively
+ * makes all buffers free and discards any data in buffers.
+ */
+void rx_reset_buffers(SLMP_INFO *info)
+{
+	rx_free_frame_buffers(info, 0, info->rx_buf_count - 1);
+}
+
+/* Free the buffers used by a received frame
+ *
+ * info   pointer to device instance data
+ * first  index of 1st receive buffer of frame
+ * last   index of last receive buffer of frame
+ */
+void rx_free_frame_buffers(SLMP_INFO *info, unsigned int first, unsigned int last)
+{
+	int done = 0;
+
+	while(!done) {
+	        /* reset current buffer for reuse */
+		info->rx_buf_list[first].status = 0xff;
+
+	        if (first == last) {
+	                done = 1;
+	                /* set new last rx descriptor address */
+			write_reg16(info, RXDMA + EDA, info->rx_buf_list_ex[first].phys_entry);
+	        }
+
+	        first++;
+		if (first == info->rx_buf_count)
+			first = 0;
+	}
+
+	/* set current buffer to next buffer after last buffer of frame */
+	info->current_rx_buf = first;
+}
+
+/* Return a received frame from the receive DMA buffers.
+ * Only frames received without errors are returned.
+ *
+ * Return Value:	1 if frame returned, otherwise 0
+ */
+int rx_get_frame(SLMP_INFO *info)
+{
+	unsigned int StartIndex, EndIndex;	/* index of 1st and last buffers of Rx frame */
+	unsigned short status;
+	unsigned int framesize = 0;
+	int ReturnCode = 0;
+	unsigned long flags;
+	struct tty_struct *tty = info->tty;
+	unsigned char addr_field = 0xff;
+   	SCADESC *desc;
+	SCADESC_EX *desc_ex;
+
+CheckAgain:
+	/* assume no frame returned, set zero length */
+	framesize = 0;
+	addr_field = 0xff;
+
+	/*
+	 * current_rx_buf points to the 1st buffer of the next available
+	 * receive frame. To find the last buffer of the frame look for
+	 * a non-zero status field in the buffer entries. (The status
+	 * field is set by the 16C32 after completing a receive frame.
+	 */
+	StartIndex = EndIndex = info->current_rx_buf;
+
+	for ( ;; ) {
+		desc = &info->rx_buf_list[EndIndex];
+		desc_ex = &info->rx_buf_list_ex[EndIndex];
+
+		if (desc->status == 0xff)
+			goto Cleanup;	/* current desc still in use, no frames available */
+
+		if (framesize == 0 && info->params.addr_filter != 0xff)
+			addr_field = desc_ex->virt_addr[0];
+
+		framesize += desc->length;
+
+		/* Status != 0 means last buffer of frame */
+		if (desc->status)
+			break;
+
+		EndIndex++;
+		if (EndIndex == info->rx_buf_count)
+			EndIndex = 0;
+
+		if (EndIndex == info->current_rx_buf) {
+			/* all buffers have been 'used' but none mark	   */
+			/* the end of a frame. Reset buffers and receiver. */
+			if ( info->rx_enabled ){
+				spin_lock_irqsave(&info->lock,flags);
+				rx_start(info);
+				spin_unlock_irqrestore(&info->lock,flags);
+			}
+			goto Cleanup;
+		}
+
+	}
+
+	/* check status of receive frame */
+
+	/* frame status is byte stored after frame data
+	 *
+	 * 7 EOM (end of msg), 1 = last buffer of frame
+	 * 6 Short Frame, 1 = short frame
+	 * 5 Abort, 1 = frame aborted
+	 * 4 Residue, 1 = last byte is partial
+	 * 3 Overrun, 1 = overrun occurred during frame reception
+	 * 2 CRC,     1 = CRC error detected
+	 *
+	 */
+	status = desc->status;
+
+	/* ignore CRC bit if not using CRC (bit is undefined) */
+	/* Note:CRC is not save to data buffer */
+	if (info->params.crc_type == HDLC_CRC_NONE)
+		status &= ~BIT2;
+
+	if (framesize == 0 ||
+		 (addr_field != 0xff && addr_field != info->params.addr_filter)) {
+		/* discard 0 byte frames, this seems to occur sometime
+		 * when remote is idling flags.
+		 */
+		rx_free_frame_buffers(info, StartIndex, EndIndex);
+		goto CheckAgain;
+	}
+
+	if (framesize < 2)
+		status |= BIT6;
+
+	if (status & (BIT6+BIT5+BIT3+BIT2)) {
+		/* received frame has errors,
+		 * update counts and mark frame size as 0
+		 */
+		if (status & BIT6)
+			info->icount.rxshort++;
+		else if (status & BIT5)
+			info->icount.rxabort++;
+		else if (status & BIT3)
+			info->icount.rxover++;
+		else
+			info->icount.rxcrc++;
+
+		framesize = 0;
+
+#ifdef CONFIG_SYNCLINK_SYNCPPP
+		info->netstats.rx_errors++;
+		info->netstats.rx_frame_errors++;
+#endif
+	}
+
+	if ( debug_level >= DEBUG_LEVEL_BH )
+		printk("%s(%d):%s rx_get_frame() status=%04X size=%d\n",
+			__FILE__,__LINE__,info->device_name,status,framesize);
+
+	if ( debug_level >= DEBUG_LEVEL_DATA )
+		trace_block(info,info->rx_buf_list_ex[StartIndex].virt_addr,
+			MIN(framesize,SCABUFSIZE),0);
+
+	if (framesize) {
+		if (framesize > info->max_frame_size)
+			info->icount.rxlong++;
+		else {
+			/* copy dma buffer(s) to contiguous intermediate buffer */
+			int copy_count = framesize;
+			int index = StartIndex;
+			unsigned char *ptmp = info->tmp_rx_buf;
+			info->tmp_rx_buf_count = framesize;
+
+			info->icount.rxok++;
+
+			while(copy_count) {
+				int partial_count = MIN(copy_count,SCABUFSIZE);
+				memcpy( ptmp,
+					info->rx_buf_list_ex[index].virt_addr,
+					partial_count );
+				ptmp += partial_count;
+				copy_count -= partial_count;
+
+				if ( ++index == info->rx_buf_count )
+					index = 0;
+			}
+
+#ifdef CONFIG_SYNCLINK_SYNCPPP
+			if (info->netcount) {
+				/* pass frame to syncppp device */
+				sppp_rx_done(info,info->tmp_rx_buf,framesize);
+			}
+			else
+#endif
+			{
+				if ( tty && tty->ldisc.receive_buf ) {
+					/* Call the line discipline receive callback directly. */
+					tty->ldisc.receive_buf(tty,
+						info->tmp_rx_buf,
+						info->flag_buf,
+						framesize);
+				}
+			}
+		}
+	}
+	/* Free the buffers used by this frame. */
+	rx_free_frame_buffers( info, StartIndex, EndIndex );
+
+	ReturnCode = 1;
+
+Cleanup:
+	if ( info->rx_enabled && info->rx_overflow ) {
+		/* Receiver is enabled, but needs to restarted due to
+		 * rx buffer overflow. If buffers are empty, restart receiver.
+		 */
+		if (info->rx_buf_list[EndIndex].status == 0xff) {
+			spin_lock_irqsave(&info->lock,flags);
+			rx_start(info);
+			spin_unlock_irqrestore(&info->lock,flags);
+		}
+	}
+
+	return ReturnCode;
+}
+
+/* load the transmit DMA buffer with data
+ */
+void tx_load_dma_buffer(SLMP_INFO *info, const char *buf, unsigned int count)
+{
+	unsigned short copy_count;
+	unsigned int i = 0;
+	SCADESC *desc;
+	SCADESC_EX *desc_ex;
+
+	if ( debug_level >= DEBUG_LEVEL_DATA )
+		trace_block(info,buf, MIN(count,SCABUFSIZE), 1);
+
+	/* Copy source buffer to one or more DMA buffers, starting with
+	 * the first transmit dma buffer.
+	 */
+	for(i=0;;)
+	{
+		copy_count = MIN(count,SCABUFSIZE);
+
+		desc = &info->tx_buf_list[i];
+		desc_ex = &info->tx_buf_list_ex[i];
+
+		load_pci_memory(info, desc_ex->virt_addr,buf,copy_count);
+
+		desc->length = copy_count;
+		desc->status = 0;
+
+		buf += copy_count;
+		count -= copy_count;
+
+		if (!count)
+			break;
+
+		i++;
+		if (i >= info->tx_buf_count)
+			i = 0;
+	}
+
+	info->tx_buf_list[i].status = 0x81;	/* set EOM and EOT status */
+	info->last_tx_buf = ++i;
+}
+
+int register_test(SLMP_INFO *info)
+{
+	static unsigned char testval[] = {0x00, 0xff, 0xaa, 0x55, 0x69, 0x96};
+	static unsigned int count = sizeof(testval)/sizeof(unsigned char);
+	unsigned int i;
+	int rc = TRUE;
+	unsigned long flags;
+
+	spin_lock_irqsave(&info->lock,flags);
+	reset_port(info);
+
+	/* assume failure */
+	info->init_error = DiagStatus_AddressFailure;
+
+	/* Write bit patterns to various registers but do it out of */
+	/* sync, then read back and verify values. */
+
+	for (i = 0 ; i < count ; i++) {
+		write_reg(info, TMC, testval[i]);
+		write_reg(info, IDL, testval[(i+1)%count]);
+		write_reg(info, SA0, testval[(i+2)%count]);
+		write_reg(info, SA1, testval[(i+3)%count]);
+
+		if ( (read_reg(info, TMC) != testval[i]) ||
+			  (read_reg(info, IDL) != testval[(i+1)%count]) ||
+			  (read_reg(info, SA0) != testval[(i+2)%count]) ||
+			  (read_reg(info, SA1) != testval[(i+3)%count]) )
+		{
+			rc = FALSE;
+			break;
+		}
+	}
+
+	reset_port(info);
+	spin_unlock_irqrestore(&info->lock,flags);
+
+	return rc;
+}
+
+int irq_test(SLMP_INFO *info)
+{
+	unsigned long timeout;
+	unsigned long flags;
+
+	unsigned char timer = (info->port_num & 1) ? TIMER2 : TIMER0;
+
+	spin_lock_irqsave(&info->lock,flags);
+	reset_port(info);
+
+	/* assume failure */
+	info->init_error = DiagStatus_IrqFailure;
+	info->irq_occurred = FALSE;
+
+	/* setup timer0 on SCA0 to interrupt */
+
+	/* IER2<7..4> = timer<3..0> interrupt enables (1=enabled) */
+	write_reg(info, IER2, (unsigned char)((info->port_num & 1) ? BIT6 : BIT4));
+
+	write_reg(info, (unsigned char)(timer + TEPR), 0);	/* timer expand prescale */
+	write_reg16(info, (unsigned char)(timer + TCONR), 1);	/* timer constant */
+
+
+	/* TMCS, Timer Control/Status Register
+	 *
+	 * 07      CMF, Compare match flag (read only) 1=match
+	 * 06      ECMI, CMF Interrupt Enable: 1=enabled
+	 * 05      Reserved, must be 0
+	 * 04      TME, Timer Enable
+	 * 03..00  Reserved, must be 0
+	 *
+	 * 0101 0000
+	 */
+	write_reg(info, (unsigned char)(timer + TMCS), 0x50);
+
+	spin_unlock_irqrestore(&info->lock,flags);
+
+	timeout=100;
+	while( timeout-- && !info->irq_occurred ) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(jiffies_from_ms(10));
+	}
+
+	spin_lock_irqsave(&info->lock,flags);
+	reset_port(info);
+	spin_unlock_irqrestore(&info->lock,flags);
+
+	return info->irq_occurred;
+}
+
+/* initialize individual SCA device (2 ports)
+ */
+int sca_init(SLMP_INFO *info)
+{
+	/* set wait controller to single mem partition (low), no wait states */
+	write_reg(info, PABR0, 0);	/* wait controller addr boundary 0 */
+	write_reg(info, PABR1, 0);	/* wait controller addr boundary 1 */
+	write_reg(info, WCRL, 0);	/* wait controller low range */
+	write_reg(info, WCRM, 0);	/* wait controller mid range */
+	write_reg(info, WCRH, 0);	/* wait controller high range */
+
+	/* DPCR, DMA Priority Control
+	 *
+	 * 07..05  Not used, must be 0
+	 * 04      BRC, bus release condition: 0=all transfers complete
+	 * 03      CCC, channel change condition: 0=every cycle
+	 * 02..00  PR<2..0>, priority 100=round robin
+	 *
+	 * 00000100 = 0x04
+	 */
+	write_reg(info, DPCR, dma_priority);
+
+	/* DMA Master Enable, BIT7: 1=enable all channels */
+	write_reg(info, DMER, 0x80);
+
+	/* enable all interrupt classes */
+	write_reg(info, IER0, 0xff);	/* TxRDY,RxRDY,TxINT,RxINT (ports 0-1) */
+	write_reg(info, IER1, 0xff);	/* DMIB,DMIA (channels 0-3) */
+	write_reg(info, IER2, 0xf0);	/* TIRQ (timers 0-3) */
+
+	/* ITCR, interrupt control register
+	 * 07      IPC, interrupt priority, 0=MSCI->DMA
+	 * 06..05  IAK<1..0>, Acknowledge cycle, 00=non-ack cycle
+	 * 04      VOS, Vector Output, 0=unmodified vector
+	 * 03..00  Reserved, must be 0
+	 */
+	write_reg(info, ITCR, 0);
+
+	return TRUE;
+}
+
+/* initialize adapter hardware
+ */
+int init_adapter(SLMP_INFO *info)
+{
+	int i;
+
+	/* Set BIT30 of Local Control Reg 0x50 to reset SCA */
+	volatile u32 *MiscCtrl = (u32 *)(info->lcr_base + 0x50);
+	u32 readval;
+
+	info->misc_ctrl_value |= BIT30;
+	*MiscCtrl = info->misc_ctrl_value;
+
+	/*
+	 * Force at least 170ns delay before clearing
+	 * reset bit. Each read from LCR takes at least
+	 * 30ns so 10 times for 300ns to be safe.
+	 */
+	for(i=0;i<10;i++)
+		readval = *MiscCtrl;
+
+	info->misc_ctrl_value &= ~BIT30;
+	*MiscCtrl = info->misc_ctrl_value;
+
+	/* init control reg (all DTRs off, all clksel=input) */
+	info->ctrlreg_value = 0xaa;
+	write_control_reg(info);
+
+	{
+		volatile u32 *LCR1BRDR = (u32 *)(info->lcr_base + 0x2c);
+		lcr1_brdr_value &= ~(BIT5 + BIT4 + BIT3);
+
+		switch(read_ahead_count)
+		{
+		case 16:
+			lcr1_brdr_value |= BIT5 + BIT4 + BIT3;
+			break;
+		case 8:
+			lcr1_brdr_value |= BIT5 + BIT4;
+			break;
+		case 4:
+			lcr1_brdr_value |= BIT5 + BIT3;
+			break;
+		case 0:
+			lcr1_brdr_value |= BIT5;
+			break;
+		}
+
+		*LCR1BRDR = lcr1_brdr_value;
+		*MiscCtrl = misc_ctrl_value;
+	}
+
+	sca_init(info->port_array[0]);
+	sca_init(info->port_array[2]);
+
+	return TRUE;
+}
+
+/* Loopback an HDLC frame to test the hardware
+ * interrupt and DMA functions.
+ */
+int loopback_test(SLMP_INFO *info)
+{
+#define TESTFRAMESIZE 20
+
+	unsigned long timeout;
+	u16 count = TESTFRAMESIZE;
+	unsigned char buf[TESTFRAMESIZE];
+	int rc = FALSE;
+	unsigned long flags;
+
+	struct tty_struct *oldtty = info->tty;
+	u32 speed = info->params.clock_speed;
+
+	info->params.clock_speed = 3686400;
+	info->tty = 0;
+
+	/* assume failure */
+	info->init_error = DiagStatus_DmaFailure;
+
+	/* build and send transmit frame */
+	for (count = 0; count < TESTFRAMESIZE;++count)
+		buf[count] = (unsigned char)count;
+
+	memset(info->tmp_rx_buf,0,TESTFRAMESIZE);
+
+	/* program hardware for HDLC and enabled receiver */
+	spin_lock_irqsave(&info->lock,flags);
+	hdlc_mode(info);
+	enable_loopback(info,1);
+       	rx_start(info);
+	info->tx_count = count;
+	tx_load_dma_buffer(info,buf,count);
+	tx_start(info);
+	spin_unlock_irqrestore(&info->lock,flags);
+
+	/* wait for receive complete */
+	/* Set a timeout for waiting for interrupt. */
+	for ( timeout = 100; timeout; --timeout ) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(jiffies_from_ms(10));
+
+		if (rx_get_frame(info)) {
+			rc = TRUE;
+			break;
+		}
+	}
+
+	/* verify received frame length and contents */
+	if (rc == TRUE &&
+		( info->tmp_rx_buf_count != count ||
+		  memcmp(buf, info->tmp_rx_buf,count))) {
+		rc = FALSE;
+	}
+
+	spin_lock_irqsave(&info->lock,flags);
+	reset_adapter(info);
+	spin_unlock_irqrestore(&info->lock,flags);
+
+	info->params.clock_speed = speed;
+	info->tty = oldtty;
+
+	return rc;
+}
+
+/* Perform diagnostics on hardware
+ */
+int adapter_test( SLMP_INFO *info )
+{
+	unsigned long flags;
+	if ( debug_level >= DEBUG_LEVEL_INFO )
+		printk( "%s(%d):Testing device %s\n",
+			__FILE__,__LINE__,info->device_name );
+
+	spin_lock_irqsave(&info->lock,flags);
+	init_adapter(info);
+	spin_unlock_irqrestore(&info->lock,flags);
+
+	info->port_array[0]->port_count = 0;
+
+	if ( register_test(info->port_array[0]) &&
+		register_test(info->port_array[1])) {
+
+		info->port_array[0]->port_count = 2;
+
+		if ( register_test(info->port_array[2]) &&
+			register_test(info->port_array[3]) )
+			info->port_array[0]->port_count += 2;
+	}
+	else {
+		printk( "%s(%d):Register test failure for device %s Addr=%08lX\n",
+			__FILE__,__LINE__,info->device_name, (unsigned long)(info->phys_sca_base));
+		return -ENODEV;
+	}
+
+	if ( !irq_test(info->port_array[0]) ||
+		!irq_test(info->port_array[1]) ||
+		 (info->port_count == 4 && !irq_test(info->port_array[2])) ||
+		 (info->port_count == 4 && !irq_test(info->port_array[3]))) {
+		printk( "%s(%d):Interrupt test failure for device %s IRQ=%d\n",
+			__FILE__,__LINE__,info->device_name, (unsigned short)(info->irq_level) );
+		return -ENODEV;
+	}
+
+	if (!loopback_test(info->port_array[0]) ||
+		!loopback_test(info->port_array[1]) ||
+		 (info->port_count == 4 && !loopback_test(info->port_array[2])) ||
+		 (info->port_count == 4 && !loopback_test(info->port_array[3]))) {
+		printk( "%s(%d):DMA test failure for device %s\n",
+			__FILE__,__LINE__,info->device_name);
+		return -ENODEV;
+	}
+
+	if ( debug_level >= DEBUG_LEVEL_INFO )
+		printk( "%s(%d):device %s passed diagnostics\n",
+			__FILE__,__LINE__,info->device_name );
+
+	info->port_array[0]->init_error = 0;
+	info->port_array[1]->init_error = 0;
+	if ( info->port_count > 2 ) {
+		info->port_array[2]->init_error = 0;
+		info->port_array[3]->init_error = 0;
+	}
+
+	return 0;
+}
+
+/* Test the shared memory on a PCI adapter.
+ */
+int memory_test(SLMP_INFO *info)
+{
+	static unsigned long testval[] = { 0x0, 0x55555555, 0xaaaaaaaa,
+		0x66666666, 0x99999999, 0xffffffff, 0x12345678 };
+	unsigned long count = sizeof(testval)/sizeof(unsigned long);
+	unsigned long i;
+	unsigned long limit = SCA_MEM_SIZE/sizeof(unsigned long);
+	unsigned long * addr = (unsigned long *)info->memory_base;
+
+	/* Test data lines with test pattern at one location. */
+
+	for ( i = 0 ; i < count ; i++ ) {
+		*addr = testval[i];
+		if ( *addr != testval[i] )
+			return FALSE;
+	}
+
+	/* Test address lines with incrementing pattern over */
+	/* entire address range. */
+
+	for ( i = 0 ; i < limit ; i++ ) {
+		*addr = i * 4;
+		addr++;
+	}
+
+	addr = (unsigned long *)info->memory_base;
+
+	for ( i = 0 ; i < limit ; i++ ) {
+		if ( *addr != i * 4 )
+			return FALSE;
+		addr++;
+	}
+
+	memset( info->memory_base, 0, SCA_MEM_SIZE );
+	return TRUE;
+}
+
+/* Load data into PCI adapter shared memory.
+ *
+ * The PCI9050 releases control of the local bus
+ * after completing the current read or write operation.
+ *
+ * While the PCI9050 write FIFO not empty, the
+ * PCI9050 treats all of the writes as a single transaction
+ * and does not release the bus. This causes DMA latency problems
+ * at high speeds when copying large data blocks to the shared memory.
+ *
+ * This function breaks a write into multiple transations by
+ * interleaving a read which flushes the write FIFO and 'completes'
+ * the write transation. This allows any pending DMA request to gain control
+ * of the local bus in a timely fasion.
+ */
+void load_pci_memory(SLMP_INFO *info, char* dest, const char* src, unsigned short count)
+{
+	/* A load interval of 16 allows for 4 32-bit writes at */
+	/* 136ns each for a maximum latency of 542ns on the local bus.*/
+
+	unsigned short interval = count / sca_pci_load_interval;
+	unsigned short i;
+
+	for ( i = 0 ; i < interval ; i++ )
+	{
+		memcpy(dest, src, sca_pci_load_interval);
+		read_status_reg(info);
+		dest += sca_pci_load_interval;
+		src += sca_pci_load_interval;
+	}
+
+	memcpy(dest, src, count % sca_pci_load_interval);
+}
+
+void trace_block(SLMP_INFO *info,const char* data, int count, int xmit)
+{
+	int i;
+	int linecount;
+	if (xmit)
+		printk("%s tx data:\n",info->device_name);
+	else
+		printk("%s rx data:\n",info->device_name);
+
+	while(count) {
+		if (count > 16)
+			linecount = 16;
+		else
+			linecount = count;
+
+		for(i=0;i<linecount;i++)
+			printk("%02X ",(unsigned char)data[i]);
+		for(;i<17;i++)
+			printk("   ");
+		for(i=0;i<linecount;i++) {
+			if (data[i]>=040 && data[i]<=0176)
+				printk("%c",data[i]);
+			else
+				printk(".");
+		}
+		printk("\n");
+
+		data  += linecount;
+		count -= linecount;
+	}
+}	/* end of trace_block() */
+
+/* called when HDLC frame times out
+ * update stats and do tx completion processing
+ */
+void tx_timeout(unsigned long context)
+{
+	SLMP_INFO *info = (SLMP_INFO*)context;
+	unsigned long flags;
+
+	if ( debug_level >= DEBUG_LEVEL_INFO )
+		printk( "%s(%d):%s tx_timeout()\n",
+			__FILE__,__LINE__,info->device_name);
+	if(info->tx_active && info->params.mode == MGSL_MODE_HDLC) {
+		info->icount.txtimeout++;
+	}
+	spin_lock_irqsave(&info->lock,flags);
+	info->tx_active = 0;
+	info->tx_count = info->tx_put = info->tx_get = 0;
+
+	spin_unlock_irqrestore(&info->lock,flags);
+
+#ifdef CONFIG_SYNCLINK_SYNCPPP
+	if (info->netcount)
+		sppp_tx_done(info);
+	else
+#endif
+		bh_transmit(info);
+}
+
+/* called to periodically check the DSR/RI modem signal input status
+ */
+void status_timeout(unsigned long context)
+{
+	u16 status = 0;
+	SLMP_INFO *info = (SLMP_INFO*)context;
+	unsigned long flags;
+	unsigned char delta;
+
+
+	spin_lock_irqsave(&info->lock,flags);
+	get_signals(info);
+	spin_unlock_irqrestore(&info->lock,flags);
+
+	/* check for DSR/RI state change */
+
+	delta = info->old_signals ^ info->serial_signals;
+	info->old_signals = info->serial_signals;
+
+	if (delta & SerialSignal_DSR)
+		status |= MISCSTATUS_DSR_LATCHED|(info->serial_signals&SerialSignal_DSR);
+
+	if (delta & SerialSignal_RI)
+		status |= MISCSTATUS_RI_LATCHED|(info->serial_signals&SerialSignal_RI);
+
+	if (delta & SerialSignal_DCD)
+		status |= MISCSTATUS_DCD_LATCHED|(info->serial_signals&SerialSignal_DCD);
+
+	if (delta & SerialSignal_CTS)
+		status |= MISCSTATUS_CTS_LATCHED|(info->serial_signals&SerialSignal_CTS);
+
+	if (status)
+		isr_io_pin(info,status);
+
+	info->status_timer.data = (unsigned long)info;
+	info->status_timer.function = status_timeout;
+	info->status_timer.expires = jiffies + jiffies_from_ms(10);
+	add_timer(&info->status_timer);
+}
+
+
+/* Register Access Routines -
+ * All registers are memory mapped
+ */
+#define CALC_REGADDR() \
+	unsigned char * RegAddr = (unsigned char*)(info->sca_base + Addr); \
+	if (info->port_num > 1) \
+		RegAddr += 256;	    		/* port 0-1 SCA0, 2-3 SCA1 */ \
+	if ( info->port_num & 1) { \
+		if (Addr > 0x7f) \
+			RegAddr += 0x40;	/* DMA access */ \
+		else if (Addr > 0x1f && Addr < 0x60) \
+			RegAddr += 0x20;	/* MSCI access */ \
+	}
+
+
+unsigned char read_reg(SLMP_INFO * info, unsigned char Addr)
+{
+	CALC_REGADDR();
+	return *RegAddr;
+}
+void write_reg(SLMP_INFO * info, unsigned char Addr, unsigned char Value)
+{
+	CALC_REGADDR();
+	*RegAddr = Value;
+}
+
+u16 read_reg16(SLMP_INFO * info, unsigned char Addr)
+{
+	CALC_REGADDR();
+	return *((u16 *)RegAddr);
+}
+
+void write_reg16(SLMP_INFO * info, unsigned char Addr, u16 Value)
+{
+	CALC_REGADDR();
+	*((u16 *)RegAddr) = Value;
+}
+
+unsigned char read_status_reg(SLMP_INFO * info)
+{
+	unsigned char *RegAddr = (unsigned char *)info->statctrl_base;
+	return *RegAddr;
+}
+
+void write_control_reg(SLMP_INFO * info)
+{
+	unsigned char *RegAddr = (unsigned char *)info->statctrl_base;
+	*RegAddr = info->port_array[0]->ctrlreg_value;
+}
+
+
+static int __init synclinkmp_init_one (struct pci_dev *dev,
+				       const struct pci_device_id *ent)
+{
+	if (pci_enable_device(dev)) {
+		printk("error enabling pci device %p\n", dev);
+		return -EIO;
+	}
+	device_init( ++synclinkmp_adapter_count, dev );
+	return 0;
+}
+
+static void __devexit synclinkmp_remove_one (struct pci_dev *dev)
+{
+}
diff -Nru a/drivers/ide/Config.help b/drivers/ide/Config.help
--- a/drivers/ide/Config.help	Sun Apr 28 20:10:37 2002
+++ b/drivers/ide/Config.help	Sun Apr 28 20:10:37 2002
@@ -749,35 +749,6 @@
 
   Generally say N here.
 
-CONFIG_BLK_DEV_IDE_TCQ
-  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 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_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
   Technology Express, Inc. ITE8172 SBC.  Vendor page at
diff -Nru a/drivers/ide/Config.in b/drivers/ide/Config.in
--- a/drivers/ide/Config.in	Sun Apr 28 20:10:38 2002
+++ b/drivers/ide/Config.in	Sun Apr 28 20:10:38 2002
@@ -47,11 +47,6 @@
 	 dep_bool '      Use PCI DMA by default when available' CONFIG_IDEDMA_PCI_AUTO $CONFIG_BLK_DEV_IDEDMA_PCI
          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" != "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	Sun Apr 28 20:10:37 2002
+++ b/drivers/ide/Makefile	Sun Apr 28 20:10:37 2002
@@ -44,7 +44,6 @@
 ide-obj-$(CONFIG_BLK_DEV_HT6560B)	+= ht6560b.o
 ide-obj-$(CONFIG_BLK_DEV_IDE_ICSIDE)	+= icside.o
 ide-obj-$(CONFIG_BLK_DEV_IDEDMA_PCI)	+= ide-dma.o
-ide-obj-$(CONFIG_BLK_DEV_IDE_TCQ)	+= ide-tcq.o
 ide-obj-$(CONFIG_BLK_DEV_IDEPCI)	+= ide-pci.o
 ide-obj-$(CONFIG_BLK_DEV_ISAPNP)	+= ide-pnp.o
 ide-obj-$(CONFIG_BLK_DEV_IDE_PMAC)	+= ide-pmac.o
diff -Nru a/drivers/ide/ataraid.c b/drivers/ide/ataraid.c
--- a/drivers/ide/ataraid.c	Sun Apr 28 20:10:36 2002
+++ b/drivers/ide/ataraid.c	Sun Apr 28 20:10:36 2002
@@ -34,8 +34,6 @@
 
 #include "ataraid.h"
 
-                                        
-static int ataraid_hardsect_size[256];
 static int ataraid_blksize_size[256];
 
 static struct raid_device_operations* ataraid_ops[16];
@@ -239,17 +237,13 @@
 	int i;
         for(i=0;i<256;i++)
 	{
-        	ataraid_hardsect_size[i] = 512;
 		ataraid_blksize_size[i] = 1024;  
 		ataraid_readahead[i] = 1023;
 	}
 	
 	if (blksize_size[ATAMAJOR]==NULL)
 		blksize_size[ATAMAJOR] = ataraid_blksize_size;
-	if (hardsect_size[ATAMAJOR]==NULL)
-		hardsect_size[ATAMAJOR] = ataraid_hardsect_size;
-	
-	
+
 	/* setup the gendisk structure */	
 	ataraid_gendisk.part = kmalloc(256 * sizeof(struct hd_struct),GFP_KERNEL);
 	if (ataraid_gendisk.part==NULL) {
@@ -286,7 +280,6 @@
 static void __exit ataraid_exit(void)
 {
 	unregister_blkdev(ATAMAJOR, "ataraid");
-	hardsect_size[ATAMAJOR] = NULL;
 	blk_size[ATAMAJOR] = NULL;
 	blksize_size[ATAMAJOR] = NULL;                       
 
diff -Nru a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c
--- a/drivers/ide/ide-cd.c	Sun Apr 28 20:10:36 2002
+++ b/drivers/ide/ide-cd.c	Sun Apr 28 20:10:36 2002
@@ -558,10 +558,6 @@
 	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);
 }
 
@@ -1166,11 +1162,12 @@
 	return ide_stopped;
 }
 
-static ide_startstop_t cdrom_start_seek_continuation (ide_drive_t *drive)
+static ide_startstop_t cdrom_start_seek_continuation(struct ata_device *drive)
 {
 	unsigned char cmd[CDROM_PACKET_SIZE];
 	struct request *rq = HWGROUP(drive)->rq;
-	int sector, frame, nskip;
+	sector_t sector;
+	int frame, nskip;
 
 	sector = rq->sector;
 	nskip = (sector % SECTORS_PER_FRAME);
@@ -1185,14 +1182,14 @@
 	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)
+static ide_startstop_t cdrom_start_seek(struct ata_device *drive, sector_t block)
 {
 	struct cdrom_info *info = drive->driver_data;
 
 	info->dma = 0;
 	info->cmd = 0;
 	info->start_seek = jiffies;
-	return cdrom_start_packet_command (drive, 0, cdrom_start_seek_continuation);
+	return cdrom_start_packet_command(drive, 0, cdrom_start_seek_continuation);
 }
 
 /*
@@ -1203,7 +1200,7 @@
 static void restore_request (struct request *rq)
 {
 	if (rq->buffer != bio_data(rq->bio)) {
-		int n = (rq->buffer - (char *) bio_data(rq->bio)) / SECTOR_SIZE;
+		sector_t n = (rq->buffer - (char *) bio_data(rq->bio)) / SECTOR_SIZE;
 		rq->buffer = bio_data(rq->bio);
 		rq->nr_sectors += n;
 		rq->sector -= n;
@@ -1217,22 +1214,13 @@
 /*
  * Start a read request from the CD-ROM.
  */
-static ide_startstop_t cdrom_start_read(struct ata_device *drive, struct ata_request *ar, unsigned int block)
+static ide_startstop_t cdrom_start_read(struct ata_device *drive, sector_t block)
 {
 	struct cdrom_info *info = drive->driver_data;
-	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);
-	}
-
+	struct request *rq = HWGROUP(drive)->rq;
 
 	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;
@@ -1647,8 +1635,6 @@
 	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();
@@ -1663,32 +1649,10 @@
 			CDROM_CONFIG_FLAGS(drive)->seeking = 0;
 		}
 		if (IDE_LARGE_SEEK(info->last_block, block, IDECD_SEEK_THRESHOLD) && drive->dsc_overlap)
-			action = cdrom_start_seek (drive, block);
+			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, ar, block);
+				action = cdrom_start_read(drive, block);
 			else
 				action = cdrom_start_write(drive, rq);
 		}
@@ -1872,7 +1836,7 @@
 	return cdrom_queue_packet_command(drive, cmd, sense, &pc);
 }
 
-static int cdrom_read_capacity(ide_drive_t *drive, unsigned long *capacity,
+static int cdrom_read_capacity(ide_drive_t *drive, u32 *capacity,
 			       struct request_sense *sense)
 {
 	struct {
@@ -2062,7 +2026,8 @@
 	/* Now try to get the total cdrom capacity. */
 	minor = (drive->select.b.unit) << PARTN_BITS;
 	dev = mk_kdev(drive->channel->major, minor);
-	stat = cdrom_get_last_written(dev, &toc->capacity);
+	/* FIXME: This is making worng assumptions about register layout. */
+	stat = cdrom_get_last_written(dev, (unsigned long *) &toc->capacity);
 	if (stat)
 		stat = cdrom_read_capacity(drive, &toc->capacity, sense);
 	if (stat)
@@ -2721,7 +2686,7 @@
 }
 
 static
-int ide_cdrom_setup (ide_drive_t *drive)
+int ide_cdrom_setup(ide_drive_t *drive)
 {
 	struct cdrom_info *info = drive->driver_data;
 	struct cdrom_device_info *cdi = &info->devinfo;
@@ -2737,7 +2702,6 @@
 
 	blk_queue_prep_rq(&drive->queue, ll_10byte_cmd_build);
 
-	drive->special.all	= 0;
 	drive->ready_stat	= 0;
 
 	CDROM_STATE_FLAGS (drive)->media_changed = 1;
@@ -2926,10 +2890,9 @@
 	set_blocksize(mk_kdev(drive->channel->major, minor), CD_FRAMESIZE);
 }
 
-static
-unsigned long ide_cdrom_capacity (ide_drive_t *drive)
+static sector_t ide_cdrom_capacity(struct ata_device *drive)
 {
-	unsigned long capacity;
+	u32 capacity;
 
 	if (cdrom_read_capacity(drive, &capacity, NULL))
 		return 0;
@@ -2969,9 +2932,7 @@
 	release:		ide_cdrom_release,
 	check_media_change:	ide_cdrom_check_media_change,
 	revalidate:		ide_cdrom_revalidate,
-	pre_reset:		NULL,
 	capacity:		ide_cdrom_capacity,
-	special:		NULL,
 	proc:			NULL
 };
 
diff -Nru a/drivers/ide/ide-cd.h b/drivers/ide/ide-cd.h
--- a/drivers/ide/ide-cd.h	Sun Apr 28 20:10:38 2002
+++ b/drivers/ide/ide-cd.h	Sun Apr 28 20:10:38 2002
@@ -151,12 +151,11 @@
 };
 
 struct atapi_toc {
-	int    last_session_lba;
-	int    xa_flag;
-	unsigned long capacity;
+	int last_session_lba;
+	int xa_flag;
+	u32 capacity;
 	struct atapi_toc_header hdr;
-	struct atapi_toc_entry  ent[MAX_TRACKS+1];
-	  /* One extra for the leadout. */
+	struct atapi_toc_entry ent[MAX_TRACKS+1];  /* one extra for the leadout. */
 };
 
 
diff -Nru a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c
--- a/drivers/ide/ide-disk.c	Sun Apr 28 20:10:30 2002
+++ b/drivers/ide/ide-disk.c	Sun Apr 28 20:10:30 2002
@@ -88,162 +88,125 @@
 	return 0;	/* lba_capacity value may be bad */
 }
 
-/*
- * Determine the apriopriate hardware command correspnding to the action in
- * question, depending upon the device capabilities and setup.
- */
 static u8 get_command(ide_drive_t *drive, int cmd)
 {
 	int lba48bit = (drive->id->cfs_enable_2 & 0x0400) ? 1 : 0;
-	/* Well, calculating the command in this variable may be an
-	 * overoptimization. */
-	u8 command = WIN_NOP;
 
 #if 1
 	lba48bit = drive->addressing;
 #endif
 
-	/*
-	 * 48-bit commands are pretty sanely laid out
-	 */
 	if (lba48bit) {
-		command = cmd == READ ? WIN_READ_EXT : WIN_WRITE_EXT;
-
-		if (drive->using_dma) {
-			command++;		/* WIN_*DMA_EXT */
-			if (drive->using_tcq)
-				command++;	/* WIN_*DMA_QUEUED_EXT */
-		} else if (drive->mult_count)
-			command += 5;		/* WIN_MULT*_EXT */
-
-		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;
+		if (cmd == READ) {
+			if (drive->using_dma)
+				return WIN_READDMA_EXT;
+			else if (drive->mult_count)
+				return WIN_MULTREAD_EXT;
+			else
+				return WIN_READ_EXT;
+		} else if (cmd == WRITE) {
+			if (drive->using_dma)
+				return WIN_WRITEDMA_EXT;
+			else if (drive->mult_count)
+				return WIN_MULTWRITE_EXT;
 			else
-				command = WIN_READDMA;
-		} else if (drive->mult_count)
-			command = WIN_MULTREAD;
-		else
-			command = WIN_READ;
+				return WIN_WRITE_EXT;
+		}
 	} else {
-		if (drive->using_dma) {
-			if (drive->using_tcq)
-				command = WIN_WRITEDMA_QUEUED;
+		if (cmd == READ) {
+			if (drive->using_dma)
+				return WIN_READDMA;
+			else if (drive->mult_count)
+				return WIN_MULTREAD;
+			else
+				return WIN_READ;
+		} else if (cmd == WRITE) {
+			if (drive->using_dma)
+				return WIN_WRITEDMA;
+			else if (drive->mult_count)
+				return WIN_MULTWRITE;
 			else
-				command = WIN_WRITEDMA;
-		} else if (drive->mult_count)
-			command = WIN_MULTWRITE;
-		else
-			command = WIN_WRITE;
+				return WIN_WRITE;
+		}
 	}
-
-	return command;
+	return WIN_NOP;
 }
 
-static ide_startstop_t chs_do_request(ide_drive_t *drive, struct ata_request *ar, sector_t block)
+static ide_startstop_t chs_do_request(struct ata_device *drive, struct request *rq, sector_t block)
 {
-	struct ata_taskfile *args = &ar->ar_task;
-	struct request *rq = ar->ar_rq;
-	int sectors = rq->nr_sectors;
-
-	unsigned int track = (block / drive->sect);
-	unsigned int sect = (block % drive->sect) + 1;
-	unsigned int head = (track % drive->head);
-	unsigned int cyl = (track / drive->head);
+	struct ata_taskfile args;
+	int sectors;
 
-	memset(&args->taskfile, 0, sizeof(struct hd_drive_task_hdr));
-	memset(&args->hobfile, 0, sizeof(struct hd_drive_hob_hdr));
+	unsigned int track	= (block / drive->sect);
+	unsigned int sect	= (block % drive->sect) + 1;
+	unsigned int head	= (track % drive->head);
+	unsigned int cyl	= (track / drive->head);
 
+	sectors = rq->nr_sectors;
 	if (sectors == 256)
 		sectors = 0;
 
-	if (ar->ar_flags & ATA_AR_QUEUED) {
-		unsigned long flags;
+	memset(&args, 0, sizeof(args));
 
-		args->taskfile.feature = sectors;
-		args->taskfile.sector_count = ar->ar_tag << 3;
+	args.taskfile.sector_count = sectors;
 
-		spin_lock_irqsave(DRIVE_LOCK(drive), flags);
-		blkdev_dequeue_request(rq);
-		spin_unlock_irqrestore(DRIVE_LOCK(drive), flags);
-	} else
-		args->taskfile.sector_count   = sectors;
-
-	args->taskfile.sector_number = sect;
-	args->taskfile.low_cylinder = cyl;
-	args->taskfile.high_cylinder = (cyl>>8);
-
-	args->taskfile.device_head = head;
-	args->taskfile.device_head |= drive->select.all;
-	args->taskfile.command = get_command(drive, rq_data_dir(rq));
+	args.taskfile.sector_number = sect;
+	args.taskfile.low_cylinder = cyl;
+	args.taskfile.high_cylinder = (cyl>>8);
+
+	args.taskfile.device_head = head;
+	args.taskfile.device_head |= drive->select.all;
+	args.taskfile.command =  get_command(drive, rq_data_dir(rq));
 
 #ifdef DEBUG
 	printk("%s: %sing: ", drive->name,
 		(rq_data_dir(rq)==READ) ? "read" : "writ");
+	if (lba)	printk("LBAsect=%lld, ", block);
+	else		printk("CHS=%d/%d/%d, ", cyl, head, sect);
 	printk("sectors=%ld, ", rq->nr_sectors);
-	printk("CHS=%d/%d/%d, ", cyl, head, sect);
-	printk("buffer=0x%08lx\n", (unsigned long) rq->buffer);
+	printk("buffer=%p\n", rq->buffer);
 #endif
 
-	ide_cmd_type_parser(args);
-	args->ar = ar;
-	rq->special = ar;
+	ide_cmd_type_parser(&args);
+	rq->special = &args;
 
-	return ata_taskfile(drive, args, rq);
+	return ata_taskfile(drive, &args, rq);
 }
 
-static ide_startstop_t lba28_do_request(ide_drive_t *drive, struct ata_request *ar, sector_t block)
+static ide_startstop_t lba28_do_request(struct ata_device *drive, struct request *rq, sector_t block)
 {
-	struct ata_taskfile *args = &ar->ar_task;
-	struct request *rq = ar->ar_rq;
-	int sectors = rq->nr_sectors;
+	struct ata_taskfile args;
+	int sectors;
 
+	sectors = rq->nr_sectors;
 	if (sectors == 256)
 		sectors = 0;
 
-	memset(&args->taskfile, 0, sizeof(struct hd_drive_task_hdr));
-	memset(&args->hobfile, 0, sizeof(struct hd_drive_hob_hdr));
-
-	if (ar->ar_flags & ATA_AR_QUEUED) {
-		unsigned long flags;
-
-		args->taskfile.feature = sectors;
-		args->taskfile.sector_count = ar->ar_tag << 3;
-
-		spin_lock_irqsave(DRIVE_LOCK(drive), flags);
-		blkdev_dequeue_request(rq);
-		spin_unlock_irqrestore(DRIVE_LOCK(drive), flags);
-	} else
-		args->taskfile.sector_count = sectors;
-
-	args->taskfile.sector_number = block;
-	args->taskfile.low_cylinder = (block >>= 8);
-
-	args->taskfile.high_cylinder = (block >>= 8);
+	memset(&args, 0, sizeof(args));
 
-	args->taskfile.device_head = ((block >> 8) & 0x0f);
-	args->taskfile.device_head |= drive->select.all;
-	args->taskfile.command = get_command(drive, rq_data_dir(rq));
+	args.taskfile.sector_count = sectors;
+	args.taskfile.sector_number = block;
+	args.taskfile.low_cylinder = (block >>= 8);
+
+	args.taskfile.high_cylinder = (block >>= 8);
+
+	args.taskfile.device_head = ((block >> 8) & 0x0f);
+	args.taskfile.device_head |= drive->select.all;
+	args.taskfile.command = get_command(drive, rq_data_dir(rq));
 
 #ifdef DEBUG
 	printk("%s: %sing: ", drive->name,
 		(rq_data_dir(rq)==READ) ? "read" : "writ");
-	printk("sector=%lx, sectors=%ld, ", block, rq->nr_sectors);
-	printk("buffer=0x%08lx\n", (unsigned long) rq->buffer);
+	if (lba)	printk("LBAsect=%lld, ", block);
+	else		printk("CHS=%d/%d/%d, ", cyl, head, sect);
+	printk("sectors=%ld, ", rq->nr_sectors);
+	printk("buffer=%p\n", rq->buffer);
 #endif
 
-	ide_cmd_type_parser(args);
-	args->ar = ar;
-	rq->special = ar;
+	ide_cmd_type_parser(&args);
+	rq->special = &args;
 
-	return ata_taskfile(drive, args, rq);
+	return ata_taskfile(drive, &args, rq);
 }
 
 /*
@@ -251,58 +214,47 @@
  * 320173056  == 163929 MB or 48bit addressing
  * 1073741822 == 549756 MB or 48bit addressing fake drive
  */
-static ide_startstop_t lba48_do_request(ide_drive_t *drive, struct ata_request *ar, sector_t block)
-{
-	struct ata_taskfile *args = &ar->ar_task;
-	struct request *rq = ar->ar_rq;
-	int sectors = rq->nr_sectors;
 
-	memset(&args->taskfile, 0, sizeof(struct hd_drive_task_hdr));
-	memset(&args->hobfile, 0, sizeof(struct hd_drive_hob_hdr));
+static ide_startstop_t lba48_do_request(struct ata_device *drive, struct request *rq, sector_t block)
+{
+	struct ata_taskfile args;
+	int sectors;
 
+	sectors = rq->nr_sectors;
 	if (sectors == 65536)
 		sectors = 0;
 
-	if (ar->ar_flags & ATA_AR_QUEUED) {
-		unsigned long flags;
+	memset(&args, 0, sizeof(args));
 
-		args->taskfile.feature = sectors;
-		args->hobfile.feature = sectors >> 8;
-		args->taskfile.sector_count = ar->ar_tag << 3;
-
-		spin_lock_irqsave(DRIVE_LOCK(drive), flags);
-		blkdev_dequeue_request(rq);
-		spin_unlock_irqrestore(DRIVE_LOCK(drive), flags);
-	} else {
-		args->taskfile.sector_count = sectors;
-		args->hobfile.sector_count = sectors >> 8;
-	}
+	args.taskfile.sector_count = sectors;
+	args.hobfile.sector_count = sectors >> 8;
 
-	args->taskfile.sector_number = block;
-	args->taskfile.low_cylinder = (block >>= 8);
-	args->taskfile.high_cylinder = (block >>= 8);
-
-	args->hobfile.sector_number = (block >>= 8);
-	args->hobfile.low_cylinder = (block >>= 8);
-	args->hobfile.high_cylinder = (block >>= 8);
-
-	args->taskfile.device_head = drive->select.all;
-	args->hobfile.device_head = args->taskfile.device_head;
-	args->hobfile.control = (drive->ctl|0x80);
-	args->taskfile.command = get_command(drive, rq_data_dir(rq));
+	args.taskfile.sector_number = block;		/* low lba */
+	args.taskfile.low_cylinder = (block >>= 8);	/* mid lba */
+	args.taskfile.high_cylinder = (block >>= 8);	/* hi  lba */
+
+	args.hobfile.sector_number = (block >>= 8);	/* low lba */
+	args.hobfile.low_cylinder = (block >>= 8);	/* mid lba */
+	args.hobfile.high_cylinder = (block >>= 8);	/* hi  lba */
+
+	args.taskfile.device_head = drive->select.all;
+	args.hobfile.device_head = args.taskfile.device_head;
+	args.hobfile.control = (drive->ctl|0x80);
+	args.taskfile.command = get_command(drive, rq_data_dir(rq));
 
 #ifdef DEBUG
 	printk("%s: %sing: ", drive->name,
 		(rq_data_dir(rq)==READ) ? "read" : "writ");
-	printk("sector=%lx, sectors=%ld, ", block, rq->nr_sectors);
-	printk("buffer=0x%08lx\n", (unsigned long) rq->buffer);
+	if (lba)	printk("LBAsect=%lld, ", block);
+	else		printk("CHS=%d/%d/%d, ", cyl, head, sect);
+	printk("sectors=%ld, ", rq->nr_sectors);
+	printk("buffer=%p\n",rq->buffer);
 #endif
 
-	ide_cmd_type_parser(args);
-	args->ar = ar;
-	rq->special = ar;
+	ide_cmd_type_parser(&args);
+	rq->special = &args;
 
-	return ata_taskfile(drive, args, rq);
+	return ata_taskfile(drive, &args, rq);
 }
 
 /*
@@ -310,18 +262,15 @@
  * otherwise, to address sectors.  It also takes care of issuing special
  * DRIVE_CMDs.
  */
-static ide_startstop_t idedisk_do_request(ide_drive_t *drive, struct request *rq, sector_t block)
+static ide_startstop_t idedisk_do_request(struct ata_device *drive, struct request *rq, sector_t block)
 {
-	unsigned long flags;
-	struct ata_request *ar;
-
 	/*
 	 * Wait until all request have bin finished.
 	 */
 
 	while (drive->blocked) {
 		yield();
-		printk("ide: Request while drive blocked?");
+		printk(KERN_ERR "ide: Request while drive blocked?");
 	}
 
 	if (!(rq->flags & REQ_CMD)) {
@@ -331,54 +280,21 @@
 	}
 
 	if (IS_PDC4030_DRIVE) {
-		extern ide_startstop_t promise_rw_disk(ide_drive_t *, struct request *, unsigned long);
+		extern ide_startstop_t promise_rw_disk(struct ata_device *, struct request *, unsigned long);
 
 		return promise_rw_disk(drive, rq, block);
 	}
 
-	/*
-	 * 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;
-
-	if (drive->using_tcq) {
-		int tag = ide_get_tag(drive);
-
-		BUG_ON(drive->tcq->active_tag != -1);
-
-		/* Set the tag: */
-		ar->ar_flags |= ATA_AR_QUEUED;
-		ar->ar_tag = tag;
-		drive->tcq->ar[tag] = ar;
-		drive->tcq->active_tag = tag;
-		ar->ar_time = jiffies;
-		drive->tcq->queued++;
-	}
-
-	spin_unlock_irqrestore(DRIVE_LOCK(drive), flags);
-
 	/* 48-bit LBA */
 	if ((drive->id->cfs_enable_2 & 0x0400) && (drive->addressing))
-		return lba48_do_request(drive, ar, block);
+		return lba48_do_request(drive, rq, block);
 
 	/* 28-bit LBA */
 	if (drive->select.b.lba)
-		return lba28_do_request(drive, ar, block);
+		return lba28_do_request(drive, rq, block);
 
 	/* 28-bit CHS */
-	return chs_do_request(drive, ar, block);
+	return chs_do_request(drive, rq, block);
 }
 
 static int idedisk_open (struct inode *inode, struct file *filp, ide_drive_t *drive)
@@ -450,256 +366,19 @@
 	return drive->removable;
 }
 
-/*
- * Queries for true maximum capacity of the drive.
- * Returns maximum LBA address (> 0) of the drive, 0 if failed.
- */
-static unsigned long idedisk_read_native_max_address(ide_drive_t *drive)
-{
-	struct ata_taskfile args;
-	unsigned long addr = 0;
-
-	if (!(drive->id->command_set_1 & 0x0400) &&
-	    !(drive->id->cfs_enable_2 & 0x0100))
-		return addr;
-
-	/* Create IDE/ATA command request structure */
-	memset(&args, 0, sizeof(args));
-	args.taskfile.device_head = 0x40;
-	args.taskfile.command = WIN_READ_NATIVE_MAX;
-	args.handler = task_no_data_intr;
-
-	/* submit command request */
-	ide_raw_taskfile(drive, &args, NULL);
-
-	/* if OK, compute maximum address value */
-	if ((args.taskfile.command & 0x01) == 0) {
-		addr = ((args.taskfile.device_head & 0x0f) << 24)
-		     | (args.taskfile.high_cylinder << 16)
-		     | (args.taskfile.low_cylinder <<  8)
-		     | args.taskfile.sector_number;
-	}
-
-	addr++;	/* since the return value is (maxlba - 1), we add 1 */
-
-	return addr;
-}
-
-static unsigned long long idedisk_read_native_max_address_ext(ide_drive_t *drive)
-{
-	struct ata_taskfile args;
-	unsigned long long addr = 0;
-
-	/* Create IDE/ATA command request structure */
-	memset(&args, 0, sizeof(args));
-
-	args.taskfile.device_head = 0x40;
-	args.taskfile.command = WIN_READ_NATIVE_MAX_EXT;
-	args.handler = task_no_data_intr;
-
-        /* submit command request */
-        ide_raw_taskfile(drive, &args, NULL);
-
-	/* if OK, compute maximum address value */
-	if ((args.taskfile.command & 0x01) == 0) {
-		u32 high = (args.hobfile.high_cylinder << 16) |
-			   (args.hobfile.low_cylinder << 8) |
-			    args.hobfile.sector_number;
-		u32 low  = (args.taskfile.high_cylinder << 16) |
-			   (args.taskfile.low_cylinder << 8) |
-			    args.taskfile.sector_number;
-		addr = ((__u64)high << 24) | low;
-	}
-
-	addr++;	/* since the return value is (maxlba - 1), we add 1 */
-
-	return addr;
-}
-
-#ifdef CONFIG_IDEDISK_STROKE
-/*
- * Sets maximum virtual LBA address of the drive.
- * Returns new maximum virtual LBA address (> 0) or 0 on failure.
- */
-static unsigned long idedisk_set_max_address(ide_drive_t *drive, unsigned long addr_req)
-{
-	struct ata_taskfile args;
-	unsigned long addr_set = 0;
-
-	addr_req--;
-	/* Create IDE/ATA command request structure */
-	memset(&args, 0, sizeof(args));
-
-	args.taskfile.sector_number = (addr_req >> 0);
-	args.taskfile.low_cylinder = (addr_req >> 8);
-	args.taskfile.high_cylinder = (addr_req >> 16);
-
-	args.taskfile.device_head = ((addr_req >> 24) & 0x0f) | 0x40;
-	args.taskfile.command = WIN_SET_MAX;
-	args.handler = task_no_data_intr;
-	/* submit command request */
-	ide_raw_taskfile(drive, &args, NULL);
-	/* if OK, read new maximum address value */
-	if ((args.taskfile.command & 0x01) == 0) {
-		addr_set = ((args.taskfile.device_head & 0x0f) << 24)
-			 | (args.taskfile.high_cylinder << 16)
-			 | (args.taskfile.low_cylinder <<  8)
-			 | args.taskfile.sector_number;
-	}
-	addr_set++;
-	return addr_set;
-}
-
-static unsigned long long idedisk_set_max_address_ext(ide_drive_t *drive, unsigned long long addr_req)
-{
-	struct ata_taskfile args;
-	unsigned long long addr_set = 0;
-
-	addr_req--;
-	/* Create IDE/ATA command request structure */
-	memset(&args, 0, sizeof(args));
-
-	args.taskfile.sector_number = (addr_req >>  0);
-	args.taskfile.low_cylinder = (addr_req >>= 8);
-	args.taskfile.high_cylinder = (addr_req >>= 8);
-	args.taskfile.device_head = 0x40;
-	args.taskfile.command = WIN_SET_MAX_EXT;
-
-	args.hobfile.sector_number = (addr_req >>= 8);
-	args.hobfile.low_cylinder = (addr_req >>= 8);
-	args.hobfile.high_cylinder = (addr_req >>= 8);
-
-	args.hobfile.device_head = 0x40;
-	args.hobfile.control = (drive->ctl | 0x80);
-
-        args.handler = task_no_data_intr;
-	/* submit command request */
-	ide_raw_taskfile(drive, &args, NULL);
-	/* if OK, compute maximum address value */
-	if ((args.taskfile.command & 0x01) == 0) {
-		u32 high = (args.hobfile.high_cylinder << 16) |
-			   (args.hobfile.low_cylinder << 8) |
-			    args.hobfile.sector_number;
-		u32 low  = (args.taskfile.high_cylinder << 16) |
-			   (args.taskfile.low_cylinder << 8) |
-			    args.taskfile.sector_number;
-		addr_set = ((__u64)high << 24) | low;
-	}
-	return addr_set;
-}
-
-/*
- * Tests if the drive supports Host Protected Area feature.
- * Returns true if supported, false otherwise.
- */
-static inline int idedisk_supports_host_protected_area(ide_drive_t *drive)
-{
-	int flag = (drive->id->cfs_enable_1 & 0x0400) ? 1 : 0;
-	printk("%s: host protected area => %d\n", drive->name, flag);
-	return flag;
-}
-
-#endif
-
-/*
- * Compute drive->capacity, the full capacity of the drive
- * Called with drive->id != NULL.
- *
- * To compute capacity, this uses either of
- *
- *    1. CHS value set by user       (whatever user sets will be trusted)
- *    2. LBA value from target drive (require new ATA feature)
- *    3. LBA value from system BIOS  (new one is OK, old one may break)
- *    4. CHS value from system BIOS  (traditional style)
- *
- * in above order (i.e., if value of higher priority is available,
- * reset will be ignored).
- */
-static void init_idedisk_capacity (ide_drive_t  *drive)
-{
-	struct hd_driveid *id = drive->id;
-	unsigned long capacity = drive->cyl * drive->head * drive->sect;
-	unsigned long set_max = idedisk_read_native_max_address(drive);
-	unsigned long long capacity_2 = capacity;
-	unsigned long long set_max_ext;
-
-	drive->capacity48 = 0;
-	drive->select.b.lba = 0;
-
-	if (id->cfs_enable_2 & 0x0400) {
-		capacity_2 = id->lba_capacity_2;
-		drive->cyl = (unsigned int) capacity_2 / (drive->head * drive->sect);
-		drive->head		= drive->bios_head = 255;
-		drive->sect		= drive->bios_sect = 63;
-		drive->select.b.lba	= 1;
-		set_max_ext = idedisk_read_native_max_address_ext(drive);
-		if (set_max_ext > capacity_2) {
-#ifdef CONFIG_IDEDISK_STROKE
-			set_max_ext = idedisk_read_native_max_address_ext(drive);
-			set_max_ext = idedisk_set_max_address_ext(drive, set_max_ext);
-			if (set_max_ext) {
-				drive->capacity48 = capacity_2 = set_max_ext;
-				drive->cyl = (unsigned int) set_max_ext / (drive->head * drive->sect);
-				drive->select.b.lba = 1;
-				drive->id->lba_capacity_2 = capacity_2;
-                        }
-#else
-			printk("%s: setmax_ext LBA %llu, native  %llu\n",
-				drive->name, set_max_ext, capacity_2);
-#endif
-		}
-		drive->bios_cyl		= drive->cyl;
-		drive->capacity48	= capacity_2;
-		drive->capacity		= (unsigned long) capacity_2;
-		return;
-	/* Determine capacity, and use LBA if the drive properly supports it */
-	} else if ((id->capability & 2) && lba_capacity_is_ok(id)) {
-		capacity = id->lba_capacity;
-		drive->cyl = capacity / (drive->head * drive->sect);
-		drive->select.b.lba = 1;
-	}
-
-	if (set_max > capacity) {
-#ifdef CONFIG_IDEDISK_STROKE
-		set_max = idedisk_read_native_max_address(drive);
-		set_max = idedisk_set_max_address(drive, set_max);
-		if (set_max) {
-			drive->capacity = capacity = set_max;
-			drive->cyl = set_max / (drive->head * drive->sect);
-			drive->select.b.lba = 1;
-			drive->id->lba_capacity = capacity;
-		}
-#else
-		printk("%s: setmax LBA %lu, native  %lu\n",
-			drive->name, set_max, capacity);
-#endif
-	}
-
-	drive->capacity = capacity;
-
-	if ((id->command_set_2 & 0x0400) && (id->cfs_enable_2 & 0x0400)) {
-                drive->capacity48 = id->lba_capacity_2;
-		drive->head = 255;
-		drive->sect = 63;
-		drive->cyl = (unsigned long)(drive->capacity48) / (drive->head * drive->sect);
-	}
-}
-
-static unsigned long idedisk_capacity (ide_drive_t *drive)
+static sector_t idedisk_capacity(struct ata_device *drive)
 {
-	if (drive->id->cfs_enable_2 & 0x0400)
-		return (drive->capacity48 - drive->sect0);
-	return (drive->capacity - drive->sect0);
+	return drive->capacity - drive->sect0;
 }
 
-static ide_startstop_t idedisk_special (ide_drive_t *drive)
+static ide_startstop_t idedisk_special(struct ata_device *drive)
 {
-	special_t *s = &drive->special;
+	unsigned char special_cmd = drive->special_cmd;
 
-	if (s->b.set_geometry) {
+	if (special_cmd & ATA_SPECIAL_GEOMETRY) {
 		struct ata_taskfile args;
 
-		s->b.set_geometry	= 0;
+		drive->special_cmd &= ~ATA_SPECIAL_GEOMETRY;
 
 		memset(&args, 0, sizeof(args));
 		args.taskfile.sector_number	= drive->sect;
@@ -712,8 +391,9 @@
 			args.handler = set_geometry_intr;;
 		}
 		ata_taskfile(drive, &args, NULL);
-	} else if (s->b.recalibrate) {
-		s->b.recalibrate = 0;
+	} else if (special_cmd & ATA_SPECIAL_RECALIBRATE) {
+		drive->special_cmd &= ~ATA_SPECIAL_RECALIBRATE;
+
 		if (!IS_PDC4030_DRIVE) {
 			struct ata_taskfile args;
 
@@ -723,8 +403,8 @@
 			args.handler = recal_intr;
 			ata_taskfile(drive, &args, NULL);
 		}
-	} else if (s->b.set_multmode) {
-		s->b.set_multmode = 0;
+	} else if (special_cmd & ATA_SPECIAL_MMODE) {
+		drive->special_cmd &= ~ATA_SPECIAL_MMODE;
 		if (drive->id && drive->mult_req > drive->id->max_multsect)
 			drive->mult_req = drive->id->max_multsect;
 		if (!IS_PDC4030_DRIVE) {
@@ -737,10 +417,10 @@
 
 			ata_taskfile(drive, &args, NULL);
 		}
-	} else if (s->all) {
-		int special = s->all;
-		s->all = 0;
-		printk(KERN_ERR "%s: bad special flag: 0x%02x\n", drive->name, special);
+	} else if (special_cmd) {
+		drive->special_cmd = 0;
+
+		printk(KERN_ERR "%s: bad special flag: 0x%02x\n", drive->name, special_cmd);
 		return ide_stopped;
 	}
 	return IS_PDC4030_DRIVE ? ide_stopped : ide_started;
@@ -750,13 +430,14 @@
 {
 	int legacy = (drive->id->cfs_enable_2 & 0x0400) ? 0 : 1;
 
-	drive->special.all = 0;
-	drive->special.b.set_geometry = legacy;
-	drive->special.b.recalibrate  = legacy;
+	if (legacy)
+		drive->special_cmd = (ATA_SPECIAL_GEOMETRY | ATA_SPECIAL_RECALIBRATE);
+	else
+		drive->special_cmd = 0;
 	if (OK_TO_RESET_CONTROLLER)
 		drive->mult_count = 0;
 	if (drive->mult_req != drive->mult_count)
-		drive->special.b.set_multmode = 1;
+		drive->special_cmd |= ATA_SPECIAL_MMODE;
 }
 
 #ifdef CONFIG_PROC_FS
@@ -861,71 +542,11 @@
 	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
 }
 
-#ifdef CONFIG_BLK_DEV_IDE_TCQ
-static int proc_idedisk_read_tcq
-	(char *page, char **start, off_t off, int count, int *eof, void *data)
-{
-	ide_drive_t	*drive = (ide_drive_t *) data;
-	char		*out = page;
-	int		len, cmds, i;
-	unsigned long tag_mask = 0, flags, cur_jif = jiffies, max_jif;
-
-	if (!drive->tcq) {
-		len = sprintf(out, "not configured\n");
-		PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
-	}
-
-	spin_lock_irqsave(&ide_lock, flags);
-
-	len = sprintf(out, "TCQ currently on:\t%s\n", drive->using_tcq ? "yes" : "no");
-	len += sprintf(out+len, "Max queue depth:\t%d\n",drive->queue_depth);
-	len += sprintf(out+len, "Max achieved depth:\t%d\n",drive->tcq->max_depth);
-	len += sprintf(out+len, "Max depth since last:\t%d\n",drive->tcq->max_last_depth);
-	len += sprintf(out+len, "Current depth:\t\t%d\n", drive->tcq->queued);
-	max_jif = 0;
-	len += sprintf(out+len, "Active tags:\t\t[ ");
-	for (i = 0, cmds = 0; i < drive->queue_depth; i++) {
-		struct ata_request *ar = IDE_GET_AR(drive, i);
-
-		if (!ar)
-			continue;
-
-		__set_bit(i, &tag_mask);
-		len += sprintf(out+len, "%d, ", i);
-		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 (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 ");
-
-	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;
-
-	spin_unlock_irqrestore(&ide_lock, flags);
-	PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
-}
-#endif
-
 static ide_proc_entry_t idedisk_proc[] = {
 	{ "cache",		S_IFREG|S_IRUGO,	proc_idedisk_read_cache,		NULL },
 	{ "geometry",		S_IFREG|S_IRUGO,	proc_ide_read_geometry,			NULL },
 	{ "smart_values",	S_IFREG|S_IRUSR,	proc_idedisk_read_smart_values,		NULL },
 	{ "smart_thresholds",	S_IFREG|S_IRUSR,	proc_idedisk_read_smart_thresholds,	NULL },
-#ifdef CONFIG_BLK_DEV_IDE_TCQ
-	{ "tcq",		S_IFREG|S_IRUSR,	proc_idedisk_read_tcq,	NULL },
-#endif
 	{ NULL, 0, NULL, NULL }
 };
 
@@ -936,19 +557,23 @@
 #endif	/* CONFIG_PROC_FS */
 
 /*
- * This is tightly woven into the driver->do_special can not touch.
+ * This is tightly woven into the driver->special can not touch.
  * DON'T do it again until a total personality rewrite is committed.
  */
 static int set_multcount(ide_drive_t *drive, int arg)
 {
-	struct request rq;
+	struct ata_taskfile args;
 
-	if (drive->special.b.set_multmode)
+	if (drive->special_cmd & ATA_SPECIAL_MMODE)
 		return -EBUSY;
-	ide_init_drive_cmd (&rq);
+
+	memset(&args, 0, sizeof(args));
+
 	drive->mult_req = arg;
-	drive->special.b.set_multmode = 1;
-	ide_do_drive_cmd (drive, &rq, ide_wait);
+	drive->special_cmd |= ATA_SPECIAL_MMODE;
+
+	ide_raw_taskfile(drive, &args, NULL);
+
 	return (drive->mult_count == arg) ? 0 : -EIO;
 }
 
@@ -959,6 +584,7 @@
 	drive->nowerr = arg;
 	drive->bad_wstat = arg ? BAD_R_STAT : BAD_W_STAT;
 	spin_unlock_irq(&ide_lock);
+
 	return 0;
 }
 
@@ -1007,24 +633,6 @@
 	return 0;
 }
 
-#ifdef CONFIG_BLK_DEV_IDE_TCQ
-static int set_using_tcq(ide_drive_t *drive, int arg)
-{
-	if (!drive->driver)
-		return -EPERM;
-	if (!drive->channel->dmaproc)
-		return -EPERM;
-	if (arg == drive->queue_depth && drive->using_tcq)
-		return 0;
-
-	drive->queue_depth = arg ? arg : 1;
-	if (drive->channel->dmaproc(arg ? ide_dma_queued_on : ide_dma_queued_off, drive))
-		return -EIO;
-
-	return 0;
-}
-#endif
-
 static int probe_lba_addressing (ide_drive_t *drive, int arg)
 {
 	drive->addressing =  0;
@@ -1056,9 +664,6 @@
 	ide_add_setting(drive,	"acoustic",		SETTING_RW,					HDIO_GET_ACOUSTIC,	HDIO_SET_ACOUSTIC,	TYPE_BYTE,	0,	254,				1,	1,	&drive->acoustic,		set_acoustic);
 	ide_add_setting(drive,	"failures",		SETTING_RW,					-1,			-1,			TYPE_INT,	0,	65535,				1,	1,	&drive->failures,		NULL);
 	ide_add_setting(drive,	"max_failures",		SETTING_RW,					-1,			-1,			TYPE_INT,	0,	65535,				1,	1,	&drive->max_failures,		NULL);
-#ifdef CONFIG_BLK_DEV_IDE_TCQ
-	ide_add_setting(drive,	"using_tcq",		SETTING_RW,					HDIO_GET_QDMA,		HDIO_SET_QDMA,		TYPE_BYTE,	0,	IDE_MAX_TAG,			1,		1,		&drive->using_tcq,		set_using_tcq);
-#endif
 }
 
 static int idedisk_suspend(struct device *dev, u32 state, u32 level)
@@ -1113,12 +718,153 @@
 	resume: idedisk_resume,
 };
 
-static void idedisk_setup(ide_drive_t *drive)
+/*
+ * Queries for true maximum capacity of the drive.
+ * Returns maximum LBA address (> 0) of the drive, 0 if failed.
+ */
+static unsigned long native_max_address(struct ata_device *drive)
+{
+	struct ata_taskfile args;
+	unsigned long addr = 0;
+
+	if (!(drive->id->command_set_1 & 0x0400) &&
+	    !(drive->id->cfs_enable_2 & 0x0100))
+		return addr;
+
+	/* Create IDE/ATA command request structure */
+	memset(&args, 0, sizeof(args));
+	args.taskfile.device_head = 0x40;
+	args.taskfile.command = WIN_READ_NATIVE_MAX;
+	args.handler = task_no_data_intr;
+
+	/* submit command request */
+	ide_raw_taskfile(drive, &args, NULL);
+
+	/* if OK, compute maximum address value */
+	if ((args.taskfile.command & 0x01) == 0) {
+		addr = ((args.taskfile.device_head & 0x0f) << 24)
+		     | (args.taskfile.high_cylinder << 16)
+		     | (args.taskfile.low_cylinder <<  8)
+		     | args.taskfile.sector_number;
+	}
+
+	addr++;	/* since the return value is (maxlba - 1), we add 1 */
+
+	return addr;
+}
+
+static u64 native_max_address_ext(struct ata_device *drive)
+{
+	struct ata_taskfile args;
+	u64 addr = 0;
+
+	/* Create IDE/ATA command request structure */
+	memset(&args, 0, sizeof(args));
+
+	args.taskfile.device_head = 0x40;
+	args.taskfile.command = WIN_READ_NATIVE_MAX_EXT;
+	args.handler = task_no_data_intr;
+
+        /* submit command request */
+        ide_raw_taskfile(drive, &args, NULL);
+
+	/* if OK, compute maximum address value */
+	if ((args.taskfile.command & 0x01) == 0) {
+		u32 high = (args.hobfile.high_cylinder << 16) |
+			   (args.hobfile.low_cylinder << 8) |
+			    args.hobfile.sector_number;
+		u32 low  = (args.taskfile.high_cylinder << 16) |
+			   (args.taskfile.low_cylinder << 8) |
+			    args.taskfile.sector_number;
+		addr = ((u64)high << 24) | low;
+	}
+
+	addr++;	/* since the return value is (maxlba - 1), we add 1 */
+
+	return addr;
+}
+
+#ifdef CONFIG_IDEDISK_STROKE
+/*
+ * Sets maximum virtual LBA address of the drive.
+ * Returns new maximum virtual LBA address (> 0) or 0 on failure.
+ */
+static sector_t set_max_address(ide_drive_t *drive, sector_t addr_req)
+{
+	struct ata_taskfile args;
+	sector_t addr_set = 0;
+
+	addr_req--;
+	/* Create IDE/ATA command request structure */
+	memset(&args, 0, sizeof(args));
+
+	args.taskfile.sector_number = (addr_req >> 0);
+	args.taskfile.low_cylinder = (addr_req >> 8);
+	args.taskfile.high_cylinder = (addr_req >> 16);
+
+	args.taskfile.device_head = ((addr_req >> 24) & 0x0f) | 0x40;
+	args.taskfile.command = WIN_SET_MAX;
+	args.handler = task_no_data_intr;
+	/* submit command request */
+	ide_raw_taskfile(drive, &args, NULL);
+	/* if OK, read new maximum address value */
+	if ((args.taskfile.command & 0x01) == 0) {
+		addr_set = ((args.taskfile.device_head & 0x0f) << 24)
+			 | (args.taskfile.high_cylinder << 16)
+			 | (args.taskfile.low_cylinder <<  8)
+			 | args.taskfile.sector_number;
+	}
+	addr_set++;
+	return addr_set;
+}
+
+static u64 set_max_address_ext(ide_drive_t *drive, u64 addr_req)
+{
+	struct ata_taskfile args;
+	u64 addr_set = 0;
+
+	addr_req--;
+	/* Create IDE/ATA command request structure */
+	memset(&args, 0, sizeof(args));
+
+	args.taskfile.sector_number = (addr_req >>  0);
+	args.taskfile.low_cylinder = (addr_req >>= 8);
+	args.taskfile.high_cylinder = (addr_req >>= 8);
+	args.taskfile.device_head = 0x40;
+	args.taskfile.command = WIN_SET_MAX_EXT;
+
+	args.hobfile.sector_number = (addr_req >>= 8);
+	args.hobfile.low_cylinder = (addr_req >>= 8);
+	args.hobfile.high_cylinder = (addr_req >>= 8);
+
+	args.hobfile.device_head = 0x40;
+	args.hobfile.control = (drive->ctl | 0x80);
+
+        args.handler = task_no_data_intr;
+	/* submit command request */
+	ide_raw_taskfile(drive, &args, NULL);
+	/* if OK, compute maximum address value */
+	if ((args.taskfile.command & 0x01) == 0) {
+		u32 high = (args.hobfile.high_cylinder << 16) |
+			   (args.hobfile.low_cylinder << 8) |
+			    args.hobfile.sector_number;
+		u32 low  = (args.taskfile.high_cylinder << 16) |
+			   (args.taskfile.low_cylinder << 8) |
+			    args.taskfile.sector_number;
+		addr_set = ((u64)high << 24) | low;
+	}
+	return addr_set;
+}
+
+#endif
+
+static void idedisk_setup(struct ata_device *drive)
 {
 	int i;
 
 	struct hd_driveid *id = drive->id;
-	unsigned long capacity;
+	sector_t capacity;
+	sector_t set_max;
 	int drvid = -1;
 
 	idedisk_add_settings(drive);
@@ -1169,7 +915,7 @@
 		drive->sect    = drive->bios_sect = id->sectors;
 	}
 
-	/* Handle logical geometry translation by the drive */
+	/* Handle logical geometry translation by the drive. */
 	if ((id->field_valid & 1) && id->cur_cyls &&
 	    id->cur_heads && (id->cur_heads <= 16) && id->cur_sectors) {
 		drive->cyl  = id->cur_cyls;
@@ -1177,31 +923,126 @@
 		drive->sect = id->cur_sectors;
 	}
 
-	/* Use physical geometry if what we have still makes no sense */
+	/* Use physical geometry if what we have still makes no sense. */
 	if (drive->head > 16 && id->heads && id->heads <= 16) {
 		drive->cyl  = id->cyls;
 		drive->head = id->heads;
 		drive->sect = id->sectors;
 	}
 
-	/* calculate drive capacity, and select LBA if possible */
-	init_idedisk_capacity (drive);
+	/* Calculate drive capacity, and select LBA if possible.
+	 * drive->id != NULL is spected
+	 *
+	 * To compute capacity, this uses either of
+	 *
+	 *    1. CHS value set by user       (whatever user sets will be trusted)
+	 *    2. LBA value from target drive (require new ATA feature)
+	 *    3. LBA value from system BIOS  (new one is OK, old one may break)
+	 *    4. CHS value from system BIOS  (traditional style)
+	 *
+	 * in above order (i.e., if value of higher priority is available,
+	 * reset will be ignored).
+	 */
+	capacity = drive->cyl * drive->head * drive->sect;
+	set_max = native_max_address(drive);
+
+	drive->capacity = 0;
+	drive->select.b.lba = 0;
+
+	if (id->cfs_enable_2 & 0x0400) {
+		u64 set_max_ext;
+		u64 capacity_2;
+		capacity_2 = capacity;
+		capacity_2 = id->lba_capacity_2;
+
+		drive->cyl = (unsigned int) capacity_2 / (drive->head * drive->sect);
+		drive->head = drive->bios_head = 255;
+		drive->sect = drive->bios_sect = 63;
+
+		drive->select.b.lba = 1;
+		set_max_ext = native_max_address_ext(drive);
+		if (set_max_ext > capacity_2) {
+#ifdef CONFIG_IDEDISK_STROKE
+			set_max_ext = native_max_address_ext(drive);
+			set_max_ext = set_max_address_ext(drive, set_max_ext);
+			if (set_max_ext) {
+				drive->capacity = capacity_2 = set_max_ext;
+				drive->cyl = (unsigned int) set_max_ext / (drive->head * drive->sect);
+				drive->select.b.lba = 1;
+				drive->id->lba_capacity_2 = capacity_2;
+                        }
+#else
+			printk("%s: setmax_ext LBA %llu, native  %llu\n",
+				drive->name, set_max_ext, capacity_2);
+#endif
+		}
+		drive->bios_cyl	= drive->cyl;
+		drive->capacity	= capacity_2;
+	} else {
+
+		/*
+		 * Determine capacity, and use LBA if the drive properly
+		 * supports it.
+		 */
+
+		if ((id->capability & 2) && lba_capacity_is_ok(id)) {
+			capacity = id->lba_capacity;
+			drive->cyl = capacity / (drive->head * drive->sect);
+			drive->select.b.lba = 1;
+		}
+
+		if (set_max > capacity) {
+#ifdef CONFIG_IDEDISK_STROKE
+			set_max = native_max_address(drive);
+			set_max = set_max_address(drive, set_max);
+			if (set_max) {
+				drive->capacity = capacity = set_max;
+				drive->cyl = set_max / (drive->head * drive->sect);
+				drive->select.b.lba = 1;
+				drive->id->lba_capacity = capacity;
+			}
+#else
+			printk("%s: setmax LBA %lu, native  %lu\n",
+					drive->name, set_max, capacity);
+#endif
+		}
+
+		drive->capacity = capacity;
+
+		if ((id->command_set_2 & 0x0400) && (id->cfs_enable_2 & 0x0400)) {
+			drive->capacity = id->lba_capacity_2;
+			drive->head = 255;
+			drive->sect = 63;
+			drive->cyl = (unsigned long)(drive->capacity) / (drive->head * drive->sect);
+		}
+	}
 
 	/*
-	 * if possible, give fdisk access to more of the drive,
+	 * If possible, give fdisk access to more of the drive,
 	 * by correcting bios_cyls:
 	 */
-	capacity = idedisk_capacity (drive);
+	capacity = idedisk_capacity(drive);
 	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);
 
-	/* Give size in megabytes (MB), not mebibytes (MiB). */
-	/* We compute the exact rounded value, avoiding overflow. */
+#if 0
+
+	/* Right now we avoid this calculation, since it can result in the
+	 * usage of not supported compiler internal functions on 32 bit hosts.
+	 * However since the calculation appears to be an interesting piece of
+	 * number theory let's preserve the formula here.
+	 */
+
+	/* Give size in megabytes (MB), not mebibytes (MiB).
+	 * We compute the exact rounded value, avoiding overflow.
+	 */
 	printk(" (%ld MB)", (capacity - capacity/625 + 974)/1950);
+#endif
 
-	/* Only print cache size when it was specified */
+	/* Only print cache size when it was specified.
+	 */
 	if (id->buf_size)
 		printk (" w/%dKiB Cache", id->buf_size/2);
 
@@ -1219,14 +1060,15 @@
 		id->multsect = ((id->max_multsect/2) > 1) ? id->max_multsect : 0;
 		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;
+		if (drive->mult_req)
+			drive->special_cmd |= ATA_SPECIAL_MMODE;
 #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;
+			drive->special_cmd |= ATA_SPECIAL_MMODE;
 #endif
 	}
 
@@ -1240,6 +1082,7 @@
 
 	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	Sun Apr 28 20:10:32 2002
+++ b/drivers/ide/ide-dma.c	Sun Apr 28 20:10:32 2002
@@ -5,9 +5,7 @@
  *  May be copied or modified under the terms of the GNU General Public License
  *
  *  Special Thanks to Mark for his Six years of work.
- */
-
-/*
+*
  * This module provides support for the bus-master IDE DMA functions
  * of various PCI chipsets, including the Intel PIIX (i82371FB for
  * the 430 FX chipset), the PIIX3 (i82371SB for the 430 HX/VX and
@@ -215,78 +213,74 @@
 	return ide_error(drive, "dma_intr", stat);
 }
 
-int ide_build_sglist(struct ata_channel *hwif, struct request *rq)
-{
-	request_queue_t *q = &hwif->drives[DEVICE_NR(rq->rq_dev) & 1].queue;
-	struct ata_request *ar = rq->special;
-
-	if (!(ar->ar_flags & ATA_AR_SETUP)) {
-		ar->ar_flags |= ATA_AR_SETUP;
-		ar->ar_sg_nents = blk_rq_map_sg(q, rq, ar->ar_sg_table);
-	}
-
-	if (rq->q && ar->ar_sg_nents > rq->nr_phys_segments) {
-		printk("%s: received %d phys segments, build %d\n", __FILE__, rq->nr_phys_segments, ar->ar_sg_nents);
-		return 0;
-	} else if (!ar->ar_sg_nents) {
-		printk("%s: zero segments in request\n", __FILE__);
-		return 0;
-	}
-
-	if (rq_data_dir(rq) == READ)
-		ar->ar_sg_ddir = PCI_DMA_FROMDEVICE;
-	else
-		ar->ar_sg_ddir = PCI_DMA_TODEVICE;
-
-	return pci_map_sg(hwif->pci_dev, ar->ar_sg_table, ar->ar_sg_nents, ar->ar_sg_ddir);
-}
-
 /*
  * FIXME: taskfiles should be a map of pages, not a long virt address... /jens
  * FIXME: I agree with Jens --mdcki!
  */
-static int raw_build_sglist(struct ata_channel *ch, struct request *rq)
+static int build_sglist(struct ata_channel *ch, struct request *rq)
 {
-	struct ata_request *ar = rq->special;
-	struct scatterlist *sg = ar->ar_sg_table;
-	struct ata_taskfile *args = &ar->ar_task;
-	unsigned char *virt_addr = rq->buffer;
-	int sector_count = rq->nr_sectors;
+	struct scatterlist *sg = ch->sg_table;
 	int nents = 0;
 
-	if (args->command_type == IDE_DRIVE_TASK_RAW_WRITE)
-		ar->ar_sg_ddir = PCI_DMA_TODEVICE;
-	else
-		ar->ar_sg_ddir = PCI_DMA_FROMDEVICE;
+	if (rq->flags & REQ_DRIVE_ACB) {
+		struct ata_taskfile *args = rq->special;
+#if 1
+		unsigned char *virt_addr = rq->buffer;
+		int sector_count = rq->nr_sectors;
+#else
+		nents = blk_rq_map_sg(rq->q, rq, ch->sg_table);
+
+		if (nents > rq->nr_segments)
+			printk("ide-dma: received %d segments, build %d\n", rq->nr_segments, nents);
+#endif
 
-	if (sector_count > 128) {
-		memset(&sg[nents], 0, sizeof(*sg));
+		if (args->command_type == IDE_DRIVE_TASK_RAW_WRITE)
+			ch->sg_dma_direction = PCI_DMA_TODEVICE;
+		else
+			ch->sg_dma_direction = PCI_DMA_FROMDEVICE;
+
+		/*
+		 * FIXME: This depends upon a hard coded page size!
+		 */
+		if (sector_count > 128) {
+			memset(&sg[nents], 0, sizeof(*sg));
 
+			sg[nents].page = virt_to_page(virt_addr);
+			sg[nents].offset = (unsigned long) virt_addr & ~PAGE_MASK;
+			sg[nents].length = 128  * SECTOR_SIZE;
+			++nents;
+			virt_addr = virt_addr + (128 * SECTOR_SIZE);
+			sector_count -= 128;
+		}
+		memset(&sg[nents], 0, sizeof(*sg));
 		sg[nents].page = virt_to_page(virt_addr);
 		sg[nents].offset = (unsigned long) virt_addr & ~PAGE_MASK;
-		sg[nents].length = 128  * SECTOR_SIZE;
-		nents++;
-		virt_addr = virt_addr + (128 * SECTOR_SIZE);
-		sector_count -= 128;
-	}
-	memset(&sg[nents], 0, sizeof(*sg));
-	sg[nents].page = virt_to_page(virt_addr);
-	sg[nents].offset = (unsigned long) virt_addr & ~PAGE_MASK;
-	sg[nents].length =  sector_count  * SECTOR_SIZE;
-	nents++;
+		sg[nents].length =  sector_count  * SECTOR_SIZE;
+		++nents;
+	} else {
+		nents = blk_rq_map_sg(rq->q, rq, ch->sg_table);
+
+		if (rq->q && nents > rq->nr_phys_segments)
+			printk("ide-dma: received %d phys segments, build %d\n", rq->nr_phys_segments, nents);
 
-	return pci_map_sg(ch->pci_dev, sg, nents, ar->ar_sg_ddir);
+		if (rq_data_dir(rq) == READ)
+			ch->sg_dma_direction = PCI_DMA_FROMDEVICE;
+		else
+			ch->sg_dma_direction = PCI_DMA_TODEVICE;
+
+	}
+	return pci_map_sg(ch->pci_dev, sg, nents, ch->sg_dma_direction);
 }
 
 /*
- * Prepare a dma request.
+ * ide_build_dmatable() prepares a dma request.
  * Returns 0 if all went okay, returns 1 otherwise.
- * This may also be invoked from trm290.c
+ * May also be invoked from trm290.c
  */
-int ide_build_dmatable(ide_drive_t *drive, struct request *rq,
-		       ide_dma_action_t func)
+int ide_build_dmatable (ide_drive_t *drive, ide_dma_action_t func)
 {
 	struct ata_channel *hwif = drive->channel;
+	unsigned int *table = hwif->dmatable_cpu;
 #ifdef CONFIG_BLK_DEV_TRM290
 	unsigned int is_trm290_chipset = (hwif->chipset == ide_trm290);
 #else
@@ -295,19 +289,12 @@
 	unsigned int count = 0;
 	int i;
 	struct scatterlist *sg;
-	struct ata_request *ar = rq->special;
-	unsigned int *table = ar->ar_dmatable_cpu;
-
-	if (rq->flags & REQ_DRIVE_TASKFILE)
-		ar->ar_sg_nents = raw_build_sglist(hwif, rq);
-	else
-		ar->ar_sg_nents = ide_build_sglist(hwif, rq);
 
-	if (!ar->ar_sg_nents)
+	hwif->sg_nents = i = build_sglist(hwif, HWGROUP(drive)->rq);
+	if (!i)
 		return 0;
 
-	sg = ar->ar_sg_table;
-	i = ar->ar_sg_nents;
+	sg = hwif->sg_table;
 	while (i) {
 		u32 cur_addr;
 		u32 cur_len;
@@ -326,7 +313,7 @@
 
 			if (count++ >= PRD_ENTRIES) {
 				printk("ide-dma: req %p\n", HWGROUP(drive)->rq);
-				printk("count %d, sg_nents %d, cur_len %d, cur_addr %u\n", count, ar->ar_sg_nents, cur_len, cur_addr);
+				printk("count %d, sg_nents %d, cur_len %d, cur_addr %u\n", count, hwif->sg_nents, cur_len, cur_addr);
 				BUG();
 			}
 
@@ -345,8 +332,8 @@
 			 */
 				if (count++ >= PRD_ENTRIES) {
 					pci_unmap_sg(hwif->pci_dev, sg,
-						     ar->ar_sg_nents,
-						     ar->ar_sg_ddir);
+						     hwif->sg_nents,
+						     hwif->sg_dma_direction);
 					return 0;
 				}
 
@@ -372,12 +359,13 @@
 }
 
 /* Teardown mappings after DMA has completed.  */
-void ide_destroy_dmatable(struct ata_device *d)
+void ide_destroy_dmatable (ide_drive_t *drive)
 {
-	struct pci_dev *dev = d->channel->pci_dev;
-	struct ata_request *ar = IDE_CUR_AR(d);
+	struct pci_dev *dev = drive->channel->pci_dev;
+	struct scatterlist *sg = drive->channel->sg_table;
+	int nents = drive->channel->sg_nents;
 
-	pci_unmap_sg(dev, ar->ar_sg_table, ar->ar_sg_nents, ar->ar_sg_ddir);
+	pci_unmap_sg(dev, sg, nents, drive->channel->sg_dma_direction);
 }
 
 /*
@@ -435,7 +423,7 @@
 			printk(", UDMA(133)");	/* UDMA BIOS-enabled! */
 		}
 	} else if ((id->field_valid & 4) && (eighty_ninty_three(drive)) &&
-		  (id->dma_ultra & (id->dma_ultra >> 11) & 7)) {
+	  	  (id->dma_ultra & (id->dma_ultra >> 11) & 7)) {
 		if ((id->dma_ultra >> 13) & 1) {
 			printk(", UDMA(100)");	/* UDMA BIOS-enabled! */
 		} else if ((id->dma_ultra >> 12) & 1) {
@@ -538,41 +526,6 @@
 }
 
 /*
- * Start DMA engine.
- */
-int ide_start_dma(struct ata_channel *hwif, ide_drive_t *drive, ide_dma_action_t func)
-{
-	unsigned int reading = 0, count;
-	unsigned long dma_base = hwif->dma_base;
-	struct ata_request *ar = IDE_CUR_AR(drive);
-
-	/* This can happen with drivers abusing the special request field.
-	 */
-
-	if (!ar) {
-		printk(KERN_ERR "DMA without ATA request\n");
-
-		return 1;
-	}
-
-	if (rq_data_dir(ar->ar_rq) == READ)
-		reading = 1 << 3;
-
-	if (hwif->rwproc)
-		hwif->rwproc(drive, func);
-
-	if (!(count = ide_build_dmatable(drive, ar->ar_rq, func)))
-		return 1;	/* try PIO instead of DMA */
-
-	ar->ar_flags |= ATA_AR_SETUP;
-	outl(ar->ar_dmatable, dma_base + 4);	/* PRD table */
-	outb(reading, dma_base);		/* specify r/w */
-	outb(inb(dma_base + 2) | 6, dma_base+2);/* clear INTR & ERROR flags */
-	drive->waiting_for_dma = 1;
-	return 0;
-}
-
-/*
  * ide_dmaproc() initiates/aborts DMA read/write operations on a drive.
  *
  * The caller is assumed to have selected the drive and programmed the drive's
@@ -592,10 +545,9 @@
 {
 	struct ata_channel *hwif = drive->channel;
 	unsigned long dma_base = hwif->dma_base;
-	u8 unit = (drive->select.b.unit & 0x01);
-	unsigned int reading = 0, set_high = 1;
-	struct ata_request *ar;
-	u8 dma_stat;
+	byte unit = (drive->select.b.unit & 0x01);
+	unsigned int count, reading = 0, set_high = 1;
+	byte dma_stat;
 
 	switch (func) {
 		case ide_dma_off:
@@ -603,68 +555,55 @@
 		case ide_dma_off_quietly:
 			set_high = 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:
-			if (test_and_set_bit(IDE_DMA, &HWGROUP(drive)->flags))
-				BUG();
-			/* Note that this is done *after* the cmd has
-			 * been issued to the drive, as per the BM-IDE spec.
-			 * The Promise Ultra33 doesn't work correctly when
-			 * we do this part before issuing the drive cmd.
-			 */
-			outb(inb(dma_base)|1, dma_base);		/* start DMA */
-			return 0;
-#ifdef CONFIG_BLK_DEV_IDE_TCQ
-		case ide_dma_queued_on:
-		case ide_dma_queued_off:
-		case ide_dma_read_queued:
-		case ide_dma_write_queued:
-		case ide_dma_queued_start:
-			return ide_tcq_dmaproc(func, drive);
-#endif
-
 		case ide_dma_read:
 			reading = 1 << 3;
 		case ide_dma_write:
-			ar = IDE_CUR_AR(drive);
-
-			if (ide_start_dma(hwif, drive, func))
-				return 1;
-
+			/* active tuning based on IO direction */
+			if (hwif->rwproc)
+				hwif->rwproc(drive, func);
+
+			if (!(count = ide_build_dmatable(drive, func)))
+				return 1;	/* try PIO instead of DMA */
+			outl(hwif->dmatable_dma, dma_base + 4); /* PRD table */
+			outb(reading, dma_base);			/* specify r/w */
+			outb(inb(dma_base+2)|6, dma_base+2);		/* clear INTR & ERROR flags */
+			drive->waiting_for_dma = 1;
 			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) &&
+			if ((HWGROUP(drive)->rq->flags & REQ_DRIVE_ACB) &&
 			    (drive->addressing == 1)) {
-				struct ata_taskfile *args = &ar->ar_task;
+				struct ata_taskfile *args = HWGROUP(drive)->rq->special;
+
 				OUT_BYTE(args->taskfile.command, IDE_COMMAND_REG);
 			} else if (drive->addressing) {
 				OUT_BYTE(reading ? WIN_READDMA_EXT : WIN_WRITEDMA_EXT, IDE_COMMAND_REG);
 			} else {
 				OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG);
 			}
-			return hwif->dmaproc(ide_dma_begin, drive);
+			return drive->channel->dmaproc(ide_dma_begin, drive);
+		case ide_dma_begin:
+			/* Note that this is done *after* the cmd has
+			 * been issued to the drive, as per the BM-IDE spec.
+			 * The Promise Ultra33 doesn't work correctly when
+			 * we do this part before issuing the drive cmd.
+			 */
+			outb(inb(dma_base)|1, dma_base);		/* start DMA */
+			return 0;
 		case ide_dma_end: /* returns 1 on error, 0 otherwise */
-			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 */
+			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 */
 			return (dma_stat & 7) != 4 ? (0x10 | dma_stat) : 0;	/* verify good DMA status */
@@ -687,7 +626,7 @@
 			return 1;
 		case ide_dma_retune:
 		case ide_dma_lostirq:
-			printk(KERN_ERR "%s: chipset supported func only: %d\n", __FUNCTION__, func);
+			printk(KERN_ERR "%s: chipset supported func only: %d\n", __FUNCTION__,  func);
 			return 1;
 		default:
 			printk(KERN_ERR "%s: unsupported func: %d\n", __FUNCTION__, func);
@@ -703,6 +642,17 @@
 	if (!hwif->dma_base)
 		return;
 
+	if (hwif->dmatable_cpu) {
+		pci_free_consistent(hwif->pci_dev,
+				    PRD_ENTRIES * PRD_BYTES,
+				    hwif->dmatable_cpu,
+				    hwif->dmatable_dma);
+		hwif->dmatable_cpu = NULL;
+	}
+	if (hwif->sg_table) {
+		kfree(hwif->sg_table);
+		hwif->sg_table = NULL;
+	}
 	if ((hwif->dma_extra) && (hwif->unit == 0))
 		release_region((hwif->dma_base + 16), hwif->dma_extra);
 	release_region(hwif->dma_base, 8);
@@ -721,6 +671,20 @@
 	}
 	request_region(dma_base, num_ports, hwif->name);
 	hwif->dma_base = dma_base;
+	hwif->dmatable_cpu = pci_alloc_consistent(hwif->pci_dev,
+						  PRD_ENTRIES * PRD_BYTES,
+						  &hwif->dmatable_dma);
+	if (hwif->dmatable_cpu == NULL)
+		goto dma_alloc_failure;
+
+	hwif->sg_table = kmalloc(sizeof(struct scatterlist) * PRD_ENTRIES,
+				 GFP_KERNEL);
+	if (hwif->sg_table == NULL) {
+		pci_free_consistent(hwif->pci_dev, PRD_ENTRIES * PRD_BYTES,
+				    hwif->dmatable_cpu, hwif->dmatable_dma);
+		goto dma_alloc_failure;
+	}
+
 	hwif->dmaproc = &ide_dmaproc;
 
 	if (hwif->chipset != ide_trm290) {
@@ -731,4 +695,7 @@
 	}
 	printk("\n");
 	return;
+
+dma_alloc_failure:
+	printk(" -- ERROR, UNABLE TO ALLOCATE DMA TABLES\n");
 }
diff -Nru a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c
--- a/drivers/ide/ide-floppy.c	Sun Apr 28 20:10:32 2002
+++ b/drivers/ide/ide-floppy.c	Sun Apr 28 20:10:32 2002
@@ -1255,12 +1255,12 @@
 	pc->c[0] = IDEFLOPPY_TEST_UNIT_READY_CMD;
 }
 
-static void idefloppy_create_rw_cmd (idefloppy_floppy_t *floppy, idefloppy_pc_t *pc, struct request *rq, unsigned long sector)
+static void idefloppy_create_rw_cmd(idefloppy_floppy_t *floppy, idefloppy_pc_t *pc, struct request *rq, sector_t sector)
 {
 	int block = sector / floppy->bs_factor;
 	int blocks = rq->nr_sectors / floppy->bs_factor;
 	int cmd = rq_data_dir(rq);
-	
+
 #if IDEFLOPPY_DEBUG_LOG
 	printk ("create_rw1%d_cmd: block == %d, blocks == %d\n",
 		2 * test_bit (IDEFLOPPY_USE_READ12, &floppy->flags), block, blocks);
@@ -1287,9 +1287,9 @@
 }
 
 /*
- *	idefloppy_do_request is our request handling function.	
+ * This is our request handling function.
  */
-static ide_startstop_t idefloppy_do_request (ide_drive_t *drive, struct request *rq, unsigned long block)
+static ide_startstop_t idefloppy_do_request(struct ata_device *drive, struct request *rq, sector_t block)
 {
 	idefloppy_floppy_t *floppy = drive->driver_data;
 	idefloppy_pc_t *pc;
@@ -1297,7 +1297,7 @@
 #if IDEFLOPPY_DEBUG_LOG
 	printk (KERN_INFO "rq_status: %d, rq_dev: %u, flags: %lx, errors: %d\n",rq->rq_status,(unsigned int) rq->rq_dev,rq->flags,rq->errors);
 	printk (KERN_INFO "sector: %ld, nr_sectors: %ld, current_nr_sectors: %d\n",rq->sector,rq->nr_sectors,rq->current_nr_sectors);
-#endif /* IDEFLOPPY_DEBUG_LOG */
+#endif
 
 	if (rq->errors >= ERROR_MAX) {
 		if (floppy->failed_pc != NULL)
@@ -1314,7 +1314,7 @@
 			idefloppy_end_request(drive, 0);
 			return ide_stopped;
 		}
-		pc = idefloppy_next_pc_storage (drive);
+		pc = idefloppy_next_pc_storage(drive);
 		idefloppy_create_rw_cmd (floppy, pc, rq, block);
 	} else if (rq->flags & IDEFLOPPY_RQ) {
 		pc = (idefloppy_pc_t *) rq->buffer;
@@ -2063,9 +2063,7 @@
 	release:		idefloppy_release,
 	check_media_change:	idefloppy_check_media_change,
 	revalidate:		NULL, /* use default method */
-	pre_reset:		NULL,
 	capacity:		idefloppy_capacity,
-	special:		NULL,
 	proc:			idefloppy_proc
 };
 
diff -Nru a/drivers/ide/ide-pmac.c b/drivers/ide/ide-pmac.c
--- a/drivers/ide/ide-pmac.c	Sun Apr 28 20:10:37 2002
+++ b/drivers/ide/ide-pmac.c	Sun Apr 28 20:10:37 2002
@@ -1109,7 +1109,7 @@
 		udelay(1);
 
 	/* Build sglist */
-	if (rq->flags & REQ_DRIVE_TASKFILE) {
+	if (rq->flags & REQ_DRIVE_ACB) {
 		pmac_ide[ix].sg_nents = i = pmac_raw_build_sglist(ix, rq);
 	} else {
 		pmac_ide[ix].sg_nents = i = pmac_ide_build_sglist(ix, rq);
@@ -1386,7 +1386,7 @@
 			return 0;
 		BUG_ON(HWGROUP(drive)->handler);
 		ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, NULL);
-		if ((HWGROUP(drive)->rq->flags & REQ_DRIVE_TASKFILE) &&
+		if ((HWGROUP(drive)->rq->flags & REQ_DRIVE_ACB) &&
 		    (drive->addressing == 1)) {
 			struct ata_taskfile *args = HWGROUP(drive)->rq->special;
 			OUT_BYTE(args->taskfile.command, IDE_COMMAND_REG);
diff -Nru a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c
--- a/drivers/ide/ide-probe.c	Sun Apr 28 20:10:30 2002
+++ b/drivers/ide/ide-probe.c	Sun Apr 28 20:10:30 2002
@@ -168,9 +168,6 @@
 		}
 		printk (" drive\n");
 		drive->type = type;
-
-		goto init_queue;
-
 		return;
 	}
 
@@ -201,22 +198,6 @@
 	if (drive->channel->quirkproc)
 		drive->quirk_list = drive->channel->quirkproc(drive);
 
-init_queue:
-	/*
-	 * it's an ata drive, build command list
-	 */
-	drive->queue_depth = 1;
-#ifdef CONFIG_BLK_DEV_IDE_TCQ_DEPTH
-	drive->queue_depth = CONFIG_BLK_DEV_IDE_TCQ_DEPTH;
-#else
-	drive->queue_depth = drive->id->queue_depth + 1;
-#endif
-	if (drive->queue_depth < 1 || drive->queue_depth > IDE_MAX_TAG)
-		drive->queue_depth = IDE_MAX_TAG;
-
-	if (ide_init_commandlist(drive))
-		goto err_misc;
-
 	return;
 
 err_misc:
@@ -454,149 +435,130 @@
 }
 
 /*
- * Calculate the region that this interface occupies,
- * handling interfaces where the registers may not be
- * ordered sanely.  We deal with the CONTROL register
- * separately.
+ * This routine only knows how to look for drive units 0 and 1
+ * on an interface, so any setting of MAX_DRIVES > 2 won't work here.
  */
-static int hwif_check_regions(struct ata_channel *hwif)
+static void probe_hwif(struct ata_channel *ch)
 {
-	int region_errors = 0;
+	unsigned int unit;
+	unsigned long flags;
 
-	hwif->straight8 = 0;
-	region_errors  = ide_check_region(hwif->io_ports[IDE_DATA_OFFSET], 1);
-	region_errors += ide_check_region(hwif->io_ports[IDE_ERROR_OFFSET], 1);
-	region_errors += ide_check_region(hwif->io_ports[IDE_NSECTOR_OFFSET], 1);
-	region_errors += ide_check_region(hwif->io_ports[IDE_SECTOR_OFFSET], 1);
-	region_errors += ide_check_region(hwif->io_ports[IDE_LCYL_OFFSET], 1);
-	region_errors += ide_check_region(hwif->io_ports[IDE_HCYL_OFFSET], 1);
-	region_errors += ide_check_region(hwif->io_ports[IDE_SELECT_OFFSET], 1);
-	region_errors += ide_check_region(hwif->io_ports[IDE_STATUS_OFFSET], 1);
+	if (ch->noprobe)
+		return;
+
+	ch->straight8 = 0;
+
+	__save_flags(flags);	/* local CPU only */
+	__sti();		/* local CPU only; needed for jiffies and irq probing */
 
-	if (hwif->io_ports[IDE_CONTROL_OFFSET])
-		region_errors += ide_check_region(hwif->io_ports[IDE_CONTROL_OFFSET], 1);
-#if defined(CONFIG_AMIGA) || defined(CONFIG_MAC)
-	if (hwif->io_ports[IDE_IRQ_OFFSET])
-		region_errors += ide_check_region(hwif->io_ports[IDE_IRQ_OFFSET], 1);
-#endif /* (CONFIG_AMIGA) || (CONFIG_MAC) */
 	/*
-	 * If any errors are return, we drop the hwif interface.
+	 * Check for the presence of a channel by probing for drives on it.
 	 */
-	return(region_errors);
-}
 
-static void hwif_register(struct ata_channel *hwif)
-{
-	/* Register this hardware interface within the global device tree.
-	 */
-	sprintf(hwif->dev.bus_id, "%04x", hwif->io_ports[IDE_DATA_OFFSET]);
-	sprintf(hwif->dev.name, "ide");
-	hwif->dev.driver_data = hwif;
-#ifdef CONFIG_BLK_DEV_IDEPCI
-	if (hwif->pci_dev)
-		hwif->dev.parent = &hwif->pci_dev->dev;
-	else
-#endif
-		hwif->dev.parent = NULL; /* Would like to do = &device_legacy */
-	device_register(&hwif->dev);
+	for (unit = 0; unit < MAX_DRIVES; ++unit) {
+		struct ata_device *drive = &ch->drives[unit];
 
-	if (((unsigned long)hwif->io_ports[IDE_DATA_OFFSET] | 7) ==
-	    ((unsigned long)hwif->io_ports[IDE_STATUS_OFFSET])) {
-		ide_request_region(hwif->io_ports[IDE_DATA_OFFSET], 8, hwif->name);
-		hwif->straight8 = 1;
-	} else {
-		if (hwif->io_ports[IDE_DATA_OFFSET])
-			ide_request_region(hwif->io_ports[IDE_DATA_OFFSET], 1, hwif->name);
-		if (hwif->io_ports[IDE_ERROR_OFFSET])
-			ide_request_region(hwif->io_ports[IDE_ERROR_OFFSET], 1, hwif->name);
-		if (hwif->io_ports[IDE_NSECTOR_OFFSET])
-			ide_request_region(hwif->io_ports[IDE_NSECTOR_OFFSET], 1, hwif->name);
-		if (hwif->io_ports[IDE_SECTOR_OFFSET])
-			ide_request_region(hwif->io_ports[IDE_SECTOR_OFFSET], 1, hwif->name);
-		if (hwif->io_ports[IDE_LCYL_OFFSET])
-			ide_request_region(hwif->io_ports[IDE_LCYL_OFFSET], 1, hwif->name);
-		if (hwif->io_ports[IDE_HCYL_OFFSET])
-			ide_request_region(hwif->io_ports[IDE_HCYL_OFFSET], 1, hwif->name);
-		if (hwif->io_ports[IDE_SELECT_OFFSET])
-			ide_request_region(hwif->io_ports[IDE_SELECT_OFFSET], 1, hwif->name);
-		if (hwif->io_ports[IDE_STATUS_OFFSET])
-			ide_request_region(hwif->io_ports[IDE_STATUS_OFFSET], 1, hwif->name);
+		probe_for_drive(drive);
 
+		if (drive->present && !ch->present) {
+			ch->present = 1;
+		}
 	}
-	if (hwif->io_ports[IDE_CONTROL_OFFSET])
-		ide_request_region(hwif->io_ports[IDE_CONTROL_OFFSET], 1, hwif->name);
-#if defined(CONFIG_AMIGA) || defined(CONFIG_MAC)
-	if (hwif->io_ports[IDE_IRQ_OFFSET])
-		ide_request_region(hwif->io_ports[IDE_IRQ_OFFSET], 1, hwif->name);
-#endif
-}
 
-/*
- * This routine only knows how to look for drive units 0 and 1
- * on an interface, so any setting of MAX_DRIVES > 2 won't work here.
- */
-static void probe_hwif(struct ata_channel *hwif)
-{
-	unsigned int unit;
-	unsigned long flags;
+	if (ch->present) {
+		int error = 0;
 
-	if (hwif->noprobe)
-		return;
+		if (((unsigned long)ch->io_ports[IDE_DATA_OFFSET] | 7) ==
+				((unsigned long)ch->io_ports[IDE_STATUS_OFFSET])) {
+			error += !request_region(ch->io_ports[IDE_DATA_OFFSET], 8, ch->name);
+			ch->straight8 = 1;
+		} else {
+			if (ch->io_ports[IDE_DATA_OFFSET])
+				error += !request_region(ch->io_ports[IDE_DATA_OFFSET], 1, ch->name);
+			if (ch->io_ports[IDE_ERROR_OFFSET])
+				error += !request_region(ch->io_ports[IDE_ERROR_OFFSET], 1, ch->name);
+			if (ch->io_ports[IDE_NSECTOR_OFFSET])
+				error += !request_region(ch->io_ports[IDE_NSECTOR_OFFSET], 1, ch->name);
+			if (ch->io_ports[IDE_SECTOR_OFFSET])
+				error += !request_region(ch->io_ports[IDE_SECTOR_OFFSET], 1, ch->name);
+			if (ch->io_ports[IDE_LCYL_OFFSET])
+				error += !request_region(ch->io_ports[IDE_LCYL_OFFSET], 1, ch->name);
+			if (ch->io_ports[IDE_HCYL_OFFSET])
+				error += !request_region(ch->io_ports[IDE_HCYL_OFFSET], 1, ch->name);
+			if (ch->io_ports[IDE_SELECT_OFFSET])
+				error += !request_region(ch->io_ports[IDE_SELECT_OFFSET], 1, ch->name);
+			if (ch->io_ports[IDE_STATUS_OFFSET])
+				error += !request_region(ch->io_ports[IDE_STATUS_OFFSET], 1, ch->name);
+
+		}
+		if (ch->io_ports[IDE_CONTROL_OFFSET])
+			error += !request_region(ch->io_ports[IDE_CONTROL_OFFSET], 1, ch->name);
+#if defined(CONFIG_AMIGA) || defined(CONFIG_MAC)
+		if (ch->io_ports[IDE_IRQ_OFFSET])
+			error += !request_region(ch->io_ports[IDE_IRQ_OFFSET], 1, ch->name);
+#endif
 
-	if (
+		/* Some neccessary register area was already used. Skip this
+		 * device.
+		 */
+		if (
 #if CONFIG_BLK_DEV_PDC4030
-	    (hwif->chipset != ide_pdc4030 || hwif->unit == 0) &&
+				(ch->chipset != ide_pdc4030 || ch->unit == 0) &&
 #endif
-	    hwif_check_regions(hwif)) {
-		int msgout = 0;
-		for (unit = 0; unit < MAX_DRIVES; ++unit) {
-			ide_drive_t *drive = &hwif->drives[unit];
-			if (drive->present) {
-				drive->present = 0;
-				printk("%s: ERROR, PORTS ALREADY IN USE\n", drive->name);
-				msgout = 1;
-			}
+				error) {
+			/* FIXME: We should be dealing properly with partial IO
+			 * region allocations here.
+			 */
+			ch->present = 0;
+			printk("%s: error: ports already in use!\n", ch->name);
+
 		}
-		if (!msgout)
-			printk("%s: ports already in use, skipping probe\n", hwif->name);
-		return;
 	}
 
-	__save_flags(flags);	/* local CPU only */
-	__sti();		/* local CPU only; needed for jiffies and irq probing */
-	/*
-	 * Second drive should only exist if first drive was found,
-	 * but a lot of cdrom drives are configured as single slaves.
-	 */
-	for (unit = 0; unit < MAX_DRIVES; ++unit) {
-		ide_drive_t *drive = &hwif->drives[unit];
-		probe_for_drive (drive);
-		if (drive->present && !hwif->present) {
-			hwif->present = 1;
-			hwif_register(hwif);
-		}
-	}
-	if (hwif->io_ports[IDE_CONTROL_OFFSET] && hwif->reset) {
-		unsigned long timeout = jiffies + WAIT_WORSTCASE;
-		byte stat;
-
-		printk("%s: reset\n", hwif->name);
-		OUT_BYTE(12, hwif->io_ports[IDE_CONTROL_OFFSET]);
-		udelay(10);
-		OUT_BYTE(8, hwif->io_ports[IDE_CONTROL_OFFSET]);
-		do {
-			ide_delay_50ms();
-			stat = IN_BYTE(hwif->io_ports[IDE_STATUS_OFFSET]);
-		} while ((stat & BUSY_STAT) && 0 < (signed long)(timeout - jiffies));
+	if (ch->present) {
+		/* Register this hardware interface within the global device tree.
+		 */
+		sprintf(ch->dev.bus_id, "%04x", ch->io_ports[IDE_DATA_OFFSET]);
+		sprintf(ch->dev.name, "ide");
+		ch->dev.driver_data = ch;
+#ifdef CONFIG_BLK_DEV_IDEPCI
+		if (ch->pci_dev)
+			ch->dev.parent = &ch->pci_dev->dev;
+		else
+#endif
+			ch->dev.parent = NULL; /* Would like to do = &device_legacy */
+
+		device_register(&ch->dev);
+
+		if (ch->io_ports[IDE_CONTROL_OFFSET] && ch->reset) {
+			unsigned long timeout = jiffies + WAIT_WORSTCASE;
+			byte stat;
+
+			printk("%s: reset\n", ch->name);
+			OUT_BYTE(12, ch->io_ports[IDE_CONTROL_OFFSET]);
+			udelay(10);
+			OUT_BYTE(8, ch->io_ports[IDE_CONTROL_OFFSET]);
+			do {
+				ide_delay_50ms();
+				stat = IN_BYTE(ch->io_ports[IDE_STATUS_OFFSET]);
+			} while ((stat & BUSY_STAT) && 0 < (signed long)(timeout - jiffies));
 
+		}
 	}
+
 	__restore_flags(flags);	/* local CPU only */
-	for (unit = 0; unit < MAX_DRIVES; ++unit) {
-		ide_drive_t *drive = &hwif->drives[unit];
 
-		if (drive->present && (drive->autotune == 1)) {
-			if (drive->channel->tuneproc != NULL)
-				drive->channel->tuneproc(drive, 255);	/* auto-tune PIO mode */
+	/*
+	 * Now setup the PIO transfer modes of the drives on this channel.
+	 */
+	if (ch->present) {
+		for (unit = 0; unit < MAX_DRIVES; ++unit) {
+			struct ata_device *drive = &ch->drives[unit];
+
+			if (drive->present && (drive->autotune == 1)) {
+				if (drive->channel->tuneproc)
+					drive->channel->tuneproc(drive, 255);	/* auto-tune PIO mode */
+			}
 		}
 	}
 }
@@ -604,12 +566,12 @@
 /*
  * init request queue
  */
-static void ide_init_queue(ide_drive_t *drive)
+static void init_device_queue(struct ata_device *drive)
 {
 	request_queue_t *q = &drive->queue;
 	int max_sectors = 255;
 
-	q->queuedata = HWGROUP(drive);
+	q->queuedata = drive->channel;
 	blk_init_queue(q, do_ide_request, &ide_lock);
 	blk_queue_segment_boundary(q, 0xffff);
 
@@ -656,23 +618,24 @@
 #endif
 
 /*
- * This routine sets up the irq for an ide interface, and creates a new
- * hwgroup for the irq/hwif if none was previously assigned.
+ * This routine sets up the irq for an ide interface, and creates a new hwgroup
+ * for the irq/channel if none was previously assigned.
  *
- * Much of the code is for correctly detecting/handling irq sharing
- * and irq serialization situations.  This is somewhat complex because
- * it handles static as well as dynamic (PCMCIA) IDE interfaces.
+ * Much of the code is for correctly detecting/handling irq sharing and irq
+ * serialization situations.  This is somewhat complex because it handles
+ * static as well as dynamic (PCMCIA) IDE interfaces.
  *
- * The SA_INTERRUPT in sa_flags means ide_intr() is always entered with
- * interrupts completely disabled.  This can be bad for interrupt latency,
- * but anything else has led to problems on some machines.  We re-enable
- * interrupts as much as we can safely do in most places.
+ * The SA_INTERRUPT in sa_flags means ata_irq_request() is always entered with
+ * interrupts completely disabled.  This can be bad for interrupt latency, but
+ * anything else has led to problems on some machines.  We re-enable interrupts
+ * as much as we can safely do in most places.
  */
-static int init_irq(struct ata_channel *hwif)
+static int init_irq(struct ata_channel *ch)
 {
 	unsigned long flags;
-	unsigned int index;
-	ide_hwgroup_t *hwgroup, *new_hwgroup;
+	int i;
+	ide_hwgroup_t *hwgroup;
+	ide_hwgroup_t *new_hwgroup;
 	struct ata_channel *match = NULL;
 
 
@@ -681,26 +644,27 @@
 	new_hwgroup = kmalloc(sizeof(ide_hwgroup_t),GFP_KERNEL);
 
 	spin_lock_irqsave(&ide_lock, flags);
-	hwif->hwgroup = NULL;
+	ch->hwgroup = NULL;
 
 #if MAX_HWIFS > 1
 	/*
-	 * Group up with any other hwifs that share our irq(s).
+	 * Group up with any other channels that share our irq(s).
 	 */
-	for (index = 0; index < MAX_HWIFS; index++) {
-		struct ata_channel *h = &ide_hwifs[index];
-		if (h->hwgroup) {  /* scan only initialized hwif's */
-			if (hwif->irq == h->irq) {
-				hwif->sharing_irq = h->sharing_irq = 1;
-				if (hwif->chipset != ide_pci || h->chipset != ide_pci)
-					save_match(hwif, h, &match);
+	for (i = 0; i < MAX_HWIFS; ++i) {
+		struct ata_channel *h = &ide_hwifs[i];
+
+		if (h->hwgroup) {  /* scan only initialized channels */
+			if (ch->irq == h->irq) {
+				ch->sharing_irq = h->sharing_irq = 1;
+				if (ch->chipset != ide_pci || h->chipset != ide_pci)
+					save_match(ch, h, &match);
 
 				/* FIXME: This is still confusing. What would
 				 * happen if we match-ed two times?
 				 */
 
-				if (hwif->serialized || h->serialized)
-					save_match(hwif, h, &match);
+				if (ch->serialized || h->serialized)
+					save_match(ch, h, &match);
 			}
 		}
 	}
@@ -719,80 +683,78 @@
 			return 1;
 		}
 		memset(hwgroup, 0, sizeof(ide_hwgroup_t));
-		hwgroup->hwif     = hwif->next = hwif;
 		hwgroup->rq       = NULL;
 		hwgroup->handler  = NULL;
 		hwgroup->drive    = NULL;
 		hwgroup->flags	  = 0;
+
 		init_timer(&hwgroup->timer);
 		hwgroup->timer.function = &ide_timer_expiry;
 		hwgroup->timer.data = (unsigned long) hwgroup;
 	}
 
 	/*
-	 * Allocate the irq, if not already obtained for another hwif
+	 * Allocate the irq, if not already obtained for another channel
 	 */
-	if (!match || match->irq != hwif->irq) {
+	if (!match || match->irq != ch->irq) {
 #ifdef CONFIG_IDEPCI_SHARE_IRQ
-		int sa = IDE_CHIPSET_IS_PCI(hwif->chipset) ? SA_SHIRQ : SA_INTERRUPT;
+		int sa = IDE_CHIPSET_IS_PCI(ch->chipset) ? SA_SHIRQ : SA_INTERRUPT;
 #else
-		int sa = IDE_CHIPSET_IS_PCI(hwif->chipset) ? SA_INTERRUPT|SA_SHIRQ : SA_INTERRUPT;
+		int sa = IDE_CHIPSET_IS_PCI(ch->chipset) ? SA_INTERRUPT|SA_SHIRQ : SA_INTERRUPT;
 #endif
 
-		if (hwif->io_ports[IDE_CONTROL_OFFSET])
-			OUT_BYTE(0x08, hwif->io_ports[IDE_CONTROL_OFFSET]); /* clear nIEN */
+		if (ch->io_ports[IDE_CONTROL_OFFSET])
+			OUT_BYTE(0x08, ch->io_ports[IDE_CONTROL_OFFSET]); /* clear nIEN */
 
-		if (ide_request_irq(hwif->irq, &ide_intr, sa, hwif->name, hwgroup)) {
+		if (request_irq(ch->irq, &ata_irq_request, sa, ch->name, ch)) {
 			if (!match)
 				kfree(hwgroup);
 			spin_unlock_irqrestore(&ide_lock, flags);
+
 			return 1;
 		}
 	}
 
 	/*
-	 * Everything is okay, so link us into the hwgroup.
+	 * Everything is okay.
 	 */
-	hwif->hwgroup = hwgroup;
-	hwif->next = hwgroup->hwif->next;
-	hwgroup->hwif->next = hwif;
+	ch->hwgroup = hwgroup;
+
+	for (i = 0; i < MAX_DRIVES; ++i) {
+		struct ata_device *drive = &ch->drives[i];
 
-	for (index = 0; index < MAX_DRIVES; ++index) {
-		ide_drive_t *drive = &hwif->drives[index];
 		if (!drive->present)
 			continue;
+
 		if (!hwgroup->drive)
 			hwgroup->drive = drive;
+
 		drive->next = hwgroup->drive->next;
 		hwgroup->drive->next = drive;
-		ide_init_queue(drive);
-	}
-	if (!hwgroup->hwif) {
-		hwgroup->hwif = hwgroup->drive->channel;
-#ifdef DEBUG
-		printk("%s : Adding missed hwif to hwgroup!!\n", hwif->name);
-#endif
+
+		init_device_queue(drive);
 	}
 	spin_unlock_irqrestore(&ide_lock, flags);
 
 #if !defined(__mc68000__) && !defined(CONFIG_APUS) && !defined(__sparc__)
-	printk("%s at 0x%03x-0x%03x,0x%03x on irq %d", hwif->name,
-		hwif->io_ports[IDE_DATA_OFFSET],
-		hwif->io_ports[IDE_DATA_OFFSET]+7,
-		hwif->io_ports[IDE_CONTROL_OFFSET], hwif->irq);
+	printk("%s at 0x%03x-0x%03x,0x%03x on irq %d", ch->name,
+		ch->io_ports[IDE_DATA_OFFSET],
+		ch->io_ports[IDE_DATA_OFFSET]+7,
+		ch->io_ports[IDE_CONTROL_OFFSET], ch->irq);
 #elif defined(__sparc__)
-	printk("%s at 0x%03lx-0x%03lx,0x%03lx on irq %s", hwif->name,
-		hwif->io_ports[IDE_DATA_OFFSET],
-		hwif->io_ports[IDE_DATA_OFFSET]+7,
-		hwif->io_ports[IDE_CONTROL_OFFSET], __irq_itoa(hwif->irq));
+	printk("%s at 0x%03lx-0x%03lx,0x%03lx on irq %s", ch->name,
+		ch->io_ports[IDE_DATA_OFFSET],
+		ch->io_ports[IDE_DATA_OFFSET]+7,
+		ch->io_ports[IDE_CONTROL_OFFSET], __irq_itoa(ch->irq));
 #else
-	printk("%s at %p on irq 0x%08x", hwif->name,
-		hwif->io_ports[IDE_DATA_OFFSET], hwif->irq);
+	printk("%s at %p on irq 0x%08x", ch->name,
+		ch->io_ports[IDE_DATA_OFFSET], ch->irq);
 #endif /* __mc68000__ && CONFIG_APUS */
 	if (match)
 		printk(" (%sed with %s)",
-			hwif->sharing_irq ? "shar" : "serializ", match->name);
+			ch->sharing_irq ? "shar" : "serializ", match->name);
 	printk("\n");
+
 	return 0;
 }
 
@@ -887,15 +849,15 @@
 		printk("%s: CANNOT SHARE IRQ WITH OLD HARDDISK DRIVER (hd.c)\n", hwif->name);
 		return (hwif->present = 0);
 	}
-#endif /* CONFIG_BLK_DEV_HD */
-	
+#endif
+
 	hwif->present = 0; /* we set it back to 1 if all is ok below */
 
 	if (devfs_register_blkdev (hwif->major, hwif->name, ide_fops)) {
 		printk("%s: UNABLE TO GET MAJOR NUMBER %d\n", hwif->name, hwif->major);
 		return (hwif->present = 0);
 	}
-	
+
 	if (init_irq(hwif)) {
 		int i = hwif->irq;
 		/*
@@ -916,7 +878,7 @@
 		printk("%s: probed IRQ %d failed, using default.\n",
 			hwif->name, hwif->irq);
 	}
-	
+
 	init_gendisk(hwif);
 	blk_dev[hwif->major].data = hwif;
 	blk_dev[hwif->major].queue = ide_get_queue;
@@ -929,7 +891,7 @@
 {
 	unsigned int index;
 	int probe[MAX_HWIFS];
-	
+
 	memset(probe, 0, MAX_HWIFS * sizeof(int));
 	for (index = 0; index < MAX_HWIFS; ++index)
 		probe[index] = !ide_hwifs[index].present;
diff -Nru a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c
--- a/drivers/ide/ide-proc.c	Sun Apr 28 20:10:30 2002
+++ b/drivers/ide/ide-proc.c	Sun Apr 28 20:10:30 2002
@@ -160,8 +160,8 @@
 	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
 }
 
-static int proc_ide_read_channel
-	(char *page, char **start, off_t off, int count, int *eof, void *data)
+static int proc_ide_read_channel(char *page, char **start,
+		off_t off, int count, int *eof, void *data)
 {
 	struct ata_channel *hwif = data;
 	int		len;
diff -Nru a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c
--- a/drivers/ide/ide-tape.c	Sun Apr 28 20:10:32 2002
+++ b/drivers/ide/ide-tape.c	Sun Apr 28 20:10:32 2002
@@ -2614,9 +2614,9 @@
 }
 
 /*
- *	idetape_do_request is our request handling function.	
+ * This is our request handling function.
  */
-static ide_startstop_t idetape_do_request (ide_drive_t *drive, struct request *rq, unsigned long block)
+static ide_startstop_t idetape_do_request(struct ata_device *drive, struct request *rq, sector_t block)
 {
 	idetape_tape_t *tape = drive->driver_data;
 	idetape_pc_t *pc;
diff -Nru a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c
--- a/drivers/ide/ide-taskfile.c	Sun Apr 28 20:10:37 2002
+++ b/drivers/ide/ide-taskfile.c	Sun Apr 28 20:10:37 2002
@@ -308,8 +308,7 @@
 
 static ide_startstop_t pre_task_mulout_intr(ide_drive_t *drive, struct request *rq)
 {
-	struct ata_request *ar = rq->special;
-	struct ata_taskfile *args = &ar->ar_task;
+	struct ata_taskfile *args = rq->special;
 	ide_startstop_t startstop;
 
 	/*
@@ -464,35 +463,11 @@
 		if (args->prehandler != NULL)
 			return args->prehandler(drive, rq);
 	} else {
-		ide_dma_action_t dmaaction;
-		u8 command;
-
-		if (!drive->using_dma)
-			return ide_started;
-
-		command = args->taskfile.command;
-
-#ifdef CONFIG_BLK_DEV_IDE_TCQ
-		if (drive->using_tcq) {
-			if (command == WIN_READDMA_QUEUED
-			    || command == WIN_READDMA_QUEUED_EXT
-			    || command == WIN_WRITEDMA_QUEUED
-			    || command == WIN_READDMA_QUEUED_EXT)
-				return ide_start_tag(ide_dma_queued_start, drive, rq->special);
-		}
-#endif
-
-		if (command == WIN_WRITEDMA || command == WIN_WRITEDMA_EXT)
-			dmaaction = ide_dma_write;
-		else if (command == WIN_READDMA || command == WIN_READDMA_EXT)
-			dmaaction = ide_dma_read;
-		else
-			return ide_stopped;
-
-		if (!drive->channel->dmaproc(dmaaction, drive))
-			return ide_started;
-
-		return ide_stopped;
+		/* for dma commands we down set the handler */
+		if (drive->using_dma &&
+		!(drive->channel->dmaproc(((args->taskfile.command == WIN_WRITEDMA)
+					|| (args->taskfile.command == WIN_WRITEDMA_EXT))
+					? ide_dma_write : ide_dma_read, drive)));
 	}
 
 	return ide_started;
@@ -501,15 +476,15 @@
 /*
  * This is invoked on completion of a WIN_SETMULT cmd.
  */
-ide_startstop_t set_multmode_intr (ide_drive_t *drive)
+ide_startstop_t set_multmode_intr(struct ata_device *drive)
 {
-	byte stat;
+	u8 stat;
 
 	if (OK_STAT(stat=GET_STAT(),READY_STAT,BAD_STAT)) {
 		drive->mult_count = drive->mult_req;
 	} else {
 		drive->mult_req = drive->mult_count = 0;
-		drive->special.b.recalibrate = 1;
+		drive->special_cmd |= ATA_SPECIAL_RECALIBRATE;
 		ide_dump_status(drive, "set_multmode", stat);
 	}
 	return ide_stopped;
@@ -545,30 +520,12 @@
 }
 
 /*
- * 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 = IDE_CUR_AR(drive);
-	struct ata_taskfile *args = &ar->ar_task;
-	u8 stat = GET_STAT();
+	struct ata_taskfile *args = HWGROUP(drive)->rq->special;
+	byte stat		= GET_STAT();
 
 	ide__sti();	/* local CPU only */
 
@@ -628,8 +585,7 @@
 
 static ide_startstop_t pre_task_out_intr(ide_drive_t *drive, struct request *rq)
 {
-	struct ata_request *ar = rq->special;
-	struct ata_taskfile *args = &ar->ar_task;
+	struct ata_taskfile *args = rq->special;
 	ide_startstop_t startstop;
 
 	if (ide_wait_stat(&startstop, drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) {
@@ -909,7 +865,6 @@
 			return;
 
 		case WIN_NOP:
-			args->handler = task_no_data_quiet_intr;
 			args->command_type = IDE_DRIVE_TASK_NO_DATA;
 			return;
 
@@ -924,54 +879,30 @@
 	}
 }
 
-/*
- * This function is intended to be used prior to invoking ide_do_drive_cmd().
- */
-void init_taskfile_request(struct request *rq)
-{
-	memset(rq, 0, sizeof(*rq));
-	rq->flags = REQ_DRIVE_TASKFILE;
-}
-
 int ide_raw_taskfile(ide_drive_t *drive, struct ata_taskfile *args, byte *buf)
 {
 	struct request rq;
-	struct ata_request star;
-	int ret;
 
-	ata_ar_init(drive, &star);
-	init_taskfile_request(&rq);
+	memset(&rq, 0, sizeof(rq));
+	rq.flags = REQ_DRIVE_ACB;
 	rq.buffer = buf;
 
-	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 = &star;
-
-	ret = ide_do_drive_cmd(drive, &rq, ide_wait);
+	rq.special = args;
 
-	/*
-	 * copy back status etc
-	 */
-	memcpy(args, &star.ar_task, sizeof(*args));
-	return ret;
+	return ide_do_drive_cmd(drive, &rq, ide_wait);
 }
 
 /*
  * Implement generic ioctls invoked from userspace to imlpement specific
  * functionality.
  *
- * FIXME:
- *
- * 1. Rewrite hdparm to use the ide_task_ioctl function.
- *
- * 2. Publish it.
- *
- * 3. Kill this and HDIO_DRIVE_CMD alltogether.
+ * Unfortunately every single low level programm out there is using this
+ * interface.
  */
 
 int ide_cmd_ioctl(ide_drive_t *drive, unsigned long arg)
@@ -979,22 +910,19 @@
 	int err = 0;
 	u8 vals[4];
 	u8 *argbuf = vals;
-	byte xfer_rate = 0;
+	u8 xfer_rate = 0;
 	int argsize = 4;
 	struct ata_taskfile args;
 	struct request rq;
 
-	/*
-	 * First phase.
+	ide_init_drive_cmd(&rq);
+
+	/* Wait for drive ready.
 	 */
-	if (NULL == (void *) arg) {
-		struct request rq;
-		ide_init_drive_cmd(&rq);
+	if (!arg)
 		return ide_do_drive_cmd(drive, &rq, ide_wait);
-	}
 
-	/*
-	 * Second phase.
+	/* Second phase.
 	 */
 	if (copy_from_user(vals, (void *)arg, 4))
 		return -EFAULT;
@@ -1016,6 +944,8 @@
 		memset(argbuf + 4, 0, argsize - 4);
 	}
 
+	/* Always make sure the transfer reate has been setup.
+	 */
 	if (set_transfer(drive, &args)) {
 		xfer_rate = vals[1];
 		if (ide_ata66_check(drive, &args))
@@ -1024,7 +954,6 @@
 
 	/* Issue ATA command and wait for completion.
 	 */
-	ide_init_drive_cmd(&rq);
 	rq.buffer = argbuf;
 	err = ide_do_drive_cmd(drive, &rq, ide_wait);
 
@@ -1034,44 +963,22 @@
 			drive->channel->speedproc(drive, xfer_rate);
 		ide_driveid_update(drive);
 	}
+
 abort:
 	if (copy_to_user((void *)arg, argbuf, argsize))
 		err = -EFAULT;
+
 	if (argsize > 4)
 		kfree(argbuf);
 
 	return err;
 }
 
-int ide_task_ioctl(ide_drive_t *drive, unsigned long arg)
-{
-	int err = 0;
-	u8 args[7];
-	u8 *argbuf;
-	int argsize = 7;
-	struct request rq;
-
-	argbuf = args;
-
-	if (copy_from_user(args, (void *)arg, 7))
-		return -EFAULT;
-
-	ide_init_drive_cmd(&rq);
-	rq.flags = REQ_DRIVE_TASK;
-	rq.buffer = argbuf;
-	err = ide_do_drive_cmd(drive, &rq, ide_wait);
-	if (copy_to_user((void *)arg, argbuf, argsize))
-		err = -EFAULT;
-	return err;
-}
-
 EXPORT_SYMBOL(drive_is_ready);
-
 EXPORT_SYMBOL(ata_read);
 EXPORT_SYMBOL(ata_write);
 EXPORT_SYMBOL(atapi_read);
 EXPORT_SYMBOL(atapi_write);
-
 EXPORT_SYMBOL(ata_taskfile);
 EXPORT_SYMBOL(recal_intr);
 EXPORT_SYMBOL(set_geometry_intr);
@@ -1079,6 +986,4 @@
 EXPORT_SYMBOL(task_no_data_intr);
 EXPORT_SYMBOL(ide_raw_taskfile);
 EXPORT_SYMBOL(ide_cmd_type_parser);
-
 EXPORT_SYMBOL(ide_cmd_ioctl);
-EXPORT_SYMBOL(ide_task_ioctl);
diff -Nru a/drivers/ide/ide-tcq.c b/drivers/ide/ide-tcq.c
--- a/drivers/ide/ide-tcq.c	Sun Apr 28 20:10:38 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,689 +0,0 @@
-/*
- * Copyright (C) 2001, 2002 Jens Axboe <axboe@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * 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
- */
-
-/*
- * Support for the DMA queued protocol, which enables ATA disk drives to
- * use tagged command queueing.
- */
-#include <linux/config.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/ide.h>
-
-#include <asm/delay.h>
-
-/*
- * warning: it will be _very_ verbose if defined
- */
-#undef IDE_TCQ_DEBUG
-
-#ifdef IDE_TCQ_DEBUG
-#define TCQ_PRINTK printk
-#else
-#define TCQ_PRINTK(x...)
-#endif
-
-/*
- * use nIEN or not
- */
-#undef IDE_TCQ_NIEN
-
-/*
- * we are leaving the SERVICE interrupt alone, IBM drives have it
- * on per default and it can't be turned off. Doesn't matter, this
- * is the sane config.
- */
-#undef IDE_TCQ_FIDDLE_SI
-
-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 set)
-{
-#ifdef IDE_TCQ_NIEN
-	if (IDE_CONTROL_REG) {
-		int mask = set ? 0x02 : 0x00;
-
-		OUT_BYTE(drive->ctl | mask, IDE_CONTROL_REG);
-	}
-#endif
-}
-
-/*
- * if we encounter _any_ error doing I/O to one of the tags, we must
- * invalidate the pending queue. clear the software busy queue and requeue
- * on the request queue for restart. issue a WIN_NOP to clear hardware queue
- */
-static void ide_tcq_invalidate_queue(ide_drive_t *drive)
-{
-	request_queue_t *q = &drive->queue;
-	unsigned long flags;
-	struct ata_request *ar;
-	int i;
-
-	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.
-	 */
-	for (i = drive->queue_depth - 1; i; i--) {
-		ar = drive->tcq->ar[i];
-		if (!ar)
-			continue;
-
-		ar->ar_rq->special = NULL;
-		ar->ar_rq->flags &= ~REQ_STARTED;
-		_elv_add_request(q, ar->ar_rq, 0, 0);
-		ata_ar_put(drive, ar);
-	}
-
-	drive->tcq->queued = 0;
-	drive->using_tcq = 0;
-	drive->queue_depth = 1;
-	clear_bit(IDE_BUSY, &HWGROUP(drive)->flags);
-	clear_bit(IDE_DMA, &HWGROUP(drive)->flags);
-	HWGROUP(drive)->handler = NULL;
-
-	/*
-	 * do some internal stuff -- we really need this command to be
-	 * executed before any new commands are started. issue a NOP
-	 * to clear internal queue on drive
-	 */
-	ar = ata_ar_get(drive);
-
-	memset(&ar->ar_task, 0, sizeof(ar->ar_task));
-	AR_TASK_CMD(ar) = WIN_NOP;
-	ide_cmd_type_parser(&ar->ar_task);
-	ar->ar_rq = &HWGROUP(drive)->wrq;
-	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;
-	_elv_add_request(q, ar->ar_rq, 0, 0);
-
-	/*
-	 * make sure that nIEN is cleared
-	 */
-	drive_ctl_nien(drive, 0);
-
-	/*
-	 * start doing stuff again
-	 */
-	q->request_fn(q);
-	spin_unlock_irqrestore(&ide_lock, flags);
-	printk("ide_tcq_invalidate_queue: done\n");
-}
-
-void ide_tcq_intr_timeout(unsigned long data)
-{
-	ide_hwgroup_t *hwgroup = (ide_hwgroup_t *) data;
-	unsigned long flags;
-	ide_drive_t *drive;
-
-	printk("ide_tcq_intr_timeout: timeout waiting for interrupt...\n");
-
-	spin_lock_irqsave(&ide_lock, 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");
-	if ((drive = hwgroup->drive) == NULL)
-		printk("ide_tcq_intr_timeout: missing drive!\n");
-
-	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);
-}
-
-void ide_tcq_set_intr(ide_hwgroup_t *hwgroup, ide_handler_t *handler)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&ide_lock, flags);
-
-	/*
-	 * always just bump the timer for now, the timeout handling will
-	 * have to be changed to be per-command
-	 */
-	hwgroup->timer.function = ide_tcq_intr_timeout;
-	hwgroup->timer.data = (unsigned long) hwgroup;
-	mod_timer(&hwgroup->timer, jiffies + 5 * HZ);
-
-	hwgroup->handler = handler;
-	spin_unlock_irqrestore(&ide_lock, flags);
-}
-
-/*
- * wait 400ns, then poll for busy_mask to clear from alt status
- */
-#define IDE_TCQ_WAIT	(10000)
-int ide_tcq_wait_altstat(ide_drive_t *drive, byte *stat, byte busy_mask)
-{
-	int i = 0;
-
-	udelay(1);
-
-	while ((*stat = GET_ALTSTAT()) & busy_mask) {
-		udelay(10);
-
-		if (unlikely(i++ > IDE_TCQ_WAIT))
-			return 1;
-	}
-
-	return 0;
-}
-
-/*
- * issue SERVICE command to drive -- drive must have been selected first,
- * and it must have reported a need for service (status has SERVICE_STAT set)
- *
- * Also, nIEN must be set as not to need protection against ide_dmaq_intr
- */
-ide_startstop_t ide_service(ide_drive_t *drive)
-{
-	struct ata_request *ar;
-	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))
-		return ide_stopped;
-
-	/*
-	 * need to select the right drive first...
-	 */
-	if (drive != HWGROUP(drive)->drive) {
-		SELECT_DRIVE(drive->channel, drive);
-		udelay(10);
-	}
-
-	drive_ctl_nien(drive, 1);
-
-	/*
-	 * send SERVICE, wait 400ns, wait for BUSY_STAT to clear
-	 */
-	OUT_BYTE(WIN_QUEUED_SERVICE, IDE_COMMAND_REG);
-
-	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;
-	}
-
-	drive_ctl_nien(drive, 0);
-
-	/*
-	 * FIXME, invalidate queue
-	 */
-	if (stat & ERR_STAT) {
-		ide_dump_status(drive, "ide_service", stat);
-		ide_tcq_invalidate_queue(drive);
-		return ide_stopped;
-	}
-
-	/*
-	 * should not happen, a buggy device could introduce loop
-	 */
-	if ((feat = GET_FEAT()) & NSEC_REL) {
-		printk("%s: release in service\n", drive->name);
-		IDE_SET_CUR_TAG(drive, IDE_INACTIVE_TAG);
-		return ide_stopped;
-	}
-
-	tag = feat >> 3;
-	IDE_SET_CUR_TAG(drive, tag);
-
-	TCQ_PRINTK("ide_service: stat %x, feat %x\n", stat, feat);
-
-	if ((ar = IDE_CUR_TAG(drive)) == NULL) {
-		printk("ide_service: missing request for tag %d\n", tag);
-		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);
-		ret = drive->channel->dmaproc(ide_dma_read_queued, drive);
-	} else {
-		TCQ_PRINTK("ide_service: starting WRITE %x\n", stat);
-		ret = drive->channel->dmaproc(ide_dma_write_queued, drive);
-	}
-
-	/*
-	 * dmaproc set intr handler
-	 */
-	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;
-
-	if ((stat = GET_STAT()) & SERVICE_STAT)
-		return ide_service(drive);
-
-	/*
-	 * we have pending commands, wait for interrupt
-	 */
-	ide_tcq_set_intr(HWGROUP(drive), ide_dmaq_intr);
-	return ide_started;
-}
-
-ide_startstop_t ide_dmaq_complete(ide_drive_t *drive, byte stat)
-{
-	struct ata_request *ar = IDE_CUR_TAG(drive);
-	byte dma_stat;
-
-	/*
-	 * transfer was in progress, stop DMA engine
-	 */
-	dma_stat = drive->channel->dmaproc(ide_dma_end, drive);
-
-	/*
-	 * must be end of I/O, check status and complete as necessary
-	 */
-	if (unlikely(!OK_STAT(stat, READY_STAT, drive->bad_wstat | DRQ_STAT))) {
-		printk("ide_dmaq_intr: %s: error status %x\n", drive->name, stat);
-		ide_dump_status(drive, "ide_dmaq_intr", stat);
-		ide_tcq_invalidate_queue(drive);
-		return ide_stopped;
-	}
-
-	if (dma_stat)
-		printk("%s: bad DMA status (dma_stat=%x)\n", drive->name, dma_stat);
-
-	TCQ_PRINTK("ide_dmaq_intr: ending %p, tag %d\n", ar, ar->ar_tag);
-	ide_end_queued_request(drive, !dma_stat, ar->ar_rq);
-
-	/*
-	 * 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);
-}
-
-/*
- * intr handler for queued dma operations. this can be entered for two
- * reasons:
- *
- * 1) device has completed dma transfer
- * 2) service request to start a command
- *
- * if the drive has an active tag, we first complete that request before
- * processing any pending SERVICE.
- */
-ide_startstop_t ide_dmaq_intr(ide_drive_t *drive)
-{
-	byte stat = GET_STAT();
-
-	TCQ_PRINTK("ide_dmaq_intr: stat=%x, tag %d\n", stat, drive->tcq->active_tag);
-
-	/*
-	 * if a command completion interrupt is pending, do that first and
-	 * check service afterwards
-	 */
-	if (drive->tcq->active_tag != IDE_INACTIVE_TAG)
-		return ide_dmaq_complete(drive, stat);
-
-	/*
-	 * service interrupt
-	 */
-	if (stat & SERVICE_STAT) {
-		TCQ_PRINTK("ide_dmaq_intr: SERV (stat=%x)\n", stat);
-		return ide_service(drive);
-	}
-
-	printk("ide_dmaq_intr: stat=%x, not expected\n", stat);
-	return ide_check_service(drive);
-}
-
-/*
- * 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;
-
-	/*
-	 * bit 14 and 1 must be set in word 83 of the device id to indicate
-	 * support for dma queued protocol, and bit 15 must be cleared
-	 */
-	if ((drive->id->command_set_2 & tcq_bits) ^ tcq_mask)
-		return -EIO;
-
-	memset(&args, 0, sizeof(args));
-	args.taskfile.feature = SETFEATURES_EN_WCACHE;
-	args.taskfile.command = WIN_SETFEATURES;
-	ide_cmd_type_parser(&args);
-
-	if (ide_raw_taskfile(drive, &args, NULL)) {
-		printk("%s: failed to enable write cache\n", drive->name);
-		return 1;
-	}
-
-	/*
-	 * disable RELease interrupt, it's quicker to poll this after
-	 * having sent the command opcode
-	 */
-	memset(&args, 0, sizeof(args));
-	args.taskfile.feature = SETFEATURES_DIS_RI;
-	args.taskfile.command = WIN_SETFEATURES;
-	ide_cmd_type_parser(&args);
-
-	if (ide_raw_taskfile(drive, &args, NULL)) {
-		printk("%s: disabling release interrupt fail\n", drive->name);
-		return 1;
-	}
-
-#ifdef IDE_TCQ_FIDDLE_SI
-	/*
-	 * enable SERVICE interrupt
-	 */
-	memset(&args, 0, sizeof(args));
-	args.taskfile.feature = SETFEATURES_EN_SI;
-	args.taskfile.command = WIN_SETFEATURES;
-	ide_cmd_type_parser(&args);
-
-	if (ide_raw_taskfile(drive, &args, NULL)) {
-		printk("%s: enabling service interrupt fail\n", drive->name);
-		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;
-}
-
-/*
- * for now assume that command list is always as big as we need and don't
- * attempt to shrink it on tcq disable
- */
-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) {
-		if (drive->using_tcq)
-			printk("%s: TCQ disabled\n", drive->name);
-		drive->using_tcq = 0;
-		return 0;
-	}
-
-	if (ide_tcq_configure(drive)) {
-		drive->using_tcq = 0;
-		return 1;
-	}
-
-	/*
-	 * 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);
-
-	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;
-}
-
-int ide_tcq_dmaproc(ide_dma_action_t func, ide_drive_t *drive)
-{
-	struct ata_channel *hwif = drive->channel;
-	unsigned int reading = 0, enable_tcq = 1;
-	struct ata_request *ar;
-	byte stat, feat;
-
-	switch (func) {
-		/*
-		 * invoked from a SERVICE interrupt, command etc already known.
-		 * just need to start the dma engine for this tag
-		 */
-		case ide_dma_read_queued:
-			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 == IDE_INACTIVE_TAG);
-
-			if (!test_bit(IDE_BUSY, &HWGROUP(drive)->flags))
-				printk("queued_rw: IDE_BUSY not set\n");
-
-			if (ide_tcq_wait_dataphase(drive))
-				return ide_stopped;
-
-			if (ide_start_dma(hwif, drive, func))
-				return 1;
-
-			ide_tcq_set_intr(HWGROUP(drive), ide_dmaq_intr);
-			return hwif->dmaproc(ide_dma_begin, drive);
-
-			/*
-			 * start a queued command from scratch
-			 */
-		case ide_dma_queued_start:
-			BUG_ON(drive->tcq->active_tag == IDE_INACTIVE_TAG);
-			ar = IDE_CUR_TAG(drive);
-
-			/*
-			 * set nIEN, tag start operation will enable again when
-			 * it is safe
-			 */
-			drive_ctl_nien(drive, 1);
-
-			OUT_BYTE(AR_TASK_CMD(ar), IDE_COMMAND_REG);
-
-			if (ide_tcq_wait_altstat(drive, &stat, BUSY_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) {
-				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");
-
-				if ((stat = GET_STAT()) & SERVICE_STAT)
-					return ide_service(drive);
-
-				return ide_released;
-			}
-
-			drive->tcq->immed_comp++;
-
-			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
-			 */
-			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;
-	}
-
-	return 1;
-}
-
-int ide_build_sglist (struct ata_channel *hwif, struct request *rq);
-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
-	 */
-	if (!ide_build_sglist(drive->channel, ar->ar_rq))
-		return ide_stopped;
-
-	IDE_SET_CUR_TAG(drive, ar->ar_tag);
-	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	Sun Apr 28 20:10:36 2002
+++ b/drivers/ide/ide.c	Sun Apr 28 20:10:36 2002
@@ -215,7 +215,7 @@
  * This is used by the Atari code to obtain access to the IDE interrupt,
  * which is shared between several drivers.
  */
-static int	ide_intr_lock;
+static int irq_lock;
 #endif
 
 int noautodma = 0;
@@ -283,6 +283,7 @@
 	hwif->major = ide_major[index];
 	sprintf(hwif->name, "ide%d", index);
 	hwif->bus_state = BUSSTATE_ON;
+
 	for (unit = 0; unit < MAX_DRIVES; ++unit) {
 		ide_drive_t *drive = &hwif->drives[unit];
 
@@ -292,10 +293,10 @@
 		drive->ctl			= 0x08;
 		drive->ready_stat		= READY_STAT;
 		drive->bad_wstat		= BAD_W_STAT;
-		drive->special.b.recalibrate	= 1;
-		drive->special.b.set_geometry	= 1;
+		drive->special_cmd = (ATA_SPECIAL_RECALIBRATE | ATA_SPECIAL_GEOMETRY);
 		sprintf(drive->name, "hd%c", 'a' + (index * MAX_DRIVES) + unit);
 		drive->max_failures		= IDE_DEFAULT_MAX_FAILURES;
+
 		init_waitqueue_head(&drive->wqueue);
 	}
 }
@@ -368,41 +369,7 @@
 	return 0;	/* no, it is not a flash memory card */
 }
 
-void ide_end_queued_request(ide_drive_t *drive, int uptodate, struct request *rq)
-{
-	unsigned long flags;
-
-	BUG_ON(!(rq->flags & REQ_STARTED));
-	BUG_ON(!rq->special);
-
-	if (!end_that_request_first(rq, uptodate, rq->hard_nr_sectors)) {
-		struct ata_request *ar = rq->special;
-
-		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);
-	}
-}
-
-int __ide_end_request(ide_drive_t *drive, int uptodate, int nr_secs)
+int __ide_end_request(struct ata_device *drive, int uptodate, int nr_secs)
 {
 	struct request *rq;
 	unsigned long flags;
@@ -421,26 +388,19 @@
 		nr_secs = rq->hard_cur_sectors;
 
 	/*
-	 * decide whether to reenable DMA -- 3 is a random magic for now,
-	 * if we DMA timeout more than 3 times, just stay in PIO
+	 * Decide whether to reenable DMA -- 3 is a random magic for now,
+	 * if we DMA timeout more than 3 times, just stay in PIO.
 	 */
+
 	if (drive->state == DMA_PIO_RETRY && drive->retry_pio <= 3) {
 		drive->state = 0;
-		HWGROUP(drive)->hwif->dmaproc(ide_dma_on, drive);
+		drive->channel->dmaproc(ide_dma_on, drive);
 	}
 
 	if (!end_that_request_first(rq, uptodate, nr_secs)) {
-		struct ata_request *ar = rq->special;
-
 		add_blkdev_randomness(major(rq->rq_dev));
-		/*
-		 * request with ATA_AR_QUEUED set have already been
-		 * dequeued, but doing it twice is ok
-		 */
 		blkdev_dequeue_request(rq);
 		HWGROUP(drive)->rq = NULL;
-		if (ar)
-			ata_ar_put(drive, ar);
 		end_that_request_last(rq);
 		ret = 0;
 	}
@@ -497,7 +457,7 @@
  * The capacity of a drive according to its current geometry/LBA settings in
  * sectors.
  */
-unsigned long ata_capacity(ide_drive_t *drive)
+sector_t ata_capacity(struct ata_device *drive)
 {
 	if (!drive->present || !drive->driver)
 		return 0;
@@ -514,31 +474,31 @@
 
 /*
  * This is used to issue WIN_SPECIFY, WIN_RESTORE, and WIN_SETMULT commands to
- * a drive.  It used to do much more, but has been scaled back.
+ * a drive.
  */
-static ide_startstop_t ata_special (ide_drive_t *drive)
+static ide_startstop_t ata_special(struct ata_device *drive)
 {
-	special_t *s = &drive->special;
+	unsigned char special_cmd = drive->special_cmd;
 
 #ifdef DEBUG
-	printk("%s: ata_special: 0x%02x\n", drive->name, s->all);
+	printk("%s: ata_special: 0x%02x\n", drive->name, special_cmd);
 #endif
-	if (s->b.set_tune) {
-		s->b.set_tune = 0;
+	if (special_cmd & ATA_SPECIAL_TUNE) {
+		drive->special_cmd &= ~ATA_SPECIAL_TUNE;
 		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);
 		else {
-			drive->special.all = 0;
+			drive->special_cmd = 0;
 			drive->mult_req = 0;
 
 			return ide_stopped;
 		}
-	} else if (s->all) {
-		printk("%s: bad special flag: 0x%02x\n", drive->name, s->all);
-		s->all = 0;
+	} else if (special_cmd) {
+		printk("%s: bad special flag: 0x%02x\n", drive->name, special_cmd);
+		drive->special_cmd = 0;
 	}
 
 	return ide_stopped;
@@ -609,44 +569,45 @@
 {
 	ide_hwgroup_t *hwgroup = HWGROUP(drive);
 	struct ata_channel *hwif = drive->channel;
-	byte tmp;
+	u8 stat;
 
-	if (!OK_STAT(tmp=GET_STAT(), 0, BUSY_STAT)) {
+	if (!OK_STAT(stat=GET_STAT(), 0, BUSY_STAT)) {
 		if (time_before(jiffies, hwgroup->poll_timeout)) {
 			BUG_ON(HWGROUP(drive)->handler);
 			ide_set_handler (drive, &reset_pollfunc, HZ/20, NULL);
 			return ide_started;	/* continue polling */
 		}
-		printk("%s: reset timed-out, status=0x%02x\n", hwif->name, tmp);
+		printk("%s: reset timed-out, status=0x%02x\n", hwif->name, stat);
 		drive->failures++;
 	} else  {
 		printk("%s: reset: ", hwif->name);
-		if ((tmp = GET_ERR()) == 1) {
+		if ((stat = GET_ERR()) == 1) {
 			printk("success\n");
 			drive->failures = 0;
 		} else {
-			drive->failures++;
+			char *msg;
+
 #if FANCY_STATUS_DUMPS
-			printk("master: ");
-			switch (tmp & 0x7f) {
-				case 1: printk("passed");
-					break;
-				case 2: printk("formatter device error");
-					break;
-				case 3: printk("sector buffer error");
-					break;
-				case 4: printk("ECC circuitry error");
-					break;
-				case 5: printk("controlling MPU error");
-					break;
-				default:printk("error (0x%02x?)", tmp);
-			}
-			if (tmp & 0x80)
-				printk("; slave: failed");
-			printk("\n");
-#else
-			printk("failed\n");
+			u8 val;
+			static const char *messages[5] = {
+				" passed",
+				" formatter device",
+				" sector buffer",
+				" ECC circuitry",
+				" controlling MPU error"
+			};
+
+			printk("master:");
+			val = stat & 0x7f;
+			if (val >= 1 && val <= 5)
+				msg = messages[val -1];
+			else
+				msg = "";
+			if (stat & 0x80)
+				printk("; slave:");
 #endif
+			printk("%s error [%02x]\n", msg, stat);
+			drive->failures++;
 		}
 	}
 	hwgroup->poll_timeout = 0;	/* done polling */
@@ -654,20 +615,21 @@
 }
 
 /*
- * do_reset1() attempts to recover a confused drive by resetting it.
- * Unfortunately, resetting a disk drive actually resets all devices on
- * the same interface, so it can really be thought of as resetting the
- * interface rather than resetting the drive.
+ * Attempt to recover a confused drive by resetting it.  Unfortunately,
+ * resetting a disk drive actually resets all devices on the same interface, so
+ * it can really be thought of as resetting the interface rather than resetting
+ * the drive.
  *
  * ATAPI devices have their own reset mechanism which allows them to be
  * individually reset without clobbering other devices on the same interface.
  *
- * Unfortunately, the IDE interface does not generate an interrupt to let
- * us know when the reset operation has finished, so we must poll for this.
- * Equally poor, though, is the fact that this may a very long time to complete,
- * (up to 30 seconds worst case).  So, instead of busy-waiting here for it,
- * we set a timer to poll at 50ms intervals.
+ * Unfortunately, the IDE interface does not generate an interrupt to let us
+ * know when the reset operation has finished, so we must poll for this.
+ * Equally poor, though, is the fact that this may a very long time to
+ * complete, (up to 30 seconds worst case).  So, instead of busy-waiting here
+ * for it, we set a timer to poll at 50ms intervals.
  */
+
 static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi)
 {
 	unsigned int unit;
@@ -734,6 +696,7 @@
 #endif
 
 	__restore_flags (flags);	/* local CPU only */
+
 	return ide_started;
 }
 
@@ -747,7 +710,7 @@
 /*
  * Clean up after success/failure of an explicit drive cmd
  */
-void ide_end_drive_cmd(ide_drive_t *drive, byte stat, byte err)
+void ide_end_drive_cmd(struct ata_device *drive, byte stat, byte err)
 {
 	unsigned long flags;
 	struct request *rq;
@@ -756,31 +719,17 @@
 	rq = HWGROUP(drive)->rq;
 
 	if (rq->flags & REQ_DRIVE_CMD) {
-		byte *args = (byte *) rq->buffer;
-		rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT);
+		u8 *args = rq->buffer;
+		rq->errors = !OK_STAT(stat, READY_STAT, BAD_STAT);
 		if (args) {
 			args[0] = stat;
 			args[1] = err;
 			args[2] = IN_BYTE(IDE_NSECTOR_REG);
 		}
-	} else if (rq->flags & REQ_DRIVE_TASK) {
-		byte *args = (byte *) rq->buffer;
-		rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT);
-		if (args) {
-			args[0] = stat;
-			args[1] = err;
-			args[2] = IN_BYTE(IDE_NSECTOR_REG);
-			args[3] = IN_BYTE(IDE_SECTOR_REG);
-			args[4] = IN_BYTE(IDE_LCYL_REG);
-			args[5] = IN_BYTE(IDE_HCYL_REG);
-			args[6] = IN_BYTE(IDE_SELECT_REG);
-		}
-	} else if (rq->flags & REQ_DRIVE_TASKFILE) {
-		struct ata_request *ar = rq->special;
-		struct ata_taskfile *args = &ar->ar_task;
+	} else if (rq->flags & REQ_DRIVE_ACB) {
+		struct ata_taskfile *args = rq->special;
 
 		rq->errors = !OK_STAT(stat, READY_STAT, BAD_STAT);
-
 		if (args) {
 			args->taskfile.feature = err;
 			args->taskfile.sector_count = IN_BYTE(IDE_NSECTOR_REG);
@@ -803,7 +752,6 @@
 				args->hobfile.high_cylinder = IN_BYTE(IDE_HCYL_REG);
 			}
 		}
-		ata_ar_put(drive, ar);
 	}
 
 	blkdev_dequeue_request(rq);
@@ -823,23 +771,33 @@
 
 	__save_flags (flags);	/* local CPU only */
 	ide__sti();		/* local CPU only */
-	printk("%s: %s: status=0x%02x", drive->name, msg, stat);
-#if FANCY_STATUS_DUMPS
+#if !(FANCY_STATUS_DUMPS)
+	printk("%s: %s: status=0x%02x\n", drive->name, msg, stat);
+#else
 	printk(" { ");
-	if (stat & BUSY_STAT)
-		printk("Busy ");
-	else {
-		if (stat & READY_STAT)	printk("DriveReady ");
-		if (stat & WRERR_STAT)	printk("DeviceFault ");
-		if (stat & SEEK_STAT)	printk("SeekComplete ");
-		if (stat & DRQ_STAT)	printk("DataRequest ");
-		if (stat & ECC_STAT)	printk("CorrectedError ");
-		if (stat & INDEX_STAT)	printk("Index ");
-		if (stat & ERR_STAT)	printk("Error ");
+	{
+		char *msg = "";
+		if (stat & BUSY_STAT)
+			msg = "Busy";
+		else {
+			if (stat & READY_STAT)
+				msg = "DriveReady";
+			if (stat & WRERR_STAT)
+				msg = "DeviceFault";
+			if (stat & SEEK_STAT)
+				msg = "SeekComplete";
+			if (stat & DRQ_STAT)
+				msg = "DataRequest";
+			if (stat & ECC_STAT)
+				msg = "CorrectedError";
+			if (stat & INDEX_STAT)
+				msg = "Index";
+			if (stat & ERR_STAT)
+				msg = "Error";
+		}
 	}
-	printk("}");
-#endif	/* FANCY_STATUS_DUMPS */
-	printk("\n");
+	printk("%s }\n", msg);
+#endif
 	if ((stat & (BUSY_STAT|ERR_STAT)) == ERR_STAT) {
 		err = GET_ERR();
 		printk("%s: %s: error=0x%02x", drive->name, msg, err);
@@ -885,7 +843,7 @@
 					printk(", sector=%ld", HWGROUP(drive)->rq->sector);
 			}
 		}
-#endif	/* FANCY_STATUS_DUMPS */
+#endif
 		printk("\n");
 	}
 	__restore_flags (flags);	/* local CPU only */
@@ -920,11 +878,6 @@
 	struct request *rq;
 	byte err;
 
-	/*
-	 * FIXME: remember to invalidate tcq queue when drive->using_tcq
-	 * and atomic_read(&drive->tcq->queued) /jens
-	 */
-
 	err = ide_dump_status(drive, msg, stat);
 	if (drive == NULL || (rq = HWGROUP(drive)->rq) == NULL)
 		return ide_stopped;
@@ -958,7 +911,6 @@
 		OUT_BYTE(WIN_IDLEIMMEDIATE,IDE_COMMAND_REG);	/* force an abort */
 
 	if (rq->errors >= ERROR_MAX) {
-		/* ATA-PATTERN */
 		if (ata_ops(drive) && ata_ops(drive)->end_request)
 			ata_ops(drive)->end_request(drive, 0);
 		else
@@ -969,7 +921,7 @@
 			return do_reset1(drive, 0);
 		}
 		if ((rq->errors & ERROR_RECAL) == ERROR_RECAL)
-			drive->special.b.recalibrate = 1;
+			drive->special_cmd |= ATA_SPECIAL_RECALIBRATE;
 		++rq->errors;
 	}
 	return ide_stopped;
@@ -1071,47 +1023,55 @@
  */
 static ide_startstop_t start_request(ide_drive_t *drive, struct request *rq)
 {
-	ide_startstop_t startstop;
-	unsigned long block;
-	unsigned int minor = minor(rq->rq_dev), unit = minor >> PARTN_BITS;
-	struct ata_channel *hwif = drive->channel;
+	sector_t block;
+	unsigned int minor = minor(rq->rq_dev);
+	unsigned int unit = minor >> PARTN_BITS;
+	struct ata_channel *ch = drive->channel;
 
 	BUG_ON(!(rq->flags & REQ_STARTED));
 
 #ifdef DEBUG
-	printk("%s: start_request: current=0x%08lx\n", hwif->name, (unsigned long) rq);
+	printk("%s: start_request: current=0x%08lx\n", ch->name, (unsigned long) rq);
 #endif
 
 	/* bail early if we've exceeded max_failures */
-	if (drive->max_failures && (drive->failures > drive->max_failures)) {
+	if (drive->max_failures && (drive->failures > drive->max_failures))
 		goto kill_rq;
-	}
 
 	if (unit >= MAX_DRIVES) {
-		printk("%s: bad device number: %s\n", hwif->name, kdevname(rq->rq_dev));
+		printk(KERN_ERR "%s: bad device number: %s\n", ch->name, kdevname(rq->rq_dev));
 		goto kill_rq;
 	}
+
 	block    = rq->sector;
 
-	/* Strange disk manager remap */
+	/* Strange disk manager remap.
+	 */
 	if ((rq->flags & REQ_CMD) &&
 	    (drive->type == ATA_DISK || drive->type == ATA_FLOPPY)) {
 		block += drive->sect0;
 	}
-	/* Yecch - this will shift the entire interval,
-	   possibly killing some innocent following sector */
+
+	/* Yecch - this will shift the entire interval, possibly killing some
+	 * innocent following sector.
+	 */
 	if (block == 0 && drive->remap_0_to_1 == 1)
 		block = 1;  /* redirect MBR access to EZ-Drive partn table */
 
 #if (DISK_RECOVERY_TIME > 0)
-	while ((read_timer() - hwif->last_time) < DISK_RECOVERY_TIME);
+	while ((read_timer() - ch->last_time) < DISK_RECOVERY_TIME);
 #endif
 
-	SELECT_DRIVE(hwif, drive);
-	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);
-		return startstop;
+	{
+		ide_startstop_t res;
+
+		SELECT_DRIVE(ch, drive);
+		if (ide_wait_stat(&res, drive, drive->ready_stat,
+					BUSY_STAT|DRQ_STAT, WAIT_READY)) {
+			printk(KERN_WARNING "%s: drive not ready for command\n", drive->name);
+
+			return res;
+		}
 	}
 
 	/* FIXME: We can see nicely here that all commands should be submitted
@@ -1119,113 +1079,93 @@
 	 * go as soon as possible!
 	 */
 
-	if (!drive->special.all) {
-		if (rq->flags & (REQ_DRIVE_CMD | REQ_DRIVE_TASK | REQ_DRIVE_TASKFILE)) {
-			/* This issues a special drive command, usually
-			 * initiated by ioctl() from the external hdparm
-			 * program.
-			 */
-
-			if (rq->flags & REQ_DRIVE_TASKFILE) {
-				struct ata_request *ar = rq->special;
-				struct ata_taskfile *args;
+	if (drive->special_cmd)
+		return ata_special(drive);
 
-				if (!ar)
-					goto args_error;
+	/* This issues a special drive command, usually initiated by ioctl()
+	 * from the external hdparm program.
+	 */
+	if (rq->flags & REQ_DRIVE_ACB) {
+		struct ata_taskfile *args = rq->special;
 
-				args = &ar->ar_task;
+		if (!(args))
+			goto args_error;
 
-				ata_taskfile(drive, args, NULL);
+		ata_taskfile(drive, args, NULL);
 
-				if (((args->command_type == IDE_DRIVE_TASK_RAW_WRITE) ||
+		if (((args->command_type == IDE_DRIVE_TASK_RAW_WRITE) ||
 					(args->command_type == IDE_DRIVE_TASK_OUT)) &&
-						args->prehandler && args->handler)
-					return args->prehandler(drive, rq);
-				return ide_started;
-
-			} else if (rq->flags & REQ_DRIVE_TASK) {
-				byte *args = rq->buffer;
-				byte sel;
+				args->prehandler && args->handler)
+			return args->prehandler(drive, rq);
 
-				if (!(args)) goto args_error;
-#ifdef DEBUG
-				printk("%s: DRIVE_TASK_CMD ", drive->name);
-				printk("cmd=0x%02x ", args[0]);
-				printk("fr=0x%02x ", args[1]);
-				printk("ns=0x%02x ", args[2]);
-				printk("sc=0x%02x ", args[3]);
-				printk("lcyl=0x%02x ", args[4]);
-				printk("hcyl=0x%02x ", args[5]);
-				printk("sel=0x%02x\n", args[6]);
-#endif
-				OUT_BYTE(args[1], IDE_FEATURE_REG);
-				OUT_BYTE(args[3], IDE_SECTOR_REG);
-				OUT_BYTE(args[4], IDE_LCYL_REG);
-				OUT_BYTE(args[5], IDE_HCYL_REG);
-				sel = (args[6] & ~0x10);
-				if (drive->select.b.unit)
-					sel |= 0x10;
-				OUT_BYTE(sel, IDE_SELECT_REG);
-				ide_cmd(drive, args[0], args[2], &drive_cmd_intr);
-				return ide_started;
-			} else if (rq->flags & REQ_DRIVE_CMD) {
-				byte *args = rq->buffer;
-				if (!(args)) goto args_error;
-#ifdef DEBUG
-				printk("%s: DRIVE_CMD ", drive->name);
-				printk("cmd=0x%02x ", args[0]);
-				printk("sc=0x%02x ", args[1]);
-				printk("fr=0x%02x ", args[2]);
-				printk("xx=0x%02x\n", args[3]);
-#endif
-				if (args[0] == WIN_SMART) {
-					OUT_BYTE(0x4f, IDE_LCYL_REG);
-					OUT_BYTE(0xc2, IDE_HCYL_REG);
-					OUT_BYTE(args[2],IDE_FEATURE_REG);
-					OUT_BYTE(args[1],IDE_SECTOR_REG);
-					ide_cmd(drive, args[0], args[3], &drive_cmd_intr);
+		return ide_started;
+	}
 
-					return ide_started;
-				}
-				OUT_BYTE(args[2],IDE_FEATURE_REG);
-				ide_cmd(drive, args[0], args[1], &drive_cmd_intr);
-				return ide_started;
-			}
+	if (rq->flags & REQ_DRIVE_CMD) {
+		u8 *args = rq->buffer;
 
-args_error:
-			/*
-			 * NULL is actually a valid way of waiting for all
-			 * current requests to be flushed from the queue.
-			 */
+		if (!(args))
+			goto args_error;
 #ifdef DEBUG
-			printk("%s: DRIVE_CMD (null)\n", drive->name);
-#endif
-			ide_end_drive_cmd(drive, GET_STAT(), GET_ERR());
-			return ide_stopped;
+		printk("%s: DRIVE_CMD ", drive->name);
+		printk("cmd=0x%02x ", args[0]);
+		printk("sc=0x%02x ", args[1]);
+		printk("fr=0x%02x ", args[2]);
+		printk("xx=0x%02x\n", args[3]);
+#endif
+		if (args[0] == WIN_SMART) {
+			OUT_BYTE(0x4f, IDE_LCYL_REG);
+			OUT_BYTE(0xc2, IDE_HCYL_REG);
+			OUT_BYTE(args[2],IDE_FEATURE_REG);
+			OUT_BYTE(args[1],IDE_SECTOR_REG);
+			ide_cmd(drive, args[0], args[3], &drive_cmd_intr);
+
+			return ide_started;
 		}
+		OUT_BYTE(args[2],IDE_FEATURE_REG);
+		ide_cmd(drive, args[0], args[1], &drive_cmd_intr);
 
-		/* The normal way of execution is to pass execute the request
-		 * handler.
-		 */
+		return ide_started;
+	}
 
-		if (ata_ops(drive)) {
-			if (ata_ops(drive)->do_request)
-				return ata_ops(drive)->do_request(drive, rq, block);
-			else {
-				ide_end_request(drive, 0);
-				return ide_stopped;
-			}
+	/* The normal way of execution is to pass and execute the request
+	 * handler down to the device type driver.
+	 */
+	if (ata_ops(drive)) {
+		if (ata_ops(drive)->do_request)
+			return ata_ops(drive)->do_request(drive, rq, block);
+		else {
+			ide_end_request(drive, 0);
+			return ide_stopped;
 		}
-		printk(KERN_WARNING "%s: device type %d not supported\n",
-		       drive->name, drive->type);
-		goto kill_rq;
 	}
-	return ata_special(drive);
+
+	/*
+	 * Error handling:
+	 */
+
+	printk(KERN_WARNING "%s: device type %d not supported\n",
+			drive->name, drive->type);
+
 kill_rq:
 	if (ata_ops(drive) && ata_ops(drive)->end_request)
 		ata_ops(drive)->end_request(drive, 0);
 	else
 		ide_end_request(drive, 0);
+
+	return ide_stopped;
+
+args_error:
+
+	/* NULL as arguemnt is used by ioctls as a way of waiting for all
+	 * current requests to be flushed from the queue.
+	 */
+
+#ifdef DEBUG
+	printk("%s: DRIVE_CMD (null)\n", drive->name);
+#endif
+	ide_end_drive_cmd(drive, GET_STAT(), GET_ERR());
+
 	return ide_stopped;
 }
 
@@ -1258,174 +1198,29 @@
 /*
  * Select the next drive which will be serviced.
  */
-static ide_drive_t *choose_drive(ide_hwgroup_t *hwgroup)
+static struct ata_device *choose_drive(struct ata_device *cur)
 {
-	ide_drive_t *tmp;
-	ide_drive_t *drive = NULL;
-	unsigned long sleep = 0;
+	struct ata_device *drive = cur;
+	struct ata_device *best = NULL;
 
-	tmp = hwgroup->drive;
 	do {
-		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 (!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 (!blk_queue_plugged(&tmp->queue))
-					drive = tmp;
+				if (!blk_queue_plugged(&drive->queue))
+					best = drive;
 			}
 		}
-		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);
-
-	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;
+		drive = drive->next;
+	} while (drive != cur);
+	return best;
 }
 
 /*
- * 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;
-}
-
-/*
- * Issue a new request to a drive from hwgroup
+ * Issue a new request to a drive from hwgroup.
  * Caller must have already done spin_lock_irqsave(&ide_lock, ...)
  *
  * A hwgroup is a serialized group of IDE interfaces.  Usually there is
@@ -1454,38 +1249,101 @@
  * will start the next request from the queue.  If no more work remains,
  * the driver will clear the hwgroup->flags IDE_BUSY flag and exit.
  */
-static void ide_do_request(ide_hwgroup_t *hwgroup, int masked_irq)
+static void ide_do_request(struct ata_channel *ch, int masked_irq)
 {
-	ide_drive_t *drive;
-	struct ata_channel *hwif;
+	ide_hwgroup_t *hwgroup = ch->hwgroup;
+	struct ata_device *drive;
+	ide_startstop_t	startstop;
+	struct request	*rq;
 
-	ide_get_lock(&ide_intr_lock, ide_intr, hwgroup);/* for atari only: POSSIBLY BROKEN HERE(?) */
+	ide_get_lock(&irq_lock, ata_irq_request, hwgroup);/* for atari only: POSSIBLY BROKEN HERE(?) */
 
 	__cli();	/* necessary paranoia: ensure IRQs are masked on local CPU */
 
 	while (!test_and_set_bit(IDE_BUSY, &hwgroup->flags)) {
+		drive = choose_drive(hwgroup->drive);
 
-		/*
-		 * will clear IDE_BUSY, if appropriate
-		 */
-		if ((drive = choose_drive(hwgroup)) == NULL)
-			break;
+		if (drive == NULL) {
+			unsigned long sleep = 0;
 
-		hwif = drive->channel;
-		if (hwgroup->hwif->sharing_irq && hwif != hwgroup->hwif && IDE_CONTROL_REG) {
-			/* set nIEN for previous hwif */
-			if (hwif->intrproc)
-				hwif->intrproc(drive);
+			hwgroup->rq = NULL;
+
+			drive = hwgroup->drive;
+			do {
+				if (drive->PADAM_sleep && (!sleep || time_after(sleep, drive->PADAM_sleep)))
+					sleep = drive->PADAM_sleep;
+
+				drive = drive->next;
+			} while (drive!= 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(&irq_lock);/* for atari only */
+				clear_bit(IDE_BUSY, &hwgroup->flags);
+			}
+			return;		/* no more work for this hwgroup (for now) */
+		}
+		ch = drive->channel;
+
+		if (hwgroup->drive->channel->sharing_irq && ch != hwgroup->drive->channel && ch->io_ports[IDE_CONTROL_OFFSET]) {
+			/* set nIEN for previous channel */
+
+			if (ch->intrproc)
+				ch->intrproc(drive);
 			else
-				OUT_BYTE(drive->ctl|2, IDE_CONTROL_REG);
+				OUT_BYTE((drive)->ctl|2, ch->io_ports[IDE_CONTROL_OFFSET]);
 		}
-		hwgroup->hwif = hwif;
 		hwgroup->drive = drive;
+		drive->PADAM_sleep = 0;
+		drive->PADAM_service_start = jiffies;
+
+		if (blk_queue_plugged(&drive->queue))
+			BUG();
 
 		/*
-		 * main queueing loop
+		 * just continuing an interrupted request maybe
+		 */
+		rq = hwgroup->rq = elv_next_request(&drive->queue);
+
+		/*
+		 * 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.
 		 */
-		ide_queue_commands(drive, masked_irq);
+
+		if (masked_irq && ch->irq != masked_irq)
+			disable_irq_nosync(ch->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 && ch->irq != masked_irq)
+			enable_irq(ch->irq);
+		if (startstop == ide_stopped)
+			clear_bit(IDE_BUSY, &hwgroup->flags);
 	}
 }
 
@@ -1494,57 +1352,36 @@
  */
 request_queue_t *ide_get_queue(kdev_t dev)
 {
-	struct ata_channel *channel = (struct ata_channel *)blk_dev[major(dev)].data;
+	struct ata_channel *ch = (struct ata_channel *)blk_dev[major(dev)].data;
 
 	/* FIXME: ALLERT: This discriminates between master and slave! */
-	return &channel->drives[DEVICE_NR(dev) & 1].queue;
+	return &ch->drives[DEVICE_NR(dev) & 1].queue;
 }
 
-/*
- * Passes the stuff to ide_do_request
- */
 void do_ide_request(request_queue_t *q)
 {
 	ide_do_request(q->queuedata, 0);
 }
 
 /*
- * un-busy the hwgroup etc, and clear any pending DMA status. we want to
+ * Un-busy the hwgroup etc, and clear any pending DMA status. we want to
  * retry the current request in PIO mode instead of risking tossing it
  * all away
- *
- * FIXME: needs a bit of tcq work
  */
 void ide_dma_timeout_retry(ide_drive_t *drive)
 {
 	struct ata_channel *hwif = drive->channel;
-	struct request *rq = NULL;
-	struct ata_request *ar = NULL;
-
-	if (drive->using_tcq) {
-		if (drive->tcq->active_tag != -1) {
-			ar = IDE_CUR_AR(drive);
-			rq = ar->ar_rq;
-		}
-	} else {
-		rq = HWGROUP(drive)->rq;
-		ar = rq->special;
-	}
+	struct request *rq;
 
 	/*
 	 * end current dma transaction
 	 */
-	if (rq)
-		hwif->dmaproc(ide_dma_end, drive);
+	hwif->dmaproc(ide_dma_end, drive);
 
 	/*
 	 * complain a little, later we might remove some of this verbosity
 	 */
-	printk("%s: timeout waiting for DMA", drive->name);
-	if (drive->using_tcq)
-		printk(" queued, active tag %d", drive->tcq->active_tag);
-	printk("\n");
-
+	printk("%s: timeout waiting for DMA\n", drive->name);
 	hwif->dmaproc(ide_dma_timeout, drive);
 
 	/*
@@ -1560,25 +1397,15 @@
 	 * un-busy drive etc (hwgroup->busy is cleared on return) and
 	 * make sure request is sane
 	 */
+	rq = HWGROUP(drive)->rq;
 	HWGROUP(drive)->rq = NULL;
 
-	if (!rq)
-		return;
-
 	rq->errors = 0;
 	if (rq->bio) {
 		rq->sector = rq->bio->bi_sector;
 		rq->current_nr_sectors = bio_iovec(rq->bio)->bv_len >> 9;
 		rq->buffer = NULL;
 	}
-
-	/*
-	 *  this request was not on the queue any more
-	 */
-	if (ar->ar_flags & ATA_AR_QUEUED) {
-		ata_ar_put(drive, ar);
-		_elv_add_request(&drive->queue, rq, 0, 0);
-	}
 }
 
 /*
@@ -1595,29 +1422,33 @@
 	unsigned long	wait;
 
 	/*
-	 * a global lock protects timers etc -- shouldn't get contention
-	 * worth mentioning
+	 * A global lock protects timers etc -- shouldn't get contention
+	 * worth mentioning.
 	 */
+
 	spin_lock_irqsave(&ide_lock, flags);
 	del_timer(&hwgroup->timer);
 
 	if ((handler = hwgroup->handler) == NULL) {
+
 		/*
-		 * Either a marginal timeout occurred
-		 * (got the interrupt just as timer expired),
-		 * or we were "sleeping" to give other devices a chance.
-		 * Either way, we don't really want to complain about anything.
+		 * Either a marginal timeout occurred (got the interrupt just
+		 * as timer expired), or we were "sleeping" to give other
+		 * devices a chance.  Either way, we don't really want to
+		 * complain about anything.
 		 */
+
 		if (test_and_clear_bit(IDE_SLEEP, &hwgroup->flags))
 			clear_bit(IDE_BUSY, &hwgroup->flags);
 	} else {
-		ide_drive_t *drive = hwgroup->drive;
+		struct ata_device *drive = hwgroup->drive;
 		if (!drive) {
 			printk("ide_timer_expiry: hwgroup->drive was NULL\n");
 			hwgroup->handler = NULL;
 		} else {
-			struct ata_channel *hwif;
+			struct ata_channel *ch;
 			ide_startstop_t startstop;
+
 			/* paranoia */
 			if (!test_and_set_bit(IDE_BUSY, &hwgroup->flags))
 				printk("%s: ide_timer_expiry: hwgroup was not busy??\n", drive->name);
@@ -1638,19 +1469,19 @@
 			 * mask the specific IRQ:
 			 */
 			spin_unlock(&ide_lock);
-			hwif  = drive->channel;
+			ch = drive->channel;
 #if DISABLE_IRQ_NOSYNC
-			disable_irq_nosync(hwif->irq);
+			disable_irq_nosync(ch->irq);
 #else
-			disable_irq(hwif->irq);	/* disable_irq_nosync ?? */
+			disable_irq(ch->irq);	/* disable_irq_nosync ?? */
 #endif
 			__cli();	/* local CPU only, as if we were handling an interrupt */
 			if (hwgroup->poll_timeout != 0) {
 				startstop = handler(drive);
 			} else if (drive_is_ready(drive)) {
 				if (drive->waiting_for_dma)
-					(void) hwgroup->hwif->dmaproc(ide_dma_lostirq, drive);
-				(void)ide_ack_intr(hwif);
+					ch->dmaproc(ide_dma_lostirq, drive);
+				(void) ide_ack_intr(ch);
 				printk("%s: lost interrupt\n", drive->name);
 				startstop = handler(drive);
 			} else {
@@ -1660,15 +1491,17 @@
 				} else
 					startstop = ide_error(drive, "irq timeout", GET_STAT());
 			}
-			set_recovery_timer(hwif);
+			set_recovery_timer(ch);
 			drive->PADAM_service_time = jiffies - drive->PADAM_service_start;
-			enable_irq(hwif->irq);
+			enable_irq(ch->irq);
 			spin_lock_irq(&ide_lock);
 			if (startstop == ide_stopped)
 				clear_bit(IDE_BUSY, &hwgroup->flags);
 		}
 	}
-	ide_do_request(hwgroup, 0);
+
+	ide_do_request(hwgroup->drive->channel, 0);
+
 	spin_unlock_irqrestore(&ide_lock, flags);
 }
 
@@ -1695,55 +1528,58 @@
  * accidentally invoked as a result of any valid command completion interrupt.
  *
  */
-static void unexpected_intr(int irq, ide_hwgroup_t *hwgroup)
+static void unexpected_irq(int irq)
 {
-	u8 stat;
-	struct ata_channel *hwif = hwgroup->hwif;
+	int i;
 
-	/*
-	 * handle the unexpected interrupt
-	 */
-	do {
-		if (hwif->irq != irq)
+	for (i = 0; i < MAX_HWIFS; ++i) {
+		u8 stat;
+		struct ata_channel *tmp = &ide_hwifs[i];
+
+		if (!tmp->present)
 			continue;
 
-		stat = IN_BYTE(hwif->io_ports[IDE_STATUS_OFFSET]);
+		if (tmp->irq != irq)
+			continue;
+
+		stat = IN_BYTE(tmp->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);
+				printk("%s: unexpected interrupt, status=0x%02x, count=%d\n",
+						tmp->name, stat, count);
 			}
 		}
-		hwif = hwif->next;
-	} while (hwif != hwgroup->hwif);
+	}
 }
 
 /*
  * entry point for all interrupts, caller does __cli() for us
  */
-void ide_intr(int irq, void *dev_id, struct pt_regs *regs)
+void ata_irq_request(int irq, void *data, struct pt_regs *regs)
 {
+	struct ata_channel *ch = data;
+	ide_hwgroup_t *hwgroup = ch->hwgroup;
+
 	unsigned long flags;
-	ide_hwgroup_t *hwgroup = (ide_hwgroup_t *)dev_id;
-	struct ata_channel *hwif;
-	ide_drive_t *drive;
+	struct ata_device *drive;
 	ide_handler_t *handler;
 	ide_startstop_t startstop;
 
 	spin_lock_irqsave(&ide_lock, flags);
-	hwif = hwgroup->hwif;
 
-	if (!ide_ack_intr(hwif))
+	if (!ide_ack_intr(ch))
 		goto out_lock;
 
 	if ((handler = hwgroup->handler) == NULL || hwgroup->poll_timeout != 0) {
-		printk(KERN_INFO "ide: unexpected interrupt %d %d\n", hwif->unit, irq);
-
+#if 0
+		printk(KERN_INFO "ide: unexpected interrupt %d %d\n", ch->unit, irq);
+#endif
 		/*
 		 * Not expecting an interrupt from this drive.
 		 * That means this could be:
@@ -1757,20 +1593,19 @@
 		 * so in that case we just ignore it and hope it goes away.
 		 */
 #ifdef CONFIG_BLK_DEV_IDEPCI
-		if (hwif->pci_dev && !hwif->pci_dev->vendor)
+		if (ch->pci_dev && !ch->pci_dev->vendor)
 #endif
 		{
-			/*
-			 * Probably not a shared PCI interrupt,
-			 * so we can safely try to do something about it:
+			/* Probably not a shared PCI interrupt, so we can
+			 * safely try to do something about it:
 			 */
-			unexpected_intr(irq, hwgroup);
+			unexpected_irq(irq);
 #ifdef CONFIG_BLK_DEV_IDEPCI
 		} else {
 			/*
 			 * Whack the status register, just in case we have a leftover pending IRQ.
 			 */
-			IN_BYTE(hwif->io_ports[IDE_STATUS_OFFSET]);
+			IN_BYTE(ch->io_ports[IDE_STATUS_OFFSET]);
 #endif
 		}
 		goto out_lock;
@@ -1794,12 +1629,12 @@
 	}
 	/* paranoia */
 	if (!test_and_set_bit(IDE_BUSY, &hwgroup->flags))
-		printk("%s: ide_intr: hwgroup was not busy??\n", drive->name);
+		printk(KERN_ERR "%s: %s: hwgroup was not busy!?\n", drive->name, __FUNCTION__);
 	hwgroup->handler = NULL;
 	del_timer(&hwgroup->timer);
 	spin_unlock(&ide_lock);
 
-	if (hwif->unmask)
+	if (ch->unmask)
 		ide__sti();	/* local CPU only */
 	startstop = handler(drive);		/* service this interrupt, may set handler for next interrupt */
 	spin_lock_irq(&ide_lock);
@@ -1814,16 +1649,13 @@
 	set_recovery_timer(drive->channel);
 	drive->PADAM_service_time = jiffies - drive->PADAM_service_start;
 	if (startstop == ide_stopped) {
-		if (hwgroup->handler == NULL) { /* paranoia */
+		if (hwgroup->handler == NULL) {	/* paranoia */
 			clear_bit(IDE_BUSY, &hwgroup->flags);
-			if (test_bit(IDE_DMA, &hwgroup->flags))
-				printk("ide_intr: illegal clear\n");
-			ide_do_request(hwgroup, hwif->irq);
+			ide_do_request(ch, ch->irq);
 		} else {
-			printk("%s: ide_intr: huh? expected NULL handler on exit\n", drive->name);
+			printk("%s: %s: huh? expected NULL handler on exit\n", drive->name, __FUNCTION__);
 		}
-	} else if (startstop == ide_released)
-		ide_queue_commands(drive, hwif->irq);
+	}
 
 out_lock:
 	spin_unlock_irqrestore(&ide_lock, flags);
@@ -1856,7 +1688,7 @@
 /*
  * This function is intended to be used prior to invoking ide_do_drive_cmd().
  */
-void ide_init_drive_cmd (struct request *rq)
+void ide_init_drive_cmd(struct request *rq)
 {
 	memset(rq, 0, sizeof(*rq));
 	rq->flags = REQ_DRIVE_CMD;
@@ -1898,7 +1730,6 @@
 	if (drive->channel->chipset == ide_pdc4030 && rq->buffer != NULL)
 		return -ENOSYS;  /* special drive cmds not supported */
 #endif
-	rq->flags |= REQ_STARTED;
 	rq->errors = 0;
 	rq->rq_status = RQ_ACTIVE;
 	rq->rq_dev = mk_kdev(major,(drive->select.b.unit)<<PARTN_BITS);
@@ -1915,12 +1746,13 @@
 			queue_head = queue_head->next;
 	}
 	q->elevator.elevator_add_req_fn(q, rq, queue_head);
-	ide_do_request(hwgroup, 0);
+	ide_do_request(drive->channel, 0);
 	spin_unlock_irqrestore(&ide_lock, flags);
 	if (action == ide_wait) {
 		wait_for_completion(&wait);	/* wait for it to be serviced */
 		return rq->errors ? -EIO : 0;	/* return -EIO if errors */
 	}
+
 	return 0;
 
 }
@@ -2098,37 +1930,58 @@
  * Note that we only release the standard ports, and do not even try to handle
  * any extra ports allocated for weird IDE interface chipsets.
  */
-static void hwif_unregister(struct ata_channel *hwif)
+static void hwif_unregister(struct ata_channel *ch)
 {
-	if (hwif->straight8) {
-		ide_release_region(hwif->io_ports[IDE_DATA_OFFSET], 8);
+	int i;
+	ide_hwgroup_t *hwgroup = ch->hwgroup;
+
+	/*
+	 * Free the irq if we were the only channel using it.
+	 */
+	int n = 0;
+
+	for (i = 0; i < MAX_HWIFS; ++i) {
+		struct ata_channel *tmp = &ide_hwifs[i];
+
+		if (!tmp->present)
+			continue;
+
+		if (tmp->irq == ch->irq)
+			++n;
+	}
+	if (n == 1)
+		free_irq(ch->irq, hwgroup);
+
+
+	if (ch->straight8) {
+		release_region(ch->io_ports[IDE_DATA_OFFSET], 8);
 	} else {
-		if (hwif->io_ports[IDE_DATA_OFFSET])
-			ide_release_region(hwif->io_ports[IDE_DATA_OFFSET], 1);
-		if (hwif->io_ports[IDE_ERROR_OFFSET])
-			ide_release_region(hwif->io_ports[IDE_ERROR_OFFSET], 1);
-		if (hwif->io_ports[IDE_NSECTOR_OFFSET])
-			ide_release_region(hwif->io_ports[IDE_NSECTOR_OFFSET], 1);
-		if (hwif->io_ports[IDE_SECTOR_OFFSET])
-			ide_release_region(hwif->io_ports[IDE_SECTOR_OFFSET], 1);
-		if (hwif->io_ports[IDE_LCYL_OFFSET])
-			ide_release_region(hwif->io_ports[IDE_LCYL_OFFSET], 1);
-		if (hwif->io_ports[IDE_HCYL_OFFSET])
-			ide_release_region(hwif->io_ports[IDE_HCYL_OFFSET], 1);
-		if (hwif->io_ports[IDE_SELECT_OFFSET])
-			ide_release_region(hwif->io_ports[IDE_SELECT_OFFSET], 1);
-		if (hwif->io_ports[IDE_STATUS_OFFSET])
-			ide_release_region(hwif->io_ports[IDE_STATUS_OFFSET], 1);
+		if (ch->io_ports[IDE_DATA_OFFSET])
+			release_region(ch->io_ports[IDE_DATA_OFFSET], 1);
+		if (ch->io_ports[IDE_ERROR_OFFSET])
+			release_region(ch->io_ports[IDE_ERROR_OFFSET], 1);
+		if (ch->io_ports[IDE_NSECTOR_OFFSET])
+			release_region(ch->io_ports[IDE_NSECTOR_OFFSET], 1);
+		if (ch->io_ports[IDE_SECTOR_OFFSET])
+			release_region(ch->io_ports[IDE_SECTOR_OFFSET], 1);
+		if (ch->io_ports[IDE_LCYL_OFFSET])
+			release_region(ch->io_ports[IDE_LCYL_OFFSET], 1);
+		if (ch->io_ports[IDE_HCYL_OFFSET])
+			release_region(ch->io_ports[IDE_HCYL_OFFSET], 1);
+		if (ch->io_ports[IDE_SELECT_OFFSET])
+			release_region(ch->io_ports[IDE_SELECT_OFFSET], 1);
+		if (ch->io_ports[IDE_STATUS_OFFSET])
+			release_region(ch->io_ports[IDE_STATUS_OFFSET], 1);
 	}
-	if (hwif->io_ports[IDE_CONTROL_OFFSET])
-		ide_release_region(hwif->io_ports[IDE_CONTROL_OFFSET], 1);
+	if (ch->io_ports[IDE_CONTROL_OFFSET])
+		release_region(ch->io_ports[IDE_CONTROL_OFFSET], 1);
 #if defined(CONFIG_AMIGA) || defined(CONFIG_MAC)
-	if (hwif->io_ports[IDE_IRQ_OFFSET])
-		ide_release_region(hwif->io_ports[IDE_IRQ_OFFSET], 1);
+	if (ch->io_ports[IDE_IRQ_OFFSET])
+		release_region(ch->io_ports[IDE_IRQ_OFFSET], 1);
 #endif
 }
 
-void ide_unregister(struct ata_channel *channel)
+void ide_unregister(struct ata_channel *ch)
 {
 	struct gendisk *gd;
 	ide_drive_t *drive, *d;
@@ -2137,13 +1990,14 @@
 	unsigned long flags;
 	unsigned int p, minor;
 	struct ata_channel old_hwif;
+	int n = 0;
 
 	spin_lock_irqsave(&ide_lock, flags);
-	if (!channel->present)
+	if (!ch->present)
 		goto abort;
-	put_device(&channel->dev);
+	put_device(&ch->dev);
 	for (unit = 0; unit < MAX_DRIVES; ++unit) {
-		drive = &channel->drives[unit];
+		drive = &ch->drives[unit];
 		if (!drive->present)
 			continue;
 		if (drive->busy || drive->usage)
@@ -2156,55 +2010,40 @@
 				ide_unregister_subdriver(drive);
 		}
 	}
-	channel->present = 0;
+	ch->present = 0;
 
 	/*
 	 * All clear?  Then blow away the buffer cache
 	 */
 	spin_unlock_irqrestore(&ide_lock, flags);
 	for (unit = 0; unit < MAX_DRIVES; ++unit) {
-		drive = &channel->drives[unit];
+		drive = &ch->drives[unit];
 		if (!drive->present)
 			continue;
 		minor = drive->select.b.unit << PARTN_BITS;
 		for (p = 0; p < (1<<PARTN_BITS); ++p) {
 			if (drive->part[p].nr_sects > 0) {
-				kdev_t devp = mk_kdev(channel->major, minor+p);
+				kdev_t devp = mk_kdev(ch->major, minor+p);
 				invalidate_device(devp, 0);
 			}
 		}
 	}
 #ifdef CONFIG_PROC_FS
-	destroy_proc_ide_drives(channel);
+	destroy_proc_ide_drives(ch);
 #endif
 	spin_lock_irqsave(&ide_lock, flags);
-	hwgroup = channel->hwgroup;
 
-	/*
-	 * free the irq if we were the only hwif using it
-	 */
-	{
-		struct ata_channel *g;
-		int irq_count = 0;
-
-		g = hwgroup->hwif;
-		do {
-			if (g->irq == channel->irq)
-				++irq_count;
-			g = g->next;
-		} while (g != hwgroup->hwif);
-		if (irq_count == 1)
-			free_irq(channel->irq, hwgroup);
-	}
-	hwif_unregister(channel);
+	hwif_unregister(ch);
 
 	/*
-	 * Remove us from the hwgroup, and free
-	 * the hwgroup if we were the only member
+	 * Remove us from the hwgroup, and free the hwgroup if we were the only
+	 * member.
 	 */
+
+	hwgroup = ch->hwgroup;
 	d = hwgroup->drive;
 	for (i = 0; i < MAX_DRIVES; ++i) {
-		drive = &channel->drives[i];
+		drive = &ch->drives[i];
 		if (drive->de) {
 			devfs_unregister (drive->de);
 			drive->de = NULL;
@@ -2222,31 +2061,36 @@
 		}
 		drive->present = 0;
 		blk_cleanup_queue(&drive->queue);
-		ide_teardown_commandlist(drive);
 	}
 	if (d->present)
 		hwgroup->drive = d;
-	while (hwgroup->hwif->next != channel)
-		hwgroup->hwif = hwgroup->hwif->next;
-	hwgroup->hwif->next = channel->next;
-	if (hwgroup->hwif == channel)
-		kfree(hwgroup);
-	else
-		hwgroup->hwif = hwgroup->drive->channel;
+
+	n = 0;
+	for (i = 0; i < MAX_HWIFS; ++i) {
+		struct ata_channel *tmp = &ide_hwifs[i];
+
+		if (!tmp->present)
+			continue;
+
+		if (tmp->hwgroup == ch->hwgroup)
+			++n;
+	}
+	if (n == 1)
+		kfree(ch->hwgroup);
 
 #if defined(CONFIG_BLK_DEV_IDEDMA) && !defined(CONFIG_DMA_NONPCI)
-	ide_release_dma(channel);
+	ide_release_dma(ch);
 #endif
 
 	/*
 	 * Remove us from the kernel's knowledge.
 	 */
-	unregister_blkdev(channel->major, channel->name);
-	kfree(blksize_size[channel->major]);
-	blk_dev[channel->major].data = NULL;
-	blk_dev[channel->major].queue = NULL;
-	blk_clear(channel->major);
-	gd = channel->gd;
+	unregister_blkdev(ch->major, ch->name);
+	kfree(blksize_size[ch->major]);
+	blk_dev[ch->major].data = NULL;
+	blk_dev[ch->major].queue = NULL;
+	blk_clear(ch->major);
+	gd = ch->gd;
 	if (gd) {
 		del_gendisk(gd);
 		kfree(gd->sizes);
@@ -2256,7 +2100,7 @@
 		if (gd->flags)
 			kfree (gd->flags);
 		kfree(gd);
-		channel->gd = NULL;
+		ch->gd = NULL;
 	}
 
 	/*
@@ -2264,42 +2108,41 @@
 	 * it.
 	 */
 
-	old_hwif = *channel;
-	init_hwif_data(channel, channel->index);
-	channel->hwgroup = old_hwif.hwgroup;
-	channel->tuneproc = old_hwif.tuneproc;
-	channel->speedproc = old_hwif.speedproc;
-	channel->selectproc = old_hwif.selectproc;
-	channel->resetproc = old_hwif.resetproc;
-	channel->intrproc = old_hwif.intrproc;
-	channel->maskproc = old_hwif.maskproc;
-	channel->quirkproc = old_hwif.quirkproc;
-	channel->rwproc	= old_hwif.rwproc;
-	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;
-	channel->dma_base = old_hwif.dma_base;
-	channel->dma_extra = old_hwif.dma_extra;
-	channel->config_data = old_hwif.config_data;
-	channel->select_data = old_hwif.select_data;
-	channel->proc = old_hwif.proc;
+	old_hwif = *ch;
+	init_hwif_data(ch, ch->index);
+	ch->hwgroup = old_hwif.hwgroup;
+	ch->tuneproc = old_hwif.tuneproc;
+	ch->speedproc = old_hwif.speedproc;
+	ch->selectproc = old_hwif.selectproc;
+	ch->resetproc = old_hwif.resetproc;
+	ch->intrproc = old_hwif.intrproc;
+	ch->maskproc = old_hwif.maskproc;
+	ch->quirkproc = old_hwif.quirkproc;
+	ch->rwproc	= old_hwif.rwproc;
+	ch->ata_read = old_hwif.ata_read;
+	ch->ata_write = old_hwif.ata_write;
+	ch->atapi_read = old_hwif.atapi_read;
+	ch->atapi_write = old_hwif.atapi_write;
+	ch->dmaproc = old_hwif.dmaproc;
+	ch->busproc = old_hwif.busproc;
+	ch->bus_state = old_hwif.bus_state;
+	ch->dma_base = old_hwif.dma_base;
+	ch->dma_extra = old_hwif.dma_extra;
+	ch->config_data = old_hwif.config_data;
+	ch->select_data = old_hwif.select_data;
+	ch->proc = old_hwif.proc;
 #ifndef CONFIG_BLK_DEV_IDECS
-	channel->irq = old_hwif.irq;
+	ch->irq = old_hwif.irq;
 #endif
-	channel->major = old_hwif.major;
-	channel->chipset = old_hwif.chipset;
-	channel->autodma = old_hwif.autodma;
-	channel->udma_four = old_hwif.udma_four;
+	ch->major = old_hwif.major;
+	ch->chipset = old_hwif.chipset;
+	ch->autodma = old_hwif.autodma;
+	ch->udma_four = old_hwif.udma_four;
 #ifdef CONFIG_BLK_DEV_IDEPCI
-	channel->pci_dev = old_hwif.pci_dev;
-#else
-	channel->pci_dev = NULL;
+	ch->pci_dev = old_hwif.pci_dev;
 #endif
-	channel->straight8 = old_hwif.straight8;
+	ch->straight8 = old_hwif.straight8;
+
 abort:
 	spin_unlock_irqrestore(&ide_lock, flags);
 }
@@ -2566,18 +2409,21 @@
 	return 0;
 }
 
-static int set_pio_mode (ide_drive_t *drive, int arg)
+static int set_pio_mode(ide_drive_t *drive, int arg)
 {
 	struct request rq;
 
 	if (!drive->channel->tuneproc)
 		return -ENOSYS;
-	if (drive->special.b.set_tune)
+
+	if (drive->special_cmd & ATA_SPECIAL_TUNE)
 		return -EBUSY;
+
 	ide_init_drive_cmd(&rq);
-	drive->tune_req = (byte) arg;
-	drive->special.b.set_tune = 1;
+	drive->tune_req = (u8) arg;
+	drive->special_cmd |= ATA_SPECIAL_TUNE;
 	ide_do_drive_cmd(drive, &rq, ide_wait);
+
 	return 0;
 }
 
@@ -2613,8 +2459,7 @@
 #endif /* CONFIG_BLK_DEV_IDECS */
 }
 
-static int ide_ioctl (struct inode *inode, struct file *file,
-			unsigned int cmd, unsigned long arg)
+static int ide_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
 {
 	int err = 0, major, minor;
 	ide_drive_t *drive;
@@ -2648,7 +2493,7 @@
 		}
 	}
 
-	ide_init_drive_cmd (&rq);
+	ide_init_drive_cmd(&rq);
 	switch (cmd) {
 		case HDIO_GETGEO:
 		{
@@ -2698,13 +2543,12 @@
 				return -EACCES;
 			return ide_revalidate_disk(inode->i_rdev);
 
-		case HDIO_OBSOLETE_IDENTITY:
 		case HDIO_GET_IDENTITY:
 			if (minor(inode->i_rdev) & PARTN_MASK)
 				return -EINVAL;
 			if (drive->id == NULL)
 				return -ENOMSG;
-			if (copy_to_user((char *)arg, (char *)drive->id, (cmd == HDIO_GET_IDENTITY) ? sizeof(*drive->id) : 142))
+			if (copy_to_user((char *)arg, (char *)drive->id, sizeof(*drive->id)))
 				return -EFAULT;
 			return 0;
 
@@ -2718,11 +2562,6 @@
 				return -EACCES;
 			return ide_cmd_ioctl(drive, arg);
 
-		case HDIO_DRIVE_TASK:
-			if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
-				return -EACCES;
-			return ide_task_ioctl(drive, arg);
-
 		case HDIO_SET_NICE:
 			if (!capable(CAP_SYS_ADMIN)) return -EACCES;
 			if (arg != (arg & ((1 << IDE_NICE_DSC_OVERLAP))))
@@ -2752,7 +2591,7 @@
 		 */
 		case CDROMEJECT:
 		case CDROMCLOSETRAY:
-			return block_ioctl(inode->i_rdev, cmd, arg);
+			return block_ioctl(inode->i_bdev, cmd, arg);
 
 		case HDIO_GET_BUSSTATE:
 			if (!capable(CAP_SYS_ADMIN))
@@ -2775,86 +2614,6 @@
 	}
 }
 
-int ide_build_commandlist(ide_drive_t *drive)
-{
-#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;
-	int i, cur;
-
-	spin_lock_irqsave(&ide_lock, flags);
-
-	cur = 0;
-	list_for_each(p, &drive->free_req)
-		cur++;
-
-	/*
-	 * for now, just don't shrink it...
-	 */
-	if (drive->queue_depth <= cur) {
-		spin_unlock_irqrestore(&ide_lock, flags);
-		return 0;
-	}
-
-	for (i = cur; i < drive->queue_depth; i++) {
-		ar = kmalloc(sizeof(*ar), GFP_ATOMIC);
-		if (!ar)
-			break;
-
-		memset(ar, 0, sizeof(*ar));
-		INIT_LIST_HEAD(&ar->ar_queue);
-
-		ar->ar_sg_table = kmalloc(PRD_SEGMENTS * sizeof(struct scatterlist), GFP_ATOMIC);
-		if (!ar->ar_sg_table) {
-			kfree(ar);
-			break;
-		}
-
-		ar->ar_dmatable_cpu = pci_alloc_consistent(pdev, PRD_SEGMENTS * PRD_BYTES, &ar->ar_dmatable);
-		if (!ar->ar_dmatable_cpu) {
-			kfree(ar->ar_sg_table);
-			kfree(ar);
-			break;
-		}
-
-		/*
-		 * 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;
-}
-
-int ide_init_commandlist(ide_drive_t *drive)
-{
-	INIT_LIST_HEAD(&drive->free_req);
-
-	return ide_build_commandlist(drive);
-}
-
-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)
 {
 	ide_drive_t *drive;
@@ -3426,9 +3185,6 @@
 
 			drive->channel->dmaproc(ide_dma_off_quietly, drive);
 			drive->channel->dmaproc(ide_dma_check, drive);
-#ifdef CONFIG_BLK_DEV_IDE_TCQ_DEFAULT
-			drive->channel->dmaproc(ide_dma_queued_on, drive);
-#endif /* CONFIG_BLK_DEV_IDE_TCQ_DEFAULT */
 		}
 		/* Only CD-ROMs and tape drives support DSC overlap. */
 		drive->dsc_overlap = (drive->next != drive
@@ -3530,7 +3286,6 @@
 EXPORT_SYMBOL(ide_lock);
 EXPORT_SYMBOL(drive_is_flashcard);
 EXPORT_SYMBOL(ide_timer_expiry);
-EXPORT_SYMBOL(ide_intr);
 EXPORT_SYMBOL(ide_get_queue);
 EXPORT_SYMBOL(ide_add_generic_settings);
 EXPORT_SYMBOL(do_ide_request);
@@ -3708,7 +3463,7 @@
 #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
 # if defined(__mc68000__) || defined(CONFIG_APUS)
 	if (ide_hwifs[0].io_ports[IDE_DATA_OFFSET]) {
-		ide_get_lock(&ide_intr_lock, NULL, NULL);/* for atari only */
+		ide_get_lock(&irq_lock, NULL, NULL);/* for atari only */
 		disable_irq(ide_hwifs[0].irq);	/* disable_irq_nosync ?? */
 //		disable_irq_nosync(ide_hwifs[0].irq);
 	}
@@ -3719,7 +3474,7 @@
 # if defined(__mc68000__) || defined(CONFIG_APUS)
 	if (ide_hwifs[0].io_ports[IDE_DATA_OFFSET]) {
 		enable_irq(ide_hwifs[0].irq);
-		ide_release_lock(&ide_intr_lock);/* for atari only */
+		ide_release_lock(&irq_lock);/* for atari only */
 	}
 # endif
 #endif
diff -Nru a/drivers/ide/pdc202xx.c b/drivers/ide/pdc202xx.c
--- a/drivers/ide/pdc202xx.c	Sun Apr 28 20:10:38 2002
+++ b/drivers/ide/pdc202xx.c	Sun Apr 28 20:10:38 2002
@@ -1057,12 +1057,6 @@
 		case ide_dma_timeout:
 			if (drive->channel->resetproc != NULL)
 				drive->channel->resetproc(drive);
-		/*
-		 * we cannot support queued operations on promise, so fail to
-		 * to enable it...
-		 */
-		case ide_dma_queued_on:
-			return 1;
 		default:
 			break;
 	}
diff -Nru a/drivers/ide/trm290.c b/drivers/ide/trm290.c
--- a/drivers/ide/trm290.c	Sun Apr 28 20:10:32 2002
+++ b/drivers/ide/trm290.c	Sun Apr 28 20:10:32 2002
@@ -283,15 +283,6 @@
 		old = inw(hwif->config_data) & ~1;
 		if (old != compat && inb(old+2) == 0xff) {
 			compat += (next_offset += 0x400);	/* leave lower 10 bits untouched */
-#if 1
-			if (ide_check_region(compat + 2, 1))
-				printk("Aieee %s: ide_check_region failure at 0x%04x\n", hwif->name, (compat + 2));
-			/*
-			 * The region check is not needed; however.........
-			 * Since this is the checked in ide-probe.c,
-			 * this is only an assignment.
-			 */
-#endif
 			hwif->io_ports[IDE_CONTROL_OFFSET] = compat + 2;
 			outw(compat|1, hwif->config_data);
 			printk("%s: control basereg workaround: old=0x%04x, new=0x%04x\n", hwif->name, old, inw(hwif->config_data) & ~1);
diff -Nru a/drivers/isdn/Config.in b/drivers/isdn/Config.in
--- a/drivers/isdn/Config.in	Sun Apr 28 20:10:37 2002
+++ b/drivers/isdn/Config.in	Sun Apr 28 20:10:37 2002
@@ -7,7 +7,7 @@
 if [ "$CONFIG_NET" != "n" ]; then
    bool 'ISDN support' CONFIG_ISDN_BOOL
 
-   if [ "$CONFIG_ISDN_BOOL" == "y" ]; then
+   if [ "$CONFIG_ISDN_BOOL" = "y" ]; then
       mainmenu_option next_comment
       comment 'Old ISDN4Linux'
 
diff -Nru a/drivers/isdn/capi/Config.in b/drivers/isdn/capi/Config.in
--- a/drivers/isdn/capi/Config.in	Sun Apr 28 20:10:32 2002
+++ b/drivers/isdn/capi/Config.in	Sun Apr 28 20:10:32 2002
@@ -2,18 +2,15 @@
 # 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
+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
-   dep_tristate    '  CAPI2.0 capidrv interface support' CONFIG_ISDN_CAPI_CAPIDRV $CONFIG_ISDN_CAPI $CONFIG_ISDN
 fi
+dep_tristate    '  CAPI2.0 capidrv interface support' CONFIG_ISDN_CAPI_CAPIDRV $CONFIG_ISDN_CAPI $CONFIG_ISDN
diff -Nru a/drivers/isdn/hardware/avm/Config.in b/drivers/isdn/hardware/avm/Config.in
--- a/drivers/isdn/hardware/avm/Config.in	Sun Apr 28 20:10:30 2002
+++ b/drivers/isdn/hardware/avm/Config.in	Sun Apr 28 20:10:30 2002
@@ -14,7 +14,7 @@
    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
-q
+
    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
diff -Nru a/drivers/isdn/i4l/Makefile b/drivers/isdn/i4l/Makefile
--- a/drivers/isdn/i4l/Makefile	Sun Apr 28 20:10:34 2002
+++ b/drivers/isdn/i4l/Makefile	Sun Apr 28 20:10:34 2002
@@ -18,7 +18,6 @@
 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)
 
diff -Nru a/drivers/md/linear.c b/drivers/md/linear.c
--- a/drivers/md/linear.c	Sun Apr 28 20:10:35 2002
+++ b/drivers/md/linear.c	Sun Apr 28 20:10:35 2002
@@ -56,6 +56,8 @@
 		dev_info_t *disk = conf->disks + j;
 
 		disk->dev = rdev->dev;
+		disk->bdev = rdev->bdev;
+		atomic_inc(&rdev->bdev->bd_count);
 		disk->size = rdev->size;
 		disk->offset = curr_offset;
 
@@ -101,8 +103,12 @@
 	return 0;
 
 out:
-	if (conf)
+	if (conf) {
+		for (i = 0; i < MD_SB_DISKS; i++)
+			if (conf->disks[i].bdev)
+				bdput(conf->disks[i].bdev);
 		kfree(conf);
+	}
 	MOD_DEC_USE_COUNT;
 	return 1;
 }
@@ -110,7 +116,11 @@
 static int linear_stop (mddev_t *mddev)
 {
 	linear_conf_t *conf = mddev_to_conf(mddev);
+	int i;
   
+	for (i = 0; i < MD_SB_DISKS; i++)
+		if (conf->disks[i].bdev)
+			bdput(conf->disks[i].bdev);
 	kfree(conf->hash_table);
 	kfree(conf);
 
@@ -146,7 +156,7 @@
 		bio_io_error(bio);
 		return 0;
 	}
-	bio->bi_dev = tmp_dev->dev;
+	bio->bi_bdev = tmp_dev->bdev;
 	bio->bi_sector = bio->bi_sector - (tmp_dev->offset << 1);
 
 	return 1;
diff -Nru a/drivers/md/lvm.c b/drivers/md/lvm.c
--- a/drivers/md/lvm.c	Sun Apr 28 20:10:37 2002
+++ b/drivers/md/lvm.c	Sun Apr 28 20:10:37 2002
@@ -1,3 +1,4 @@
+#error Broken until maintainers will sanitize kdev_t handling
 /*
  * kernel/lvm.c
  *
@@ -857,7 +858,7 @@
 	switch (command) {
 	case BLKSSZGET:
 		/* get block device sector size as needed e.g. by fdisk */
-		return put_user(get_hardsect_size(inode->i_rdev), (int *) arg);
+		return put_user(bdev_hardsect_size(inode->i_bdev), (int *) arg);
 
 	case BLKGETSIZE:
 		/* return device size */
diff -Nru a/drivers/md/md.c b/drivers/md/md.c
--- a/drivers/md/md.c	Sun Apr 28 20:10:34 2002
+++ b/drivers/md/md.c	Sun Apr 28 20:10:34 2002
@@ -172,7 +172,7 @@
 
 static int md_make_request (request_queue_t *q, struct bio *bio)
 {
-	mddev_t *mddev = kdev_to_mddev(bio->bi_dev);
+	mddev_t *mddev = kdev_to_mddev(to_kdev_t(bio->bi_bdev->bd_dev));
 
 	if (mddev && mddev->pers)
 		return mddev->pers->make_request(mddev, bio_rw(bio), bio);
@@ -1577,7 +1577,7 @@
 	if (!md_size[mdidx(mddev)])
 		md_size[mdidx(mddev)] = sb->size * data_disks;
 
-	readahead = (blk_get_readahead(rdev->dev) * 512) / PAGE_SIZE;
+	readahead = (blk_get_readahead(rdev->bdev) * 512) / PAGE_SIZE;
 	if (!sb->level || (sb->level == 4) || (sb->level == 5)) {
 		readahead = (mddev->sb->chunk_size>>PAGE_SHIFT) * 4 * data_disks;
 		if (readahead < data_disks * (MAX_SECTORS>>(PAGE_SHIFT-9))*2)
@@ -1711,8 +1711,8 @@
 			continue;
 		invalidate_device(rdev->dev, 1);
 		md_blocksizes[mdidx(mddev)] = 1024;
-		if (get_hardsect_size(rdev->dev) > md_blocksizes[mdidx(mddev)])
-			md_blocksizes[mdidx(mddev)] = get_hardsect_size(rdev->dev);
+		if (bdev_hardsect_size(rdev->bdev) > md_blocksizes[mdidx(mddev)])
+			md_blocksizes[mdidx(mddev)] = bdev_hardsect_size(rdev->bdev);
 	}
 	mddev->pers = pers[pnum];
 
@@ -2556,9 +2556,14 @@
 
 static int set_disk_faulty(mddev_t *mddev, kdev_t dev)
 {
+	mdk_rdev_t *rdev;
 	int ret;
 
-	ret = md_error(mddev, dev);
+	rdev = find_rdev(mddev, dev);
+	if (!rdev)
+		return 0;
+
+	ret = md_error(mddev, rdev->bdev);
 	return ret;
 }
 
@@ -3035,9 +3040,10 @@
 }
 
 
-int md_error(mddev_t *mddev, kdev_t rdev)
+int md_error(mddev_t *mddev, struct block_device *bdev)
 {
 	mdk_rdev_t * rrdev;
+	kdev_t rdev = to_kdev_t(bdev->bd_dev);
 
 	dprintk("md_error dev:(%d:%d), rdev:(%d:%d), (caller: %p,%p,%p,%p).\n",
 		major(dev),minor(dev),major(rdev),minor(rdev),
diff -Nru a/drivers/md/multipath.c b/drivers/md/multipath.c
--- a/drivers/md/multipath.c	Sun Apr 28 20:10:36 2002
+++ b/drivers/md/multipath.c	Sun Apr 28 20:10:36 2002
@@ -49,7 +49,7 @@
 
 
 static mdk_personality_t multipath_personality;
-static md_spinlock_t retry_list_lock = MD_SPIN_LOCK_UNLOCKED;
+static spinlock_t retry_list_lock = SPIN_LOCK_UNLOCKED;
 struct multipath_bh *multipath_retry_list = NULL, **multipath_retry_tail;
 
 static int multipath_diskop(mddev_t *mddev, mdp_disk_t **d, int state);
@@ -61,16 +61,15 @@
 	struct multipath_bh *mp_bh = NULL;
 
 	do {
-		md_spin_lock_irq(&conf->device_lock);
+		spin_lock_irq(&conf->device_lock);
 		if (!conf->freer1_blocked && conf->freer1) {
 			mp_bh = conf->freer1;
 			conf->freer1 = mp_bh->next_mp;
 			conf->freer1_cnt--;
 			mp_bh->next_mp = NULL;
 			mp_bh->state = (1 << MPBH_PreAlloc);
-			mp_bh->bh_req.b_state = 0;
 		}
-		md_spin_unlock_irq(&conf->device_lock);
+		spin_unlock_irq(&conf->device_lock);
 		if (mp_bh)
 			return mp_bh;
 		mp_bh = (struct multipath_bh *) kmalloc(sizeof(struct multipath_bh),
@@ -94,6 +93,7 @@
 
 	if (test_bit(MPBH_PreAlloc, &mp_bh->state)) {
 		unsigned long flags;
+		mp_bh->bio = NULL;
 		spin_lock_irqsave(&conf->device_lock, flags);
 		mp_bh->next_mp = conf->freer1;
 		conf->freer1 = mp_bh;
@@ -126,18 +126,18 @@
 
 static void multipath_shrink_mpbh(multipath_conf_t *conf)
 {
-	md_spin_lock_irq(&conf->device_lock);
+	spin_lock_irq(&conf->device_lock);
 	while (conf->freer1) {
 		struct multipath_bh *mp_bh = conf->freer1;
 		conf->freer1 = mp_bh->next_mp;
 		conf->freer1_cnt--;
 		kfree(mp_bh);
 	}
-	md_spin_unlock_irq(&conf->device_lock);
+	spin_unlock_irq(&conf->device_lock);
 }
 
 
-static int multipath_map (mddev_t *mddev, kdev_t *rdev)
+static int multipath_map (mddev_t *mddev, struct block_device **bdev)
 {
 	multipath_conf_t *conf = mddev_to_conf(mddev);
 	int i, disks = MD_SB_DISKS;
@@ -149,7 +149,7 @@
 
 	for (i = 0; i < disks; i++) {
 		if (conf->multipaths[i].operational) {
-			*rdev = conf->multipaths[i].dev;
+			*bdev = conf->multipaths[i].bdev;
 			return (0);
 		}
 	}
@@ -164,13 +164,13 @@
 	mddev_t *mddev = mp_bh->mddev;
 	multipath_conf_t *conf = mddev_to_conf(mddev);
 
-	md_spin_lock_irqsave(&retry_list_lock, flags);
+	spin_lock_irqsave(&retry_list_lock, flags);
 	if (multipath_retry_list == NULL)
 		multipath_retry_tail = &multipath_retry_list;
 	*multipath_retry_tail = mp_bh;
 	multipath_retry_tail = &mp_bh->next_mp;
 	mp_bh->next_mp = NULL;
-	md_spin_unlock_irqrestore(&retry_list_lock, flags);
+	spin_unlock_irqrestore(&retry_list_lock, flags);
 	md_wakeup_thread(conf->thread);
 }
 
@@ -182,21 +182,23 @@
  */
 static void multipath_end_bh_io (struct multipath_bh *mp_bh, int uptodate)
 {
-	struct buffer_head *bh = mp_bh->master_bh;
+	struct bio *bio = mp_bh->master_bio;
 
-	bh->b_end_io(bh, uptodate);
+	bio_endio(bio, uptodate);
+	bio_put(mp_bh->bio);
 	multipath_free_mpbh(mp_bh);
 }
 
-void multipath_end_request (struct buffer_head *bh, int uptodate)
+void multipath_end_request(struct bio *bio)
 {
-	struct multipath_bh * mp_bh = (struct multipath_bh *)(bh->b_private);
+	int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
+	struct multipath_bh * mp_bh = (struct multipath_bh *)(bio->bi_private);
 
 	/*
 	 * this branch is our 'one multipath IO has finished' event handler:
 	 */
 	if (!uptodate)
-		md_error (mp_bh->mddev, bh->b_dev);
+		md_error (mp_bh->mddev, bio->bi_bdev);
 	else
 		/*
 		 * Set MPBH_Uptodate in our master buffer_head, so that
@@ -217,8 +219,8 @@
 	/*
 	 * oops, IO error:
 	 */
-	printk(KERN_ERR "multipath: %s: rescheduling block %lu\n", 
-		 partition_name(bh->b_dev), bh->b_blocknr);
+	printk(KERN_ERR "multipath: %s: rescheduling sector %lu\n", 
+		 bdev_partition_name(bio->bi_bdev), bio->bi_sector);
 	multipath_reschedule_retry(mp_bh);
 	return;
 }
@@ -239,17 +241,13 @@
 	return 0;
 }
 
-static int multipath_make_request (mddev_t *mddev, int rw,
-			       struct buffer_head * bh)
+static int multipath_make_request (mddev_t *mddev, int rw, struct bio * bio)
 {
 	multipath_conf_t *conf = mddev_to_conf(mddev);
-	struct buffer_head *bh_req;
+	struct bio *real_bio;
 	struct multipath_bh * mp_bh;
 	struct multipath_info *multipath;
 
-	if (!buffer_locked(bh))
-		BUG();
-	
 /*
  * make_request() can abort the operation when READA is being
  * used and no empty request is available.
@@ -261,7 +259,7 @@
 
 	mp_bh = multipath_alloc_mpbh (conf);
 
-	mp_bh->master_bh = bh;
+	mp_bh->master_bio = bio;
 	mp_bh->mddev = mddev;
 	mp_bh->cmd = rw;
 
@@ -270,16 +268,13 @@
 	 */
 	multipath = conf->multipaths + multipath_read_balance(conf);
 
-	bh_req = &mp_bh->bh_req;
-	memcpy(bh_req, bh, sizeof(*bh));
-	bh_req->b_blocknr = bh->b_rsector;
-	bh_req->b_dev = multipath->dev;
-	/* FIXME - later we will need bdev here */
-	bh_req->b_rdev = multipath->dev;
-/*	bh_req->b_rsector = bh->n_rsector; */
-	bh_req->b_end_io = multipath_end_request;
-	bh_req->b_private = mp_bh;
-	generic_make_request (rw, bh_req);
+	real_bio = bio_clone(bio, GFP_NOIO);
+	real_bio->bi_bdev = multipath->bdev;
+	real_bio->bi_rw = rw;
+	real_bio->bi_end_io = multipath_end_request;
+	real_bio->bi_private = mp_bh;
+	mp_bh->bio = real_bio;
+	generic_make_request(real_bio);
 	return 0;
 }
 
@@ -429,9 +424,10 @@
 	mdp_super_t *sb = mddev->sb;
 	mdp_disk_t *failed_desc, *spare_desc, *added_desc;
 	mdk_rdev_t *spare_rdev, *failed_rdev;
+	struct block_device *bdev;
 
 	print_multipath_conf(conf);
-	md_spin_lock_irq(&conf->device_lock);
+	spin_lock_irq(&conf->device_lock);
 	/*
 	 * find the disk ...
 	 */
@@ -607,7 +603,7 @@
 
 		*d = failed_desc;
 
-		if (kdev_none(sdisk->dev))
+		if (!sdisk->bdev)
 			sdisk->used_slot = 0;
 		/*
 		 * this really activates the spare.
@@ -632,9 +628,12 @@
 			err = 1;
 			goto abort;
 		}
+		bdev = rdisk->bdev;
 		rdisk->dev = NODEV;
+		rdisk->bdev = NULL;
 		rdisk->used_slot = 0;
 		conf->nr_disks--;
+		bdput(bdev);
 		break;
 
 	case DISKOP_HOT_ADD_DISK:
@@ -650,6 +649,8 @@
 		adisk->number = added_desc->number;
 		adisk->raid_disk = added_desc->raid_disk;
 		adisk->dev = mk_kdev(added_desc->major,added_desc->minor);
+		/* it will be held open by rdev */
+		adisk->bdev = bdget(kdev_t_to_nr(adisk->dev));
 
 		adisk->operational = 0;
 		adisk->spare = 1;
@@ -659,12 +660,12 @@
 		break;
 
 	default:
-		MD_BUG();	
+		MD_BUG();
 		err = 1;
 		goto abort;
 	}
 abort:
-	md_spin_unlock_irq(&conf->device_lock);
+	spin_unlock_irq(&conf->device_lock);
 
 	print_multipath_conf(conf);
 	return err;
@@ -688,19 +689,18 @@
 static void multipathd (void *data)
 {
 	struct multipath_bh *mp_bh;
-	struct buffer_head *bh;
+	struct bio *bio;
 	unsigned long flags;
 	mddev_t *mddev;
-	kdev_t dev;
-
+	struct block_device *bdev;
 
 	for (;;) {
-		md_spin_lock_irqsave(&retry_list_lock, flags);
+		spin_lock_irqsave(&retry_list_lock, flags);
 		mp_bh = multipath_retry_list;
 		if (!mp_bh)
 			break;
 		multipath_retry_list = mp_bh->next_mp;
-		md_spin_unlock_irqrestore(&retry_list_lock, flags);
+		spin_unlock_irqrestore(&retry_list_lock, flags);
 
 		mddev = mp_bh->mddev;
 		if (mddev->sb_dirty) {
@@ -708,22 +708,21 @@
 			mddev->sb_dirty = 0;
 			md_update_sb(mddev);
 		}
-		bh = &mp_bh->bh_req;
-		dev = bh->b_dev;
+		bio = mp_bh->bio;
+		bdev = bio->bi_bdev;
 		
-		multipath_map (mddev, &bh->b_dev);
-		if (kdev_same(bh->b_dev, dev)) {
-			printk (IO_ERROR, partition_name(bh->b_dev), bh->b_blocknr);
+		multipath_map (mddev, &bio->bi_bdev);
+		if (bio->bi_bdev == bdev) {
+			printk(IO_ERROR,
+				bdev_partition_name(bio->bi_bdev), bio->bi_sector);
 			multipath_end_bh_io(mp_bh, 0);
 		} else {
-			printk (REDIRECT_SECTOR,
-				partition_name(bh->b_dev), bh->b_blocknr);
-			bh->b_rdev = bh->b_dev;
-			bh->b_rsector = bh->b_blocknr;
-			generic_make_request (mp_bh->cmd, bh);
+			printk(REDIRECT_SECTOR,
+				bdev_partition_name(bio->bi_bdev), bio->bi_sector);
+			generic_make_request(bio);
 		}
 	}
-	md_spin_unlock_irqrestore(&retry_list_lock, flags);
+	spin_unlock_irqrestore(&retry_list_lock, flags);
 }
 #undef IO_ERROR
 #undef REDIRECT_SECTOR
@@ -740,6 +739,7 @@
 	multipath_conf_t *conf = mddev_to_conf(mddev);
 	int disks = MD_SB_DISKS;
 	kdev_t dev;
+	struct block_device *bdev;
 	struct buffer_head *bh = NULL;
 	int i, rc = 0;
 	char *buffer = NULL;
@@ -749,8 +749,9 @@
 			continue;
 		printk("(checking disk %d)\n",i);
 		dev = conf->multipaths[i].dev;
+		bdev = conf->multipaths[i].bdev;
 		set_blocksize(dev, 4096);
-		if ((bh = bread(dev, row / 4, 4096)) == NULL)
+		if ((bh = __bread(bdev, row / 4, 4096)) == NULL)
 			break;
 		if (!buffer) {
 			buffer = (char *) __get_free_page(GFP_KERNEL);
@@ -762,17 +763,17 @@
 			break;
 		}
 		bforget(bh);
-		fsync_dev(dev);
-		invalidate_buffers(dev);
+		fsync_bdev(bdev);
+		invalidate_bdev(bdev, 0);
 		bh = NULL;
 	}
 	if (buffer)
 		free_page((unsigned long) buffer);
 	if (bh) {
-		dev = bh->b_dev;
+		bdev = bh->b_bdev;
 		bforget(bh);
-		fsync_dev(dev);
-		invalidate_buffers(dev);
+		fsync_bdev(bdev);
+		invalidate_bdev(bdev, 0);
 	}
 	return rc;
 }
@@ -837,7 +838,7 @@
 	mdp_super_t *sb = mddev->sb;
 	mdp_disk_t *desc, *desc2;
 	mdk_rdev_t *rdev, *def_rdev = NULL;
-	struct md_list_head *tmp;
+	struct list_head *tmp;
 	int num_rdevs = 0;
 
 	MOD_INC_USE_COUNT;
@@ -894,6 +895,8 @@
 		disk->number = desc->number;
 		disk->raid_disk = desc->raid_disk;
 		disk->dev = rdev->dev;
+		disk->bdev = rdev->bdev;
+		atomic_inc(&rdev->bdev->bd_count);
 		disk->operational = 0;
 		disk->spare = 1;
 		disk->used_slot = 1;
@@ -952,7 +955,7 @@
 	sb->spare_disks = num_rdevs - 1;
 	mddev->sb_dirty = 1;
 	conf->mddev = mddev;
-	conf->device_lock = MD_SPIN_LOCK_UNLOCKED;
+	conf->device_lock = SPIN_LOCK_UNLOCKED;
 
 	init_waitqueue_head(&conf->wait_buffer);
 
@@ -1018,6 +1021,9 @@
 
 out_free_conf:
 	multipath_shrink_mpbh(conf);
+	for (i = 0; i < MD_SB_DISKS; i++)
+		if (conf->multipaths[i].bdev)
+			bdput(conf->multipaths[i].bdev);
 	kfree(conf);
 	mddev->private = NULL;
 out:
@@ -1040,9 +1046,13 @@
 static int multipath_stop (mddev_t *mddev)
 {
 	multipath_conf_t *conf = mddev_to_conf(mddev);
+	int i;
 
 	md_unregister_thread(conf->thread);
 	multipath_shrink_mpbh(conf);
+	for (i = 0; i < MD_SB_DISKS; i++)
+		if (conf->multipaths[i].bdev)
+			bdput(conf->multipaths[i].bdev);
 	kfree(conf);
 	mddev->private = NULL;
 	MOD_DEC_USE_COUNT;
@@ -1060,12 +1070,12 @@
 	diskop:		multipath_diskop,
 };
 
-static int md__init multipath_init (void)
+static int __init multipath_init (void)
 {
 	return register_md_personality (MULTIPATH, &multipath_personality);
 }
 
-static void multipath_exit (void)
+static void __exit multipath_exit (void)
 {
 	unregister_md_personality (MULTIPATH);
 }
diff -Nru a/drivers/md/raid0.c b/drivers/md/raid0.c
--- a/drivers/md/raid0.c	Sun Apr 28 20:10:37 2002
+++ b/drivers/md/raid0.c	Sun Apr 28 20:10:37 2002
@@ -264,7 +264,7 @@
 	 * The new BH_Lock semantics in ll_rw_blk.c guarantee that this
 	 * is the only IO operation happening on this bh.
 	 */
-	bio->bi_dev = tmp_dev->dev;
+	bio->bi_bdev = tmp_dev->bdev;
 	bio->bi_sector = rsect;
 
 	/*
diff -Nru a/drivers/md/raid1.c b/drivers/md/raid1.c
--- a/drivers/md/raid1.c	Sun Apr 28 20:10:30 2002
+++ b/drivers/md/raid1.c	Sun Apr 28 20:10:30 2002
@@ -186,7 +186,7 @@
 	mempool_free(r1_bio, conf->r1buf_pool);
 }
 
-static int map(mddev_t *mddev, kdev_t *rdev)
+static int map(mddev_t *mddev, struct block_device **bdev)
 {
 	conf_t *conf = mddev_to_conf(mddev);
 	int i, disks = MD_SB_DISKS;
@@ -198,7 +198,7 @@
 
 	for (i = 0; i < disks; i++) {
 		if (conf->mirrors[i].operational) {
-			*rdev = conf->mirrors[i].dev;
+			*bdev = conf->mirrors[i].bdev;
 			return 0;
 		}
 	}
@@ -255,7 +255,7 @@
 	 * this branch is our 'one mirror IO has finished' event handler:
 	 */
 	if (!uptodate)
-		md_error(r1_bio->mddev, bio->bi_dev);
+		md_error(r1_bio->mddev, bio->bi_bdev);
 	else
 		/*
 		 * Set R1BIO_Uptodate in our master bio, so that
@@ -283,7 +283,7 @@
 		 * oops, read error:
 		 */
 		printk(KERN_ERR "raid1: %s: rescheduling sector %lu\n",
-			partition_name(bio->bi_dev), r1_bio->sector);
+			bdev_partition_name(bio->bi_bdev), r1_bio->sector);
 		reschedule_retry(r1_bio);
 		return;
 	}
@@ -479,7 +479,7 @@
 		r1_bio->read_bio = read_bio;
 
 		read_bio->bi_sector = r1_bio->sector;
-		read_bio->bi_dev = mirror->dev;
+		read_bio->bi_bdev = mirror->bdev;
 		read_bio->bi_end_io = end_request;
 		read_bio->bi_rw = rw;
 		read_bio->bi_private = r1_bio;
@@ -503,7 +503,7 @@
 		r1_bio->write_bios[i] = mbio;
 
 		mbio->bi_sector	= r1_bio->sector;
-		mbio->bi_dev = conf->mirrors[i].dev;
+		mbio->bi_bdev = conf->mirrors[i].bdev;
 		mbio->bi_end_io	= end_request;
 		mbio->bi_rw = rw;
 		mbio->bi_private = r1_bio;
@@ -665,6 +665,7 @@
 	mdp_super_t *sb = mddev->sb;
 	mdp_disk_t *failed_desc, *spare_desc, *added_desc;
 	mdk_rdev_t *spare_rdev, *failed_rdev;
+	struct block_device *bdev;
 
 	print_conf(conf);
 	spin_lock_irq(&conf->device_lock);
@@ -850,7 +851,7 @@
 
 		*d = failed_desc;
 
-		if (kdev_none(sdisk->dev))
+		if (!sdisk->bdev)
 			sdisk->used_slot = 0;
 		/*
 		 * this really activates the spare.
@@ -876,9 +877,12 @@
 			err = 1;
 			goto abort;
 		}
+		bdev = rdisk->bdev;
 		rdisk->dev = NODEV;
+		rdisk->bdev = NULL;
 		rdisk->used_slot = 0;
 		conf->nr_disks--;
+		bdput(bdev);
 		break;
 
 	case DISKOP_HOT_ADD_DISK:
@@ -894,6 +898,8 @@
 		adisk->number = added_desc->number;
 		adisk->raid_disk = added_desc->raid_disk;
 		adisk->dev = mk_kdev(added_desc->major, added_desc->minor);
+		/* it will be held open by rdev */
+		adisk->bdev = bdget(kdev_t_to_nr(adisk->dev));
 
 		adisk->operational = 0;
 		adisk->write_only = 0;
@@ -941,7 +947,7 @@
 	 * We don't do much here, just schedule handling by raid1d
 	 */
 	if (!uptodate)
-		md_error (r1_bio->mddev, bio->bi_dev);
+		md_error (r1_bio->mddev, bio->bi_bdev);
 	else
 		set_bit(R1BIO_Uptodate, &r1_bio->state);
 	reschedule_retry(r1_bio);
@@ -955,7 +961,7 @@
 	int i;
 
 	if (!uptodate)
-		md_error(mddev, bio->bi_dev);
+		md_error(mddev, bio->bi_bdev);
 
 	for (i = 0; i < MD_SB_DISKS; i++)
 		if (r1_bio->write_bios[i] == bio) {
@@ -989,7 +995,7 @@
 		 * There is no point trying a read-for-reconstruct as
 		 * reconstruct is about to be aborted
 		 */
-		printk(IO_ERROR, partition_name(bio->bi_dev), r1_bio->sector);
+		printk(IO_ERROR, bdev_partition_name(bio->bi_bdev), r1_bio->sector);
 		md_done_sync(mddev, r1_bio->master_bio->bi_size >> 9, 0);
 		resume_device(conf);
 		put_buf(r1_bio);
@@ -1014,7 +1020,7 @@
 		if (r1_bio->write_bios[i])
 			BUG();
 		r1_bio->write_bios[i] = mbio;
-		mbio->bi_dev = conf->mirrors[i].dev;
+		mbio->bi_bdev = conf->mirrors[i].bdev;
 		mbio->bi_sector = r1_bio->sector;
 		mbio->bi_end_io	= end_sync_write;
 		mbio->bi_rw = WRITE;
@@ -1032,7 +1038,7 @@
 		 * Nowhere to write this to... I guess we
 		 * must be done
 		 */
-		printk(IO_ERROR, partition_name(bio->bi_dev), r1_bio->sector);
+		printk(IO_ERROR, bdev_partition_name(bio->bi_bdev), r1_bio->sector);
 		md_done_sync(mddev, r1_bio->master_bio->bi_size >> 9, 0);
 		resume_device(conf);
 		put_buf(r1_bio);
@@ -1043,7 +1049,7 @@
 		if (!mbio)
 			continue;
 
-		md_sync_acct(mbio->bi_dev, mbio->bi_size >> 9);
+		md_sync_acct(to_kdev_t(mbio->bi_bdev->bd_dev), mbio->bi_size >> 9);
 		generic_make_request(mbio);
 		atomic_inc(&conf->mirrors[i].nr_pending);
 	}
@@ -1065,7 +1071,7 @@
 	unsigned long flags;
 	mddev_t *mddev;
 	conf_t *conf;
-	kdev_t dev;
+	struct block_device *bdev;
 
 
 	for (;;) {
@@ -1090,15 +1096,15 @@
 			break;
 		case READ:
 		case READA:
-			dev = bio->bi_dev;
-			map(mddev, &bio->bi_dev);
-			if (kdev_same(bio->bi_dev, dev)) {
-				printk(IO_ERROR, partition_name(bio->bi_dev), r1_bio->sector);
+			bdev = bio->bi_bdev;
+			map(mddev, &bio->bi_bdev);
+			if (bio->bi_bdev == bdev) {
+				printk(IO_ERROR, bdev_partition_name(bio->bi_bdev), r1_bio->sector);
 				raid_end_bio_io(r1_bio, 0);
 				break;
 			}
 			printk(REDIRECT_SECTOR,
-				partition_name(bio->bi_dev), r1_bio->sector);
+				bdev_partition_name(bio->bi_bdev), r1_bio->sector);
 			bio->bi_sector = r1_bio->sector;
 			bio->bi_rw = r1_bio->cmd;
 
@@ -1229,7 +1235,7 @@
 	read_bio = bio_clone(r1_bio->master_bio, GFP_NOIO);
 
 	read_bio->bi_sector = sector_nr;
-	read_bio->bi_dev = mirror->dev;
+	read_bio->bi_bdev = mirror->bdev;
 	read_bio->bi_end_io = end_sync_read;
 	read_bio->bi_rw = READ;
 	read_bio->bi_private = r1_bio;
@@ -1238,7 +1244,7 @@
 		BUG();
 	r1_bio->read_bio = read_bio;
 
-	md_sync_acct(read_bio->bi_dev, nr_sectors);
+	md_sync_acct(to_kdev_t(read_bio->bi_bdev->bd_dev), nr_sectors);
 
 	generic_make_request(read_bio);
 	atomic_inc(&conf->mirrors[conf->last_used].nr_pending);
@@ -1345,6 +1351,8 @@
 			disk->number = descriptor->number;
 			disk->raid_disk = disk_idx;
 			disk->dev = rdev->dev;
+			disk->bdev = rdev->bdev;
+			atomic_inc(&rdev->bdev->bd_count);
 			disk->operational = 0;
 			disk->write_only = 0;
 			disk->spare = 0;
@@ -1376,6 +1384,8 @@
 			disk->number = descriptor->number;
 			disk->raid_disk = disk_idx;
 			disk->dev = rdev->dev;
+			disk->bdev = rdev->bdev;
+			atomic_inc(&rdev->bdev->bd_count);
 			disk->operational = 1;
 			disk->write_only = 0;
 			disk->spare = 0;
@@ -1390,6 +1400,8 @@
 			disk->number = descriptor->number;
 			disk->raid_disk = disk_idx;
 			disk->dev = rdev->dev;
+			disk->bdev = rdev->bdev;
+			atomic_inc(&rdev->bdev->bd_count);
 			disk->operational = 0;
 			disk->write_only = 0;
 			disk->spare = 1;
@@ -1419,11 +1431,10 @@
 
 		if (disk_faulty(descriptor) && (disk_idx < conf->raid_disks) &&
 				!disk->used_slot) {
-
 			disk->number = descriptor->number;
 			disk->raid_disk = disk_idx;
 			disk->dev = NODEV;
-
+			disk->bdev = NULL;
 			disk->operational = 0;
 			disk->write_only = 0;
 			disk->spare = 0;
@@ -1499,6 +1510,9 @@
 out_free_conf:
 	if (conf->r1bio_pool)
 		mempool_destroy(conf->r1bio_pool);
+	for (i = 0; i < MD_SB_DISKS; i++)
+		if (conf->mirrors[i].bdev)
+			bdput(conf->mirrors[i].bdev);
 	kfree(conf);
 	mddev->private = NULL;
 out:
@@ -1542,12 +1556,16 @@
 static int stop(mddev_t *mddev)
 {
 	conf_t *conf = mddev_to_conf(mddev);
+	int i;
 
 	md_unregister_thread(conf->thread);
 	if (conf->resync_thread)
 		md_unregister_thread(conf->resync_thread);
 	if (conf->r1bio_pool)
 		mempool_destroy(conf->r1bio_pool);
+	for (i = 0; i < MD_SB_DISKS; i++)
+		if (conf->mirrors[i].bdev)
+			bdput(conf->mirrors[i].bdev);
 	kfree(conf);
 	mddev->private = NULL;
 	MOD_DEC_USE_COUNT;
diff -Nru a/drivers/md/raid5.c b/drivers/md/raid5.c
--- a/drivers/md/raid5.c	Sun Apr 28 20:10:32 2002
+++ b/drivers/md/raid5.c	Sun Apr 28 20:10:32 2002
@@ -418,7 +418,7 @@
 			buffer->b_end_io(buffer, 1);
 		}
 	} else {
-		md_error(conf->mddev, bh->b_dev);
+		md_error(conf->mddev, bh->b_bdev);
 		clear_bit(BH_Uptodate, &bh->b_state);
 	}
 	/* must restore b_page before unlocking buffer... */
@@ -452,7 +452,7 @@
 
 	md_spin_lock_irqsave(&conf->device_lock, flags);
 	if (!uptodate)
-		md_error(conf->mddev, bh->b_dev);
+		md_error(conf->mddev, bh->b_bdev);
 	clear_bit(BH_Lock, &bh->b_state);
 	set_bit(STRIPE_HANDLE, &sh->state);
 	__release_stripe(conf, sh);
diff -Nru a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c
--- a/drivers/media/radio/radio-sf16fmi.c	Sun Apr 28 20:10:35 2002
+++ b/drivers/media/radio/radio-sf16fmi.c	Sun Apr 28 20:10:35 2002
@@ -271,6 +271,7 @@
 	while (id_table[i].card_vendor != 0 && dev == NULL) {
 		dev = isapnp_find_dev(NULL, id_table[i].vendor,
 				      id_table[i].function, NULL);
+		i++;
 	}
 
 	if (!dev)
diff -Nru a/drivers/media/video/bttv-cards.c b/drivers/media/video/bttv-cards.c
--- a/drivers/media/video/bttv-cards.c	Sun Apr 28 20:10:36 2002
+++ b/drivers/media/video/bttv-cards.c	Sun Apr 28 20:10:36 2002
@@ -1190,6 +1190,16 @@
         no_msp34xx:     1,
         pll:            PLL_28,
         tuner_type:     -1,
+},{
+	/* http://www.aopen.com/products/video/va1000.htm */
+	name:           "AOPEN VA1000",
+	video_inputs:   3, /* coax, AV, s-vid */
+	audio_inputs:   1,
+	tuner:          0,
+	tuner_type:     TUNER_LG_PAL, /* actually TP18PSB12P (PAL B/G) */
+	audiomux:       { 2, 0, 0, 0 },
+	muxsel:         { 2, 3, 1, 0 },
+	pll:            PLL_28,
 }};
 
 const int bttv_num_tvcards = (sizeof(bttv_tvcards)/sizeof(struct tvcard));
diff -Nru a/drivers/media/video/bttv-driver.c b/drivers/media/video/bttv-driver.c
--- a/drivers/media/video/bttv-driver.c	Sun Apr 28 20:10:32 2002
+++ b/drivers/media/video/bttv-driver.c	Sun Apr 28 20:10:32 2002
@@ -3394,7 +3394,7 @@
         name:     "bttv",
         id_table: bttv_pci_tbl,
         probe:    bttv_probe,
-        remove:   bttv_remove,
+        remove:   __devexit_p(bttv_remove),
 };
 
 static int bttv_init_module(void)
diff -Nru a/drivers/mtd/devices/blkmtd.c b/drivers/mtd/devices/blkmtd.c
--- a/drivers/mtd/devices/blkmtd.c	Sun Apr 28 20:10:30 2002
+++ b/drivers/mtd/devices/blkmtd.c	Sun Apr 28 20:10:30 2002
@@ -165,17 +165,15 @@
   int err;
   int sectornr, sectors, i;
   struct kiobuf *iobuf;
-  kdev_t dev;
   unsigned long *blocks;
 
   if(!rawdevice) {
     printk("blkmtd: readpage: PANIC file->private_data == NULL\n");
     return -EIO;
   }
-  dev = to_kdev_t(rawdevice->binding->bd_dev);
 
   DEBUG(2, "blkmtd: readpage called, dev = `%s' page = %p index = %ld\n",
-	bdevname(dev), page, page->index);
+	bdevname(rawdevice->binding), page, page->index);
 
   if(Page_Uptodate(page)) {
     DEBUG(2, "blkmtd: readpage page %ld is already upto date\n", page->index);
@@ -356,7 +354,6 @@
       int sectornr = item->pagenr << (PAGE_SHIFT - item->rawdevice->sector_bits);
       int sectorcnt = item->pagecnt << (PAGE_SHIFT - item->rawdevice->sector_bits);
       int max_sectors = KIO_MAX_SECTORS >> (item->rawdevice->sector_bits - 9);
-      kdev_t dev = to_kdev_t(item->rawdevice->binding->bd_dev);
 
       /* If we are writing to the last page on the device and it doesnt end
        * on a page boundary, subtract the number of sectors that dont exist.
@@ -546,7 +543,7 @@
 
   /* check erase region has valid start and length */
   DEBUG(2, "blkmtd: erase: dev = `%s' from = 0x%x len = 0x%lx\n",
-	bdevname(rawdevice->binding->bd_dev), from, len);
+	bdevname(rawdevice->binding), from, len);
   while(numregions) {
     DEBUG(3, "blkmtd: checking erase region = 0x%08X size = 0x%X num = 0x%x\n",
 	  einfo->offset, einfo->erasesize, einfo->numblocks);
@@ -648,7 +645,7 @@
   *retlen = 0;
 
   DEBUG(2, "blkmtd: read: dev = `%s' from = %ld len = %d buf = %p\n",
-	bdevname(rawdevice->binding->bd_dev), (long int)from, len, buf);
+	bdevname(rawdevice->binding), (long int)from, len, buf);
 
   pagenr = from >> PAGE_SHIFT;
   offset = from - (pagenr << PAGE_SHIFT);
@@ -718,7 +715,7 @@
 
   *retlen = 0;
   DEBUG(2, "blkmtd: write: dev = `%s' to = %ld len = %d buf = %p\n",
-	bdevname(rawdevice->binding->bd_dev), (long int)to, len, buf);
+	bdevname(rawdevice->binding), (long int)to, len, buf);
 
   /* handle readonly and out of range numbers */
 
@@ -1131,7 +1128,7 @@
     return 1;
   }
 
-  DEBUG(1, "blkmtd: devname = %s\n", bdevname(rdev));
+  DEBUG(1, "blkmtd: devname = %s\n", __bdevname(rdev));
   blocksize = BLOCK_SIZE;
 
   blocksize = bs ? bs : block_size(rdev);
diff -Nru a/drivers/mtd/ftl.c b/drivers/mtd/ftl.c
--- a/drivers/mtd/ftl.c	Sun Apr 28 20:10:34 2002
+++ b/drivers/mtd/ftl.c	Sun Apr 28 20:10:34 2002
@@ -95,8 +95,6 @@
 /* Funky stuff for setting up a block device */
 #define MAJOR_NR		FTL_MAJOR
 #define DEVICE_NAME		"ftl"
-#define DEVICE_REQUEST		do_ftl_request
-#define DEVICE_ON(device)
 #define DEVICE_OFF(device)
 
 #define DEVICE_NR(minor)	((minor)>>5)
diff -Nru a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c
--- a/drivers/mtd/nftlcore.c	Sun Apr 28 20:10:30 2002
+++ b/drivers/mtd/nftlcore.c	Sun Apr 28 20:10:30 2002
@@ -40,7 +40,6 @@
 
 /* NFTL block device stuff */
 #define MAJOR_NR NFTL_MAJOR
-#define DEVICE_REQUEST nftl_request
 #define DEVICE_OFF(device)
 
 
diff -Nru a/drivers/net/fc/iph5526.c b/drivers/net/fc/iph5526.c
--- a/drivers/net/fc/iph5526.c	Sun Apr 28 20:10:32 2002
+++ b/drivers/net/fc/iph5526.c	Sun Apr 28 20:10:32 2002
@@ -3870,8 +3870,11 @@
 		/* Wait for the Link to come up and the login process 
 		 * to complete. 
 		 */
-		for(timeout = jiffies + 10*HZ; (timeout > jiffies) && ((fi->g.link_up == FALSE) || (fi->g.port_discovery == TRUE) || (fi->g.explore_fabric == TRUE) || (fi->g.perform_adisc == TRUE));)
+		for(timeout = jiffies + 10*HZ; time_before(jiffies, timeout) && ((fi->g.link_up == FALSE) || (fi->g.port_discovery == TRUE) || (fi->g.explore_fabric == TRUE) || (fi->g.perform_adisc == TRUE));)
+		{
+			cpu_relax();
 			barrier();
+		}
 		
 		count++;
 		no_of_hosts++;
diff -Nru a/drivers/net/rrunner.c b/drivers/net/rrunner.c
--- a/drivers/net/rrunner.c	Sun Apr 28 20:10:32 2002
+++ b/drivers/net/rrunner.c	Sun Apr 28 20:10:32 2002
@@ -773,7 +773,7 @@
 	 * Give the FirmWare time to chew on the `get running' command.
 	 */
 	myjif = jiffies + 5 * HZ;
-	while ((jiffies < myjif) && !rrpriv->fw_running);
+	while (time_before(jiffies, myjif) && !rrpriv->fw_running);
 
 	netif_start_queue(dev);
 
diff -Nru a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
--- a/drivers/s390/block/dasd.c	Sun Apr 28 20:10:36 2002
+++ b/drivers/s390/block/dasd.c	Sun Apr 28 20:10:36 2002
@@ -723,13 +723,6 @@
 		goto out_blksize_size;
 	memset (blksize_size[major], 0, (1 << MINORBITS) * sizeof (int));
 
-        /* init_hardsect_size */
-	hardsect_size[major] =
-	    (int *) kmalloc ((1 << MINORBITS) * sizeof (int), GFP_ATOMIC);
-	if (!hardsect_size[major])
-		goto out_hardsect_size;
-	memset (hardsect_size[major], 0, (1 << MINORBITS) * sizeof (int));
-
 	/* finally do the gendisk stuff */
 	major_info->gendisk.part = kmalloc ((1 << MINORBITS) *
 					    sizeof (struct hd_struct),
@@ -748,10 +741,6 @@
 
         /* error handling - free the prior allocated memory */  
       out_gendisk:
-	kfree (hardsect_size[major]);
-	hardsect_size[major] = NULL;
-
-      out_hardsect_size:
 	kfree (blksize_size[major]);
 	blksize_size[major] = NULL;
 
@@ -813,7 +802,6 @@
 
 	kfree (blk_size[major]);
 	kfree (blksize_size[major]);
-	kfree (hardsect_size[major]);
 
 	blk_clear(major);
 
@@ -3341,6 +3329,7 @@
         device->request_queue = kmalloc(sizeof(request_queue_t),GFP_KERNEL);
         device->request_queue->queuedata = device;
         blk_init_queue (device->request_queue, do_dasd_request);
+	blk_queue_hardsect_size(device->request_queue, device->sizes.bp_block);
         elevator_init (&(device->request_queue->elevator),ELEVATOR_NOOP);
 
         for (i = 0; i < (1 << DASD_PARTN_BITS); i++) {
@@ -3350,7 +3339,6 @@
                                  sizes.s2b_shift) >> 1;
                 else
                         device->major_info->gendisk.sizes[minor + i] = 0;
-                hardsect_size[major][minor + i] = device->sizes.bp_block;
                 blksize_size[major][minor + i] = device->sizes.bp_block;
 		blk_queue_max_sectors(device->request_queue,
 		 device->discipline->max_blocks << device->sizes.s2b_shift);
@@ -3381,7 +3369,6 @@
         for (i = 0; i < (1 << DASD_PARTN_BITS); i++) {
                 destroy_buffers(MKDEV(major,minor+i));
                 device->major_info->gendisk.sizes[minor + i] = 0;
-                hardsect_size[major][minor + i] = 0;
                 blksize_size[major][minor + i] = 0;
         }
         if (device->request_queue) {
diff -Nru a/drivers/s390/block/xpram.c b/drivers/s390/block/xpram.c
--- a/drivers/s390/block/xpram.c	Sun Apr 28 20:10:37 2002
+++ b/drivers/s390/block/xpram.c	Sun Apr 28 20:10:37 2002
@@ -190,7 +190,6 @@
 
 Xpram_Dev *xpram_devices = NULL;
 int *xpram_blksizes = NULL;
-int *xpram_hardsects = NULL;
 int *xpram_offsets = NULL;   /* partition offsets */
 
 #define MIN(x,y) ((x) < (y) ? (x) : (y))
@@ -1022,6 +1021,7 @@
 #elif (XPRAM_VERSION == 24)
 	q = BLK_DEFAULT_QUEUE (major);
 	blk_init_queue (q, xpram_request);
+	blk_queue_hardsect_size(q, xpram_hardsect);
 #endif /* V22/V24 */
 
 	/* we want to have XPRAM_UNUSED blocks security buffer between devices */
@@ -1072,16 +1072,6 @@
 		xpram_blksizes[i] = xpram_blksize;
 	blksize_size[major]=xpram_blksizes;
 
-	xpram_hardsects = kmalloc(xpram_devs * sizeof(int), GFP_KERNEL);
-	if (!xpram_hardsects) {
-		PRINT_ERR("Not enough memory for xpram_hardsects\n");
-                PRINT_ERR("Giving up xpram\n");
-		goto fail_malloc_hardsects;
-	}
-	for (i=0; i < xpram_devs; i++) /* all the same hardsect */
-		xpram_hardsects[i] = xpram_hardsect;
-	hardsect_size[major]=xpram_hardsects;
-   
 	/* 
 	 * allocate the devices -- we can't have them static, as the number
 	 * can be specified at load time
@@ -1154,12 +1144,9 @@
 #endif /* V24 */
  fail_malloc_blksizes:
 	kfree (xpram_offsets);
- fail_malloc_hardsects:
 	kfree (xpram_blksizes);
 	blksize_size[major] = NULL;
  fail_malloc_devices:
-	kfree(xpram_hardsects);
-	hardsect_size[major] = NULL;
  fail_malloc:
 #if (XPRAM_VERSION == 22)
 	blk_dev[major].request_fn = NULL;
@@ -1197,7 +1184,6 @@
 	blk_dev[major].request_fn = NULL;
 #endif /* V22 */
 	kfree(blksize_size[major]);
-	kfree(hardsect_size[major]);
 	kfree(xpram_offsets);
 	blk_clear(major);
 
diff -Nru a/drivers/s390/char/tape34xx.c b/drivers/s390/char/tape34xx.c
--- a/drivers/s390/char/tape34xx.c	Sun Apr 28 20:10:31 2002
+++ b/drivers/s390/char/tape34xx.c	Sun Apr 28 20:10:31 2002
@@ -1343,13 +1343,15 @@
 	ccw_req_t *cqr;
 	ccw1_t *ccw;
 	__u8 *data;
-	int s2b = blksize_size[tapeblock_major][ti->blk_minor]/hardsect_size[tapeblock_major][ti->blk_minor];
+	kdev_t dev = mk_kdev(tapeblock_major, ti->blk_minor);
+	unsigned bsize = block_size(dev);
+	int s2b = bsize/queue_hardsect_size(&ti->request_queue);
 	int realcount;
 	int size,bhct = 0;
 	struct buffer_head* bh;
 	for (bh = req->bh; bh; bh = bh->b_reqnext) {
-		if (bh->b_size > blksize_size[tapeblock_major][ti->blk_minor])
-			for (size = 0; size < bh->b_size; size += blksize_size[tapeblock_major][ti->blk_minor])
+		if (bh->b_size > bsize)
+			for (size = 0; size < bh->b_size; size += bsize)
 				bhct++;
 		else
 			bhct++;
@@ -1396,17 +1398,17 @@
 	ti->position=realcount+req->nr_sectors/s2b;
 	for (bh=req->bh;bh!=NULL;) {
 	        ccw->flags = CCW_FLAG_CC;
-		if (bh->b_size >= blksize_size[tapeblock_major][ti->blk_minor]) {
-			for (size = 0; size < bh->b_size; size += blksize_size[tapeblock_major][ti->blk_minor]) {
+		if (bh->b_size >= bsize) {
+			for (size = 0; size < bh->b_size; size += bsize) {
 			        ccw++;
 				ccw->flags = CCW_FLAG_CC;
 				ccw->cmd_code = READ_FORWARD;
-				ccw->count = blksize_size[tapeblock_major][ti->blk_minor];
+				ccw->count = bsize;
 				set_normalized_cda (ccw, __pa (bh->b_data + size));
 			}
 			bh = bh->b_reqnext;
 		} else {	/* group N bhs to fit into byt_per_blk */
-			for (size = 0; bh != NULL && size < blksize_size[tapeblock_major][ti->blk_minor];) {
+			for (size = 0; bh != NULL && size < bsize;) {
 				ccw++;
 				ccw->flags = CCW_FLAG_DC;
 				ccw->cmd_code = READ_FORWARD;
@@ -1415,10 +1417,10 @@
 				size += bh->b_size;
 				bh = bh->b_reqnext;
 			}
-			if (size != blksize_size[tapeblock_major][ti->blk_minor]) {
+			if (size != bsize) {
 				PRINT_WARN ("Cannot fulfill small request %d vs. %d (%ld sects)\n",
 					    size,
-					    blksize_size[tapeblock_major][ti->blk_minor],
+					    bsize,
 					    req->nr_sectors);
 				kfree(data);
 				tape_free_request (cqr);
diff -Nru a/drivers/s390/char/tapeblock.c b/drivers/s390/char/tapeblock.c
--- a/drivers/s390/char/tapeblock.c	Sun Apr 28 20:10:35 2002
+++ b/drivers/s390/char/tapeblock.c	Sun Apr 28 20:10:35 2002
@@ -75,7 +75,6 @@
 tapeblock_setup(tape_info_t* ti) {
     blk_size[tapeblock_major][ti->blk_minor]=0; // this will be detected
     blksize_size[tapeblock_major][ti->blk_minor]=2048; // blocks are 2k by default.
-    hardsect_size[tapeblock_major][ti->blk_minor]=512;
     blk_init_queue (&ti->request_queue, tape_request_fn); 
 #ifdef CONFIG_DEVFS_FS
     tapeblock_mkdevfstree(ti);
@@ -106,8 +105,6 @@
     memset(blk_size[tapeblock_major],0,256*sizeof(int));
     blksize_size[tapeblock_major] = (int*) kmalloc (256*sizeof(int),GFP_ATOMIC);
     memset(blksize_size[tapeblock_major],0,256*sizeof(int));
-    hardsect_size[tapeblock_major] = (int*) kmalloc (256*sizeof(int),GFP_ATOMIC);
-    memset(hardsect_size[tapeblock_major],0,256*sizeof(int));
     max_sectors[tapeblock_major] = (int*) kmalloc (256*sizeof(int),GFP_ATOMIC);
     memset(max_sectors[tapeblock_major],0,256*sizeof(int));
     blkfront = kmalloc(sizeof(tape_frontend_t),GFP_KERNEL);
diff -Nru a/drivers/scsi/Config.help b/drivers/scsi/Config.help
--- a/drivers/scsi/Config.help	Sun Apr 28 20:10:37 2002
+++ b/drivers/scsi/Config.help	Sun Apr 28 20:10:37 2002
@@ -145,6 +145,13 @@
   so most people can say N here and should in fact do so, because it
   is safer.
 
+CONFIG_SCSI_REPORT_LUNS
+  If you want to build with SCSI REPORT LUNS support in the kernel, say Y here.
+  The REPORT LUNS command is useful for devices (such as disk arrays) with
+  large numbers of LUNs where the LUN values are not contiguous (sparse LUN).
+  REPORT LUNS scanning is done only for SCSI-3 devices. Most users can safely
+  answer N here.
+
 CONFIG_SCSI_CONSTANTS
   The error messages regarding your SCSI hardware will be easier to
   understand if you say Y here; it will enlarge your kernel by about
diff -Nru a/drivers/scsi/Config.in b/drivers/scsi/Config.in
--- a/drivers/scsi/Config.in	Sun Apr 28 20:10:35 2002
+++ b/drivers/scsi/Config.in	Sun Apr 28 20:10:35 2002
@@ -21,6 +21,7 @@
 comment 'Some SCSI devices (e.g. CD jukebox) support multiple LUNs'
 
 bool '  Probe all LUNs on each SCSI device' CONFIG_SCSI_MULTI_LUN
+bool '  Build with SCSI REPORT LUNS support' CONFIG_SCSI_REPORT_LUNS
   
 bool '  Verbose SCSI error reporting (kernel size +=12K)' CONFIG_SCSI_CONSTANTS
 bool '  SCSI logging facility' CONFIG_SCSI_LOGGING
diff -Nru a/drivers/scsi/aic7xxx/Makefile b/drivers/scsi/aic7xxx/Makefile
--- a/drivers/scsi/aic7xxx/Makefile	Sun Apr 28 20:10:39 2002
+++ b/drivers/scsi/aic7xxx/Makefile	Sun Apr 28 20:10:39 2002
@@ -3,22 +3,21 @@
 #
 
 O_TARGET := aic7xxx_drv.o
-MOD_TARGET = aic7xxx.o
 
-obj-$(CONFIG_SCSI_AIC7XXX)	+= aic7xxx_mod.o
+obj-$(CONFIG_SCSI_AIC7XXX)	+= aic7xxx.o
 
 # Core files
-aic7xxx_mod-objs	+= aic7xxx.o aic7xxx_93cx6.o aic7770.o
+aic7xxx-objs	+= aic7xxx_core.o aic7xxx_93cx6.o aic7770.o
 
 # Platform Specific Files
-aic7xxx_mod-objs	+= aic7xxx_linux.o aic7xxx_proc.o aic7770_linux.o
+aic7xxx-objs	+= aic7xxx_linux.o aic7xxx_proc.o aic7770_linux.o
 
 # PCI Specific Files
 ifeq ($(CONFIG_PCI),y)
   # Core PCI files
-  aic7xxx_mod-objs	+= aic7xxx_pci.o
+  aic7xxx-objs	+= aic7xxx_pci.o
   # Platform Specific PCI Files
-  aic7xxx_mod-objs	+= aic7xxx_linux_pci.o
+  aic7xxx-objs	+= aic7xxx_linux_pci.o
 endif
 
 #EXTRA_CFLAGS += -g
@@ -32,5 +31,3 @@
 
 aicasm/aicasm: aicasm/*.[chyl]
 	$(MAKE) -C aicasm
-
-aix7xxx_mod.o: aic7xxx_seq.h aic7xxx_reg.h
diff -Nru a/drivers/scsi/aic7xxx/aic7xxx.c b/drivers/scsi/aic7xxx/aic7xxx.c
--- a/drivers/scsi/aic7xxx/aic7xxx.c	Sun Apr 28 20:10:36 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,6988 +0,0 @@
-/*
- * Core routines and tables shareable across OS platforms.
- *
- * Copyright (c) 1994-2001 Justin T. Gibbs.
- * Copyright (c) 2000-2001 Adaptec Inc.
- * All rights reserved.
- *
- * 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,
- *    without modification.
- * 2. Redistributions in binary form must reproduce at minimum a disclaimer
- *    substantially similar to the "NO WARRANTY" disclaimer below
- *    ("Disclaimer") and any redistribution must be conditioned upon
- *    including a substantially similar Disclaimer requirement for further
- *    binary redistribution.
- * 3. Neither the names of the above-listed copyright holders nor the names
- *    of any contributors may be used to endorse or promote products derived
- *    from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * NO WARRANTY
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
- *
- * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.c#50 $
- *
- * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx.c,v 1.61 2000/11/13 03:35:43 gibbs Exp $
- */
-
-#include "aic7xxx_osm.h"
-#include "aic7xxx_inline.h"
-#include "aicasm/aicasm_insformat.h"
-
-/****************************** Softc Data ************************************/
-struct ahc_softc_tailq ahc_tailq = TAILQ_HEAD_INITIALIZER(ahc_tailq);
-
-/***************************** Lookup Tables **********************************/
-char *ahc_chip_names[] =
-{
-	"NONE",
-	"aic7770",
-	"aic7850",
-	"aic7855",
-	"aic7859",
-	"aic7860",
-	"aic7870",
-	"aic7880",
-	"aic7895",
-	"aic7895C",
-	"aic7890/91",
-	"aic7896/97",
-	"aic7892",
-	"aic7899"
-};
-static const u_int num_chip_names = NUM_ELEMENTS(ahc_chip_names);
-
-/*
- * Hardware error codes.
- */
-struct ahc_hard_error_entry {
-        uint8_t errno;
-	char *errmesg;
-};
-
-static struct ahc_hard_error_entry ahc_hard_errors[] = {
-	{ ILLHADDR,	"Illegal Host Access" },
-	{ ILLSADDR,	"Illegal Sequencer Address referrenced" },
-	{ ILLOPCODE,	"Illegal Opcode in sequencer program" },
-	{ SQPARERR,	"Sequencer Parity Error" },
-	{ DPARERR,	"Data-path Parity Error" },
-	{ MPARERR,	"Scratch or SCB Memory Parity Error" },
-	{ PCIERRSTAT,	"PCI Error detected" },
-	{ CIOPARERR,	"CIOBUS Parity Error" },
-};
-static const u_int num_errors = NUM_ELEMENTS(ahc_hard_errors);
-
-static struct ahc_phase_table_entry ahc_phase_table[] =
-{
-	{ P_DATAOUT,	MSG_NOOP,		"in Data-out phase"	},
-	{ P_DATAIN,	MSG_INITIATOR_DET_ERR,	"in Data-in phase"	},
-	{ P_DATAOUT_DT,	MSG_NOOP,		"in DT Data-out phase"	},
-	{ P_DATAIN_DT,	MSG_INITIATOR_DET_ERR,	"in DT Data-in phase"	},
-	{ P_COMMAND,	MSG_NOOP,		"in Command phase"	},
-	{ P_MESGOUT,	MSG_NOOP,		"in Message-out phase"	},
-	{ P_STATUS,	MSG_INITIATOR_DET_ERR,	"in Status phase"	},
-	{ P_MESGIN,	MSG_PARITY_ERROR,	"in Message-in phase"	},
-	{ P_BUSFREE,	MSG_NOOP,		"while idle"		},
-	{ 0,		MSG_NOOP,		"in unknown phase"	}
-};
-
-/*
- * In most cases we only wish to itterate over real phases, so
- * exclude the last element from the count.
- */
-static const u_int num_phases = NUM_ELEMENTS(ahc_phase_table) - 1;
-
-/*
- * Valid SCSIRATE values.  (p. 3-17)
- * Provides a mapping of tranfer periods in ns to the proper value to
- * stick in the scsixfer reg.
- */
-static struct ahc_syncrate ahc_syncrates[] =
-{
-      /* ultra2    fast/ultra  period     rate */
-	{ 0x42,      0x000,      9,      "80.0" },
-	{ 0x03,      0x000,     10,      "40.0" },
-	{ 0x04,      0x000,     11,      "33.0" },
-	{ 0x05,      0x100,     12,      "20.0" },
-	{ 0x06,      0x110,     15,      "16.0" },
-	{ 0x07,      0x120,     18,      "13.4" },
-	{ 0x08,      0x000,     25,      "10.0" },
-	{ 0x19,      0x010,     31,      "8.0"  },
-	{ 0x1a,      0x020,     37,      "6.67" },
-	{ 0x1b,      0x030,     43,      "5.7"  },
-	{ 0x1c,      0x040,     50,      "5.0"  },
-	{ 0x00,      0x050,     56,      "4.4"  },
-	{ 0x00,      0x060,     62,      "4.0"  },
-	{ 0x00,      0x070,     68,      "3.6"  },
-	{ 0x00,      0x000,      0,      NULL   }
-};
-
-/* Our Sequencer Program */
-#include "aic7xxx_seq.h"
-
-/**************************** Function Declarations ***************************/
-static void		ahc_force_renegotiation(struct ahc_softc *ahc);
-static struct ahc_tmode_tstate*
-			ahc_alloc_tstate(struct ahc_softc *ahc,
-					 u_int scsi_id, char channel);
-#ifdef AHC_TARGET_MODE
-static void		ahc_free_tstate(struct ahc_softc *ahc,
-					u_int scsi_id, char channel, int force);
-#endif
-static struct ahc_syncrate*
-			ahc_devlimited_syncrate(struct ahc_softc *ahc,
-					        struct ahc_initiator_tinfo *,
-						u_int *period,
-						u_int *ppr_options,
-						role_t role);
-static void		ahc_update_pending_scbs(struct ahc_softc *ahc);
-static void		ahc_fetch_devinfo(struct ahc_softc *ahc,
-					  struct ahc_devinfo *devinfo);
-static void		ahc_scb_devinfo(struct ahc_softc *ahc,
-					struct ahc_devinfo *devinfo,
-					struct scb *scb);
-static void		ahc_assert_atn(struct ahc_softc *ahc);
-static void		ahc_setup_initiator_msgout(struct ahc_softc *ahc,
-						   struct ahc_devinfo *devinfo,
-						   struct scb *scb);
-static void		ahc_build_transfer_msg(struct ahc_softc *ahc,
-					       struct ahc_devinfo *devinfo);
-static void		ahc_construct_sdtr(struct ahc_softc *ahc,
-					   struct ahc_devinfo *devinfo,
-					   u_int period, u_int offset);
-static void		ahc_construct_wdtr(struct ahc_softc *ahc,
-					   struct ahc_devinfo *devinfo,
-					   u_int bus_width);
-static void		ahc_construct_ppr(struct ahc_softc *ahc,
-					  struct ahc_devinfo *devinfo,
-					  u_int period, u_int offset,
-					  u_int bus_width, u_int ppr_options);
-static void		ahc_clear_msg_state(struct ahc_softc *ahc);
-static void		ahc_handle_message_phase(struct ahc_softc *ahc);
-typedef enum {
-	AHCMSG_1B,
-	AHCMSG_2B,
-	AHCMSG_EXT
-} ahc_msgtype;
-static int		ahc_sent_msg(struct ahc_softc *ahc, ahc_msgtype type,
-				     u_int msgval, int full);
-static int		ahc_parse_msg(struct ahc_softc *ahc,
-				      struct ahc_devinfo *devinfo);
-static int		ahc_handle_msg_reject(struct ahc_softc *ahc,
-					      struct ahc_devinfo *devinfo);
-static void		ahc_handle_ign_wide_residue(struct ahc_softc *ahc,
-						struct ahc_devinfo *devinfo);
-static void		ahc_reinitialize_dataptrs(struct ahc_softc *ahc);
-static void		ahc_handle_devreset(struct ahc_softc *ahc,
-					    struct ahc_devinfo *devinfo,
-					    cam_status status, char *message,
-					    int verbose_level);
-#if AHC_TARGET_MODE
-static void		ahc_setup_target_msgin(struct ahc_softc *ahc,
-					       struct ahc_devinfo *devinfo,
-					       struct scb *scb);
-#endif
-
-static bus_dmamap_callback_t	ahc_dmamap_cb; 
-static void			ahc_build_free_scb_list(struct ahc_softc *ahc);
-static int			ahc_init_scbdata(struct ahc_softc *ahc);
-static void			ahc_fini_scbdata(struct ahc_softc *ahc);
-static void		ahc_qinfifo_requeue(struct ahc_softc *ahc,
-					    struct scb *prev_scb,
-					    struct scb *scb);
-static int		ahc_qinfifo_count(struct ahc_softc *ahc);
-static u_int		ahc_rem_scb_from_disc_list(struct ahc_softc *ahc,
-						   u_int prev, u_int scbptr);
-static void		ahc_add_curscb_to_free_list(struct ahc_softc *ahc);
-static u_int		ahc_rem_wscb(struct ahc_softc *ahc,
-				     u_int scbpos, u_int prev);
-static void		ahc_reset_current_bus(struct ahc_softc *ahc);
-#ifdef AHC_DUMP_SEQ
-static void		ahc_dumpseq(struct ahc_softc *ahc);
-#endif
-static void		ahc_loadseq(struct ahc_softc *ahc);
-static int		ahc_check_patch(struct ahc_softc *ahc,
-					struct patch **start_patch,
-					u_int start_instr, u_int *skip_addr);
-static void		ahc_download_instr(struct ahc_softc *ahc,
-					   u_int instrptr, uint8_t *dconsts);
-#ifdef AHC_TARGET_MODE
-static void		ahc_queue_lstate_event(struct ahc_softc *ahc,
-					       struct ahc_tmode_lstate *lstate,
-					       u_int initiator_id,
-					       u_int event_type,
-					       u_int event_arg);
-static void		ahc_update_scsiid(struct ahc_softc *ahc,
-					  u_int targid_mask);
-static int		ahc_handle_target_cmd(struct ahc_softc *ahc,
-					      struct target_cmd *cmd);
-#endif
-/************************* Sequencer Execution Control ************************/
-/*
- * Restart the sequencer program from address zero
- */
-void
-ahc_restart(struct ahc_softc *ahc)
-{
-
-	ahc_pause(ahc);
-
-	ahc_outb(ahc, SCSISIGO, 0);		/* De-assert BSY */
-	ahc_outb(ahc, MSG_OUT, MSG_NOOP);	/* No message to send */
-	ahc_outb(ahc, SXFRCTL1, ahc_inb(ahc, SXFRCTL1) & ~BITBUCKET);
-	ahc_outb(ahc, LASTPHASE, P_BUSFREE);
-
-	/*
-	 * Ensure that the sequencer's idea of TQINPOS
-	 * matches our own.  The sequencer increments TQINPOS
-	 * only after it sees a DMA complete and a reset could
-	 * occur before the increment leaving the kernel to believe
-	 * the command arrived but the sequencer to not.
-	 */
-	ahc_outb(ahc, TQINPOS, ahc->tqinfifonext);
-
-	/* Always allow reselection */
-	ahc_outb(ahc, SCSISEQ,
-		 ahc_inb(ahc, SCSISEQ_TEMPLATE) & (ENSELI|ENRSELI|ENAUTOATNP));
-	if ((ahc->features & AHC_CMD_CHAN) != 0) {
-		/* Ensure that no DMA operations are in progress */
-		ahc_outb(ahc, CCSCBCNT, 0);
-		ahc_outb(ahc, CCSGCTL, 0);
-		ahc_outb(ahc, CCSCBCTL, 0);
-	}
-	/*
-	 * If we were in the process of DMA'ing SCB data into
-	 * an SCB, replace that SCB on the free list.  This prevents
-	 * an SCB leak.
-	 */
-	if ((ahc_inb(ahc, SEQ_FLAGS2) & SCB_DMA) != 0) {
-		ahc_add_curscb_to_free_list(ahc);
-		ahc_outb(ahc, SEQ_FLAGS2,
-			 ahc_inb(ahc, SEQ_FLAGS2) & ~SCB_DMA);
-	}
-	ahc_outb(ahc, MWI_RESIDUAL, 0);
-	ahc_outb(ahc, SEQCTL, FASTMODE);
-	ahc_outb(ahc, SEQADDR0, 0);
-	ahc_outb(ahc, SEQADDR1, 0);
-	ahc_unpause(ahc);
-}
-
-/************************* Input/Output Queues ********************************/
-void
-ahc_run_qoutfifo(struct ahc_softc *ahc)
-{
-	struct scb *scb;
-	u_int  scb_index;
-
-	ahc_sync_qoutfifo(ahc, BUS_DMASYNC_POSTREAD);
-	while (ahc->qoutfifo[ahc->qoutfifonext] != SCB_LIST_NULL) {
-
-		scb_index = ahc->qoutfifo[ahc->qoutfifonext];
-		if ((ahc->qoutfifonext & 0x03) == 0x03) {
-			u_int modnext;
-
-			/*
-			 * Clear 32bits of QOUTFIFO at a time
-			 * so that we don't clobber an incoming
-			 * byte DMA to the array on architectures
-			 * that only support 32bit load and store
-			 * operations.
-			 */
-			modnext = ahc->qoutfifonext & ~0x3;
-			*((uint32_t *)(&ahc->qoutfifo[modnext])) = 0xFFFFFFFFUL;
-			ahc_dmamap_sync(ahc, ahc->shared_data_dmat,
-					ahc->shared_data_dmamap,
-					/*offset*/modnext, /*len*/4,
-					BUS_DMASYNC_PREREAD);
-		}
-		ahc->qoutfifonext++;
-
-		scb = ahc_lookup_scb(ahc, scb_index);
-		if (scb == NULL) {
-			printf("%s: WARNING no command for scb %d "
-			       "(cmdcmplt)\nQOUTPOS = %d\n",
-			       ahc_name(ahc), scb_index,
-			       ahc->qoutfifonext - 1);
-			continue;
-		}
-
-		/*
-		 * Save off the residual
-		 * if there is one.
-		 */
-		ahc_update_residual(scb);
-		ahc_done(ahc, scb);
-	}
-}
-
-void
-ahc_run_untagged_queues(struct ahc_softc *ahc)
-{
-	int i;
-
-	for (i = 0; i < 16; i++)
-		ahc_run_untagged_queue(ahc, &ahc->untagged_queues[i]);
-}
-
-void
-ahc_run_untagged_queue(struct ahc_softc *ahc, struct scb_tailq *queue)
-{
-	struct scb *scb;
-
-	if (ahc->untagged_queue_lock != 0)
-		return;
-
-	if ((scb = TAILQ_FIRST(queue)) != NULL
-	 && (scb->flags & SCB_ACTIVE) == 0) {
-		scb->flags |= SCB_ACTIVE;
-		ahc_queue_scb(ahc, scb);
-	}
-}
-
-/************************* Interrupt Handling *********************************/
-void
-ahc_handle_brkadrint(struct ahc_softc *ahc)
-{
-	/*
-	 * We upset the sequencer :-(
-	 * Lookup the error message
-	 */
-	int i;
-	int error;
-
-	error = ahc_inb(ahc, ERROR);
-	for (i = 0; error != 1 && i < num_errors; i++)
-		error >>= 1;
-	printf("%s: brkadrint, %s at seqaddr = 0x%x\n",
-	       ahc_name(ahc), ahc_hard_errors[i].errmesg,
-	       ahc_inb(ahc, SEQADDR0) |
-	       (ahc_inb(ahc, SEQADDR1) << 8));
-
-	ahc_dump_card_state(ahc);
-
-	/* Tell everyone that this HBA is no longer availible */
-	ahc_abort_scbs(ahc, CAM_TARGET_WILDCARD, ALL_CHANNELS,
-		       CAM_LUN_WILDCARD, SCB_LIST_NULL, ROLE_UNKNOWN,
-		       CAM_NO_HBA);
-
-	/* Disable all interrupt sources by resetting the controller */
-	ahc_shutdown(ahc);
-}
-
-void
-ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
-{
-	struct scb *scb;
-	struct ahc_devinfo devinfo;
-	
-	ahc_fetch_devinfo(ahc, &devinfo);
-
-	/*
-	 * Clear the upper byte that holds SEQINT status
-	 * codes and clear the SEQINT bit. We will unpause
-	 * the sequencer, if appropriate, after servicing
-	 * the request.
-	 */
-	ahc_outb(ahc, CLRINT, CLRSEQINT);
-	switch (intstat & SEQINT_MASK) {
-	case BAD_STATUS:
-	{
-		u_int  scb_index;
-		struct hardware_scb *hscb;
-
-		/*
-		 * Set the default return value to 0 (don't
-		 * send sense).  The sense code will change
-		 * this if needed.
-		 */
-		ahc_outb(ahc, RETURN_1, 0);
-
-		/*
-		 * The sequencer will notify us when a command
-		 * has an error that would be of interest to
-		 * the kernel.  This allows us to leave the sequencer
-		 * running in the common case of command completes
-		 * without error.  The sequencer will already have
-		 * dma'd the SCB back up to us, so we can reference
-		 * the in kernel copy directly.
-		 */
-		scb_index = ahc_inb(ahc, SCB_TAG);
-		scb = ahc_lookup_scb(ahc, scb_index);
-		if (scb == NULL) {
-			printf("%s:%c:%d: ahc_intr - referenced scb "
-			       "not valid during seqint 0x%x scb(%d)\n",
-			       ahc_name(ahc), devinfo.channel,
-			       devinfo.target, intstat, scb_index);
-			ahc_dump_card_state(ahc);
-			panic("for safety");
-			goto unpause;
-		}
-
-		hscb = scb->hscb; 
-
-		/* Don't want to clobber the original sense code */
-		if ((scb->flags & SCB_SENSE) != 0) {
-			/*
-			 * Clear the SCB_SENSE Flag and have
-			 * the sequencer do a normal command
-			 * complete.
-			 */
-			scb->flags &= ~SCB_SENSE;
-			ahc_set_transaction_status(scb, CAM_AUTOSENSE_FAIL);
-			break;
-		}
-		ahc_set_transaction_status(scb, CAM_SCSI_STATUS_ERROR);
-		/* Freeze the queue until the client sees the error. */
-		ahc_freeze_devq(ahc, scb);
-		ahc_freeze_scb(scb);
-		ahc_set_scsi_status(scb, hscb->shared_data.status.scsi_status);
-		switch (hscb->shared_data.status.scsi_status) {
-		case SCSI_STATUS_OK:
-			printf("%s: Interrupted for staus of 0???\n",
-			       ahc_name(ahc));
-			break;
-		case SCSI_STATUS_CMD_TERMINATED:
-		case SCSI_STATUS_CHECK_COND:
-		{
-			struct ahc_dma_seg *sg;
-			struct scsi_sense *sc;
-			struct ahc_initiator_tinfo *targ_info;
-			struct ahc_tmode_tstate *tstate;
-			struct ahc_transinfo *tinfo;
-#ifdef AHC_DEBUG
-			if (ahc_debug & AHC_SHOWSENSE) {
-				ahc_print_path(ahc, scb);
-				printf("SCB %d: requests Check Status\n",
-				       scb->hscb->tag);
-			}
-#endif
-
-			if (ahc_perform_autosense(scb) == 0)
-				break;
-
-			targ_info = ahc_fetch_transinfo(ahc,
-							devinfo.channel,
-							devinfo.our_scsiid,
-							devinfo.target,
-							&tstate);
-			tinfo = &targ_info->curr;
-			sg = scb->sg_list;
-			sc = (struct scsi_sense *)(&hscb->shared_data.cdb); 
-			/*
-			 * Save off the residual if there is one.
-			 */
-			ahc_update_residual(scb);
-#ifdef AHC_DEBUG
-			if (ahc_debug & AHC_SHOWSENSE) {
-				ahc_print_path(ahc, scb);
-				printf("Sending Sense\n");
-			}
-#endif
-			sg->addr = ahc_get_sense_bufaddr(ahc, scb);
-			sg->len = ahc_get_sense_bufsize(ahc, scb);
-			sg->len |= AHC_DMA_LAST_SEG;
-
-			/* Fixup byte order */
-			sg->addr = ahc_htole32(sg->addr);
-			sg->len = ahc_htole32(sg->len);
-
-			sc->opcode = REQUEST_SENSE;
-			sc->byte2 = 0;
-			if (tinfo->protocol_version <= SCSI_REV_2
-			 && SCB_GET_LUN(scb) < 8)
-				sc->byte2 = SCB_GET_LUN(scb) << 5;
-			sc->unused[0] = 0;
-			sc->unused[1] = 0;
-			sc->length = sg->len;
-			sc->control = 0;
-
-			/*
-			 * We can't allow the target to disconnect.
-			 * This will be an untagged transaction and
-			 * having the target disconnect will make this
-			 * transaction indestinguishable from outstanding
-			 * tagged transactions.
-			 */
-			hscb->control = 0;
-
-			/*
-			 * This request sense could be because the
-			 * the device lost power or in some other
-			 * way has lost our transfer negotiations.
-			 * Renegotiate if appropriate.  Unit attention
-			 * errors will be reported before any data
-			 * phases occur.
-			 */
-			if (ahc_get_residual(scb) 
-			 == ahc_get_transfer_length(scb)) {
-				ahc_update_neg_request(ahc, &devinfo,
-						       tstate, targ_info,
-						       /*force*/TRUE);
-			}
-			if (tstate->auto_negotiate & devinfo.target_mask) {
-				hscb->control |= MK_MESSAGE;
-				scb->flags &= ~SCB_NEGOTIATE;
-				scb->flags |= SCB_AUTO_NEGOTIATE;
-			}
-			hscb->cdb_len = sizeof(*sc);
-			hscb->dataptr = sg->addr; 
-			hscb->datacnt = sg->len;
-			hscb->sgptr = scb->sg_list_phys | SG_FULL_RESID;
-			hscb->sgptr = ahc_htole32(hscb->sgptr);
-			scb->sg_count = 1;
-			scb->flags |= SCB_SENSE;
-			ahc_qinfifo_requeue_tail(ahc, scb);
-			ahc_outb(ahc, RETURN_1, SEND_SENSE);
-#ifdef __FreeBSD__
-			/*
-			 * Ensure we have enough time to actually
-			 * retrieve the sense.
-			 */
-			untimeout(ahc_timeout, (caddr_t)scb,
-				  scb->io_ctx->ccb_h.timeout_ch);
-			scb->io_ctx->ccb_h.timeout_ch =
-			    timeout(ahc_timeout, (caddr_t)scb, 5 * hz);
-#endif
-			break;
-		}
-		default:
-			break;
-		}
-		break;
-	}
-	case NO_MATCH:
-	{
-		/* Ensure we don't leave the selection hardware on */
-		ahc_outb(ahc, SCSISEQ,
-			 ahc_inb(ahc, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP));
-
-		printf("%s:%c:%d: no active SCB for reconnecting "
-		       "target - issuing BUS DEVICE RESET\n",
-		       ahc_name(ahc), devinfo.channel, devinfo.target);
-		printf("SAVED_SCSIID == 0x%x, SAVED_LUN == 0x%x, "
-		       "ARG_1 == 0x%x ACCUM = 0x%x\n",
-		       ahc_inb(ahc, SAVED_SCSIID), ahc_inb(ahc, SAVED_LUN),
-		       ahc_inb(ahc, ARG_1), ahc_inb(ahc, ACCUM));
-		printf("SEQ_FLAGS == 0x%x, SCBPTR == 0x%x, BTT == 0x%x, "
-		       "SINDEX == 0x%x\n",
-		       ahc_inb(ahc, SEQ_FLAGS), ahc_inb(ahc, SCBPTR),
-		       ahc_index_busy_tcl(ahc,
-			    BUILD_TCL(ahc_inb(ahc, SAVED_SCSIID),
-				      ahc_inb(ahc, SAVED_LUN))),
-		       ahc_inb(ahc, SINDEX));
-		printf("SCSIID == 0x%x, SCB_SCSIID == 0x%x, SCB_LUN == 0x%x, "
-		       "SCB_TAG == 0x%x, SCB_CONTROL == 0x%x\n",
-		       ahc_inb(ahc, SCSIID), ahc_inb(ahc, SCB_SCSIID),
-		       ahc_inb(ahc, SCB_LUN), ahc_inb(ahc, SCB_TAG),
-		       ahc_inb(ahc, SCB_CONTROL));
-		printf("SCSIBUSL == 0x%x, SCSISIGI == 0x%x\n",
-		       ahc_inb(ahc, SCSIBUSL), ahc_inb(ahc, SCSISIGI));
-		printf("SXFRCTL0 == 0x%x\n", ahc_inb(ahc, SXFRCTL0));
-		printf("SEQCTL == 0x%x\n", ahc_inb(ahc, SEQCTL));
-		ahc_dump_card_state(ahc);
-		ahc->msgout_buf[0] = MSG_BUS_DEV_RESET;
-		ahc->msgout_len = 1;
-		ahc->msgout_index = 0;
-		ahc->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
-		ahc_outb(ahc, MSG_OUT, HOST_MSG);
-		ahc_assert_atn(ahc);
-		break;
-	}
-	case SEND_REJECT: 
-	{
-		u_int rejbyte = ahc_inb(ahc, ACCUM);
-		printf("%s:%c:%d: Warning - unknown message received from "
-		       "target (0x%x).  Rejecting\n", 
-		       ahc_name(ahc), devinfo.channel, devinfo.target, rejbyte);
-		break; 
-	}
-	case NO_IDENT: 
-	{
-		/*
-		 * The reconnecting target either did not send an identify
-		 * message, or did, but we didn't find an SCB to match and
-		 * before it could respond to our ATN/abort, it hit a dataphase.
-		 * The only safe thing to do is to blow it away with a bus
-		 * reset.
-		 */
-		int found;
-
-		printf("%s:%c:%d: Target did not send an IDENTIFY message. "
-		       "LASTPHASE = 0x%x, SAVED_SCSIID == 0x%x\n",
-		       ahc_name(ahc), devinfo.channel, devinfo.target,
-		       ahc_inb(ahc, LASTPHASE), ahc_inb(ahc, SAVED_SCSIID));
-		found = ahc_reset_channel(ahc, devinfo.channel, 
-					  /*initiate reset*/TRUE);
-		printf("%s: Issued Channel %c Bus Reset. "
-		       "%d SCBs aborted\n", ahc_name(ahc), devinfo.channel,
-		       found);
-		return;
-	}
-	case IGN_WIDE_RES:
-		ahc_handle_ign_wide_residue(ahc, &devinfo);
-		break;
-	case PDATA_REINIT:
-		ahc_reinitialize_dataptrs(ahc);
-		break;
-	case BAD_PHASE:
-	{
-		u_int lastphase;
-
-		lastphase = ahc_inb(ahc, LASTPHASE);
-		printf("%s:%c:%d: unknown scsi bus phase %x, "
-		       "lastphase = 0x%x.  Attempting to continue\n",
-		       ahc_name(ahc), devinfo.channel, devinfo.target,
-		       lastphase, ahc_inb(ahc, SCSISIGI));
-		break;
-	}
-	case MISSED_BUSFREE:
-	{
-		u_int lastphase;
-
-		lastphase = ahc_inb(ahc, LASTPHASE);
-		printf("%s:%c:%d: Missed busfree. "
-		       "Lastphase = 0x%x, Curphase = 0x%x\n",
-		       ahc_name(ahc), devinfo.channel, devinfo.target,
-		       lastphase, ahc_inb(ahc, SCSISIGI));
-		ahc_restart(ahc);
-		return;
-	}
-	case HOST_MSG_LOOP:
-	{
-		/*
-		 * The sequencer has encountered a message phase
-		 * that requires host assistance for completion.
-		 * While handling the message phase(s), we will be
-		 * notified by the sequencer after each byte is
-		 * transfered so we can track bus phase changes.
-		 *
-		 * If this is the first time we've seen a HOST_MSG_LOOP
-		 * interrupt, initialize the state of the host message
-		 * loop.
-		 */
-		if (ahc->msg_type == MSG_TYPE_NONE) {
-			struct scb *scb;
-			u_int scb_index;
-			u_int bus_phase;
-
-			bus_phase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
-			if (bus_phase != P_MESGIN
-			 && bus_phase != P_MESGOUT) {
-				printf("ahc_intr: HOST_MSG_LOOP bad "
-				       "phase 0x%x\n",
-				      bus_phase);
-				/*
-				 * Probably transitioned to bus free before
-				 * we got here.  Just punt the message.
-				 */
-				ahc_clear_intstat(ahc);
-				ahc_restart(ahc);
-				return;
-			}
-
-			scb_index = ahc_inb(ahc, SCB_TAG);
-			scb = ahc_lookup_scb(ahc, scb_index);
-			if (devinfo.role == ROLE_INITIATOR) {
-				if (scb == NULL)
-					panic("HOST_MSG_LOOP with "
-					      "invalid SCB %x\n", scb_index);
-
-				if (bus_phase == P_MESGOUT)
-					ahc_setup_initiator_msgout(ahc,
-								   &devinfo,
-								   scb);
-				else {
-					ahc->msg_type =
-					    MSG_TYPE_INITIATOR_MSGIN;
-					ahc->msgin_index = 0;
-				}
-			}
-#if AHC_TARGET_MODE
-			else {
-				if (bus_phase == P_MESGOUT) {
-					ahc->msg_type =
-					    MSG_TYPE_TARGET_MSGOUT;
-					ahc->msgin_index = 0;
-				}
-				else 
-					ahc_setup_target_msgin(ahc,
-							       &devinfo,
-							       scb);
-			}
-#endif
-		}
-
-		ahc_handle_message_phase(ahc);
-		break;
-	}
-	case PERR_DETECTED:
-	{
-		/*
-		 * If we've cleared the parity error interrupt
-		 * but the sequencer still believes that SCSIPERR
-		 * is true, it must be that the parity error is
-		 * for the currently presented byte on the bus,
-		 * and we are not in a phase (data-in) where we will
-		 * eventually ack this byte.  Ack the byte and
-		 * throw it away in the hope that the target will
-		 * take us to message out to deliver the appropriate
-		 * error message.
-		 */
-		if ((intstat & SCSIINT) == 0
-		 && (ahc_inb(ahc, SSTAT1) & SCSIPERR) != 0) {
-
-			if ((ahc->features & AHC_DT) == 0) {
-				u_int curphase;
-
-				/*
-				 * The hardware will only let you ack bytes
-				 * if the expected phase in SCSISIGO matches
-				 * the current phase.  Make sure this is
-				 * currently the case.
-				 */
-				curphase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
-				ahc_outb(ahc, LASTPHASE, curphase);
-				ahc_outb(ahc, SCSISIGO, curphase);
-			}
-			ahc_inb(ahc, SCSIDATL);
-		}
-		break;
-	}
-	case DATA_OVERRUN:
-	{
-		/*
-		 * When the sequencer detects an overrun, it
-		 * places the controller in "BITBUCKET" mode
-		 * and allows the target to complete its transfer.
-		 * Unfortunately, none of the counters get updated
-		 * when the controller is in this mode, so we have
-		 * no way of knowing how large the overrun was.
-		 */
-		u_int scbindex = ahc_inb(ahc, SCB_TAG);
-		u_int lastphase = ahc_inb(ahc, LASTPHASE);
-		u_int i;
-
-		scb = ahc_lookup_scb(ahc, scbindex);
-		for (i = 0; i < num_phases; i++) {
-			if (lastphase == ahc_phase_table[i].phase)
-				break;
-		}
-		ahc_print_path(ahc, scb);
-		printf("data overrun detected %s."
-		       "  Tag == 0x%x.\n",
-		       ahc_phase_table[i].phasemsg,
-  		       scb->hscb->tag);
-		ahc_print_path(ahc, scb);
-		printf("%s seen Data Phase.  Length = %ld.  NumSGs = %d.\n",
-		       ahc_inb(ahc, SEQ_FLAGS) & DPHASE ? "Have" : "Haven't",
-		       ahc_get_transfer_length(scb), scb->sg_count);
-		if (scb->sg_count > 0) {
-			for (i = 0; i < scb->sg_count; i++) {
-
-				printf("sg[%d] - Addr 0x%x%x : Length %d\n",
-				       i,
-				       (ahc_le32toh(scb->sg_list[i].len) >> 24
-				        & SG_HIGH_ADDR_BITS),
-				       ahc_le32toh(scb->sg_list[i].addr),
-				       ahc_le32toh(scb->sg_list[i].len)
-				       & AHC_SG_LEN_MASK);
-			}
-		}
-		/*
-		 * Set this and it will take effect when the
-		 * target does a command complete.
-		 */
-		ahc_freeze_devq(ahc, scb);
-		ahc_set_transaction_status(scb, CAM_DATA_RUN_ERR);
-		ahc_freeze_scb(scb);
-
-		if ((ahc->features & AHC_ULTRA2) != 0) {
-			/*
-			 * Clear the channel in case we return
-			 * to data phase later.
-			 */
-			ahc_outb(ahc, SXFRCTL0,
-				 ahc_inb(ahc, SXFRCTL0) | CLRSTCNT|CLRCHN);
-			ahc_outb(ahc, SXFRCTL0,
-				 ahc_inb(ahc, SXFRCTL0) | CLRSTCNT|CLRCHN);
-		}
-		if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
-			u_int dscommand1;
-
-			/* Ensure HHADDR is 0 for future DMA operations. */
-			dscommand1 = ahc_inb(ahc, DSCOMMAND1);
-			ahc_outb(ahc, DSCOMMAND1, dscommand1 | HADDLDSEL0);
-			ahc_outb(ahc, HADDR, 0);
-			ahc_outb(ahc, DSCOMMAND1, dscommand1);
-		}
-		break;
-	}
-	case MKMSG_FAILED:
-	{
-		u_int scbindex;
-
-		printf("%s:%c:%d:%d: Attempt to issue message failed\n",
-		       ahc_name(ahc), devinfo.channel, devinfo.target,
-		       devinfo.lun);
-		scbindex = ahc_inb(ahc, SCB_TAG);
-		scb = ahc_lookup_scb(ahc, scbindex);
-		if (scb != NULL
-		 && (scb->flags & SCB_RECOVERY_SCB) != 0)
-			/*
-			 * Ensure that we didn't put a second instance of this
-			 * SCB into the QINFIFO.
-			 */
-			ahc_search_qinfifo(ahc, SCB_GET_TARGET(ahc, scb),
-					   SCB_GET_CHANNEL(ahc, scb),
-					   SCB_GET_LUN(scb), scb->hscb->tag,
-					   ROLE_INITIATOR, /*status*/0,
-					   SEARCH_REMOVE);
-		break;
-	}
-	case NO_FREE_SCB:
-	{
-		printf("%s: No free or disconnected SCBs\n", ahc_name(ahc));
-		ahc_dump_card_state(ahc);
-		panic("for safety");
-		break;
-	}
-	case SCB_MISMATCH:
-	{
-		u_int scbptr;
-
-		scbptr = ahc_inb(ahc, SCBPTR);
-		printf("Bogus TAG after DMA.  SCBPTR %d, tag %d, our tag %d\n",
-		       scbptr, ahc_inb(ahc, ARG_1),
-		       ahc->scb_data->hscbs[scbptr].tag);
-		ahc_dump_card_state(ahc);
-		panic("for saftey");
-		break;
-	}
-	case OUT_OF_RANGE:
-	{
-		printf("%s: BTT calculation out of range\n", ahc_name(ahc));
-		printf("SAVED_SCSIID == 0x%x, SAVED_LUN == 0x%x, "
-		       "ARG_1 == 0x%x ACCUM = 0x%x\n",
-		       ahc_inb(ahc, SAVED_SCSIID), ahc_inb(ahc, SAVED_LUN),
-		       ahc_inb(ahc, ARG_1), ahc_inb(ahc, ACCUM));
-		printf("SEQ_FLAGS == 0x%x, SCBPTR == 0x%x, BTT == 0x%x, "
-		       "SINDEX == 0x%x\n, A == 0x%x\n",
-		       ahc_inb(ahc, SEQ_FLAGS), ahc_inb(ahc, SCBPTR),
-		       ahc_index_busy_tcl(ahc,
-			    BUILD_TCL(ahc_inb(ahc, SAVED_SCSIID),
-				      ahc_inb(ahc, SAVED_LUN))),
-		       ahc_inb(ahc, SINDEX),
-		       ahc_inb(ahc, ACCUM));
-		printf("SCSIID == 0x%x, SCB_SCSIID == 0x%x, SCB_LUN == 0x%x, "
-		       "SCB_TAG == 0x%x, SCB_CONTROL == 0x%x\n",
-		       ahc_inb(ahc, SCSIID), ahc_inb(ahc, SCB_SCSIID),
-		       ahc_inb(ahc, SCB_LUN), ahc_inb(ahc, SCB_TAG),
-		       ahc_inb(ahc, SCB_CONTROL));
-		printf("SCSIBUSL == 0x%x, SCSISIGI == 0x%x\n",
-		       ahc_inb(ahc, SCSIBUSL), ahc_inb(ahc, SCSISIGI));
-		ahc_dump_card_state(ahc);
-		panic("for safety");
-		break;
-	}
-	default:
-		printf("ahc_intr: seqint, "
-		       "intstat == 0x%x, scsisigi = 0x%x\n",
-		       intstat, ahc_inb(ahc, SCSISIGI));
-		break;
-	}
-unpause:
-	/*
-	 *  The sequencer is paused immediately on
-	 *  a SEQINT, so we should restart it when
-	 *  we're done.
-	 */
-	ahc_unpause(ahc);
-}
-
-void
-ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
-{
-	u_int	scb_index;
-	u_int	status0;
-	u_int	status;
-	struct	scb *scb;
-	char	cur_channel;
-	char	intr_channel;
-
-	/* Make sure the sequencer is in a safe location. */
-	ahc_clear_critical_section(ahc);
-
-	if ((ahc->features & AHC_TWIN) != 0
-	 && ((ahc_inb(ahc, SBLKCTL) & SELBUSB) != 0))
-		cur_channel = 'B';
-	else
-		cur_channel = 'A';
-	intr_channel = cur_channel;
-
-	if ((ahc->features & AHC_ULTRA2) != 0)
-		status0 = ahc_inb(ahc, SSTAT0) & IOERR;
-	else
-		status0 = 0;
-	status = ahc_inb(ahc, SSTAT1) & (SELTO|SCSIRSTI|BUSFREE|SCSIPERR);
-	if (status == 0 && status0 == 0) {
-		if ((ahc->features & AHC_TWIN) != 0) {
-			/* Try the other channel */
-		 	ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) ^ SELBUSB);
-			status = ahc_inb(ahc, SSTAT1)
-			       & (SELTO|SCSIRSTI|BUSFREE|SCSIPERR);
-			intr_channel = (cur_channel == 'A') ? 'B' : 'A';
-		}
-		if (status == 0) {
-			printf("%s: Spurious SCSI interrupt\n", ahc_name(ahc));
-			ahc_outb(ahc, CLRINT, CLRSCSIINT);
-			ahc_unpause(ahc);
-			return;
-		}
-	}
-
-	scb_index = ahc_inb(ahc, SCB_TAG);
-	scb = ahc_lookup_scb(ahc, scb_index);
-	if (scb != NULL
-	 && (ahc_inb(ahc, SEQ_FLAGS) & IDENTIFY_SEEN) == 0)
-		scb = NULL;
-
-	if ((ahc->features & AHC_ULTRA2) != 0
-	 && (status0 & IOERR) != 0) {
-		int now_lvd;
-
-		now_lvd = ahc_inb(ahc, SBLKCTL) & ENAB40;
-		printf("%s: Transceiver State Has Changed to %s mode\n",
-		       ahc_name(ahc), now_lvd ? "LVD" : "SE");
-		ahc_outb(ahc, CLRSINT0, CLRIOERR);
-		/*
-		 * When transitioning to SE mode, the reset line
-		 * glitches, triggering an arbitration bug in some
-		 * Ultra2 controllers.  This bug is cleared when we
-		 * assert the reset line.  Since a reset glitch has
-		 * already occurred with this transition and a
-		 * transceiver state change is handled just like
-		 * a bus reset anyway, asserting the reset line
-		 * ourselves is safe.
-		 */
-		ahc_reset_channel(ahc, intr_channel,
-				 /*Initiate Reset*/now_lvd == 0);
-	} else if ((status & SCSIRSTI) != 0) {
-		printf("%s: Someone reset channel %c\n",
-			ahc_name(ahc), intr_channel);
-		if (intr_channel != cur_channel)
-		 	ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) ^ SELBUSB);
-		ahc_reset_channel(ahc, intr_channel, /*Initiate Reset*/FALSE);
-	} else if ((status & SCSIPERR) != 0) {
-		/*
-		 * Determine the bus phase and queue an appropriate message.
-		 * SCSIPERR is latched true as soon as a parity error
-		 * occurs.  If the sequencer acked the transfer that
-		 * caused the parity error and the currently presented
-		 * transfer on the bus has correct parity, SCSIPERR will
-		 * be cleared by CLRSCSIPERR.  Use this to determine if
-		 * we should look at the last phase the sequencer recorded,
-		 * or the current phase presented on the bus.
-		 */
-		u_int mesg_out;
-		u_int curphase;
-		u_int errorphase;
-		u_int lastphase;
-		u_int scsirate;
-		u_int i;
-		u_int sstat2;
-
-		lastphase = ahc_inb(ahc, LASTPHASE);
-		curphase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
-		sstat2 = ahc_inb(ahc, SSTAT2);
-		ahc_outb(ahc, CLRSINT1, CLRSCSIPERR);
-		/*
-		 * For all phases save DATA, the sequencer won't
-		 * automatically ack a byte that has a parity error
-		 * in it.  So the only way that the current phase
-		 * could be 'data-in' is if the parity error is for
-		 * an already acked byte in the data phase.  During
-		 * synchronous data-in transfers, we may actually
-		 * ack bytes before latching the current phase in
-		 * LASTPHASE, leading to the discrepancy between
-		 * curphase and lastphase.
-		 */
-		if ((ahc_inb(ahc, SSTAT1) & SCSIPERR) != 0
-		 || curphase == P_DATAIN || curphase == P_DATAIN_DT)
-			errorphase = curphase;
-		else
-			errorphase = lastphase;
-
-		for (i = 0; i < num_phases; i++) {
-			if (errorphase == ahc_phase_table[i].phase)
-				break;
-		}
-		mesg_out = ahc_phase_table[i].mesg_out;
-		if (scb != NULL)
-			ahc_print_path(ahc, scb);
-		else
-			printf("%s:%c:%d: ", ahc_name(ahc), intr_channel,
-			       SCSIID_TARGET(ahc, ahc_inb(ahc, SAVED_SCSIID)));
-		scsirate = ahc_inb(ahc, SCSIRATE);
-		printf("parity error detected %s. "
-		       "SEQADDR(0x%x) SCSIRATE(0x%x)\n",
-		       ahc_phase_table[i].phasemsg,
-		       ahc_inb(ahc, SEQADDR0) | (ahc_inb(ahc, SEQADDR1) << 8),
-		       scsirate);
-
-		if ((ahc->features & AHC_DT) != 0) {
-
-			if ((sstat2 & CRCVALERR) != 0)
-				printf("\tCRC Value Mismatch\n");
-			if ((sstat2 & CRCENDERR) != 0)
-				printf("\tNo terminal CRC packet recevied\n");
-			if ((sstat2 & CRCREQERR) != 0)
-				printf("\tIllegal CRC packet request\n");
-			if ((sstat2 & DUAL_EDGE_ERR) != 0)
-				printf("\tUnexpected %sDT Data Phase\n",
-				       (scsirate & SINGLE_EDGE) ? "" : "non-");
-		}
-
-		/*
-		 * We've set the hardware to assert ATN if we   
-		 * get a parity error on "in" phases, so all we  
-		 * need to do is stuff the message buffer with
-		 * the appropriate message.  "In" phases have set
-		 * mesg_out to something other than MSG_NOP.
-		 */
-		if (mesg_out != MSG_NOOP) {
-			if (ahc->msg_type != MSG_TYPE_NONE)
-				ahc->send_msg_perror = TRUE;
-			else
-				ahc_outb(ahc, MSG_OUT, mesg_out);
-		}
-		/*
-		 * Force a renegotiation with this target just in
-		 * case we are out of sync for some external reason
-		 * unknown (or unreported) by the target.
-		 */
-		ahc_force_renegotiation(ahc);
-		ahc_outb(ahc, CLRINT, CLRSCSIINT);
-		ahc_unpause(ahc);
-	} else if ((status & SELTO) != 0) {
-		u_int	scbptr;
-
-		/* Stop the selection */
-		ahc_outb(ahc, SCSISEQ, 0);
-
-		/* No more pending messages */
-		ahc_clear_msg_state(ahc);
-
-		/* Clear interrupt state */
-		ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENBUSFREE);
-		ahc_outb(ahc, CLRSINT1, CLRSELTIMEO|CLRBUSFREE|CLRSCSIPERR);
-
-		/*
-		 * Although the driver does not care about the
-		 * 'Selection in Progress' status bit, the busy
-		 * LED does.  SELINGO is only cleared by a sucessfull
-		 * selection, so we must manually clear it to insure
-		 * the LED turns off just incase no future successful
-		 * selections occur (e.g. no devices on the bus).
-		 */
-		ahc_outb(ahc, CLRSINT0, CLRSELINGO);
-
-		scbptr = ahc_inb(ahc, WAITING_SCBH);
-		ahc_outb(ahc, SCBPTR, scbptr);
-		scb_index = ahc_inb(ahc, SCB_TAG);
-
-		scb = ahc_lookup_scb(ahc, scb_index);
-		if (scb == NULL) {
-			printf("%s: ahc_intr - referenced scb not "
-			       "valid during SELTO scb(%d, %d)\n",
-			       ahc_name(ahc), scbptr, scb_index);
-		} else {
-			ahc_set_transaction_status(scb, CAM_SEL_TIMEOUT);
-			ahc_freeze_devq(ahc, scb);
-		}
-		ahc_outb(ahc, CLRINT, CLRSCSIINT);
-		/*
-		 * Force a renegotiation with this target just in
-		 * case the cable was pulled and will later be
-		 * re-attached.  The target may forget its negotiation
-		 * settings with us should it attempt to reselect
-		 * during the interruption.  The target will not issue
-		 * a unit attention in this case, so we must always
-		 * renegotiate.
-		 */
-		ahc_force_renegotiation(ahc);
-		ahc_restart(ahc);
-	} else if ((status & BUSFREE) != 0
-		&& (ahc_inb(ahc, SIMODE1) & ENBUSFREE) != 0) {
-		u_int lastphase;
-		u_int saved_scsiid;
-		u_int saved_lun;
-		u_int target;
-		u_int initiator_role_id;
-		char channel;
-		int printerror;
-
-		/*
-		 * Clear our selection hardware as soon as possible.
-		 * We may have an entry in the waiting Q for this target,
-		 * that is affected by this busfree and we don't want to
-		 * go about selecting the target while we handle the event.
-		 */
-		ahc_outb(ahc, SCSISEQ,
-			 ahc_inb(ahc, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP));
-
-		/*
-		 * Disable busfree interrupts and clear the busfree
-		 * interrupt status.  We do this here so that several
-		 * bus transactions occur prior to clearing the SCSIINT
-		 * latch.  It can take a bit for the clearing to take effect.
-		 */
-		ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENBUSFREE);
-		ahc_outb(ahc, CLRSINT1, CLRBUSFREE|CLRSCSIPERR);
-
-		/*
-		 * Look at what phase we were last in.
-		 * If its message out, chances are pretty good
-		 * that the busfree was in response to one of
-		 * our abort requests.
-		 */
-		lastphase = ahc_inb(ahc, LASTPHASE);
-		saved_scsiid = ahc_inb(ahc, SAVED_SCSIID);
-		saved_lun = ahc_inb(ahc, SAVED_LUN);
-		target = SCSIID_TARGET(ahc, saved_scsiid);
-		initiator_role_id = SCSIID_OUR_ID(saved_scsiid);
-		channel = SCSIID_CHANNEL(ahc, saved_scsiid);
-		printerror = 1;
-
-		if (lastphase == P_MESGOUT) {
-			struct ahc_devinfo devinfo;
-			u_int tag;
-
-			ahc_fetch_devinfo(ahc, &devinfo);
-			tag = SCB_LIST_NULL;
-			if (ahc_sent_msg(ahc, AHCMSG_1B, MSG_ABORT_TAG, TRUE)
-			 || ahc_sent_msg(ahc, AHCMSG_1B, MSG_ABORT, TRUE)) {
-				if (ahc->msgout_buf[ahc->msgout_index - 1]
-				 == MSG_ABORT_TAG)
-					tag = scb->hscb->tag;
-				ahc_print_path(ahc, scb);
-				printf("SCB %d - Abort%s Completed.\n",
-				       scb->hscb->tag, tag == SCB_LIST_NULL ?
-				       "" : " Tag");
-				ahc_abort_scbs(ahc, target, channel,
-					       saved_lun, tag,
-					       ROLE_INITIATOR,
-					       CAM_REQ_ABORTED);
-				printerror = 0;
-			} else if (ahc_sent_msg(ahc, AHCMSG_1B,
-						MSG_BUS_DEV_RESET, TRUE)) {
-#ifdef __FreeBSD__
-				/*
-				 * Don't mark the user's request for this BDR
-				 * as completing with CAM_BDR_SENT.  CAM3
-				 * specifies CAM_REQ_CMP.
-				 */
-				if (scb != NULL
-				 && scb->io_ctx->ccb_h.func_code== XPT_RESET_DEV
-				 && ahc_match_scb(ahc, scb, target, channel,
-						  CAM_LUN_WILDCARD,
-						  SCB_LIST_NULL,
-						  ROLE_INITIATOR)) {
-					ahc_set_transaction_status(scb, CAM_REQ_CMP);
-				}
-#endif
-				ahc_compile_devinfo(&devinfo,
-						    initiator_role_id,
-						    target,
-						    CAM_LUN_WILDCARD,
-						    channel,
-						    ROLE_INITIATOR);
-				ahc_handle_devreset(ahc, &devinfo,
-						    CAM_BDR_SENT,
-						    "Bus Device Reset",
-						    /*verbose_level*/0);
-				printerror = 0;
-			} else if (ahc_sent_msg(ahc, AHCMSG_EXT,
-						MSG_EXT_PPR, FALSE)) {
-				struct ahc_initiator_tinfo *tinfo;
-				struct ahc_tmode_tstate *tstate;
-
-				/*
-				 * PPR Rejected.  Try non-ppr negotiation
-				 * and retry command.
-				 */
-				tinfo = ahc_fetch_transinfo(ahc,
-							    devinfo.channel,
-							    devinfo.our_scsiid,
-							    devinfo.target,
-							    &tstate);
-				tinfo->curr.transport_version = 2;
-				tinfo->goal.transport_version = 2;
-				tinfo->goal.ppr_options = 0;
-				ahc_qinfifo_requeue_tail(ahc, scb);
-				printerror = 0;
-			} else if (ahc_sent_msg(ahc, AHCMSG_EXT,
-						MSG_EXT_WDTR, FALSE)
-				|| ahc_sent_msg(ahc, AHCMSG_EXT,
-						MSG_EXT_SDTR, FALSE)) {
-				/*
-				 * Negotiation Rejected.  Go-async and
-				 * retry command.
-				 */
-				ahc_set_width(ahc, &devinfo,
-					      MSG_EXT_WDTR_BUS_8_BIT,
-					      AHC_TRANS_CUR|AHC_TRANS_GOAL,
-					      /*paused*/TRUE);
-				ahc_set_syncrate(ahc, &devinfo,
-						/*syncrate*/NULL,
-						/*period*/0, /*offset*/0,
-						/*ppr_options*/0,
-						AHC_TRANS_CUR|AHC_TRANS_GOAL,
-						/*paused*/TRUE);
-				ahc_qinfifo_requeue_tail(ahc, scb);
-				printerror = 0;
-			}
-		}
-		if (printerror != 0) {
-			u_int i;
-
-			if (scb != NULL) {
-				u_int tag;
-
-				if ((scb->hscb->control & TAG_ENB) != 0)
-					tag = scb->hscb->tag;
-				else
-					tag = SCB_LIST_NULL;
-				ahc_print_path(ahc, scb);
-				ahc_abort_scbs(ahc, target, channel,
-					       SCB_GET_LUN(scb), tag,
-					       ROLE_INITIATOR,
-					       CAM_UNEXP_BUSFREE);
-			} else {
-				/*
-				 * We had not fully identified this connection,
-				 * so we cannot abort anything.
-				 */
-				printf("%s: ", ahc_name(ahc));
-			}
-			for (i = 0; i < num_phases; i++) {
-				if (lastphase == ahc_phase_table[i].phase)
-					break;
-			}
-			printf("Unexpected busfree %s\n"
-			       "SEQADDR == 0x%x\n",
-			       ahc_phase_table[i].phasemsg,
-			       ahc_inb(ahc, SEQADDR0)
-				| (ahc_inb(ahc, SEQADDR1) << 8));
-		}
-		ahc_clear_msg_state(ahc);
-		ahc_outb(ahc, CLRINT, CLRSCSIINT);
-		ahc_restart(ahc);
-	} else {
-		printf("%s: Missing case in ahc_handle_scsiint. status = %x\n",
-		       ahc_name(ahc), status);
-		ahc_outb(ahc, CLRINT, CLRSCSIINT);
-	}
-}
-
-/*
- * Force renegotiation to occur the next time we initiate
- * a command to the current device.
- */
-static void
-ahc_force_renegotiation(struct ahc_softc *ahc)
-{
-	struct	ahc_devinfo devinfo;
-	struct	ahc_initiator_tinfo *targ_info;
-	struct	ahc_tmode_tstate *tstate;
-
-	ahc_fetch_devinfo(ahc, &devinfo);
-	targ_info = ahc_fetch_transinfo(ahc,
-					devinfo.channel,
-					devinfo.our_scsiid,
-					devinfo.target,
-					&tstate);
-	ahc_update_neg_request(ahc, &devinfo, tstate,
-			       targ_info, /*force*/TRUE);
-}
-
-#define AHC_MAX_STEPS 2000
-void
-ahc_clear_critical_section(struct ahc_softc *ahc)
-{
-	int	stepping;
-	int	steps;
-	u_int	simode0;
-	u_int	simode1;
-
-	if (ahc->num_critical_sections == 0)
-		return;
-
-	stepping = FALSE;
-	steps = 0;
-	simode0 = 0;
-	simode1 = 0;
-	for (;;) {
-		struct	cs *cs;
-		u_int	seqaddr;
-		u_int	i;
-
-		seqaddr = ahc_inb(ahc, SEQADDR0)
-			| (ahc_inb(ahc, SEQADDR1) << 8);
-
-		/*
-		 * Seqaddr represents the next instruction to execute, 
-		 * so we are really executing the instruction just
-		 * before it.
-		 */
-		if (seqaddr != 0)
-			seqaddr -= 1;
-		cs = ahc->critical_sections;
-		for (i = 0; i < ahc->num_critical_sections; i++, cs++) {
-			
-			if (cs->begin < seqaddr && cs->end >= seqaddr)
-				break;
-		}
-
-		if (i == ahc->num_critical_sections)
-			break;
-
-		if (steps > AHC_MAX_STEPS) {
-			printf("%s: Infinite loop in critical section\n",
-			       ahc_name(ahc));
-			ahc_dump_card_state(ahc);
-			panic("critical section loop");
-		}
-
-		steps++;
-		if (stepping == FALSE) {
-
-			/*
-			 * Disable all interrupt sources so that the
-			 * sequencer will not be stuck by a pausing
-			 * interrupt condition while we attempt to
-			 * leave a critical section.
-			 */
-			simode0 = ahc_inb(ahc, SIMODE0);
-			ahc_outb(ahc, SIMODE0, 0);
-			simode1 = ahc_inb(ahc, SIMODE1);
-			ahc_outb(ahc, SIMODE1, 0);
-			ahc_outb(ahc, CLRINT, CLRSCSIINT);
-			ahc_outb(ahc, SEQCTL, ahc_inb(ahc, SEQCTL) | STEP);
-			stepping = TRUE;
-		}
-		ahc_outb(ahc, HCNTRL, ahc->unpause);
-		do {
-			ahc_delay(200);
-		} while (!ahc_is_paused(ahc));
-	}
-	if (stepping) {
-		ahc_outb(ahc, SIMODE0, simode0);
-		ahc_outb(ahc, SIMODE1, simode1);
-		ahc_outb(ahc, SEQCTL, ahc_inb(ahc, SEQCTL) & ~STEP);
-	}
-}
-
-/*
- * Clear any pending interrupt status.
- */
-void
-ahc_clear_intstat(struct ahc_softc *ahc)
-{
-	/* Clear any interrupt conditions this may have caused */
-	ahc_outb(ahc, CLRSINT1, CLRSELTIMEO|CLRATNO|CLRSCSIRSTI
-				|CLRBUSFREE|CLRSCSIPERR|CLRPHASECHG|
-				CLRREQINIT);
-	ahc_outb(ahc, CLRSINT0, CLRSELDO|CLRSELDI|CLRSELINGO);
-	ahc_outb(ahc, CLRINT, CLRSCSIINT);
-}
-
-/**************************** Debugging Routines ******************************/
-void
-ahc_print_scb(struct scb *scb)
-{
-	int i;
-
-	struct hardware_scb *hscb = scb->hscb;
-
-	printf("scb:%p control:0x%x scsiid:0x%x lun:%d cdb_len:%d\n",
-	       (void *)scb,
-	       hscb->control,
-	       hscb->scsiid,
-	       hscb->lun,
-	       hscb->cdb_len);
-	printf("Shared Data: ");
-	for (i = 0; i < sizeof(hscb->shared_data.cdb); i++)
-		printf("%#02x", hscb->shared_data.cdb[i]);
-	printf("        dataptr:%#x datacnt:%#x sgptr:%#x tag:%#x\n",
-		ahc_le32toh(hscb->dataptr),
-		ahc_le32toh(hscb->datacnt),
-		ahc_le32toh(hscb->sgptr),
-		hscb->tag);
-	if (scb->sg_count > 0) {
-		for (i = 0; i < scb->sg_count; i++) {
-			printf("sg[%d] - Addr 0x%x%x : Length %d\n",
-			       i,
-			       (ahc_le32toh(scb->sg_list[i].len) >> 24
-			        & SG_HIGH_ADDR_BITS),
-			       ahc_le32toh(scb->sg_list[i].addr),
-			       ahc_le32toh(scb->sg_list[i].len));
-		}
-	}
-}
-
-/************************* Transfer Negotiation *******************************/
-/*
- * Allocate per target mode instance (ID we respond to as a target)
- * transfer negotiation data structures.
- */
-static struct ahc_tmode_tstate *
-ahc_alloc_tstate(struct ahc_softc *ahc, u_int scsi_id, char channel)
-{
-	struct ahc_tmode_tstate *master_tstate;
-	struct ahc_tmode_tstate *tstate;
-	int i;
-
-	master_tstate = ahc->enabled_targets[ahc->our_id];
-	if (channel == 'B') {
-		scsi_id += 8;
-		master_tstate = ahc->enabled_targets[ahc->our_id_b + 8];
-	}
-	if (ahc->enabled_targets[scsi_id] != NULL
-	 && ahc->enabled_targets[scsi_id] != master_tstate)
-		panic("%s: ahc_alloc_tstate - Target already allocated",
-		      ahc_name(ahc));
-	tstate = malloc(sizeof(*tstate), M_DEVBUF, M_NOWAIT);
-	if (tstate == NULL)
-		return (NULL);
-
-	/*
-	 * If we have allocated a master tstate, copy user settings from
-	 * the master tstate (taken from SRAM or the EEPROM) for this
-	 * channel, but reset our current and goal settings to async/narrow
-	 * until an initiator talks to us.
-	 */
-	if (master_tstate != NULL) {
-		memcpy(tstate, master_tstate, sizeof(*tstate));
-		memset(tstate->enabled_luns, 0, sizeof(tstate->enabled_luns));
-		tstate->ultraenb = 0;
-		for (i = 0; i < 16; i++) {
-			memset(&tstate->transinfo[i].curr, 0,
-			      sizeof(tstate->transinfo[i].curr));
-			memset(&tstate->transinfo[i].goal, 0,
-			      sizeof(tstate->transinfo[i].goal));
-		}
-	} else
-		memset(tstate, 0, sizeof(*tstate));
-	ahc->enabled_targets[scsi_id] = tstate;
-	return (tstate);
-}
-
-#ifdef AHC_TARGET_MODE
-/*
- * Free per target mode instance (ID we respond to as a target)
- * transfer negotiation data structures.
- */
-static void
-ahc_free_tstate(struct ahc_softc *ahc, u_int scsi_id, char channel, int force)
-{
-	struct ahc_tmode_tstate *tstate;
-
-	/*
-	 * Don't clean up our "master" tstate.
-	 * It has our default user settings.
-	 */
-	if (((channel == 'B' && scsi_id == ahc->our_id_b)
-	  || (channel == 'A' && scsi_id == ahc->our_id))
-	 && force == FALSE)
-		return;
-
-	if (channel == 'B')
-		scsi_id += 8;
-	tstate = ahc->enabled_targets[scsi_id];
-	if (tstate != NULL)
-		free(tstate, M_DEVBUF);
-	ahc->enabled_targets[scsi_id] = NULL;
-}
-#endif
-
-/*
- * Called when we have an active connection to a target on the bus,
- * this function finds the nearest syncrate to the input period limited
- * by the capabilities of the bus connectivity of and sync settings for
- * the target.
- */
-struct ahc_syncrate *
-ahc_devlimited_syncrate(struct ahc_softc *ahc,
-			struct ahc_initiator_tinfo *tinfo,
-			u_int *period, u_int *ppr_options, role_t role) {
-	struct	ahc_transinfo *transinfo;
-	u_int	maxsync;
-
-	if ((ahc->features & AHC_ULTRA2) != 0) {
-		if ((ahc_inb(ahc, SBLKCTL) & ENAB40) != 0
-		 && (ahc_inb(ahc, SSTAT2) & EXP_ACTIVE) == 0) {
-			maxsync = AHC_SYNCRATE_DT;
-		} else {
-			maxsync = AHC_SYNCRATE_ULTRA;
-			/* Can't do DT on an SE bus */
-			*ppr_options &= ~MSG_EXT_PPR_DT_REQ;
-		}
-	} else if ((ahc->features & AHC_ULTRA) != 0) {
-		maxsync = AHC_SYNCRATE_ULTRA;
-	} else {
-		maxsync = AHC_SYNCRATE_FAST;
-	}
-	/*
-	 * Never allow a value higher than our current goal
-	 * period otherwise we may allow a target initiated
-	 * negotiation to go above the limit as set by the
-	 * user.  In the case of an initiator initiated
-	 * sync negotiation, we limit based on the user
-	 * setting.  This allows the system to still accept
-	 * incoming negotiations even if target initiated
-	 * negotiation is not performed.
-	 */
-	if (role == ROLE_TARGET)
-		transinfo = &tinfo->user;
-	else 
-		transinfo = &tinfo->goal;
-	*ppr_options &= transinfo->ppr_options;
-	if (transinfo->period == 0) {
-		*period = 0;
-		*ppr_options = 0;
-		return (NULL);
-	}
-	*period = MAX(*period, transinfo->period);
-	return (ahc_find_syncrate(ahc, period, ppr_options, maxsync));
-}
-
-/*
- * Look up the valid period to SCSIRATE conversion in our table.
- * Return the period and offset that should be sent to the target
- * if this was the beginning of an SDTR.
- */
-struct ahc_syncrate *
-ahc_find_syncrate(struct ahc_softc *ahc, u_int *period,
-		  u_int *ppr_options, u_int maxsync)
-{
-	struct ahc_syncrate *syncrate;
-
-	if ((ahc->features & AHC_DT) == 0)
-		*ppr_options &= ~MSG_EXT_PPR_DT_REQ;
-
-	/* Skip all DT only entries if DT is not available */
-	if ((*ppr_options & MSG_EXT_PPR_DT_REQ) == 0
-	 && maxsync < AHC_SYNCRATE_ULTRA2)
-		maxsync = AHC_SYNCRATE_ULTRA2;
-	
-	for (syncrate = &ahc_syncrates[maxsync];
-	     syncrate->rate != NULL;
-	     syncrate++) {
-
-		/*
-		 * The Ultra2 table doesn't go as low
-		 * as for the Fast/Ultra cards.
-		 */
-		if ((ahc->features & AHC_ULTRA2) != 0
-		 && (syncrate->sxfr_u2 == 0))
-			break;
-
-		if (*period <= syncrate->period) {
-			/*
-			 * When responding to a target that requests
-			 * sync, the requested rate may fall between
-			 * two rates that we can output, but still be
-			 * a rate that we can receive.  Because of this,
-			 * we want to respond to the target with
-			 * the same rate that it sent to us even
-			 * if the period we use to send data to it
-			 * is lower.  Only lower the response period
-			 * if we must.
-			 */
-			if (syncrate == &ahc_syncrates[maxsync])
-				*period = syncrate->period;
-
-			/*
-			 * At some speeds, we only support
-			 * ST transfers.
-			 */
-		 	if ((syncrate->sxfr_u2 & ST_SXFR) != 0)
-				*ppr_options &= ~MSG_EXT_PPR_DT_REQ;
-			break;
-		}
-	}
-
-	if ((*period == 0)
-	 || (syncrate->rate == NULL)
-	 || ((ahc->features & AHC_ULTRA2) != 0
-	  && (syncrate->sxfr_u2 == 0))) {
-		/* Use asynchronous transfers. */
-		*period = 0;
-		syncrate = NULL;
-		*ppr_options &= ~MSG_EXT_PPR_DT_REQ;
-	}
-	return (syncrate);
-}
-
-/*
- * Convert from an entry in our syncrate table to the SCSI equivalent
- * sync "period" factor.
- */
-u_int
-ahc_find_period(struct ahc_softc *ahc, u_int scsirate, u_int maxsync)
-{
-	struct ahc_syncrate *syncrate;
-
-	if ((ahc->features & AHC_ULTRA2) != 0)
-		scsirate &= SXFR_ULTRA2;
-	else
-		scsirate &= SXFR;
-
-	syncrate = &ahc_syncrates[maxsync];
-	while (syncrate->rate != NULL) {
-
-		if ((ahc->features & AHC_ULTRA2) != 0) {
-			if (syncrate->sxfr_u2 == 0)
-				break;
-			else if (scsirate == (syncrate->sxfr_u2 & SXFR_ULTRA2))
-				return (syncrate->period);
-		} else if (scsirate == (syncrate->sxfr & SXFR)) {
-				return (syncrate->period);
-		}
-		syncrate++;
-	}
-	return (0); /* async */
-}
-
-/*
- * Truncate the given synchronous offset to a value the
- * current adapter type and syncrate are capable of.
- */
-void
-ahc_validate_offset(struct ahc_softc *ahc,
-		    struct ahc_initiator_tinfo *tinfo,
-		    struct ahc_syncrate *syncrate,
-		    u_int *offset, int wide, role_t role)
-{
-	u_int maxoffset;
-
-	/* Limit offset to what we can do */
-	if (syncrate == NULL) {
-		maxoffset = 0;
-	} else if ((ahc->features & AHC_ULTRA2) != 0) {
-		maxoffset = MAX_OFFSET_ULTRA2;
-	} else {
-		if (wide)
-			maxoffset = MAX_OFFSET_16BIT;
-		else
-			maxoffset = MAX_OFFSET_8BIT;
-	}
-	*offset = MIN(*offset, maxoffset);
-	if (tinfo != NULL) {
-		if (role == ROLE_TARGET)
-			*offset = MIN(*offset, tinfo->user.offset);
-		else
-			*offset = MIN(*offset, tinfo->goal.offset);
-	}
-}
-
-/*
- * Truncate the given transfer width parameter to a value the
- * current adapter type is capable of.
- */
-void
-ahc_validate_width(struct ahc_softc *ahc, struct ahc_initiator_tinfo *tinfo,
-		   u_int *bus_width, role_t role)
-{
-	switch (*bus_width) {
-	default:
-		if (ahc->features & AHC_WIDE) {
-			/* Respond Wide */
-			*bus_width = MSG_EXT_WDTR_BUS_16_BIT;
-			break;
-		}
-		/* FALLTHROUGH */
-	case MSG_EXT_WDTR_BUS_8_BIT:
-		*bus_width = MSG_EXT_WDTR_BUS_8_BIT;
-		break;
-	}
-	if (tinfo != NULL) {
-		if (role == ROLE_TARGET)
-			*bus_width = MIN(tinfo->user.width, *bus_width);
-		else
-			*bus_width = MIN(tinfo->goal.width, *bus_width);
-	}
-}
-
-/*
- * Update the bitmask of targets for which the controller should
- * negotiate with at the next convenient oportunity.  This currently
- * means the next time we send the initial identify messages for
- * a new transaction.
- */
-int
-ahc_update_neg_request(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
-		       struct ahc_tmode_tstate *tstate,
-		       struct ahc_initiator_tinfo *tinfo, int force)
-{
-	u_int auto_negotiate_orig;
-
-	auto_negotiate_orig = tstate->auto_negotiate;
-	if (tinfo->curr.period != tinfo->goal.period
-	 || tinfo->curr.width != tinfo->goal.width
-	 || tinfo->curr.offset != tinfo->goal.offset
-	 || tinfo->curr.ppr_options != tinfo->goal.ppr_options
-	 || (force
-	  && (tinfo->goal.period != 0
-	   || tinfo->goal.width != MSG_EXT_WDTR_BUS_8_BIT
-	   || tinfo->goal.ppr_options != 0)))
-		tstate->auto_negotiate |= devinfo->target_mask;
-	else
-		tstate->auto_negotiate &= ~devinfo->target_mask;
-
-	return (auto_negotiate_orig != tstate->auto_negotiate);
-}
-
-/*
- * Update the user/goal/curr tables of synchronous negotiation
- * parameters as well as, in the case of a current or active update,
- * any data structures on the host controller.  In the case of an
- * active update, the specified target is currently talking to us on
- * the bus, so the transfer parameter update must take effect
- * immediately.
- */
-void
-ahc_set_syncrate(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
-		 struct ahc_syncrate *syncrate, u_int period,
-		 u_int offset, u_int ppr_options, u_int type, int paused)
-{
-	struct	ahc_initiator_tinfo *tinfo;
-	struct	ahc_tmode_tstate *tstate;
-	u_int	old_period;
-	u_int	old_offset;
-	u_int	old_ppr;
-	int	active;
-	int	update_needed;
-
-	active = (type & AHC_TRANS_ACTIVE) == AHC_TRANS_ACTIVE;
-	update_needed = 0;
-
-	if (syncrate == NULL) {
-		period = 0;
-		offset = 0;
-	}
-
-	tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
-				    devinfo->target, &tstate);
-
-	if ((type & AHC_TRANS_USER) != 0) {
-		tinfo->user.period = period;
-		tinfo->user.offset = offset;
-		tinfo->user.ppr_options = ppr_options;
-	}
-
-	if ((type & AHC_TRANS_GOAL) != 0) {
-		tinfo->goal.period = period;
-		tinfo->goal.offset = offset;
-		tinfo->goal.ppr_options = ppr_options;
-	}
-
-	old_period = tinfo->curr.period;
-	old_offset = tinfo->curr.offset;
-	old_ppr	   = tinfo->curr.ppr_options;
-
-	if ((type & AHC_TRANS_CUR) != 0
-	 && (old_period != period
-	  || old_offset != offset
-	  || old_ppr != ppr_options)) {
-		u_int	scsirate;
-
-		update_needed++;
-		scsirate = tinfo->scsirate;
-		if ((ahc->features & AHC_ULTRA2) != 0) {
-
-			scsirate &= ~(SXFR_ULTRA2|SINGLE_EDGE|ENABLE_CRC);
-			if (syncrate != NULL) {
-				scsirate |= syncrate->sxfr_u2;
-				if ((ppr_options & MSG_EXT_PPR_DT_REQ) != 0)
-					scsirate |= ENABLE_CRC;
-				else
-					scsirate |= SINGLE_EDGE;
-			}
-		} else {
-
-			scsirate &= ~(SXFR|SOFS);
-			/*
-			 * Ensure Ultra mode is set properly for
-			 * this target.
-			 */
-			tstate->ultraenb &= ~devinfo->target_mask;
-			if (syncrate != NULL) {
-				if (syncrate->sxfr & ULTRA_SXFR) {
-					tstate->ultraenb |=
-						devinfo->target_mask;
-				}
-				scsirate |= syncrate->sxfr & SXFR;
-				scsirate |= offset & SOFS;
-			}
-			if (active) {
-				u_int sxfrctl0;
-
-				sxfrctl0 = ahc_inb(ahc, SXFRCTL0);
-				sxfrctl0 &= ~FAST20;
-				if (tstate->ultraenb & devinfo->target_mask)
-					sxfrctl0 |= FAST20;
-				ahc_outb(ahc, SXFRCTL0, sxfrctl0);
-			}
-		}
-		if (active) {
-			ahc_outb(ahc, SCSIRATE, scsirate);
-			if ((ahc->features & AHC_ULTRA2) != 0)
-				ahc_outb(ahc, SCSIOFFSET, offset);
-		}
-
-		tinfo->scsirate = scsirate;
-		tinfo->curr.period = period;
-		tinfo->curr.offset = offset;
-		tinfo->curr.ppr_options = ppr_options;
-
-		ahc_send_async(ahc, devinfo->channel, devinfo->target,
-			       CAM_LUN_WILDCARD, AC_TRANSFER_NEG, NULL);
-		if (bootverbose) {
-			if (offset != 0) {
-				printf("%s: target %d synchronous at %sMHz%s, "
-				       "offset = 0x%x\n", ahc_name(ahc),
-				       devinfo->target, syncrate->rate,
-				       (ppr_options & MSG_EXT_PPR_DT_REQ)
-				       ? " DT" : "", offset);
-			} else {
-				printf("%s: target %d using "
-				       "asynchronous transfers\n",
-				       ahc_name(ahc), devinfo->target);
-			}
-		}
-	}
-
-	update_needed += ahc_update_neg_request(ahc, devinfo, tstate,
-						tinfo, /*force*/FALSE);
-
-	if (update_needed)
-		ahc_update_pending_scbs(ahc);
-}
-
-/*
- * Update the user/goal/curr tables of wide negotiation
- * parameters as well as, in the case of a current or active update,
- * any data structures on the host controller.  In the case of an
- * active update, the specified target is currently talking to us on
- * the bus, so the transfer parameter update must take effect
- * immediately.
- */
-void
-ahc_set_width(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
-	      u_int width, u_int type, int paused)
-{
-	struct	ahc_initiator_tinfo *tinfo;
-	struct	ahc_tmode_tstate *tstate;
-	u_int	oldwidth;
-	int	active;
-	int	update_needed;
-
-	active = (type & AHC_TRANS_ACTIVE) == AHC_TRANS_ACTIVE;
-	update_needed = 0;
-	tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
-				    devinfo->target, &tstate);
-
-	if ((type & AHC_TRANS_USER) != 0)
-		tinfo->user.width = width;
-
-	if ((type & AHC_TRANS_GOAL) != 0)
-		tinfo->goal.width = width;
-
-	oldwidth = tinfo->curr.width;
-	if ((type & AHC_TRANS_CUR) != 0 && oldwidth != width) {
-		u_int	scsirate;
-
-		update_needed++;
-		scsirate =  tinfo->scsirate;
-		scsirate &= ~WIDEXFER;
-		if (width == MSG_EXT_WDTR_BUS_16_BIT)
-			scsirate |= WIDEXFER;
-
-		tinfo->scsirate = scsirate;
-
-		if (active)
-			ahc_outb(ahc, SCSIRATE, scsirate);
-
-		tinfo->curr.width = width;
-
-		ahc_send_async(ahc, devinfo->channel, devinfo->target,
-			       CAM_LUN_WILDCARD, AC_TRANSFER_NEG, NULL);
-		if (bootverbose) {
-			printf("%s: target %d using %dbit transfers\n",
-			       ahc_name(ahc), devinfo->target,
-			       8 * (0x01 << width));
-		}
-	}
-
-	update_needed += ahc_update_neg_request(ahc, devinfo, tstate,
-						tinfo, /*force*/FALSE);
-	if (update_needed)
-		ahc_update_pending_scbs(ahc);
-}
-
-/*
- * Update the current state of tagged queuing for a given target.
- */
-void
-ahc_set_tags(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
-	     ahc_queue_alg alg)
-{
- 	ahc_platform_set_tags(ahc, devinfo, alg);
- 	ahc_send_async(ahc, devinfo->channel, devinfo->target,
- 		       devinfo->lun, AC_TRANSFER_NEG, &alg);
-}
-
-/*
- * When the transfer settings for a connection change, update any
- * in-transit SCBs to contain the new data so the hardware will
- * be set correctly during future (re)selections.
- */
-static void
-ahc_update_pending_scbs(struct ahc_softc *ahc)
-{
-	struct	scb *pending_scb;
-	int	pending_scb_count;
-	int	i;
-	int	paused;
-	u_int	saved_scbptr;
-
-	/*
-	 * Traverse the pending SCB list and ensure that all of the
-	 * SCBs there have the proper settings.
-	 */
-	pending_scb_count = 0;
-	LIST_FOREACH(pending_scb, &ahc->pending_scbs, pending_links) {
-		struct ahc_devinfo devinfo;
-		struct hardware_scb *pending_hscb;
-		struct ahc_initiator_tinfo *tinfo;
-		struct ahc_tmode_tstate *tstate;
-
-		ahc_scb_devinfo(ahc, &devinfo, pending_scb);
-		tinfo = ahc_fetch_transinfo(ahc, devinfo.channel,
-					    devinfo.our_scsiid,
-					    devinfo.target, &tstate);
-		pending_hscb = pending_scb->hscb;
-		pending_hscb->control &= ~ULTRAENB;
-		if ((tstate->ultraenb & devinfo.target_mask) != 0)
-			pending_hscb->control |= ULTRAENB;
-		pending_hscb->scsirate = tinfo->scsirate;
-		pending_hscb->scsioffset = tinfo->curr.offset;
-		if ((tstate->auto_negotiate & devinfo.target_mask) == 0
-		 && (pending_scb->flags & SCB_AUTO_NEGOTIATE) != 0) {
-			pending_scb->flags &= ~SCB_AUTO_NEGOTIATE;
-			pending_hscb->control &= ~MK_MESSAGE;
-		}
-		ahc_sync_scb(ahc, pending_scb,
-			     BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
-		pending_scb_count++;
-	}
-
-	if (pending_scb_count == 0)
-		return;
-
-	if (ahc_is_paused(ahc)) {
-		paused = 1;
-	} else {
-		paused = 0;
-		ahc_pause(ahc);
-	}
-
-	saved_scbptr = ahc_inb(ahc, SCBPTR);
-	/* Ensure that the hscbs down on the card match the new information */
-	for (i = 0; i < ahc->scb_data->maxhscbs; i++) {
-		struct	hardware_scb *pending_hscb;
-		u_int	control;
-		u_int	scb_tag;
-
-		ahc_outb(ahc, SCBPTR, i);
-		scb_tag = ahc_inb(ahc, SCB_TAG);
-		pending_scb = ahc_lookup_scb(ahc, scb_tag);
-		if (pending_scb == NULL)
-			continue;
-
-		pending_hscb = pending_scb->hscb;
-		control = ahc_inb(ahc, SCB_CONTROL);
-		control &= ~(ULTRAENB|MK_MESSAGE);
-		control |= pending_hscb->control & (ULTRAENB|MK_MESSAGE);
-		ahc_outb(ahc, SCB_CONTROL, control);
-		ahc_outb(ahc, SCB_SCSIRATE, pending_hscb->scsirate);
-		ahc_outb(ahc, SCB_SCSIOFFSET, pending_hscb->scsioffset);
-	}
-	ahc_outb(ahc, SCBPTR, saved_scbptr);
-
-	if (paused == 0)
-		ahc_unpause(ahc);
-}
-
-/**************************** Pathing Information *****************************/
-static void
-ahc_fetch_devinfo(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
-{
-	u_int	saved_scsiid;
-	role_t	role;
-	int	our_id;
-
-	if (ahc_inb(ahc, SSTAT0) & TARGET)
-		role = ROLE_TARGET;
-	else
-		role = ROLE_INITIATOR;
-
-	if (role == ROLE_TARGET
-	 && (ahc->features & AHC_MULTI_TID) != 0
-	 && (ahc_inb(ahc, SEQ_FLAGS) & CMDPHASE_PENDING) != 0) {
-		/* We were selected, so pull our id from TARGIDIN */
-		our_id = ahc_inb(ahc, TARGIDIN) & OID;
-	} else if ((ahc->features & AHC_ULTRA2) != 0)
-		our_id = ahc_inb(ahc, SCSIID_ULTRA2) & OID;
-	else
-		our_id = ahc_inb(ahc, SCSIID) & OID;
-
-	saved_scsiid = ahc_inb(ahc, SAVED_SCSIID);
-	ahc_compile_devinfo(devinfo,
-			    our_id,
-			    SCSIID_TARGET(ahc, saved_scsiid),
-			    ahc_inb(ahc, SAVED_LUN),
-			    SCSIID_CHANNEL(ahc, saved_scsiid),
-			    role);
-}
-
-struct ahc_phase_table_entry*
-ahc_lookup_phase_entry(int phase)
-{
-	struct ahc_phase_table_entry *entry;
-	struct ahc_phase_table_entry *last_entry;
-
-	/*
-	 * num_phases doesn't include the default entry which
-	 * will be returned if the phase doesn't match.
-	 */
-	last_entry = &ahc_phase_table[num_phases];
-	for (entry = ahc_phase_table; entry < last_entry; entry++) {
-		if (phase == entry->phase)
-			break;
-	}
-	return (entry);
-}
-
-void
-ahc_compile_devinfo(struct ahc_devinfo *devinfo, u_int our_id, u_int target,
-		    u_int lun, char channel, role_t role)
-{
-	devinfo->our_scsiid = our_id;
-	devinfo->target = target;
-	devinfo->lun = lun;
-	devinfo->target_offset = target;
-	devinfo->channel = channel;
-	devinfo->role = role;
-	if (channel == 'B')
-		devinfo->target_offset += 8;
-	devinfo->target_mask = (0x01 << devinfo->target_offset);
-}
-
-static void
-ahc_scb_devinfo(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
-		struct scb *scb)
-{
-	role_t	role;
-	int	our_id;
-
-	our_id = SCSIID_OUR_ID(scb->hscb->scsiid);
-	role = ROLE_INITIATOR;
-	if ((scb->hscb->control & TARGET_SCB) != 0)
-		role = ROLE_TARGET;
-	ahc_compile_devinfo(devinfo, our_id, SCB_GET_TARGET(ahc, scb),
-			    SCB_GET_LUN(scb), SCB_GET_CHANNEL(ahc, scb), role);
-}
-
-
-/************************ Message Phase Processing ****************************/
-static void
-ahc_assert_atn(struct ahc_softc *ahc)
-{
-	u_int scsisigo;
-
-	scsisigo = ATNO;
-	if ((ahc->features & AHC_DT) == 0)
-		scsisigo |= ahc_inb(ahc, SCSISIGI);
-	ahc_outb(ahc, SCSISIGO, scsisigo);
-}
-
-/*
- * When an initiator transaction with the MK_MESSAGE flag either reconnects
- * or enters the initial message out phase, we are interrupted.  Fill our
- * outgoing message buffer with the appropriate message and beging handing
- * the message phase(s) manually.
- */
-static void
-ahc_setup_initiator_msgout(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
-			   struct scb *scb)
-{
-	/*
-	 * To facilitate adding multiple messages together,
-	 * each routine should increment the index and len
-	 * variables instead of setting them explicitly.
-	 */
-	ahc->msgout_index = 0;
-	ahc->msgout_len = 0;
-
-	if ((scb->flags & SCB_DEVICE_RESET) == 0
-	 && ahc_inb(ahc, MSG_OUT) == MSG_IDENTIFYFLAG) {
-		u_int identify_msg;
-
-		identify_msg = MSG_IDENTIFYFLAG | SCB_GET_LUN(scb);
-		if ((scb->hscb->control & DISCENB) != 0)
-			identify_msg |= MSG_IDENTIFY_DISCFLAG;
-		ahc->msgout_buf[ahc->msgout_index++] = identify_msg;
-		ahc->msgout_len++;
-
-		if ((scb->hscb->control & TAG_ENB) != 0) {
-			ahc->msgout_buf[ahc->msgout_index++] =
-			    scb->hscb->control & (TAG_ENB|SCB_TAG_TYPE);
-			ahc->msgout_buf[ahc->msgout_index++] = scb->hscb->tag;
-			ahc->msgout_len += 2;
-		}
-	}
-
-	if (scb->flags & SCB_DEVICE_RESET) {
-		ahc->msgout_buf[ahc->msgout_index++] = MSG_BUS_DEV_RESET;
-		ahc->msgout_len++;
-		ahc_print_path(ahc, scb);
-		printf("Bus Device Reset Message Sent\n");
-		/*
-		 * Clear our selection hardware in advance of
-		 * the busfree.  We may have an entry in the waiting
-		 * Q for this target, and we don't want to go about
-		 * selecting while we handle the busfree and blow it
-		 * away.
-		 */
-		ahc_outb(ahc, SCSISEQ, (ahc_inb(ahc, SCSISEQ) & ~ENSELO));
-	} else if ((scb->flags & SCB_ABORT) != 0) {
-		if ((scb->hscb->control & TAG_ENB) != 0)
-			ahc->msgout_buf[ahc->msgout_index++] = MSG_ABORT_TAG;
-		else
-			ahc->msgout_buf[ahc->msgout_index++] = MSG_ABORT;
-		ahc->msgout_len++;
-		ahc_print_path(ahc, scb);
-		printf("Abort%s Message Sent\n",
-		       (scb->hscb->control & TAG_ENB) != 0 ? " Tag" : "");
-		/*
-		 * Clear our selection hardware in advance of
-		 * the busfree.  We may have an entry in the waiting
-		 * Q for this target, and we don't want to go about
-		 * selecting while we handle the busfree and blow it
-		 * away.
-		 */
-		ahc_outb(ahc, SCSISEQ, (ahc_inb(ahc, SCSISEQ) & ~ENSELO));
-	} else if ((scb->flags & (SCB_AUTO_NEGOTIATE|SCB_NEGOTIATE)) != 0) {
-		ahc_build_transfer_msg(ahc, devinfo);
-	} else {
-		printf("ahc_intr: AWAITING_MSG for an SCB that "
-		       "does not have a waiting message\n");
-		printf("SCSIID = %x, target_mask = %x\n", scb->hscb->scsiid,
-		       devinfo->target_mask);
-		panic("SCB = %d, SCB Control = %x, MSG_OUT = %x "
-		      "SCB flags = %x", scb->hscb->tag, scb->hscb->control,
-		      ahc_inb(ahc, MSG_OUT), scb->flags);
-	}
-
-	/*
-	 * Clear the MK_MESSAGE flag from the SCB so we aren't
-	 * asked to send this message again.
-	 */
-	ahc_outb(ahc, SCB_CONTROL, ahc_inb(ahc, SCB_CONTROL) & ~MK_MESSAGE);
-	scb->hscb->control &= ~MK_MESSAGE;
-	ahc->msgout_index = 0;
-	ahc->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
-}
-
-/*
- * Build an appropriate transfer negotiation message for the
- * currently active target.
- */
-static void
-ahc_build_transfer_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
-{
-	/*
-	 * We need to initiate transfer negotiations.
-	 * If our current and goal settings are identical,
-	 * we want to renegotiate due to a check condition.
-	 */
-	struct	ahc_initiator_tinfo *tinfo;
-	struct	ahc_tmode_tstate *tstate;
-	struct	ahc_syncrate *rate;
-	int	dowide;
-	int	dosync;
-	int	doppr;
-	int	use_ppr;
-	u_int	period;
-	u_int	ppr_options;
-	u_int	offset;
-
-	tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
-				    devinfo->target, &tstate);
-	/*
-	 * Filter our period based on the current connection.
-	 * If we can't perform DT transfers on this segment (not in LVD
-	 * mode for instance), then our decision to issue a PPR message
-	 * may change.
-	 */
-	period = tinfo->goal.period;
-	ppr_options = tinfo->goal.ppr_options;
-	/* Target initiated PPR is not allowed in the SCSI spec */
-	if (devinfo->role == ROLE_TARGET)
-		ppr_options = 0;
-	rate = ahc_devlimited_syncrate(ahc, tinfo, &period,
-				       &ppr_options, devinfo->role);
-	dowide = tinfo->curr.width != tinfo->goal.width;
-	dosync = tinfo->curr.period != period;
-	doppr = tinfo->curr.ppr_options != ppr_options;
-
-	if (!dowide && !dosync && !doppr) {
-		dowide = tinfo->goal.width != MSG_EXT_WDTR_BUS_8_BIT;
-		dosync = tinfo->goal.period != 0;
-		doppr = tinfo->goal.ppr_options != 0;
-	}
-
-	if (!dowide && !dosync && !doppr) {
-		panic("ahc_intr: AWAITING_MSG for negotiation, "
-		      "but no negotiation needed\n");	
-	}
-
-	use_ppr = (tinfo->curr.transport_version >= 3) || doppr;
-	/* Target initiated PPR is not allowed in the SCSI spec */
-	if (devinfo->role == ROLE_TARGET)
-		use_ppr = 0;
-
-	/*
-	 * Both the PPR message and SDTR message require the
-	 * goal syncrate to be limited to what the target device
-	 * is capable of handling (based on whether an LVD->SE
-	 * expander is on the bus), so combine these two cases.
-	 * Regardless, guarantee that if we are using WDTR and SDTR
-	 * messages that WDTR comes first.
-	 */
-	if (use_ppr || (dosync && !dowide)) {
-
-		offset = tinfo->goal.offset;
-		ahc_validate_offset(ahc, tinfo, rate, &offset,
-				    use_ppr ? tinfo->goal.width
-					    : tinfo->curr.width,
-				    devinfo->role);
-		if (use_ppr) {
-			ahc_construct_ppr(ahc, devinfo, period, offset,
-					  tinfo->goal.width, ppr_options);
-		} else {
-			ahc_construct_sdtr(ahc, devinfo, period, offset);
-		}
-	} else {
-		ahc_construct_wdtr(ahc, devinfo, tinfo->goal.width);
-	}
-}
-
-/*
- * Build a synchronous negotiation message in our message
- * buffer based on the input parameters.
- */
-static void
-ahc_construct_sdtr(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
-		   u_int period, u_int offset)
-{
-	ahc->msgout_buf[ahc->msgout_index++] = MSG_EXTENDED;
-	ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_SDTR_LEN;
-	ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_SDTR;
-	ahc->msgout_buf[ahc->msgout_index++] = period;
-	ahc->msgout_buf[ahc->msgout_index++] = offset;
-	ahc->msgout_len += 5;
-	if (bootverbose) {
-		printf("(%s:%c:%d:%d): Sending SDTR period %x, offset %x\n",
-		       ahc_name(ahc), devinfo->channel, devinfo->target,
-		       devinfo->lun, period, offset);
-	}
-}
-
-/*
- * Build a wide negotiation message in our message
- * buffer based on the input parameters.
- */
-static void
-ahc_construct_wdtr(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
-		   u_int bus_width)
-{
-	ahc->msgout_buf[ahc->msgout_index++] = MSG_EXTENDED;
-	ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_WDTR_LEN;
-	ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_WDTR;
-	ahc->msgout_buf[ahc->msgout_index++] = bus_width;
-	ahc->msgout_len += 4;
-	if (bootverbose) {
-		printf("(%s:%c:%d:%d): Sending WDTR %x\n",
-		       ahc_name(ahc), devinfo->channel, devinfo->target,
-		       devinfo->lun, bus_width);
-	}
-}
-
-/*
- * Build a parallel protocol request message in our message
- * buffer based on the input parameters.
- */
-static void
-ahc_construct_ppr(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
-		  u_int period, u_int offset, u_int bus_width,
-		  u_int ppr_options)
-{
-	ahc->msgout_buf[ahc->msgout_index++] = MSG_EXTENDED;
-	ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_PPR_LEN;
-	ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_PPR;
-	ahc->msgout_buf[ahc->msgout_index++] = period;
-	ahc->msgout_buf[ahc->msgout_index++] = 0;
-	ahc->msgout_buf[ahc->msgout_index++] = offset;
-	ahc->msgout_buf[ahc->msgout_index++] = bus_width;
-	ahc->msgout_buf[ahc->msgout_index++] = ppr_options;
-	ahc->msgout_len += 8;
-	if (bootverbose) {
-		printf("(%s:%c:%d:%d): Sending PPR bus_width %x, period %x, "
-		       "offset %x, ppr_options %x\n", ahc_name(ahc),
-		       devinfo->channel, devinfo->target, devinfo->lun,
-		       bus_width, period, offset, ppr_options);
-	}
-}
-
-/*
- * Clear any active message state.
- */
-static void
-ahc_clear_msg_state(struct ahc_softc *ahc)
-{
-	ahc->msgout_len = 0;
-	ahc->msgin_index = 0;
-	ahc->msg_type = MSG_TYPE_NONE;
-	if ((ahc_inb(ahc, SCSISIGI) & ATNI) != 0) {
-		/*
-		 * The target didn't care to respond to our
-		 * message request, so clear ATN.
-		 */
-		ahc_outb(ahc, CLRSINT1, CLRATNO);
-	}
-	ahc_outb(ahc, MSG_OUT, MSG_NOOP);
-	ahc_outb(ahc, SEQ_FLAGS2,
-		 ahc_inb(ahc, SEQ_FLAGS2) & ~TARGET_MSG_PENDING);
-}
-
-/*
- * Manual message loop handler.
- */
-static void
-ahc_handle_message_phase(struct ahc_softc *ahc)
-{ 
-	struct	ahc_devinfo devinfo;
-	u_int	bus_phase;
-	int	end_session;
-
-	ahc_fetch_devinfo(ahc, &devinfo);
-	end_session = FALSE;
-	bus_phase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
-
-reswitch:
-	switch (ahc->msg_type) {
-	case MSG_TYPE_INITIATOR_MSGOUT:
-	{
-		int lastbyte;
-		int phasemis;
-		int msgdone;
-
-		if (ahc->msgout_len == 0)
-			panic("HOST_MSG_LOOP interrupt with no active message");
-
-		phasemis = bus_phase != P_MESGOUT;
-		if (phasemis) {
-			if (bus_phase == P_MESGIN) {
-				/*
-				 * Change gears and see if
-				 * this messages is of interest to
-				 * us or should be passed back to
-				 * the sequencer.
-				 */
-				ahc_outb(ahc, CLRSINT1, CLRATNO);
-				ahc->send_msg_perror = FALSE;
-				ahc->msg_type = MSG_TYPE_INITIATOR_MSGIN;
-				ahc->msgin_index = 0;
-				goto reswitch;
-			}
-			end_session = TRUE;
-			break;
-		}
-
-		if (ahc->send_msg_perror) {
-			ahc_outb(ahc, CLRSINT1, CLRATNO);
-			ahc_outb(ahc, CLRSINT1, CLRREQINIT);
-			ahc_outb(ahc, SCSIDATL, MSG_PARITY_ERROR);
-			break;
-		}
-
-		msgdone	= ahc->msgout_index == ahc->msgout_len;
-		if (msgdone) {
-			/*
-			 * The target has requested a retry.
-			 * Re-assert ATN, reset our message index to
-			 * 0, and try again.
-			 */
-			ahc->msgout_index = 0;
-			ahc_assert_atn(ahc);
-		}
-
-		lastbyte = ahc->msgout_index == (ahc->msgout_len - 1);
-		if (lastbyte) {
-			/* Last byte is signified by dropping ATN */
-			ahc_outb(ahc, CLRSINT1, CLRATNO);
-		}
-
-		/*
-		 * Clear our interrupt status and present
-		 * the next byte on the bus.
-		 */
-		ahc_outb(ahc, CLRSINT1, CLRREQINIT);
-		ahc_outb(ahc, SCSIDATL, ahc->msgout_buf[ahc->msgout_index++]);
-		break;
-	}
-	case MSG_TYPE_INITIATOR_MSGIN:
-	{
-		int phasemis;
-		int message_done;
-
-		phasemis = bus_phase != P_MESGIN;
-
-		if (phasemis) {
-			ahc->msgin_index = 0;
-			if (bus_phase == P_MESGOUT
-			 && (ahc->send_msg_perror == TRUE
-			  || (ahc->msgout_len != 0
-			   && ahc->msgout_index == 0))) {
-				ahc->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
-				goto reswitch;
-			}
-			end_session = TRUE;
-			break;
-		}
-
-		/* Pull the byte in without acking it */
-		ahc->msgin_buf[ahc->msgin_index] = ahc_inb(ahc, SCSIBUSL);
-
-		message_done = ahc_parse_msg(ahc, &devinfo);
-
-		if (message_done) {
-			/*
-			 * Clear our incoming message buffer in case there
-			 * is another message following this one.
-			 */
-			ahc->msgin_index = 0;
-
-			/*
-			 * If this message illicited a response,
-			 * assert ATN so the target takes us to the
-			 * message out phase.
-			 */
-			if (ahc->msgout_len != 0)
-				ahc_assert_atn(ahc);
-		} else 
-			ahc->msgin_index++;
-
-		/* Ack the byte */
-		ahc_outb(ahc, CLRSINT1, CLRREQINIT);
-		ahc_inb(ahc, SCSIDATL);
-		break;
-	}
-	case MSG_TYPE_TARGET_MSGIN:
-	{
-		int msgdone;
-		int msgout_request;
-
-		if (ahc->msgout_len == 0)
-			panic("Target MSGIN with no active message");
-
-		/*
-		 * If we interrupted a mesgout session, the initiator
-		 * will not know this until our first REQ.  So, we
-		 * only honor mesgout requests after we've sent our
-		 * first byte.
-		 */
-		if ((ahc_inb(ahc, SCSISIGI) & ATNI) != 0
-		 && ahc->msgout_index > 0)
-			msgout_request = TRUE;
-		else
-			msgout_request = FALSE;
-
-		if (msgout_request) {
-
-			/*
-			 * Change gears and see if
-			 * this messages is of interest to
-			 * us or should be passed back to
-			 * the sequencer.
-			 */
-			ahc->msg_type = MSG_TYPE_TARGET_MSGOUT;
-			ahc_outb(ahc, SCSISIGO, P_MESGOUT | BSYO);
-			ahc->msgin_index = 0;
-			/* Dummy read to REQ for first byte */
-			ahc_inb(ahc, SCSIDATL);
-			ahc_outb(ahc, SXFRCTL0,
-				 ahc_inb(ahc, SXFRCTL0) | SPIOEN);
-			break;
-		}
-
-		msgdone = ahc->msgout_index == ahc->msgout_len;
-		if (msgdone) {
-			ahc_outb(ahc, SXFRCTL0,
-				 ahc_inb(ahc, SXFRCTL0) & ~SPIOEN);
-			end_session = TRUE;
-			break;
-		}
-
-		/*
-		 * Present the next byte on the bus.
-		 */
-		ahc_outb(ahc, SXFRCTL0, ahc_inb(ahc, SXFRCTL0) | SPIOEN);
-		ahc_outb(ahc, SCSIDATL, ahc->msgout_buf[ahc->msgout_index++]);
-		break;
-	}
-	case MSG_TYPE_TARGET_MSGOUT:
-	{
-		int lastbyte;
-		int msgdone;
-
-		/*
-		 * The initiator signals that this is
-		 * the last byte by dropping ATN.
-		 */
-		lastbyte = (ahc_inb(ahc, SCSISIGI) & ATNI) == 0;
-
-		/*
-		 * Read the latched byte, but turn off SPIOEN first
-		 * so that we don't inadvertently cause a REQ for the
-		 * next byte.
-		 */
-		ahc_outb(ahc, SXFRCTL0, ahc_inb(ahc, SXFRCTL0) & ~SPIOEN);
-		ahc->msgin_buf[ahc->msgin_index] = ahc_inb(ahc, SCSIDATL);
-		msgdone = ahc_parse_msg(ahc, &devinfo);
-		if (msgdone == MSGLOOP_TERMINATED) {
-			/*
-			 * The message is *really* done in that it caused
-			 * us to go to bus free.  The sequencer has already
-			 * been reset at this point, so pull the ejection
-			 * handle.
-			 */
-			return;
-		}
-		
-		ahc->msgin_index++;
-
-		/*
-		 * XXX Read spec about initiator dropping ATN too soon
-		 *     and use msgdone to detect it.
-		 */
-		if (msgdone == MSGLOOP_MSGCOMPLETE) {
-			ahc->msgin_index = 0;
-
-			/*
-			 * If this message illicited a response, transition
-			 * to the Message in phase and send it.
-			 */
-			if (ahc->msgout_len != 0) {
-				ahc_outb(ahc, SCSISIGO, P_MESGIN | BSYO);
-				ahc_outb(ahc, SXFRCTL0,
-					 ahc_inb(ahc, SXFRCTL0) | SPIOEN);
-				ahc->msg_type = MSG_TYPE_TARGET_MSGIN;
-				ahc->msgin_index = 0;
-				break;
-			}
-		}
-
-		if (lastbyte)
-			end_session = TRUE;
-		else {
-			/* Ask for the next byte. */
-			ahc_outb(ahc, SXFRCTL0,
-				 ahc_inb(ahc, SXFRCTL0) | SPIOEN);
-		}
-
-		break;
-	}
-	default:
-		panic("Unknown REQINIT message type");
-	}
-
-	if (end_session) {
-		ahc_clear_msg_state(ahc);
-		ahc_outb(ahc, RETURN_1, EXIT_MSG_LOOP);
-	} else
-		ahc_outb(ahc, RETURN_1, CONT_MSG_LOOP);
-}
-
-/*
- * See if we sent a particular extended message to the target.
- * If "full" is true, return true only if the target saw the full
- * message.  If "full" is false, return true if the target saw at
- * least the first byte of the message.
- */
-static int
-ahc_sent_msg(struct ahc_softc *ahc, ahc_msgtype type, u_int msgval, int full)
-{
-	int found;
-	u_int index;
-
-	found = FALSE;
-	index = 0;
-
-	while (index < ahc->msgout_len) {
-		if (ahc->msgout_buf[index] == MSG_EXTENDED) {
-			u_int end_index;
-
-			end_index = index + 1 + ahc->msgout_buf[index + 1];
-			if (ahc->msgout_buf[index+2] == msgval
-			 && type == AHCMSG_EXT) {
-
-				if (full) {
-					if (ahc->msgout_index > end_index)
-						found = TRUE;
-				} else if (ahc->msgout_index > index)
-					found = TRUE;
-			}
-			index = end_index;
-		} else if (ahc->msgout_buf[index] >= MSG_SIMPLE_TASK
-			&& ahc->msgout_buf[index] <= MSG_IGN_WIDE_RESIDUE) {
-
-			/* Skip tag type and tag id or residue param*/
-			index += 2;
-		} else {
-			/* Single byte message */
-			if (type == AHCMSG_1B
-			 && ahc->msgout_buf[index] == msgval
-			 && ahc->msgout_index > index)
-				found = TRUE;
-			index++;
-		}
-
-		if (found)
-			break;
-	}
-	return (found);
-}
-
-/*
- * Wait for a complete incoming message, parse it, and respond accordingly.
- */
-static int
-ahc_parse_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
-{
-	struct	ahc_initiator_tinfo *tinfo;
-	struct	ahc_tmode_tstate *tstate;
-	int	reject;
-	int	done;
-	int	response;
-	u_int	targ_scsirate;
-
-	done = MSGLOOP_IN_PROG;
-	response = FALSE;
-	reject = FALSE;
-	tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
-				    devinfo->target, &tstate);
-	targ_scsirate = tinfo->scsirate;
-
-	/*
-	 * Parse as much of the message as is availible,
-	 * rejecting it if we don't support it.  When
-	 * the entire message is availible and has been
-	 * handled, return MSGLOOP_MSGCOMPLETE, indicating
-	 * that we have parsed an entire message.
-	 *
-	 * In the case of extended messages, we accept the length
-	 * byte outright and perform more checking once we know the
-	 * extended message type.
-	 */
-	switch (ahc->msgin_buf[0]) {
-	case MSG_MESSAGE_REJECT:
-		response = ahc_handle_msg_reject(ahc, devinfo);
-		/* FALLTHROUGH */
-	case MSG_NOOP:
-		done = MSGLOOP_MSGCOMPLETE;
-		break;
-	case MSG_EXTENDED:
-	{
-		/* Wait for enough of the message to begin validation */
-		if (ahc->msgin_index < 2)
-			break;
-		switch (ahc->msgin_buf[2]) {
-		case MSG_EXT_SDTR:
-		{
-			struct	 ahc_syncrate *syncrate;
-			u_int	 period;
-			u_int	 ppr_options;
-			u_int	 offset;
-			u_int	 saved_offset;
-			
-			if (ahc->msgin_buf[1] != MSG_EXT_SDTR_LEN) {
-				reject = TRUE;
-				break;
-			}
-
-			/*
-			 * Wait until we have both args before validating
-			 * and acting on this message.
-			 *
-			 * Add one to MSG_EXT_SDTR_LEN to account for
-			 * the extended message preamble.
-			 */
-			if (ahc->msgin_index < (MSG_EXT_SDTR_LEN + 1))
-				break;
-
-			period = ahc->msgin_buf[3];
-			ppr_options = 0;
-			saved_offset = offset = ahc->msgin_buf[4];
-			syncrate = ahc_devlimited_syncrate(ahc, tinfo, &period,
-							   &ppr_options,
-							   devinfo->role);
-			ahc_validate_offset(ahc, tinfo, syncrate, &offset,
-					    targ_scsirate & WIDEXFER,
-					    devinfo->role);
-			if (bootverbose) {
-				printf("(%s:%c:%d:%d): Received "
-				       "SDTR period %x, offset %x\n\t"
-				       "Filtered to period %x, offset %x\n",
-				       ahc_name(ahc), devinfo->channel,
-				       devinfo->target, devinfo->lun,
-				       ahc->msgin_buf[3], saved_offset,
-				       period, offset);
-			}
-			ahc_set_syncrate(ahc, devinfo, 
-					 syncrate, period,
-					 offset, ppr_options,
-					 AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
-					 /*paused*/TRUE);
-
-			/*
-			 * See if we initiated Sync Negotiation
-			 * and didn't have to fall down to async
-			 * transfers.
-			 */
-			if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_SDTR, TRUE)) {
-				/* We started it */
-				if (saved_offset != offset) {
-					/* Went too low - force async */
-					reject = TRUE;
-				}
-			} else {
-				/*
-				 * Send our own SDTR in reply
-				 */
-				if (bootverbose
-				 && devinfo->role == ROLE_INITIATOR) {
-					printf("(%s:%c:%d:%d): Target "
-					       "Initiated SDTR\n",
-					       ahc_name(ahc), devinfo->channel,
-					       devinfo->target, devinfo->lun);
-				}
-				ahc->msgout_index = 0;
-				ahc->msgout_len = 0;
-				ahc_construct_sdtr(ahc, devinfo,
-						   period, offset);
-				ahc->msgout_index = 0;
-				response = TRUE;
-			}
-			done = MSGLOOP_MSGCOMPLETE;
-			break;
-		}
-		case MSG_EXT_WDTR:
-		{
-			u_int bus_width;
-			u_int saved_width;
-			u_int sending_reply;
-
-			sending_reply = FALSE;
-			if (ahc->msgin_buf[1] != MSG_EXT_WDTR_LEN) {
-				reject = TRUE;
-				break;
-			}
-
-			/*
-			 * Wait until we have our arg before validating
-			 * and acting on this message.
-			 *
-			 * Add one to MSG_EXT_WDTR_LEN to account for
-			 * the extended message preamble.
-			 */
-			if (ahc->msgin_index < (MSG_EXT_WDTR_LEN + 1))
-				break;
-
-			bus_width = ahc->msgin_buf[3];
-			saved_width = bus_width;
-			ahc_validate_width(ahc, tinfo, &bus_width,
-					   devinfo->role);
-			if (bootverbose) {
-				printf("(%s:%c:%d:%d): Received WDTR "
-				       "%x filtered to %x\n",
-				       ahc_name(ahc), devinfo->channel,
-				       devinfo->target, devinfo->lun,
-				       saved_width, bus_width);
-			}
-
-			if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_WDTR, TRUE)) {
-				/*
-				 * Don't send a WDTR back to the
-				 * target, since we asked first.
-				 * If the width went higher than our
-				 * request, reject it.
-				 */
-				if (saved_width > bus_width) {
-					reject = TRUE;
-					printf("(%s:%c:%d:%d): requested %dBit "
-					       "transfers.  Rejecting...\n",
-					       ahc_name(ahc), devinfo->channel,
-					       devinfo->target, devinfo->lun,
-					       8 * (0x01 << bus_width));
-					bus_width = 0;
-				}
-			} else {
-				/*
-				 * Send our own WDTR in reply
-				 */
-				if (bootverbose
-				 && devinfo->role == ROLE_INITIATOR) {
-					printf("(%s:%c:%d:%d): Target "
-					       "Initiated WDTR\n",
-					       ahc_name(ahc), devinfo->channel,
-					       devinfo->target, devinfo->lun);
-				}
-				ahc->msgout_index = 0;
-				ahc->msgout_len = 0;
-				ahc_construct_wdtr(ahc, devinfo, bus_width);
-				ahc->msgout_index = 0;
-				response = TRUE;
-				sending_reply = TRUE;
-			}
-			ahc_set_width(ahc, devinfo, bus_width,
-				      AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
-				      /*paused*/TRUE);
-			/* After a wide message, we are async */
-			ahc_set_syncrate(ahc, devinfo,
-					 /*syncrate*/NULL, /*period*/0,
-					 /*offset*/0, /*ppr_options*/0,
-					 AHC_TRANS_ACTIVE, /*paused*/TRUE);
-			if (sending_reply == FALSE && reject == FALSE) {
-
-				if (tinfo->goal.period) {
-					ahc->msgout_index = 0;
-					ahc->msgout_len = 0;
-					ahc_build_transfer_msg(ahc, devinfo);
-					ahc->msgout_index = 0;
-					response = TRUE;
-				}
-			}
-			done = MSGLOOP_MSGCOMPLETE;
-			break;
-		}
-		case MSG_EXT_PPR:
-		{
-			struct	ahc_syncrate *syncrate;
-			u_int	period;
-			u_int	offset;
-			u_int	bus_width;
-			u_int	ppr_options;
-			u_int	saved_width;
-			u_int	saved_offset;
-			u_int	saved_ppr_options;
-
-			if (ahc->msgin_buf[1] != MSG_EXT_PPR_LEN) {
-				reject = TRUE;
-				break;
-			}
-
-			/*
-			 * Wait until we have all args before validating
-			 * and acting on this message.
-			 *
-			 * Add one to MSG_EXT_PPR_LEN to account for
-			 * the extended message preamble.
-			 */
-			if (ahc->msgin_index < (MSG_EXT_PPR_LEN + 1))
-				break;
-
-			period = ahc->msgin_buf[3];
-			offset = ahc->msgin_buf[5];
-			bus_width = ahc->msgin_buf[6];
-			saved_width = bus_width;
-			ppr_options = ahc->msgin_buf[7];
-			/*
-			 * According to the spec, a DT only
-			 * period factor with no DT option
-			 * set implies async.
-			 */
-			if ((ppr_options & MSG_EXT_PPR_DT_REQ) == 0
-			 && period == 9)
-				offset = 0;
-			saved_ppr_options = ppr_options;
-			saved_offset = offset;
-
-			/*
-			 * Mask out any options we don't support
-			 * on any controller.  Transfer options are
-			 * only available if we are negotiating wide.
-			 */
-			ppr_options &= MSG_EXT_PPR_DT_REQ;
-			if (bus_width == 0)
-				ppr_options = 0;
-
-			ahc_validate_width(ahc, tinfo, &bus_width,
-					   devinfo->role);
-			syncrate = ahc_devlimited_syncrate(ahc, tinfo, &period,
-							   &ppr_options,
-							   devinfo->role);
-			ahc_validate_offset(ahc, tinfo, syncrate,
-					    &offset, bus_width,
-					    devinfo->role);
-
-			if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_PPR, TRUE)) {
-				/*
-				 * If we are unable to do any of the
-				 * requested options (we went too low),
-				 * then we'll have to reject the message.
-				 */
-				if (saved_width > bus_width
-				 || saved_offset != offset
-				 || saved_ppr_options != ppr_options) {
-					reject = TRUE;
-					period = 0;
-					offset = 0;
-					bus_width = 0;
-					ppr_options = 0;
-					syncrate = NULL;
-				}
-			} else {
-				if (devinfo->role != ROLE_TARGET)
-					printf("(%s:%c:%d:%d): Target "
-					       "Initiated PPR\n",
-					       ahc_name(ahc), devinfo->channel,
-					       devinfo->target, devinfo->lun);
-				else
-					printf("(%s:%c:%d:%d): Initiator "
-					       "Initiated PPR\n",
-					       ahc_name(ahc), devinfo->channel,
-					       devinfo->target, devinfo->lun);
-				ahc->msgout_index = 0;
-				ahc->msgout_len = 0;
-				ahc_construct_ppr(ahc, devinfo, period, offset,
-						  bus_width, ppr_options);
-				ahc->msgout_index = 0;
-				response = TRUE;
-			}
-			if (bootverbose) {
-				printf("(%s:%c:%d:%d): Received PPR width %x, "
-				       "period %x, offset %x,options %x\n"
-				       "\tFiltered to width %x, period %x, "
-				       "offset %x, options %x\n",
-				       ahc_name(ahc), devinfo->channel,
-				       devinfo->target, devinfo->lun,
-				       saved_width, ahc->msgin_buf[3],
-				       saved_offset, saved_ppr_options,
-				       bus_width, period, offset, ppr_options);
-			}
-			ahc_set_width(ahc, devinfo, bus_width,
-				      AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
-				      /*paused*/TRUE);
-			ahc_set_syncrate(ahc, devinfo,
-					 syncrate, period,
-					 offset, ppr_options,
-					 AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
-					 /*paused*/TRUE);
-			done = MSGLOOP_MSGCOMPLETE;
-			break;
-		}
-		default:
-			/* Unknown extended message.  Reject it. */
-			reject = TRUE;
-			break;
-		}
-		break;
-	}
-#ifdef AHC_TARGET_MODE
-	case MSG_BUS_DEV_RESET:
-		ahc_handle_devreset(ahc, devinfo,
-				    CAM_BDR_SENT,
-				    "Bus Device Reset Received",
-				    /*verbose_level*/0);
-		ahc_restart(ahc);
-		done = MSGLOOP_TERMINATED;
-		break;
-	case MSG_ABORT_TAG:
-	case MSG_ABORT:
-	case MSG_CLEAR_QUEUE:
-	{
-		int tag;
-
-		/* Target mode messages */
-		if (devinfo->role != ROLE_TARGET) {
-			reject = TRUE;
-			break;
-		}
-		tag = SCB_LIST_NULL;
-		if (ahc->msgin_buf[0] == MSG_ABORT_TAG)
-			tag = ahc_inb(ahc, INITIATOR_TAG);
-		ahc_abort_scbs(ahc, devinfo->target, devinfo->channel,
-			       devinfo->lun, tag, ROLE_TARGET,
-			       CAM_REQ_ABORTED);
-
-		tstate = ahc->enabled_targets[devinfo->our_scsiid];
-		if (tstate != NULL) {
-			struct ahc_tmode_lstate* lstate;
-
-			lstate = tstate->enabled_luns[devinfo->lun];
-			if (lstate != NULL) {
-				ahc_queue_lstate_event(ahc, lstate,
-						       devinfo->our_scsiid,
-						       ahc->msgin_buf[0],
-						       /*arg*/tag);
-				ahc_send_lstate_events(ahc, lstate);
-			}
-		}
-		ahc_restart(ahc);
-		done = MSGLOOP_TERMINATED;
-		break;
-	}
-#endif
-	case MSG_TERM_IO_PROC:
-	default:
-		reject = TRUE;
-		break;
-	}
-
-	if (reject) {
-		/*
-		 * Setup to reject the message.
-		 */
-		ahc->msgout_index = 0;
-		ahc->msgout_len = 1;
-		ahc->msgout_buf[0] = MSG_MESSAGE_REJECT;
-		done = MSGLOOP_MSGCOMPLETE;
-		response = TRUE;
-	}
-
-	if (done != MSGLOOP_IN_PROG && !response)
-		/* Clear the outgoing message buffer */
-		ahc->msgout_len = 0;
-
-	return (done);
-}
-
-/*
- * Process a message reject message.
- */
-static int
-ahc_handle_msg_reject(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
-{
-	/*
-	 * What we care about here is if we had an
-	 * outstanding SDTR or WDTR message for this
-	 * target.  If we did, this is a signal that
-	 * the target is refusing negotiation.
-	 */
-	struct scb *scb;
-	struct ahc_initiator_tinfo *tinfo;
-	struct ahc_tmode_tstate *tstate;
-	u_int scb_index;
-	u_int last_msg;
-	int   response = 0;
-
-	scb_index = ahc_inb(ahc, SCB_TAG);
-	scb = ahc_lookup_scb(ahc, scb_index);
-	tinfo = ahc_fetch_transinfo(ahc, devinfo->channel,
-				    devinfo->our_scsiid,
-				    devinfo->target, &tstate);
-	/* Might be necessary */
-	last_msg = ahc_inb(ahc, LAST_MSG);
-
-	if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_PPR, /*full*/FALSE)) {
-		/*
-		 * Target does not support the PPR message.
-		 * Attempt to negotiate SPI-2 style.
-		 */
-		if (bootverbose) {
-			printf("(%s:%c:%d:%d): PPR Rejected. "
-			       "Trying WDTR/SDTR\n",
-			       ahc_name(ahc), devinfo->channel,
-			       devinfo->target, devinfo->lun);
-		}
-		tinfo->goal.ppr_options = 0;
-		tinfo->curr.transport_version = 2;
-		tinfo->goal.transport_version = 2;
-		ahc->msgout_index = 0;
-		ahc->msgout_len = 0;
-		ahc_build_transfer_msg(ahc, devinfo);
-		ahc->msgout_index = 0;
-		response = 1;
-	} else if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_WDTR, /*full*/FALSE)) {
-
-		/* note 8bit xfers */
-		printf("(%s:%c:%d:%d): refuses WIDE negotiation.  Using "
-		       "8bit transfers\n", ahc_name(ahc),
-		       devinfo->channel, devinfo->target, devinfo->lun);
-		ahc_set_width(ahc, devinfo, MSG_EXT_WDTR_BUS_8_BIT,
-			      AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
-			      /*paused*/TRUE);
-		/*
-		 * No need to clear the sync rate.  If the target
-		 * did not accept the command, our syncrate is
-		 * unaffected.  If the target started the negotiation,
-		 * but rejected our response, we already cleared the
-		 * sync rate before sending our WDTR.
-		 */
-		if (tinfo->goal.period) {
-
-			/* Start the sync negotiation */
-			ahc->msgout_index = 0;
-			ahc->msgout_len = 0;
-			ahc_build_transfer_msg(ahc, devinfo);
-			ahc->msgout_index = 0;
-			response = 1;
-		}
-	} else if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_SDTR, /*full*/FALSE)) {
-		/* note asynch xfers and clear flag */
-		ahc_set_syncrate(ahc, devinfo, /*syncrate*/NULL, /*period*/0,
-				 /*offset*/0, /*ppr_options*/0,
-				 AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
-				 /*paused*/TRUE);
-		printf("(%s:%c:%d:%d): refuses synchronous negotiation. "
-		       "Using asynchronous transfers\n",
-		       ahc_name(ahc), devinfo->channel,
-		       devinfo->target, devinfo->lun);
-	} else if ((scb->hscb->control & MSG_SIMPLE_TASK) != 0) {
-		int tag_type;
-		int mask;
-
-		tag_type = (scb->hscb->control & MSG_SIMPLE_TASK);
-
-		if (tag_type == MSG_SIMPLE_TASK) {
-			printf("(%s:%c:%d:%d): refuses tagged commands.  "
-			       "Performing non-tagged I/O\n", ahc_name(ahc),
-			       devinfo->channel, devinfo->target, devinfo->lun);
-			ahc_set_tags(ahc, devinfo, AHC_QUEUE_NONE);
-			mask = ~0x23;
-		} else {
-			printf("(%s:%c:%d:%d): refuses %s tagged commands.  "
-			       "Performing simple queue tagged I/O only\n",
-			       ahc_name(ahc), devinfo->channel, devinfo->target,
-			       devinfo->lun, tag_type == MSG_ORDERED_TASK
-			       ? "ordered" : "head of queue");
-			ahc_set_tags(ahc, devinfo, AHC_QUEUE_BASIC);
-			mask = ~0x03;
-		}
-
-		/*
-		 * Resend the identify for this CCB as the target
-		 * may believe that the selection is invalid otherwise.
-		 */
-		ahc_outb(ahc, SCB_CONTROL,
-			 ahc_inb(ahc, SCB_CONTROL) & mask);
-	 	scb->hscb->control &= mask;
-		ahc_set_transaction_tag(scb, /*enabled*/FALSE,
-					/*type*/MSG_SIMPLE_TASK);
-		ahc_outb(ahc, MSG_OUT, MSG_IDENTIFYFLAG);
-		ahc_assert_atn(ahc);
-
-		/*
-		 * This transaction is now at the head of
-		 * the untagged queue for this target.
-		 */
-		if ((ahc->flags & AHC_SCB_BTT) == 0) {
-			struct scb_tailq *untagged_q;
-
-			untagged_q =
-			    &(ahc->untagged_queues[devinfo->target_offset]);
-			TAILQ_INSERT_HEAD(untagged_q, scb, links.tqe);
-			scb->flags |= SCB_UNTAGGEDQ;
-		}
-		ahc_busy_tcl(ahc, BUILD_TCL(scb->hscb->scsiid, devinfo->lun),
-			     scb->hscb->tag);
-
-		/*
-		 * Requeue all tagged commands for this target
-		 * currently in our posession so they can be
-		 * converted to untagged commands.
-		 */
-		ahc_search_qinfifo(ahc, SCB_GET_TARGET(ahc, scb),
-				   SCB_GET_CHANNEL(ahc, scb),
-				   SCB_GET_LUN(scb), /*tag*/SCB_LIST_NULL,
-				   ROLE_INITIATOR, CAM_REQUEUE_REQ,
-				   SEARCH_COMPLETE);
-	} else {
-		/*
-		 * Otherwise, we ignore it.
-		 */
-		printf("%s:%c:%d: Message reject for %x -- ignored\n",
-		       ahc_name(ahc), devinfo->channel, devinfo->target,
-		       last_msg);
-	}
-	return (response);
-}
-
-/*
- * Process an ingnore wide residue message.
- */
-static void
-ahc_handle_ign_wide_residue(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
-{
-	u_int scb_index;
-	struct scb *scb;
-
-	scb_index = ahc_inb(ahc, SCB_TAG);
-	scb = ahc_lookup_scb(ahc, scb_index);
-	/*
-	 * XXX Actually check data direction in the sequencer?
-	 * Perhaps add datadir to some spare bits in the hscb?
-	 */
-	if ((ahc_inb(ahc, SEQ_FLAGS) & DPHASE) == 0
-	 || ahc_get_transfer_dir(scb) != CAM_DIR_IN) {
-		/*
-		 * Ignore the message if we haven't
-		 * seen an appropriate data phase yet.
-		 */
-	} else {
-		/*
-		 * If the residual occurred on the last
-		 * transfer and the transfer request was
-		 * expected to end on an odd count, do
-		 * nothing.  Otherwise, subtract a byte
-		 * and update the residual count accordingly.
-		 */
-		uint32_t sgptr;
-
-		sgptr = ahc_inb(ahc, SCB_RESIDUAL_SGPTR);
-		if ((sgptr & SG_LIST_NULL) != 0
-		 && ahc_inb(ahc, DATA_COUNT_ODD) == 1) {
-			/*
-			 * If the residual occurred on the last
-			 * transfer and the transfer request was
-			 * expected to end on an odd count, do
-			 * nothing.
-			 */
-		} else {
-			struct ahc_dma_seg *sg;
-			uint32_t data_cnt;
-			uint32_t data_addr;
-			uint32_t sglen;
-
-			/* Pull in the rest of the sgptr */
-			sgptr |= (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 3) << 24)
-			      | (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 2) << 16)
-			      | (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 1) << 8);
-			sgptr &= SG_PTR_MASK;
-			data_cnt = (ahc_inb(ahc, SCB_RESIDUAL_DATACNT+3) << 24)
-				 | (ahc_inb(ahc, SCB_RESIDUAL_DATACNT+2) << 16)
-				 | (ahc_inb(ahc, SCB_RESIDUAL_DATACNT+1) << 8)
-				 | (ahc_inb(ahc, SCB_RESIDUAL_DATACNT));
-
-			data_addr = (ahc_inb(ahc, SHADDR + 3) << 24)
-				  | (ahc_inb(ahc, SHADDR + 2) << 16)
-				  | (ahc_inb(ahc, SHADDR + 1) << 8)
-				  | (ahc_inb(ahc, SHADDR));
-
-			data_cnt += 1;
-			data_addr -= 1;
-
-			sg = ahc_sg_bus_to_virt(scb, sgptr);
-			/*
-			 * The residual sg ptr points to the next S/G
-			 * to load so we must go back one.
-			 */
-			sg--;
-			sglen = ahc_le32toh(sg->len) & AHC_SG_LEN_MASK;
-			if (sg != scb->sg_list
-			 && sglen < (data_cnt & AHC_SG_LEN_MASK)) {
-
-				sg--;
-				sglen = ahc_le32toh(sg->len);
-				/*
-				 * Preserve High Address and SG_LIST bits
-				 * while setting the count to 1.
-				 */
-				data_cnt = 1 | (sglen & (~AHC_SG_LEN_MASK));
-				data_addr = ahc_le32toh(sg->addr)
-					  + (sglen & AHC_SG_LEN_MASK) - 1;
-
-				/*
-				 * Increment sg so it points to the
-				 * "next" sg.
-				 */
-				sg++;
-				sgptr = ahc_sg_virt_to_bus(scb, sg);
-				ahc_outb(ahc, SCB_RESIDUAL_SGPTR + 3,
-					 sgptr >> 24);
-				ahc_outb(ahc, SCB_RESIDUAL_SGPTR + 2,
-					 sgptr >> 16);
-				ahc_outb(ahc, SCB_RESIDUAL_SGPTR + 1,
-					 sgptr >> 8);
-				ahc_outb(ahc, SCB_RESIDUAL_SGPTR, sgptr);
-			}
-
-			ahc_outb(ahc, SCB_RESIDUAL_DATACNT + 3, data_cnt >> 24);
-			ahc_outb(ahc, SCB_RESIDUAL_DATACNT + 2, data_cnt >> 16);
-			ahc_outb(ahc, SCB_RESIDUAL_DATACNT + 1, data_cnt >> 8);
-			ahc_outb(ahc, SCB_RESIDUAL_DATACNT, data_cnt);
-		}
-	}
-}
-
-
-/*
- * Reinitialize the data pointers for the active transfer
- * based on its current residual.
- */
-static void
-ahc_reinitialize_dataptrs(struct ahc_softc *ahc)
-{
-	struct	 scb *scb;
-	struct	 ahc_dma_seg *sg;
-	u_int	 scb_index;
-	uint32_t sgptr;
-	uint32_t resid;
-	uint32_t dataptr;
-
-	scb_index = ahc_inb(ahc, SCB_TAG);
-	scb = ahc_lookup_scb(ahc, scb_index);
-	sgptr = (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 3) << 24)
-	      | (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 2) << 16)
-	      | (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 1) << 8)
-	      |	ahc_inb(ahc, SCB_RESIDUAL_SGPTR);
-
-	sgptr &= SG_PTR_MASK;
-	sg = ahc_sg_bus_to_virt(scb, sgptr);
-
-	/* The residual sg_ptr always points to the next sg */
-	sg--;
-
-	resid = (ahc_inb(ahc, SCB_RESIDUAL_DATACNT + 2) << 16)
-	      | (ahc_inb(ahc, SCB_RESIDUAL_DATACNT + 1) << 8)
-	      | ahc_inb(ahc, SCB_RESIDUAL_DATACNT);
-
-	dataptr = ahc_le32toh(sg->addr)
-		+ (ahc_le32toh(sg->len) & AHC_SG_LEN_MASK)
-		- resid;
-	if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
-		u_int dscommand1;
-
-		dscommand1 = ahc_inb(ahc, DSCOMMAND1);
-		ahc_outb(ahc, DSCOMMAND1, dscommand1 | HADDLDSEL0);
-		ahc_outb(ahc, HADDR,
-			 (ahc_le32toh(sg->len) >> 24) & SG_HIGH_ADDR_BITS);
-		ahc_outb(ahc, DSCOMMAND1, dscommand1);
-	}
-	ahc_outb(ahc, HADDR + 3, dataptr >> 24);
-	ahc_outb(ahc, HADDR + 2, dataptr >> 16);
-	ahc_outb(ahc, HADDR + 1, dataptr >> 8);
-	ahc_outb(ahc, HADDR, dataptr);
-	ahc_outb(ahc, HCNT + 2, resid >> 16);
-	ahc_outb(ahc, HCNT + 1, resid >> 8);
-	ahc_outb(ahc, HCNT, resid);
-	if ((ahc->features & AHC_ULTRA2) == 0) {
-		ahc_outb(ahc, STCNT + 2, resid >> 16);
-		ahc_outb(ahc, STCNT + 1, resid >> 8);
-		ahc_outb(ahc, STCNT, resid);
-	}
-}
-
-/*
- * Handle the effects of issuing a bus device reset message.
- */
-static void
-ahc_handle_devreset(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
-		    cam_status status, char *message, int verbose_level)
-{
-#ifdef AHC_TARGET_MODE
-	struct ahc_tmode_tstate* tstate;
-	u_int lun;
-#endif
-	int found;
-
-	found = ahc_abort_scbs(ahc, devinfo->target, devinfo->channel,
-			       CAM_LUN_WILDCARD, SCB_LIST_NULL, devinfo->role,
-			       status);
-
-#ifdef AHC_TARGET_MODE
-	/*
-	 * Send an immediate notify ccb to all target mord peripheral
-	 * drivers affected by this action.
-	 */
-	tstate = ahc->enabled_targets[devinfo->our_scsiid];
-	if (tstate != NULL) {
-		for (lun = 0; lun < AHC_NUM_LUNS; lun++) {
-			struct ahc_tmode_lstate* lstate;
-
-			lstate = tstate->enabled_luns[lun];
-			if (lstate == NULL)
-				continue;
-
-			ahc_queue_lstate_event(ahc, lstate, devinfo->our_scsiid,
-					       MSG_BUS_DEV_RESET, /*arg*/0);
-			ahc_send_lstate_events(ahc, lstate);
-		}
-	}
-#endif
-
-	/*
-	 * Go back to async/narrow transfers and renegotiate.
-	 */
-	ahc_set_width(ahc, devinfo, MSG_EXT_WDTR_BUS_8_BIT,
-		      AHC_TRANS_CUR, /*paused*/TRUE);
-	ahc_set_syncrate(ahc, devinfo, /*syncrate*/NULL,
-			 /*period*/0, /*offset*/0, /*ppr_options*/0,
-			 AHC_TRANS_CUR, /*paused*/TRUE);
-	
-	ahc_send_async(ahc, devinfo->channel, devinfo->target,
-		       CAM_LUN_WILDCARD, AC_SENT_BDR, NULL);
-
-	if (message != NULL
-	 && (verbose_level <= bootverbose))
-		printf("%s: %s on %c:%d. %d SCBs aborted\n", ahc_name(ahc),
-		       message, devinfo->channel, devinfo->target, found);
-}
-
-#ifdef AHC_TARGET_MODE
-static void
-ahc_setup_target_msgin(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
-		       struct scb *scb)
-{
-
-	/*              
-	 * To facilitate adding multiple messages together,
-	 * each routine should increment the index and len
-	 * variables instead of setting them explicitly.
-	 */             
-	ahc->msgout_index = 0;
-	ahc->msgout_len = 0;
-
-	if (scb != NULL && (scb->flags & SCB_AUTO_NEGOTIATE) != 0)
-		ahc_build_transfer_msg(ahc, devinfo);
-	else
-		panic("ahc_intr: AWAITING target message with no message");
-
-	ahc->msgout_index = 0;
-	ahc->msg_type = MSG_TYPE_TARGET_MSGIN;
-}
-#endif
-/**************************** Initialization **********************************/
-/*
- * Allocate a controller structure for a new device
- * and perform initial initializion.
- */
-struct ahc_softc *
-ahc_alloc(void *platform_arg, char *name)
-{
-	struct  ahc_softc *ahc;
-	int	i;
-
-#ifndef	__FreeBSD__
-	ahc = malloc(sizeof(*ahc), M_DEVBUF, M_NOWAIT);
-	if (!ahc) {
-		printf("aic7xxx: cannot malloc softc!\n");
-		free(name, M_DEVBUF);
-		return NULL;
-	}
-#else
-	ahc = device_get_softc((device_t)platform_arg);
-#endif
-	memset(ahc, 0, sizeof(*ahc));
-	LIST_INIT(&ahc->pending_scbs);
-	/* We don't know our unit number until the OSM sets it */
-	ahc->name = name;
-	ahc->unit = -1;
-	ahc->description = NULL;
-	ahc->channel = 'A';
-	ahc->channel_b = 'B';
-	ahc->chip = AHC_NONE;
-	ahc->features = AHC_FENONE;
-	ahc->bugs = AHC_BUGNONE;
-	ahc->flags = AHC_FNONE;
-
-	for (i = 0; i < 16; i++)
-		TAILQ_INIT(&ahc->untagged_queues[i]);
-	if (ahc_platform_alloc(ahc, platform_arg) != 0) {
-		ahc_free(ahc);
-		ahc = NULL;
-	}
-	return (ahc);
-}
-
-int
-ahc_softc_init(struct ahc_softc *ahc)
-{
-
-	/* The IRQMS bit is only valid on VL and EISA chips */
-	if ((ahc->chip & AHC_PCI) == 0)
-		ahc->unpause = ahc_inb(ahc, HCNTRL) & IRQMS;
-	else
-		ahc->unpause = 0;
-	ahc->pause = ahc->unpause | PAUSE; 
-	/* XXX The shared scb data stuff should be deprecated */
-	if (ahc->scb_data == NULL) {
-		ahc->scb_data = malloc(sizeof(*ahc->scb_data),
-				       M_DEVBUF, M_NOWAIT);
-		if (ahc->scb_data == NULL)
-			return (ENOMEM);
-		memset(ahc->scb_data, 0, sizeof(*ahc->scb_data));
-	}
-
-	return (0);
-}
-
-void
-ahc_softc_insert(struct ahc_softc *ahc)
-{
-	struct ahc_softc *list_ahc;
-
-#if AHC_PCI_CONFIG > 0
-	/*
-	 * Second Function PCI devices need to inherit some
-	 * settings from function 0.
-	 */
-	if ((ahc->chip & AHC_BUS_MASK) == AHC_PCI
-	 && (ahc->features & AHC_MULTI_FUNC) != 0) {
-		TAILQ_FOREACH(list_ahc, &ahc_tailq, links) {
-			ahc_dev_softc_t list_pci;
-			ahc_dev_softc_t pci;
-
-			list_pci = list_ahc->dev_softc;
-			pci = ahc->dev_softc;
-			if (ahc_get_pci_slot(list_pci) == ahc_get_pci_slot(pci)
-			 && ahc_get_pci_bus(list_pci) == ahc_get_pci_bus(pci)) {
-				struct ahc_softc *master;
-				struct ahc_softc *slave;
-
-				if (ahc_get_pci_function(list_pci) == 0) {
-					master = list_ahc;
-					slave = ahc;
-				} else {
-					master = ahc;
-					slave = list_ahc;
-				}
-				slave->flags &= ~AHC_BIOS_ENABLED; 
-				slave->flags |=
-				    master->flags & AHC_BIOS_ENABLED;
-				slave->flags &= ~AHC_PRIMARY_CHANNEL; 
-				slave->flags |=
-				    master->flags & AHC_PRIMARY_CHANNEL;
-				break;
-			}
-		}
-	}
-#endif
-
-	/*
-	 * Insertion sort into our list of softcs.
-	 */
-	list_ahc = TAILQ_FIRST(&ahc_tailq);
-	while (list_ahc != NULL
-	    && ahc_softc_comp(list_ahc, ahc) <= 0)
-		list_ahc = TAILQ_NEXT(list_ahc, links);
-	if (list_ahc != NULL)
-		TAILQ_INSERT_BEFORE(list_ahc, ahc, links);
-	else
-		TAILQ_INSERT_TAIL(&ahc_tailq, ahc, links);
-	ahc->init_level++;
-}
-
-void
-ahc_set_unit(struct ahc_softc *ahc, int unit)
-{
-	ahc->unit = unit;
-}
-
-void
-ahc_set_name(struct ahc_softc *ahc, char *name)
-{
-	if (ahc->name != NULL)
-		free(ahc->name, M_DEVBUF);
-	ahc->name = name;
-}
-
-void
-ahc_free(struct ahc_softc *ahc)
-{
-	int i;
-
-	ahc_fini_scbdata(ahc);
-	switch (ahc->init_level) {
-	default:
-	case 5:
-		ahc_shutdown(ahc);
-		TAILQ_REMOVE(&ahc_tailq, ahc, links);
-		/* FALLTHROUGH */
-	case 4:
-		ahc_dmamap_unload(ahc, ahc->shared_data_dmat,
-				  ahc->shared_data_dmamap);
-		/* FALLTHROUGH */
-	case 3:
-		ahc_dmamem_free(ahc, ahc->shared_data_dmat, ahc->qoutfifo,
-				ahc->shared_data_dmamap);
-		ahc_dmamap_destroy(ahc, ahc->shared_data_dmat,
-				   ahc->shared_data_dmamap);
-		/* FALLTHROUGH */
-	case 2:
-		ahc_dma_tag_destroy(ahc, ahc->shared_data_dmat);
-	case 1:
-#ifndef __linux__
-		ahc_dma_tag_destroy(ahc, ahc->buffer_dmat);
-#endif
-		break;
-	case 0:
-		break;
-	}
-
-#ifndef __linux__
-	ahc_dma_tag_destroy(ahc, ahc->parent_dmat);
-#endif
-	ahc_platform_free(ahc);
-	for (i = 0; i < AHC_NUM_TARGETS; i++) {
-		struct ahc_tmode_tstate *tstate;
-
-		tstate = ahc->enabled_targets[i];
-		if (tstate != NULL) {
-#if AHC_TARGET_MODE
-			int j;
-
-			for (j = 0; j < AHC_NUM_LUNS; j++) {
-				struct ahc_tmode_lstate *lstate;
-
-				lstate = tstate->enabled_luns[j];
-				if (lstate != NULL) {
-					xpt_free_path(lstate->path);
-					free(lstate, M_DEVBUF);
-				}
-			}
-#endif
-			free(tstate, M_DEVBUF);
-		}
-	}
-#if AHC_TARGET_MODE
-	if (ahc->black_hole != NULL) {
-		xpt_free_path(ahc->black_hole->path);
-		free(ahc->black_hole, M_DEVBUF);
-	}
-#endif
-	if (ahc->name != NULL)
-		free(ahc->name, M_DEVBUF);
-#ifndef __FreeBSD__
-	free(ahc, M_DEVBUF);
-#endif
-	return;
-}
-
-void
-ahc_shutdown(void *arg)
-{
-	struct	ahc_softc *ahc;
-	int	i;
-
-	ahc = (struct ahc_softc *)arg;
-
-	/* This will reset most registers to 0, but not all */
-	ahc_reset(ahc);
-	ahc_outb(ahc, SCSISEQ, 0);
-	ahc_outb(ahc, SXFRCTL0, 0);
-	ahc_outb(ahc, DSPCISTATUS, 0);
-
-	for (i = TARG_SCSIRATE; i < HA_274_BIOSCTRL; i++)
-		ahc_outb(ahc, i, 0);
-}
-
-/*
- * Reset the controller and record some information about it
- * that is only availabel just after a reset.
- */
-int
-ahc_reset(struct ahc_softc *ahc)
-{
-	u_int	sblkctl;
-	u_int	sxfrctl1_a, sxfrctl1_b;
-	int	wait;
-	
-	/*
-	 * Preserve the value of the SXFRCTL1 register for all channels.
-	 * It contains settings that affect termination and we don't want
-	 * to disturb the integrity of the bus.
-	 */
-	ahc_pause(ahc);
-	sxfrctl1_b = 0;
-	if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7770) {
-		u_int sblkctl;
-
-		/*
-		 * Save channel B's settings in case this chip
-		 * is setup for TWIN channel operation.
-		 */
-		sblkctl = ahc_inb(ahc, SBLKCTL);
-		ahc_outb(ahc, SBLKCTL, sblkctl | SELBUSB);
-		sxfrctl1_b = ahc_inb(ahc, SXFRCTL1);
-		ahc_outb(ahc, SBLKCTL, sblkctl & ~SELBUSB);
-	}
-	sxfrctl1_a = ahc_inb(ahc, SXFRCTL1);
-
-	ahc_outb(ahc, HCNTRL, CHIPRST | ahc->pause);
-
-	/*
-	 * Ensure that the reset has finished
-	 */
-	wait = 1000;
-	do {
-		ahc_delay(1000);
-	} while (--wait && !(ahc_inb(ahc, HCNTRL) & CHIPRSTACK));
-
-	if (wait == 0) {
-		printf("%s: WARNING - Failed chip reset!  "
-		       "Trying to initialize anyway.\n", ahc_name(ahc));
-	}
-	ahc_outb(ahc, HCNTRL, ahc->pause);
-
-	/* Determine channel configuration */
-	sblkctl = ahc_inb(ahc, SBLKCTL) & (SELBUSB|SELWIDE);
-	/* No Twin Channel PCI cards */
-	if ((ahc->chip & AHC_PCI) != 0)
-		sblkctl &= ~SELBUSB;
-	switch (sblkctl) {
-	case 0:
-		/* Single Narrow Channel */
-		break;
-	case 2:
-		/* Wide Channel */
-		ahc->features |= AHC_WIDE;
-		break;
-	case 8:
-		/* Twin Channel */
-		ahc->features |= AHC_TWIN;
-		break;
-	default:
-		printf(" Unsupported adapter type.  Ignoring\n");
-		return(-1);
-	}
-
-	/*
-	 * Reload sxfrctl1.
-	 *
-	 * We must always initialize STPWEN to 1 before we
-	 * restore the saved values.  STPWEN is initialized
-	 * to a tri-state condition which can only be cleared
-	 * by turning it on.
-	 */
-	if ((ahc->features & AHC_TWIN) != 0) {
-		u_int sblkctl;
-
-		sblkctl = ahc_inb(ahc, SBLKCTL);
-		ahc_outb(ahc, SBLKCTL, sblkctl | SELBUSB);
-		ahc_outb(ahc, SXFRCTL1, sxfrctl1_b);
-		ahc_outb(ahc, SBLKCTL, sblkctl & ~SELBUSB);
-	}
-	ahc_outb(ahc, SXFRCTL1, sxfrctl1_a);
-
-#ifdef AHC_DUMP_SEQ
-	if (ahc->init_level == 0)
-		ahc_dumpseq(ahc);
-#endif
-
-	return (0);
-}
-
-/*
- * Determine the number of SCBs available on the controller
- */
-int
-ahc_probe_scbs(struct ahc_softc *ahc) {
-	int i;
-
-	for (i = 0; i < AHC_SCB_MAX; i++) {
-
-		ahc_outb(ahc, SCBPTR, i);
-		ahc_outb(ahc, SCB_BASE, i);
-		if (ahc_inb(ahc, SCB_BASE) != i)
-			break;
-		ahc_outb(ahc, SCBPTR, 0);
-		if (ahc_inb(ahc, SCB_BASE) != 0)
-			break;
-	}
-	return (i);
-}
-
-static void
-ahc_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) 
-{
-	bus_addr_t *baddr;
-
-	baddr = (bus_addr_t *)arg;
-	*baddr = segs->ds_addr;
-}
-
-static void
-ahc_build_free_scb_list(struct ahc_softc *ahc)
-{
-	int i;
-
-	for (i = 0; i < ahc->scb_data->maxhscbs; i++) {
-		ahc_outb(ahc, SCBPTR, i);
-
-		/* Clear the control byte. */
-		ahc_outb(ahc, SCB_CONTROL, 0);
-
-		/* Set the next pointer */
-		if ((ahc->flags & AHC_PAGESCBS) != 0)
-			ahc_outb(ahc, SCB_NEXT, i+1);
-		else 
-			ahc_outb(ahc, SCB_NEXT, SCB_LIST_NULL);
-
-		/* Make the tag number invalid */
-		ahc_outb(ahc, SCB_TAG, SCB_LIST_NULL);
-	}
-
-	/* Make sure that the last SCB terminates the free list */
-	ahc_outb(ahc, SCBPTR, i-1);
-	ahc_outb(ahc, SCB_NEXT, SCB_LIST_NULL);
-
-	/* Ensure we clear the 0 SCB's control byte. */
-	ahc_outb(ahc, SCBPTR, 0);
-	ahc_outb(ahc, SCB_CONTROL, 0);
-}
-
-static int
-ahc_init_scbdata(struct ahc_softc *ahc)
-{
-	struct scb_data *scb_data;
-
-	scb_data = ahc->scb_data;
-	SLIST_INIT(&scb_data->free_scbs);
-	SLIST_INIT(&scb_data->sg_maps);
-
-	/* Allocate SCB resources */
-	scb_data->scbarray =
-	    (struct scb *)malloc(sizeof(struct scb) * AHC_SCB_MAX_ALLOC,
-				 M_DEVBUF, M_NOWAIT);
-	if (scb_data->scbarray == NULL)
-		return (ENOMEM);
-	memset(scb_data->scbarray, 0, sizeof(struct scb) * AHC_SCB_MAX_ALLOC);
-
-	/* Determine the number of hardware SCBs and initialize them */
-
-	scb_data->maxhscbs = ahc_probe_scbs(ahc);
-	if ((ahc->flags & AHC_PAGESCBS) != 0) {
-		/* SCB 0 heads the free list */
-		ahc_outb(ahc, FREE_SCBH, 0);
-	} else {
-		ahc_outb(ahc, FREE_SCBH, SCB_LIST_NULL);
-	}
-
-	if (ahc->scb_data->maxhscbs == 0) {
-		printf("%s: No SCB space found\n", ahc_name(ahc));
-		return (ENXIO);
-	}
-
-	ahc_build_free_scb_list(ahc);
-
-	/*
-	 * Create our DMA tags.  These tags define the kinds of device
-	 * accessible memory allocations and memory mappings we will
-	 * need to perform during normal operation.
-	 *
-	 * Unless we need to further restrict the allocation, we rely
-	 * on the restrictions of the parent dmat, hence the common
-	 * use of MAXADDR and MAXSIZE.
-	 */
-
-	/* DMA tag for our hardware scb structures */
-	if (ahc_dma_tag_create(ahc, ahc->parent_dmat, /*alignment*/1,
-			       /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
-			       /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
-			       /*highaddr*/BUS_SPACE_MAXADDR,
-			       /*filter*/NULL, /*filterarg*/NULL,
-			       AHC_SCB_MAX_ALLOC * sizeof(struct hardware_scb),
-			       /*nsegments*/1,
-			       /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
-			       /*flags*/0, &scb_data->hscb_dmat) != 0) {
-		goto error_exit;
-	}
-
-	scb_data->init_level++;
-
-	/* Allocation for our hscbs */
-	if (ahc_dmamem_alloc(ahc, scb_data->hscb_dmat,
-			     (void **)&scb_data->hscbs,
-			     BUS_DMA_NOWAIT, &scb_data->hscb_dmamap) != 0) {
-		goto error_exit;
-	}
-
-	scb_data->init_level++;
-
-	/* And permanently map them */
-	ahc_dmamap_load(ahc, scb_data->hscb_dmat, scb_data->hscb_dmamap,
-			scb_data->hscbs,
-			AHC_SCB_MAX_ALLOC * sizeof(struct hardware_scb),
-			ahc_dmamap_cb, &scb_data->hscb_busaddr, /*flags*/0);
-
-	scb_data->init_level++;
-
-	/* DMA tag for our sense buffers */
-	if (ahc_dma_tag_create(ahc, ahc->parent_dmat, /*alignment*/1,
-			       /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
-			       /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
-			       /*highaddr*/BUS_SPACE_MAXADDR,
-			       /*filter*/NULL, /*filterarg*/NULL,
-			       AHC_SCB_MAX_ALLOC * sizeof(struct scsi_sense_data),
-			       /*nsegments*/1,
-			       /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
-			       /*flags*/0, &scb_data->sense_dmat) != 0) {
-		goto error_exit;
-	}
-
-	scb_data->init_level++;
-
-	/* Allocate them */
-	if (ahc_dmamem_alloc(ahc, scb_data->sense_dmat,
-			     (void **)&scb_data->sense,
-			     BUS_DMA_NOWAIT, &scb_data->sense_dmamap) != 0) {
-		goto error_exit;
-	}
-
-	scb_data->init_level++;
-
-	/* And permanently map them */
-	ahc_dmamap_load(ahc, scb_data->sense_dmat, scb_data->sense_dmamap,
-			scb_data->sense,
-			AHC_SCB_MAX_ALLOC * sizeof(struct scsi_sense_data),
-			ahc_dmamap_cb, &scb_data->sense_busaddr, /*flags*/0);
-
-	scb_data->init_level++;
-
-	/* DMA tag for our S/G structures.  We allocate in page sized chunks */
-	if (ahc_dma_tag_create(ahc, ahc->parent_dmat, /*alignment*/1,
-			       /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
-			       /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
-			       /*highaddr*/BUS_SPACE_MAXADDR,
-			       /*filter*/NULL, /*filterarg*/NULL,
-			       PAGE_SIZE, /*nsegments*/1,
-			       /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
-			       /*flags*/0, &scb_data->sg_dmat) != 0) {
-		goto error_exit;
-	}
-
-	scb_data->init_level++;
-
-	/* Perform initial CCB allocation */
-	memset(scb_data->hscbs, 0,
-	       AHC_SCB_MAX_ALLOC * sizeof(struct hardware_scb));
-	ahc_alloc_scbs(ahc);
-
-	if (scb_data->numscbs == 0) {
-		printf("%s: ahc_init_scbdata - "
-		       "Unable to allocate initial scbs\n",
-		       ahc_name(ahc));
-		goto error_exit;
-	}
-
-	/*
-	 * Tell the sequencer which SCB will be the next one it receives.
-	 */
-	ahc->next_queued_scb = ahc_get_scb(ahc);
-	ahc_outb(ahc, NEXT_QUEUED_SCB, ahc->next_queued_scb->hscb->tag);
-
-	/*
-	 * Note that we were successfull
-	 */
-	return (0); 
-
-error_exit:
-
-	return (ENOMEM);
-}
-
-static void
-ahc_fini_scbdata(struct ahc_softc *ahc)
-{
-	struct scb_data *scb_data;
-
-	scb_data = ahc->scb_data;
-	if (scb_data == NULL)
-		return;
-
-	switch (scb_data->init_level) {
-	default:
-	case 7:
-	{
-		struct sg_map_node *sg_map;
-
-		while ((sg_map = SLIST_FIRST(&scb_data->sg_maps))!= NULL) {
-			SLIST_REMOVE_HEAD(&scb_data->sg_maps, links);
-			ahc_dmamap_unload(ahc, scb_data->sg_dmat,
-					  sg_map->sg_dmamap);
-			ahc_dmamem_free(ahc, scb_data->sg_dmat,
-					sg_map->sg_vaddr,
-					sg_map->sg_dmamap);
-			free(sg_map, M_DEVBUF);
-		}
-		ahc_dma_tag_destroy(ahc, scb_data->sg_dmat);
-	}
-	case 6:
-		ahc_dmamap_unload(ahc, scb_data->sense_dmat,
-				  scb_data->sense_dmamap);
-	case 5:
-		ahc_dmamem_free(ahc, scb_data->sense_dmat, scb_data->sense,
-				scb_data->sense_dmamap);
-		ahc_dmamap_destroy(ahc, scb_data->sense_dmat,
-				   scb_data->sense_dmamap);
-	case 4:
-		ahc_dma_tag_destroy(ahc, scb_data->sense_dmat);
-	case 3:
-		ahc_dmamap_unload(ahc, scb_data->hscb_dmat,
-				  scb_data->hscb_dmamap);
-	case 2:
-		ahc_dmamem_free(ahc, scb_data->hscb_dmat, scb_data->hscbs,
-				scb_data->hscb_dmamap);
-		ahc_dmamap_destroy(ahc, scb_data->hscb_dmat,
-				   scb_data->hscb_dmamap);
-	case 1:
-		ahc_dma_tag_destroy(ahc, scb_data->hscb_dmat);
-		break;
-	case 0:
-		break;
-	}
-	if (scb_data->scbarray != NULL)
-		free(scb_data->scbarray, M_DEVBUF);
-}
-
-void
-ahc_alloc_scbs(struct ahc_softc *ahc)
-{
-	struct scb_data *scb_data;
-	struct scb *next_scb;
-	struct sg_map_node *sg_map;
-	bus_addr_t physaddr;
-	struct ahc_dma_seg *segs;
-	int newcount;
-	int i;
-
-	scb_data = ahc->scb_data;
-	if (scb_data->numscbs >= AHC_SCB_MAX_ALLOC)
-		/* Can't allocate any more */
-		return;
-
-	next_scb = &scb_data->scbarray[scb_data->numscbs];
-
-	sg_map = malloc(sizeof(*sg_map), M_DEVBUF, M_NOWAIT);
-
-	if (sg_map == NULL)
-		return;
-
-	/* Allocate S/G space for the next batch of SCBS */
-	if (ahc_dmamem_alloc(ahc, scb_data->sg_dmat,
-			     (void **)&sg_map->sg_vaddr,
-			     BUS_DMA_NOWAIT, &sg_map->sg_dmamap) != 0) {
-		free(sg_map, M_DEVBUF);
-		return;
-	}
-
-	SLIST_INSERT_HEAD(&scb_data->sg_maps, sg_map, links);
-
-	ahc_dmamap_load(ahc, scb_data->sg_dmat, sg_map->sg_dmamap,
-			sg_map->sg_vaddr, PAGE_SIZE, ahc_dmamap_cb,
-			&sg_map->sg_physaddr, /*flags*/0);
-
-	segs = sg_map->sg_vaddr;
-	physaddr = sg_map->sg_physaddr;
-
-	newcount = (PAGE_SIZE / (AHC_NSEG * sizeof(struct ahc_dma_seg)));
-	newcount = MIN(newcount, (AHC_SCB_MAX_ALLOC - scb_data->numscbs));
-	for (i = 0; i < newcount; i++) {
-		struct scb_platform_data *pdata;
-#ifndef __linux__
-		int error;
-#endif
-		pdata = (struct scb_platform_data *)malloc(sizeof(*pdata),
-							   M_DEVBUF, M_NOWAIT);
-		if (pdata == NULL)
-			break;
-		next_scb->platform_data = pdata;
-		next_scb->sg_map = sg_map;
-		next_scb->sg_list = segs;
-		/*
-		 * The sequencer always starts with the second entry.
-		 * The first entry is embedded in the scb.
-		 */
-		next_scb->sg_list_phys = physaddr + sizeof(struct ahc_dma_seg);
-		next_scb->ahc_softc = ahc;
-		next_scb->flags = SCB_FREE;
-#ifndef __linux__
-		error = ahc_dmamap_create(ahc, ahc->buffer_dmat, /*flags*/0,
-					  &next_scb->dmamap);
-		if (error != 0)
-			break;
-#endif
-		next_scb->hscb = &scb_data->hscbs[scb_data->numscbs];
-		next_scb->hscb->tag = ahc->scb_data->numscbs;
-		SLIST_INSERT_HEAD(&ahc->scb_data->free_scbs,
-				  next_scb, links.sle);
-		segs += AHC_NSEG;
-		physaddr += (AHC_NSEG * sizeof(struct ahc_dma_seg));
-		next_scb++;
-		ahc->scb_data->numscbs++;
-	}
-}
-
-void
-ahc_controller_info(struct ahc_softc *ahc, char *buf)
-{
-	int len;
-
-	len = sprintf(buf, "%s: ", ahc_chip_names[ahc->chip & AHC_CHIPID_MASK]);
-	buf += len;
-	if ((ahc->features & AHC_TWIN) != 0)
- 		len = sprintf(buf, "Twin Channel, A SCSI Id=%d, "
-			      "B SCSI Id=%d, primary %c, ",
-			      ahc->our_id, ahc->our_id_b,
-			      (ahc->flags & AHC_PRIMARY_CHANNEL) + 'A');
-	else {
-		const char *speed;
-		const char *type;
-
-		speed = "";
-		if ((ahc->features & AHC_ULTRA) != 0) {
-			speed = "Ultra ";
-		} else if ((ahc->features & AHC_DT) != 0) {
-			speed = "Ultra160 ";
-		} else if ((ahc->features & AHC_ULTRA2) != 0) {
-			speed = "Ultra2 ";
-		}
-		if ((ahc->features & AHC_WIDE) != 0) {
-			type = "Wide";
-		} else {
-			type = "Single";
-		}
-		len = sprintf(buf, "%s%s Channel %c, SCSI Id=%d, ",
-			      speed, type, ahc->channel, ahc->our_id);
-	}
-	buf += len;
-
-	if ((ahc->flags & AHC_PAGESCBS) != 0)
-		sprintf(buf, "%d/%d SCBs",
-			ahc->scb_data->maxhscbs, AHC_MAX_QUEUE);
-	else
-		sprintf(buf, "%d SCBs", ahc->scb_data->maxhscbs);
-}
-
-/*
- * Start the board, ready for normal operation
- */
-int
-ahc_init(struct ahc_softc *ahc)
-{
-	int	 max_targ;
-	int	 i;
-	int	 term;
-	u_int	 scsi_conf;
-	u_int	 scsiseq_template;
-	u_int	 ultraenb;
-	u_int	 discenable;
-	u_int	 tagenable;
-	size_t	 driver_data_size;
-	uint32_t physaddr;
-
-#ifdef AHC_DEBUG_SEQUENCER
-	ahc->flags |= AHC_SEQUENCER_DEBUG;
-#endif
-
-#ifdef AHC_PRINT_SRAM
-	printf("Scratch Ram:");
-	for (i = 0x20; i < 0x5f; i++) {
-		if (((i % 8) == 0) && (i != 0)) {
-			printf ("\n              ");
-		}
-		printf (" 0x%x", ahc_inb(ahc, i));
-	}
-	if ((ahc->features & AHC_MORE_SRAM) != 0) {
-		for (i = 0x70; i < 0x7f; i++) {
-			if (((i % 8) == 0) && (i != 0)) {
-				printf ("\n              ");
-			}
-			printf (" 0x%x", ahc_inb(ahc, i));
-		}
-	}
-	printf ("\n");
-#endif
-	max_targ = 15;
-
-	/*
-	 * Assume we have a board at this stage and it has been reset.
-	 */
-	if ((ahc->flags & AHC_USEDEFAULTS) != 0)
-		ahc->our_id = ahc->our_id_b = 7;
-	
-	/*
-	 * Default to allowing initiator operations.
-	 */
-	ahc->flags |= AHC_INITIATORROLE;
-
-	/*
-	 * Only allow target mode features if this unit has them enabled.
-	 */
-	if ((AHC_TMODE_ENABLE & (0x1 << ahc->unit)) == 0)
-		ahc->features &= ~AHC_TARGETMODE;
-
-#ifndef __linux__
-	/* DMA tag for mapping buffers into device visible space. */
-	if (ahc_dma_tag_create(ahc, ahc->parent_dmat, /*alignment*/1,
-			       /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
-			       /*lowaddr*/BUS_SPACE_MAXADDR,
-			       /*highaddr*/BUS_SPACE_MAXADDR,
-			       /*filter*/NULL, /*filterarg*/NULL,
-			       /*maxsize*/MAXBSIZE, /*nsegments*/AHC_NSEG,
-			       /*maxsegsz*/AHC_MAXTRANSFER_SIZE,
-			       /*flags*/BUS_DMA_ALLOCNOW,
-			       &ahc->buffer_dmat) != 0) {
-		return (ENOMEM);
-	}
-#endif
-
-	ahc->init_level++;
-
-	/*
-	 * DMA tag for our command fifos and other data in system memory
-	 * the card's sequencer must be able to access.  For initiator
-	 * roles, we need to allocate space for the the qinfifo and qoutfifo.
-	 * The qinfifo and qoutfifo are composed of 256 1 byte elements. 
-	 * When providing for the target mode role, we must additionally
-	 * provide space for the incoming target command fifo and an extra
-	 * byte to deal with a dma bug in some chip versions.
-	 */
-	driver_data_size = 2 * 256 * sizeof(uint8_t);
-	if ((ahc->features & AHC_TARGETMODE) != 0)
-		driver_data_size += AHC_TMODE_CMDS * sizeof(struct target_cmd)
-				 + /*DMA WideOdd Bug Buffer*/1;
-	if (ahc_dma_tag_create(ahc, ahc->parent_dmat, /*alignment*/1,
-			       /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
-			       /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
-			       /*highaddr*/BUS_SPACE_MAXADDR,
-			       /*filter*/NULL, /*filterarg*/NULL,
-			       driver_data_size,
-			       /*nsegments*/1,
-			       /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
-			       /*flags*/0, &ahc->shared_data_dmat) != 0) {
-		return (ENOMEM);
-	}
-
-	ahc->init_level++;
-
-	/* Allocation of driver data */
-	if (ahc_dmamem_alloc(ahc, ahc->shared_data_dmat,
-			     (void **)&ahc->qoutfifo,
-			     BUS_DMA_NOWAIT, &ahc->shared_data_dmamap) != 0) {
-		return (ENOMEM);
-	}
-
-	ahc->init_level++;
-
-	/* And permanently map it in */
-	ahc_dmamap_load(ahc, ahc->shared_data_dmat, ahc->shared_data_dmamap,
-			ahc->qoutfifo, driver_data_size, ahc_dmamap_cb,
-			&ahc->shared_data_busaddr, /*flags*/0);
-
-	if ((ahc->features & AHC_TARGETMODE) != 0) {
-		ahc->targetcmds = (struct target_cmd *)ahc->qoutfifo;
-		ahc->qoutfifo = (uint8_t *)&ahc->targetcmds[AHC_TMODE_CMDS];
-		ahc->dma_bug_buf = ahc->shared_data_busaddr
-				 + driver_data_size - 1;
-		/* All target command blocks start out invalid. */
-		for (i = 0; i < AHC_TMODE_CMDS; i++)
-			ahc->targetcmds[i].cmd_valid = 0;
-		ahc_sync_tqinfifo(ahc, BUS_DMASYNC_PREREAD);
-		ahc->tqinfifonext = 1;
-		ahc_outb(ahc, KERNEL_TQINPOS, ahc->tqinfifonext - 1);
-		ahc_outb(ahc, TQINPOS, ahc->tqinfifonext);
-		ahc->qoutfifo = (uint8_t *)&ahc->targetcmds[256];
-	}
-	ahc->qinfifo = &ahc->qoutfifo[256];
-
-	ahc->init_level++;
-
-	/* Allocate SCB data now that buffer_dmat is initialized */
-	if (ahc->scb_data->maxhscbs == 0)
-		if (ahc_init_scbdata(ahc) != 0)
-			return (ENOMEM);
-
-	/*
-	 * Allocate a tstate to house information for our
-	 * initiator presence on the bus as well as the user
-	 * data for any target mode initiator.
-	 */
-	if (ahc_alloc_tstate(ahc, ahc->our_id, 'A') == NULL) {
-		printf("%s: unable to allocate ahc_tmode_tstate.  "
-		       "Failing attach\n", ahc_name(ahc));
-		return (ENOMEM);
-	}
-
-	if ((ahc->features & AHC_TWIN) != 0) {
-		if (ahc_alloc_tstate(ahc, ahc->our_id_b, 'B') == NULL) {
-			printf("%s: unable to allocate ahc_tmode_tstate.  "
-			       "Failing attach\n", ahc_name(ahc));
-			return (ENOMEM);
-		}
-	}
-
-	ahc_outb(ahc, SEQ_FLAGS, 0);
-	ahc_outb(ahc, SEQ_FLAGS2, 0);
-
-	if (ahc->scb_data->maxhscbs < AHC_SCB_MAX_ALLOC) {
-		ahc->flags |= AHC_PAGESCBS;
-	} else {
-		ahc->flags &= ~AHC_PAGESCBS;
-	}
-
-#ifdef AHC_DEBUG
-	if (ahc_debug & AHC_SHOWMISC) {
-		printf("%s: hardware scb %d bytes; kernel scb %d bytes; "
-		       "ahc_dma %d bytes\n",
-			ahc_name(ahc),
-			sizeof(struct hardware_scb),
-			sizeof(struct scb),
-			sizeof(struct ahc_dma_seg));
-	}
-#endif /* AHC_DEBUG */
-
-	/* Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels*/
-	if (ahc->features & AHC_TWIN) {
-
-		/*
-		 * The device is gated to channel B after a chip reset,
-		 * so set those values first
-		 */
-		ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) | SELBUSB);
-		term = (ahc->flags & AHC_TERM_ENB_B) != 0 ? STPWEN : 0;
-		ahc_outb(ahc, SCSIID, ahc->our_id_b);
-		scsi_conf = ahc_inb(ahc, SCSICONF + 1);
-		ahc_outb(ahc, SXFRCTL1, (scsi_conf & (ENSPCHK|STIMESEL))
-					|term|ahc->seltime_b|ENSTIMER|ACTNEGEN);
-		if ((ahc->features & AHC_ULTRA2) != 0)
-			ahc_outb(ahc, SIMODE0, ahc_inb(ahc, SIMODE0)|ENIOERR);
-		ahc_outb(ahc, SIMODE1, ENSELTIMO|ENSCSIRST|ENSCSIPERR);
-		ahc_outb(ahc, SXFRCTL0, DFON|SPIOEN);
-
-		if ((scsi_conf & RESET_SCSI) != 0
-		 && (ahc->flags & AHC_INITIATORROLE) != 0)
-			ahc->flags |= AHC_RESET_BUS_B;
-
-		/* Select Channel A */
-		ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) & ~SELBUSB);
-	}
-	term = (ahc->flags & AHC_TERM_ENB_A) != 0 ? STPWEN : 0;
-	if ((ahc->features & AHC_ULTRA2) != 0)
-		ahc_outb(ahc, SCSIID_ULTRA2, ahc->our_id);
-	else
-		ahc_outb(ahc, SCSIID, ahc->our_id);
-	scsi_conf = ahc_inb(ahc, SCSICONF);
-	ahc_outb(ahc, SXFRCTL1, (scsi_conf & (ENSPCHK|STIMESEL))
-				|term|ahc->seltime
-				|ENSTIMER|ACTNEGEN);
-	if ((ahc->features & AHC_ULTRA2) != 0)
-		ahc_outb(ahc, SIMODE0, ahc_inb(ahc, SIMODE0)|ENIOERR);
-	ahc_outb(ahc, SIMODE1, ENSELTIMO|ENSCSIRST|ENSCSIPERR);
-	ahc_outb(ahc, SXFRCTL0, DFON|SPIOEN);
-
-	if ((scsi_conf & RESET_SCSI) != 0
-	 && (ahc->flags & AHC_INITIATORROLE) != 0)
-		ahc->flags |= AHC_RESET_BUS_A;
-
-	/*
-	 * Look at the information that board initialization or
-	 * the board bios has left us.
-	 */
-	ultraenb = 0;	
-	tagenable = ALL_TARGETS_MASK;
-
-	/* Grab the disconnection disable table and invert it for our needs */
-	if (ahc->flags & AHC_USEDEFAULTS) {
-		printf("%s: Host Adapter Bios disabled.  Using default SCSI "
-			"device parameters\n", ahc_name(ahc));
-		ahc->flags |= AHC_EXTENDED_TRANS_A|AHC_EXTENDED_TRANS_B|
-			      AHC_TERM_ENB_A|AHC_TERM_ENB_B;
-		discenable = ALL_TARGETS_MASK;
-		if ((ahc->features & AHC_ULTRA) != 0)
-			ultraenb = ALL_TARGETS_MASK;
-	} else {
-		discenable = ~((ahc_inb(ahc, DISC_DSB + 1) << 8)
-			   | ahc_inb(ahc, DISC_DSB));
-		if ((ahc->features & (AHC_ULTRA|AHC_ULTRA2)) != 0)
-			ultraenb = (ahc_inb(ahc, ULTRA_ENB + 1) << 8)
-				      | ahc_inb(ahc, ULTRA_ENB);
-	}
-
-	if ((ahc->features & (AHC_WIDE|AHC_TWIN)) == 0)
-		max_targ = 7;
-
-	for (i = 0; i <= max_targ; i++) {
-		struct ahc_initiator_tinfo *tinfo;
-		struct ahc_tmode_tstate *tstate;
-		u_int our_id;
-		u_int target_id;
-		char channel;
-
-		channel = 'A';
-		our_id = ahc->our_id;
-		target_id = i;
-		if (i > 7 && (ahc->features & AHC_TWIN) != 0) {
-			channel = 'B';
-			our_id = ahc->our_id_b;
-			target_id = i % 8;
-		}
-		tinfo = ahc_fetch_transinfo(ahc, channel, our_id,
-					    target_id, &tstate);
-		/* Default to async narrow across the board */
-		memset(tinfo, 0, sizeof(*tinfo));
-		if (ahc->flags & AHC_USEDEFAULTS) {
-			if ((ahc->features & AHC_WIDE) != 0)
-				tinfo->user.width = MSG_EXT_WDTR_BUS_16_BIT;
-
-			/*
-			 * These will be truncated when we determine the
-			 * connection type we have with the target.
-			 */
-			tinfo->user.period = ahc_syncrates->period;
-			tinfo->user.offset = ~0;
-		} else {
-			u_int scsirate;
-			uint16_t mask;
-
-			/* Take the settings leftover in scratch RAM. */
-			scsirate = ahc_inb(ahc, TARG_SCSIRATE + i);
-			mask = (0x01 << i);
-			if ((ahc->features & AHC_ULTRA2) != 0) {
-				u_int offset;
-				u_int maxsync;
-
-				if ((scsirate & SOFS) == 0x0F) {
-					/*
-					 * Haven't negotiated yet,
-					 * so the format is different.
-					 */
-					scsirate = (scsirate & SXFR) >> 4
-						 | (ultraenb & mask)
-						  ? 0x08 : 0x0
-						 | (scsirate & WIDEXFER);
-					offset = MAX_OFFSET_ULTRA2;
-				} else
-					offset = ahc_inb(ahc, TARG_OFFSET + i);
-				if ((scsirate & ~WIDEXFER) == 0 && offset != 0)
-					/* Set to the lowest sync rate, 5MHz */
-					scsirate |= 0x1c;
-				maxsync = AHC_SYNCRATE_ULTRA2;
-				if ((ahc->features & AHC_DT) != 0)
-					maxsync = AHC_SYNCRATE_DT;
-				tinfo->user.period =
-				    ahc_find_period(ahc, scsirate, maxsync);
-				if (offset == 0)
-					tinfo->user.period = 0;
-				else
-					tinfo->user.offset = ~0;
-				if ((scsirate & SXFR_ULTRA2) <= 8/*10MHz*/
-				 && (ahc->features & AHC_DT) != 0)
-					tinfo->user.ppr_options =
-					    MSG_EXT_PPR_DT_REQ;
-			} else if ((scsirate & SOFS) != 0) {
-				if ((scsirate & SXFR) == 0x40
-				 && (ultraenb & mask) != 0) {
-					/* Treat 10MHz as a non-ultra speed */
-					scsirate &= ~SXFR;
-				 	ultraenb &= ~mask;
-				}
-				tinfo->user.period = 
-				    ahc_find_period(ahc, scsirate,
-						    (ultraenb & mask)
-						   ? AHC_SYNCRATE_ULTRA
-						   : AHC_SYNCRATE_FAST);
-				if (tinfo->user.period != 0)
-					tinfo->user.offset = ~0;
-			}
-			if (tinfo->user.period == 0)
-				tinfo->user.offset = 0;
-			if ((scsirate & WIDEXFER) != 0
-			 && (ahc->features & AHC_WIDE) != 0)
-				tinfo->user.width = MSG_EXT_WDTR_BUS_16_BIT;
-			tinfo->user.protocol_version = 4;
-			if ((ahc->features & AHC_DT) != 0)
-				tinfo->user.transport_version = 3;
-			else
-				tinfo->user.transport_version = 2;
-			tinfo->goal.protocol_version = 2;
-			tinfo->goal.transport_version = 2;
-			tinfo->curr.protocol_version = 2;
-			tinfo->curr.transport_version = 2;
-		}
-		tstate->ultraenb = ultraenb;
-	}
-	ahc->user_discenable = discenable;
-	ahc->user_tagenable = tagenable;
-
-	/* There are no untagged SCBs active yet. */
-	for (i = 0; i < 16; i++) {
-		ahc_unbusy_tcl(ahc, BUILD_TCL(i << 4, 0));
-		if ((ahc->flags & AHC_SCB_BTT) != 0) {
-			int lun;
-
-			/*
-			 * The SCB based BTT allows an entry per
-			 * target and lun pair.
-			 */
-			for (lun = 1; lun < AHC_NUM_LUNS; lun++)
-				ahc_unbusy_tcl(ahc, BUILD_TCL(i << 4, lun));
-		}
-	}
-
-	/* All of our queues are empty */
-	for (i = 0; i < 256; i++)
-		ahc->qoutfifo[i] = SCB_LIST_NULL;
-	ahc_sync_qoutfifo(ahc, BUS_DMASYNC_PREREAD);
-
-	for (i = 0; i < 256; i++)
-		ahc->qinfifo[i] = SCB_LIST_NULL;
-
-	if ((ahc->features & AHC_MULTI_TID) != 0) {
-		ahc_outb(ahc, TARGID, 0);
-		ahc_outb(ahc, TARGID + 1, 0);
-	}
-
-	/*
-	 * Tell the sequencer where it can find our arrays in memory.
-	 */
-	physaddr = ahc->scb_data->hscb_busaddr;
-	ahc_outb(ahc, HSCB_ADDR, physaddr & 0xFF);
-	ahc_outb(ahc, HSCB_ADDR + 1, (physaddr >> 8) & 0xFF);
-	ahc_outb(ahc, HSCB_ADDR + 2, (physaddr >> 16) & 0xFF);
-	ahc_outb(ahc, HSCB_ADDR + 3, (physaddr >> 24) & 0xFF);
-
-	physaddr = ahc->shared_data_busaddr;
-	ahc_outb(ahc, SHARED_DATA_ADDR, physaddr & 0xFF);
-	ahc_outb(ahc, SHARED_DATA_ADDR + 1, (physaddr >> 8) & 0xFF);
-	ahc_outb(ahc, SHARED_DATA_ADDR + 2, (physaddr >> 16) & 0xFF);
-	ahc_outb(ahc, SHARED_DATA_ADDR + 3, (physaddr >> 24) & 0xFF);
-
-	/*
-	 * Initialize the group code to command length table.
-	 * This overrides the values in TARG_SCSIRATE, so only
-	 * setup the table after we have processed that information.
-	 */
-	ahc_outb(ahc, CMDSIZE_TABLE, 5);
-	ahc_outb(ahc, CMDSIZE_TABLE + 1, 9);
-	ahc_outb(ahc, CMDSIZE_TABLE + 2, 9);
-	ahc_outb(ahc, CMDSIZE_TABLE + 3, 0);
-	ahc_outb(ahc, CMDSIZE_TABLE + 4, 15);
-	ahc_outb(ahc, CMDSIZE_TABLE + 5, 11);
-	ahc_outb(ahc, CMDSIZE_TABLE + 6, 0);
-	ahc_outb(ahc, CMDSIZE_TABLE + 7, 0);
-		
-	/* Tell the sequencer of our initial queue positions */
-	ahc_outb(ahc, KERNEL_QINPOS, 0);
-	ahc_outb(ahc, QINPOS, 0);
-	ahc_outb(ahc, QOUTPOS, 0);
-
-	/*
-	 * Use the built in queue management registers
-	 * if they are available.
-	 */
-	if ((ahc->features & AHC_QUEUE_REGS) != 0) {
-		ahc_outb(ahc, QOFF_CTLSTA, SCB_QSIZE_256);
-		ahc_outb(ahc, SDSCB_QOFF, 0);
-		ahc_outb(ahc, SNSCB_QOFF, 0);
-		ahc_outb(ahc, HNSCB_QOFF, 0);
-	}
-
-
-	/* We don't have any waiting selections */
-	ahc_outb(ahc, WAITING_SCBH, SCB_LIST_NULL);
-
-	/* Our disconnection list is empty too */
-	ahc_outb(ahc, DISCONNECTED_SCBH, SCB_LIST_NULL);
-
-	/* Message out buffer starts empty */
-	ahc_outb(ahc, MSG_OUT, MSG_NOOP);
-
-	/*
-	 * Setup the allowed SCSI Sequences based on operational mode.
-	 * If we are a target, we'll enalbe select in operations once
-	 * we've had a lun enabled.
-	 */
-	scsiseq_template = ENSELO|ENAUTOATNO|ENAUTOATNP;
-	if ((ahc->flags & AHC_INITIATORROLE) != 0)
-		scsiseq_template |= ENRSELI;
-	ahc_outb(ahc, SCSISEQ_TEMPLATE, scsiseq_template);
-
-	/*
-	 * Load the Sequencer program and Enable the adapter
-	 * in "fast" mode.
-	 */
-	if (bootverbose)
-		printf("%s: Downloading Sequencer Program...",
-		       ahc_name(ahc));
-
-	ahc_loadseq(ahc);
-
-	if ((ahc->features & AHC_ULTRA2) != 0) {
-		int wait;
-
-		/*
-		 * Wait for up to 500ms for our transceivers
-		 * to settle.  If the adapter does not have
-		 * a cable attached, the tranceivers may
-		 * never settle, so don't complain if we
-		 * fail here.
-		 */
-		ahc_pause(ahc);
-		for (wait = 5000;
-		     (ahc_inb(ahc, SBLKCTL) & (ENAB40|ENAB20)) == 0 && wait;
-		     wait--)
-			ahc_delay(100);
-		ahc_unpause(ahc);
-	}
-	return (0);
-}
-
-void
-ahc_intr_enable(struct ahc_softc *ahc, int enable)
-{
-	u_int hcntrl;
-
-	hcntrl = ahc_inb(ahc, HCNTRL);
-	hcntrl &= ~INTEN;
-	ahc->pause &= ~INTEN;
-	ahc->unpause &= ~INTEN;
-	if (enable) {
-		hcntrl |= INTEN;
-		ahc->pause |= INTEN;
-		ahc->unpause |= INTEN;
-	}
-	ahc_outb(ahc, HCNTRL, hcntrl);
-}
-
-/*
- * Ensure that the card is paused in a location
- * outside of all critical sections and that all
- * pending work is completed prior to returning.
- * This routine should only be called from outside
- * an interrupt context.
- */
-void
-ahc_pause_and_flushwork(struct ahc_softc *ahc)
-{
-	int intstat;
-	int maxloops;
-
-	maxloops = 1000;
-	ahc->flags |= AHC_ALL_INTERRUPTS;
-	intstat = 0;
-	do {
-		ahc_intr(ahc);
-		ahc_pause(ahc);
-		ahc_clear_critical_section(ahc);
-		if (intstat == 0xFF && (ahc->features & AHC_REMOVABLE) != 0)
-			break;
-		maxloops--;
-	} while (((intstat = ahc_inb(ahc, INTSTAT)) & INT_PEND) && --maxloops);
-	if (maxloops == 0) {
-		printf("Infinite interrupt loop, INTSTAT = %x",
-		      ahc_inb(ahc, INTSTAT));
-	}
-	ahc_platform_flushwork(ahc);
-	ahc->flags &= ~AHC_ALL_INTERRUPTS;
-}
-
-int
-ahc_suspend(struct ahc_softc *ahc)
-{
-	uint8_t *ptr;
-	int	 i;
-
-	ahc_pause_and_flushwork(ahc);
-
-	if (LIST_FIRST(&ahc->pending_scbs) != NULL)
-		return (EBUSY);
-
-#if AHC_TARGET_MODE
-	/*
-	 * XXX What about ATIOs that have not yet been serviced?
-	 * Perhaps we should just refuse to be suspended if we
-	 * are acting in a target role.
-	 */
-	if (ahc->pending_device != NULL)
-		return (EBUSY);
-#endif
-
-	/* Save volatile registers */
-	if ((ahc->features & AHC_TWIN) != 0) {
-		ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) | SELBUSB);
-		ahc->suspend_state.channel[1].scsiseq = ahc_inb(ahc, SCSISEQ);
-		ahc->suspend_state.channel[1].sxfrctl0 = ahc_inb(ahc, SXFRCTL0);
-		ahc->suspend_state.channel[1].sxfrctl1 = ahc_inb(ahc, SXFRCTL1);
-		ahc->suspend_state.channel[1].simode0 = ahc_inb(ahc, SIMODE0);
-		ahc->suspend_state.channel[1].simode1 = ahc_inb(ahc, SIMODE1);
-		ahc->suspend_state.channel[1].seltimer = ahc_inb(ahc, SELTIMER);
-		ahc->suspend_state.channel[1].seqctl = ahc_inb(ahc, SEQCTL);
-		ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) & ~SELBUSB);
-	}
-	ahc->suspend_state.channel[0].scsiseq = ahc_inb(ahc, SCSISEQ);
-	ahc->suspend_state.channel[0].sxfrctl0 = ahc_inb(ahc, SXFRCTL0);
-	ahc->suspend_state.channel[0].sxfrctl1 = ahc_inb(ahc, SXFRCTL1);
-	ahc->suspend_state.channel[0].simode0 = ahc_inb(ahc, SIMODE0);
-	ahc->suspend_state.channel[0].simode1 = ahc_inb(ahc, SIMODE1);
-	ahc->suspend_state.channel[0].seltimer = ahc_inb(ahc, SELTIMER);
-	ahc->suspend_state.channel[0].seqctl = ahc_inb(ahc, SEQCTL);
-
-	if ((ahc->chip & AHC_PCI) != 0) {
-		ahc->suspend_state.dscommand0 = ahc_inb(ahc, DSCOMMAND0);
-		ahc->suspend_state.dspcistatus = ahc_inb(ahc, DSPCISTATUS);
-	}
-
-	if ((ahc->features & AHC_DT) != 0) {
-		u_int sfunct;
-
-		sfunct = ahc_inb(ahc, SFUNCT) & ~ALT_MODE;
-		ahc_outb(ahc, SFUNCT, sfunct | ALT_MODE);
-		ahc->suspend_state.optionmode = ahc_inb(ahc, OPTIONMODE);
-		ahc_outb(ahc, SFUNCT, sfunct);
-		ahc->suspend_state.crccontrol1 = ahc_inb(ahc, CRCCONTROL1);
-	}
-
-	if ((ahc->features & AHC_MULTI_FUNC) != 0)
-		ahc->suspend_state.scbbaddr = ahc_inb(ahc, SCBBADDR);
-
-	if ((ahc->features & AHC_ULTRA2) != 0)
-		ahc->suspend_state.dff_thrsh = ahc_inb(ahc, DFF_THRSH);
-
-	ptr = ahc->suspend_state.scratch_ram;
-	for (i = 0; i < 64; i++)
-		*ptr++ = ahc_inb(ahc, SRAM_BASE + i);
-
-	if ((ahc->features & AHC_MORE_SRAM) != 0) {
-		for (i = 0; i < 16; i++)
-			*ptr++ = ahc_inb(ahc, TARG_OFFSET + i);
-	}
-
-	ptr = ahc->suspend_state.btt;
-	if ((ahc->flags & AHC_SCB_BTT) != 0) {
-		for (i = 0;i < AHC_NUM_TARGETS; i++) {
-			int j;
-
-			for (j = 0;j < AHC_NUM_LUNS; j++) {
-				u_int tcl;
-
-				tcl = BUILD_TCL(i << 4, j);
-				*ptr = ahc_index_busy_tcl(ahc, tcl);
-			}
-		}
-	}
-	ahc_shutdown(ahc);
-	return (0);
-}
-
-int
-ahc_resume(struct ahc_softc *ahc)
-{
-	uint8_t *ptr;
-	int	 i;
-
-	ahc_reset(ahc);
-
-	ahc_build_free_scb_list(ahc);
-
-	/* Restore volatile registers */
-	if ((ahc->features & AHC_TWIN) != 0) {
-		ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) | SELBUSB);
-		ahc_outb(ahc, SCSIID, ahc->our_id);
-		ahc_outb(ahc, SCSISEQ, ahc->suspend_state.channel[1].scsiseq);
-		ahc_outb(ahc, SXFRCTL0, ahc->suspend_state.channel[1].sxfrctl0);
-		ahc_outb(ahc, SXFRCTL1, ahc->suspend_state.channel[1].sxfrctl1);
-		ahc_outb(ahc, SIMODE0, ahc->suspend_state.channel[1].simode0);
-		ahc_outb(ahc, SIMODE1, ahc->suspend_state.channel[1].simode1);
-		ahc_outb(ahc, SELTIMER, ahc->suspend_state.channel[1].seltimer);
-		ahc_outb(ahc, SEQCTL, ahc->suspend_state.channel[1].seqctl);
-		ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) & ~SELBUSB);
-	}
-	ahc_outb(ahc, SCSISEQ, ahc->suspend_state.channel[0].scsiseq);
-	ahc_outb(ahc, SXFRCTL0, ahc->suspend_state.channel[0].sxfrctl0);
-	ahc_outb(ahc, SXFRCTL1, ahc->suspend_state.channel[0].sxfrctl1);
-	ahc_outb(ahc, SIMODE0, ahc->suspend_state.channel[0].simode0);
-	ahc_outb(ahc, SIMODE1, ahc->suspend_state.channel[0].simode1);
-	ahc_outb(ahc, SELTIMER, ahc->suspend_state.channel[0].seltimer);
-	ahc_outb(ahc, SEQCTL, ahc->suspend_state.channel[0].seqctl);
-	if ((ahc->features & AHC_ULTRA2) != 0)
-		ahc_outb(ahc, SCSIID_ULTRA2, ahc->our_id);
-	else
-		ahc_outb(ahc, SCSIID, ahc->our_id);
-
-	if ((ahc->chip & AHC_PCI) != 0) {
-		ahc_outb(ahc, DSCOMMAND0, ahc->suspend_state.dscommand0);
-		ahc_outb(ahc, DSPCISTATUS, ahc->suspend_state.dspcistatus);
-	}
-
-	if ((ahc->features & AHC_DT) != 0) {
-		u_int sfunct;
-
-		sfunct = ahc_inb(ahc, SFUNCT) & ~ALT_MODE;
-		ahc_outb(ahc, SFUNCT, sfunct | ALT_MODE);
-		ahc_outb(ahc, OPTIONMODE, ahc->suspend_state.optionmode);
-		ahc_outb(ahc, SFUNCT, sfunct);
-		ahc_outb(ahc, CRCCONTROL1, ahc->suspend_state.crccontrol1);
-	}
-
-	if ((ahc->features & AHC_MULTI_FUNC) != 0)
-		ahc_outb(ahc, SCBBADDR, ahc->suspend_state.scbbaddr);
-
-	if ((ahc->features & AHC_ULTRA2) != 0)
-		ahc_outb(ahc, DFF_THRSH, ahc->suspend_state.dff_thrsh);
-
-	ptr = ahc->suspend_state.scratch_ram;
-	for (i = 0; i < 64; i++)
-		ahc_outb(ahc, SRAM_BASE + i, *ptr++);
-
-	if ((ahc->features & AHC_MORE_SRAM) != 0) {
-		for (i = 0; i < 16; i++)
-			ahc_outb(ahc, TARG_OFFSET + i, *ptr++);
-	}
-
-	ptr = ahc->suspend_state.btt;
-	if ((ahc->flags & AHC_SCB_BTT) != 0) {
-		for (i = 0;i < AHC_NUM_TARGETS; i++) {
-			int j;
-
-			for (j = 0;j < AHC_NUM_LUNS; j++) {
-				u_int tcl;
-
-				tcl = BUILD_TCL(i << 4, j);
-				ahc_busy_tcl(ahc, tcl, *ptr);
-			}
-		}
-	}
-	return (0);
-}
-
-/************************** Busy Target Table *********************************/
-/*
- * Return the untagged transaction id for a given target/channel lun.
- * Optionally, clear the entry.
- */
-u_int
-ahc_index_busy_tcl(struct ahc_softc *ahc, u_int tcl)
-{
-	u_int scbid;
-	u_int target_offset;
-
-	if ((ahc->flags & AHC_SCB_BTT) != 0) {
-		u_int saved_scbptr;
-		
-		saved_scbptr = ahc_inb(ahc, SCBPTR);
-		ahc_outb(ahc, SCBPTR, TCL_LUN(tcl));
-		scbid = ahc_inb(ahc, SCB_64_BTT + TCL_TARGET_OFFSET(tcl));
-		ahc_outb(ahc, SCBPTR, saved_scbptr);
-	} else {
-		target_offset = TCL_TARGET_OFFSET(tcl);
-		scbid = ahc_inb(ahc, BUSY_TARGETS + target_offset);
-	}
-
-	return (scbid);
-}
-
-void
-ahc_unbusy_tcl(struct ahc_softc *ahc, u_int tcl)
-{
-	u_int target_offset;
-
-	if ((ahc->flags & AHC_SCB_BTT) != 0) {
-		u_int saved_scbptr;
-		
-		saved_scbptr = ahc_inb(ahc, SCBPTR);
-		ahc_outb(ahc, SCBPTR, TCL_LUN(tcl));
-		ahc_outb(ahc, SCB_64_BTT+TCL_TARGET_OFFSET(tcl), SCB_LIST_NULL);
-		ahc_outb(ahc, SCBPTR, saved_scbptr);
-	} else {
-		target_offset = TCL_TARGET_OFFSET(tcl);
-		ahc_outb(ahc, BUSY_TARGETS + target_offset, SCB_LIST_NULL);
-	}
-}
-
-void
-ahc_busy_tcl(struct ahc_softc *ahc, u_int tcl, u_int scbid)
-{
-	u_int target_offset;
-
-	if ((ahc->flags & AHC_SCB_BTT) != 0) {
-		u_int saved_scbptr;
-		
-		saved_scbptr = ahc_inb(ahc, SCBPTR);
-		ahc_outb(ahc, SCBPTR, TCL_LUN(tcl));
-		ahc_outb(ahc, SCB_64_BTT + TCL_TARGET_OFFSET(tcl), scbid);
-		ahc_outb(ahc, SCBPTR, saved_scbptr);
-	} else {
-		target_offset = TCL_TARGET_OFFSET(tcl);
-		ahc_outb(ahc, BUSY_TARGETS + target_offset, scbid);
-	}
-}
-
-/************************** SCB and SCB queue management **********************/
-int
-ahc_match_scb(struct ahc_softc *ahc, struct scb *scb, int target,
-	      char channel, int lun, u_int tag, role_t role)
-{
-	int targ = SCB_GET_TARGET(ahc, scb);
-	char chan = SCB_GET_CHANNEL(ahc, scb);
-	int slun = SCB_GET_LUN(scb);
-	int match;
-
-	match = ((chan == channel) || (channel == ALL_CHANNELS));
-	if (match != 0)
-		match = ((targ == target) || (target == CAM_TARGET_WILDCARD));
-	if (match != 0)
-		match = ((lun == slun) || (lun == CAM_LUN_WILDCARD));
-	if (match != 0) {
-#if AHC_TARGET_MODE
-		int group;
-
-		group = XPT_FC_GROUP(scb->io_ctx->ccb_h.func_code);
-		if (role == ROLE_INITIATOR) {
-			match = (group != XPT_FC_GROUP_TMODE)
-			      && ((tag == scb->hscb->tag)
-			       || (tag == SCB_LIST_NULL));
-		} else if (role == ROLE_TARGET) {
-			match = (group == XPT_FC_GROUP_TMODE)
-			      && ((tag == scb->io_ctx->csio.tag_id)
-			       || (tag == SCB_LIST_NULL));
-		}
-#else /* !AHC_TARGET_MODE */
-		match = ((tag == scb->hscb->tag) || (tag == SCB_LIST_NULL));
-#endif /* AHC_TARGET_MODE */
-	}
-
-	return match;
-}
-
-void
-ahc_freeze_devq(struct ahc_softc *ahc, struct scb *scb)
-{
-	int	target;
-	char	channel;
-	int	lun;
-
-	target = SCB_GET_TARGET(ahc, scb);
-	lun = SCB_GET_LUN(scb);
-	channel = SCB_GET_CHANNEL(ahc, scb);
-	
-	ahc_search_qinfifo(ahc, target, channel, lun,
-			   /*tag*/SCB_LIST_NULL, ROLE_UNKNOWN,
-			   CAM_REQUEUE_REQ, SEARCH_COMPLETE);
-
-	ahc_platform_freeze_devq(ahc, scb);
-}
-
-void
-ahc_qinfifo_requeue_tail(struct ahc_softc *ahc, struct scb *scb)
-{
-	struct scb *prev_scb;
-
-	prev_scb = NULL;
-	if (ahc_qinfifo_count(ahc) != 0) {
-		u_int prev_tag;
-		uint8_t prev_pos;
-
-		prev_pos = ahc->qinfifonext - 1;
-		prev_tag = ahc->qinfifo[prev_pos];
-		prev_scb = ahc_lookup_scb(ahc, prev_tag);
-	}
-	ahc_qinfifo_requeue(ahc, prev_scb, scb);
-	if ((ahc->features & AHC_QUEUE_REGS) != 0) {
-		ahc_outb(ahc, HNSCB_QOFF, ahc->qinfifonext);
-	} else {
-		ahc_outb(ahc, KERNEL_QINPOS, ahc->qinfifonext);
-	}
-}
-
-static void
-ahc_qinfifo_requeue(struct ahc_softc *ahc, struct scb *prev_scb,
-		    struct scb *scb)
-{
-	if (prev_scb == NULL) {
-		ahc_outb(ahc, NEXT_QUEUED_SCB, scb->hscb->tag);
-	} else {
-		prev_scb->hscb->next = scb->hscb->tag;
-		ahc_sync_scb(ahc, prev_scb, 
-			     BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
-	}
-	ahc->qinfifo[ahc->qinfifonext++] = scb->hscb->tag;
-	scb->hscb->next = ahc->next_queued_scb->hscb->tag;
-	ahc_sync_scb(ahc, scb, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
-}
-
-static int
-ahc_qinfifo_count(struct ahc_softc *ahc)
-{
-	u_int8_t qinpos;
-	u_int8_t diff;
-
-	if ((ahc->features & AHC_QUEUE_REGS) != 0) {
-		qinpos = ahc_inb(ahc, SNSCB_QOFF);
-		ahc_outb(ahc, SNSCB_QOFF, qinpos);
-	} else
-		qinpos = ahc_inb(ahc, QINPOS);
-	diff = ahc->qinfifonext - qinpos;
-	return (diff);
-}
-
-int
-ahc_search_qinfifo(struct ahc_softc *ahc, int target, char channel,
-		   int lun, u_int tag, role_t role, uint32_t status,
-		   ahc_search_action action)
-{
-	struct	scb *scb;
-	struct	scb *prev_scb;
-	uint8_t qinstart;
-	uint8_t qinpos;
-	uint8_t qintail;
-	uint8_t next, prev;
-	uint8_t curscbptr;
-	int	found;
-	int	maxtarget;
-	int	i;
-	int	have_qregs;
-
-	qintail = ahc->qinfifonext;
-	have_qregs = (ahc->features & AHC_QUEUE_REGS) != 0;
-	if (have_qregs) {
-		qinstart = ahc_inb(ahc, SNSCB_QOFF);
-		ahc_outb(ahc, SNSCB_QOFF, qinstart);
-	} else
-		qinstart = ahc_inb(ahc, QINPOS);
-	qinpos = qinstart;
-	next = ahc_inb(ahc, NEXT_QUEUED_SCB);
-	found = 0;
-	prev_scb = NULL;
-
-	if (action == SEARCH_COMPLETE) {
-		/*
-		 * Don't attempt to run any queued untagged transactions
-		 * until we are done with the abort process.
-		 */
-		ahc_freeze_untagged_queues(ahc);
-	}
-
-	/*
-	 * Start with an empty queue.  Entries that are not chosen
-	 * for removal will be re-added to the queue as we go.
-	 */
-	ahc->qinfifonext = qinpos;
-	ahc_outb(ahc, NEXT_QUEUED_SCB, ahc->next_queued_scb->hscb->tag);
-
-	while (qinpos != qintail) {
-		scb = ahc_lookup_scb(ahc, ahc->qinfifo[qinpos]);
-		if (scb == NULL) {
-			printf("qinpos = %d, SCB index = %d\n",
-				qinpos, ahc->qinfifo[qinpos]);
-			panic("Loop 1\n");
-		}
-
-		if (ahc_match_scb(ahc, scb, target, channel, lun, tag, role)) {
-			/*
-			 * We found an scb that needs to be acted on.
-			 */
-			found++;
-			switch (action) {
-			case SEARCH_COMPLETE:
-			{
-				cam_status ostat;
-				cam_status cstat;
-
-				ostat = ahc_get_transaction_status(scb);
-				if (ostat == CAM_REQ_INPROG)
-					ahc_set_transaction_status(scb,
-								   status);
-				cstat = ahc_get_transaction_status(scb);
-				if (cstat != CAM_REQ_CMP)
-					ahc_freeze_scb(scb);
-				if ((scb->flags & SCB_ACTIVE) == 0)
-					printf("Inactive SCB in qinfifo\n");
-				ahc_done(ahc, scb);
-
-				/* FALLTHROUGH */
-			case SEARCH_REMOVE:
-				break;
-			}
-			case SEARCH_COUNT:
-				ahc_qinfifo_requeue(ahc, prev_scb, scb);
-				prev_scb = scb;
-				break;
-			}
-		} else {
-			ahc_qinfifo_requeue(ahc, prev_scb, scb);
-			prev_scb = scb;
-		}
-		qinpos++;
-	}
-
-	if ((ahc->features & AHC_QUEUE_REGS) != 0) {
-		ahc_outb(ahc, HNSCB_QOFF, ahc->qinfifonext);
-	} else {
-		ahc_outb(ahc, KERNEL_QINPOS, ahc->qinfifonext);
-	}
-
-	if (action != SEARCH_COUNT
-	 && (found != 0)
-	 && (qinstart != ahc->qinfifonext)) {
-		/*
-		 * The sequencer may be in the process of dmaing
-		 * down the SCB at the beginning of the queue.
-		 * This could be problematic if either the first,
-		 * or the second SCB is removed from the queue
-		 * (the first SCB includes a pointer to the "next"
-		 * SCB to dma). If we have removed any entries, swap
-		 * the first element in the queue with the next HSCB
-		 * so the sequencer will notice that NEXT_QUEUED_SCB
-		 * has changed during its dma attempt and will retry
-		 * the DMA.
-		 */
-		scb = ahc_lookup_scb(ahc, ahc->qinfifo[qinstart]);
-
-		if (scb == NULL) {
-			printf("found = %d, qinstart = %d, qinfifionext = %d\n",
-				found, qinstart, ahc->qinfifonext);
-			panic("First/Second Qinfifo fixup\n");
-		}
-		/*
-		 * ahc_swap_with_next_hscb forces our next pointer to
-		 * point to the reserved SCB for future commands.  Save
-		 * and restore our original next pointer to maintain
-		 * queue integrity.
-		 */
-		next = scb->hscb->next;
-		ahc->scb_data->scbindex[scb->hscb->tag] = NULL;
-		ahc_swap_with_next_hscb(ahc, scb);
-		scb->hscb->next = next;
-		ahc->qinfifo[qinstart] = scb->hscb->tag;
-
-		/* Tell the card about the new head of the qinfifo. */
-		ahc_outb(ahc, NEXT_QUEUED_SCB, scb->hscb->tag);
-
-		/* Fixup the tail "next" pointer. */
-		qintail = ahc->qinfifonext - 1;
-		scb = ahc_lookup_scb(ahc, ahc->qinfifo[qintail]);
-		scb->hscb->next = ahc->next_queued_scb->hscb->tag;
-	}
-
-	/*
-	 * Search waiting for selection list.
-	 */
-	curscbptr = ahc_inb(ahc, SCBPTR);
-	next = ahc_inb(ahc, WAITING_SCBH);  /* Start at head of list. */
-	prev = SCB_LIST_NULL;
-
-	while (next != SCB_LIST_NULL) {
-		uint8_t scb_index;
-
-		ahc_outb(ahc, SCBPTR, next);
-		scb_index = ahc_inb(ahc, SCB_TAG);
-		if (scb_index >= ahc->scb_data->numscbs) {
-			printf("Waiting List inconsistency. "
-			       "SCB index == %d, yet numscbs == %d.",
-			       scb_index, ahc->scb_data->numscbs);
-			ahc_dump_card_state(ahc);
-			panic("for safety");
-		}
-		scb = ahc_lookup_scb(ahc, scb_index);
-		if (scb == NULL) {
-			printf("scb_index = %d, next = %d\n",
-				scb_index, next);
-			panic("Waiting List traversal\n");
-		}
-		if (ahc_match_scb(ahc, scb, target, channel,
-				  lun, SCB_LIST_NULL, role)) {
-			/*
-			 * We found an scb that needs to be acted on.
-			 */
-			found++;
-			switch (action) {
-			case SEARCH_COMPLETE:
-			{
-				cam_status ostat;
-				cam_status cstat;
-
-				ostat = ahc_get_transaction_status(scb);
-				if (ostat == CAM_REQ_INPROG)
-					ahc_set_transaction_status(scb,
-								   status);
-				cstat = ahc_get_transaction_status(scb);
-				if (cstat != CAM_REQ_CMP)
-					ahc_freeze_scb(scb);
-				if ((scb->flags & SCB_ACTIVE) == 0)
-					printf("Inactive SCB in Waiting List\n");
-				ahc_done(ahc, scb);
-				/* FALLTHROUGH */
-			}
-			case SEARCH_REMOVE:
-				next = ahc_rem_wscb(ahc, next, prev);
-				break;
-			case SEARCH_COUNT:
-				prev = next;
-				next = ahc_inb(ahc, SCB_NEXT);
-				break;
-			}
-		} else {
-			
-			prev = next;
-			next = ahc_inb(ahc, SCB_NEXT);
-		}
-	}
-	ahc_outb(ahc, SCBPTR, curscbptr);
-
-	/*
-	 * And lastly, the untagged holding queues.
-	 */
-	i = 0;
-	if ((ahc->flags & AHC_SCB_BTT) == 0) {
-
-		maxtarget = 16;
-		if (target != CAM_TARGET_WILDCARD) {
-
-			i = target;
-			if (channel == 'B')
-				i += 8;
-			maxtarget = i + 1;
-		}
-	} else {
-		maxtarget = 0;
-	}
-
-	for (; i < maxtarget; i++) {
-		struct scb_tailq *untagged_q;
-		struct scb *next_scb;
-
-		untagged_q = &(ahc->untagged_queues[i]);
-		next_scb = TAILQ_FIRST(untagged_q);
-		while (next_scb != NULL) {
-
-			scb = next_scb;
-			next_scb = TAILQ_NEXT(scb, links.tqe);
-
-			/*
-			 * The head of the list may be the currently
-			 * active untagged command for a device.
-			 * We're only searching for commands that
-			 * have not been started.  A transaction
-			 * marked active but still in the qinfifo
-			 * is removed by the qinfifo scanning code
-			 * above.
-			 */
-			if ((scb->flags & SCB_ACTIVE) != 0)
-				continue;
-
-			if (ahc_match_scb(ahc, scb, target, channel,
-					  lun, SCB_LIST_NULL, role)) {
-				/*
-				 * We found an scb that needs to be acted on.
-				 */
-				found++;
-				switch (action) {
-				case SEARCH_COMPLETE:
-				{
-					cam_status ostat;
-					cam_status cstat;
-
-					ostat = ahc_get_transaction_status(scb);
-					if (ostat == CAM_REQ_INPROG)
-						ahc_set_transaction_status(scb,
-								   status);
-					cstat = ahc_get_transaction_status(scb);
-					if (cstat != CAM_REQ_CMP)
-						ahc_freeze_scb(scb);
-					if ((scb->flags & SCB_ACTIVE) == 0)
-						printf("Inactive SCB in untaggedQ\n");
-					ahc_done(ahc, scb);
-					break;
-				}
-				case SEARCH_REMOVE:
-					TAILQ_REMOVE(untagged_q, scb,
-						     links.tqe);
-					break;
-				case SEARCH_COUNT:
-					break;
-				}
-			}
-		}
-	}
-
-	if (action == SEARCH_COMPLETE)
-		ahc_release_untagged_queues(ahc);
-	return (found);
-}
-
-int
-ahc_search_disc_list(struct ahc_softc *ahc, int target, char channel,
-		     int lun, u_int tag, int stop_on_first, int remove,
-		     int save_state)
-{
-	struct	scb *scbp;
-	u_int	next;
-	u_int	prev;
-	u_int	count;
-	u_int	active_scb;
-
-	count = 0;
-	next = ahc_inb(ahc, DISCONNECTED_SCBH);
-	prev = SCB_LIST_NULL;
-
-	if (save_state) {
-		/* restore this when we're done */
-		active_scb = ahc_inb(ahc, SCBPTR);
-	} else
-		/* Silence compiler */
-		active_scb = SCB_LIST_NULL;
-
-	while (next != SCB_LIST_NULL) {
-		u_int scb_index;
-
-		ahc_outb(ahc, SCBPTR, next);
-		scb_index = ahc_inb(ahc, SCB_TAG);
-		if (scb_index >= ahc->scb_data->numscbs) {
-			printf("Disconnected List inconsistency. "
-			       "SCB index == %d, yet numscbs == %d.",
-			       scb_index, ahc->scb_data->numscbs);
-			ahc_dump_card_state(ahc);
-			panic("for safety");
-		}
-
-		if (next == prev) {
-			panic("Disconnected List Loop. "
-			      "cur SCBPTR == %x, prev SCBPTR == %x.",
-			      next, prev);
-		}
-		scbp = ahc_lookup_scb(ahc, scb_index);
-		if (ahc_match_scb(ahc, scbp, target, channel, lun,
-				  tag, ROLE_INITIATOR)) {
-			count++;
-			if (remove) {
-				next =
-				    ahc_rem_scb_from_disc_list(ahc, prev, next);
-			} else {
-				prev = next;
-				next = ahc_inb(ahc, SCB_NEXT);
-			}
-			if (stop_on_first)
-				break;
-		} else {
-			prev = next;
-			next = ahc_inb(ahc, SCB_NEXT);
-		}
-	}
-	if (save_state)
-		ahc_outb(ahc, SCBPTR, active_scb);
-	return (count);
-}
-
-/*
- * Remove an SCB from the on chip list of disconnected transactions.
- * This is empty/unused if we are not performing SCB paging.
- */
-static u_int
-ahc_rem_scb_from_disc_list(struct ahc_softc *ahc, u_int prev, u_int scbptr)
-{
-	u_int next;
-
-	ahc_outb(ahc, SCBPTR, scbptr);
-	next = ahc_inb(ahc, SCB_NEXT);
-
-	ahc_outb(ahc, SCB_CONTROL, 0);
-
-	ahc_add_curscb_to_free_list(ahc);
-
-	if (prev != SCB_LIST_NULL) {
-		ahc_outb(ahc, SCBPTR, prev);
-		ahc_outb(ahc, SCB_NEXT, next);
-	} else
-		ahc_outb(ahc, DISCONNECTED_SCBH, next);
-
-	return (next);
-}
-
-/*
- * Add the SCB as selected by SCBPTR onto the on chip list of
- * free hardware SCBs.  This list is empty/unused if we are not
- * performing SCB paging.
- */
-static void
-ahc_add_curscb_to_free_list(struct ahc_softc *ahc)
-{
-	/*
-	 * Invalidate the tag so that our abort
-	 * routines don't think it's active.
-	 */
-	ahc_outb(ahc, SCB_TAG, SCB_LIST_NULL);
-
-	if ((ahc->flags & AHC_PAGESCBS) != 0) {
-		ahc_outb(ahc, SCB_NEXT, ahc_inb(ahc, FREE_SCBH));
-		ahc_outb(ahc, FREE_SCBH, ahc_inb(ahc, SCBPTR));
-	}
-}
-
-/*
- * Manipulate the waiting for selection list and return the
- * scb that follows the one that we remove.
- */
-static u_int
-ahc_rem_wscb(struct ahc_softc *ahc, u_int scbpos, u_int prev)
-{       
-	u_int curscb, next;
-
-	/*
-	 * Select the SCB we want to abort and
-	 * pull the next pointer out of it.
-	 */
-	curscb = ahc_inb(ahc, SCBPTR);
-	ahc_outb(ahc, SCBPTR, scbpos);
-	next = ahc_inb(ahc, SCB_NEXT);
-
-	/* Clear the necessary fields */
-	ahc_outb(ahc, SCB_CONTROL, 0);
-
-	ahc_add_curscb_to_free_list(ahc);
-
-	/* update the waiting list */
-	if (prev == SCB_LIST_NULL) {
-		/* First in the list */
-		ahc_outb(ahc, WAITING_SCBH, next); 
-
-		/*
-		 * Ensure we aren't attempting to perform
-		 * selection for this entry.
-		 */
-		ahc_outb(ahc, SCSISEQ, (ahc_inb(ahc, SCSISEQ) & ~ENSELO));
-	} else {
-		/*
-		 * Select the scb that pointed to us 
-		 * and update its next pointer.
-		 */
-		ahc_outb(ahc, SCBPTR, prev);
-		ahc_outb(ahc, SCB_NEXT, next);
-	}
-
-	/*
-	 * Point us back at the original scb position.
-	 */
-	ahc_outb(ahc, SCBPTR, curscb);
-	return next;
-}
-
-/******************************** Error Handling ******************************/
-/*
- * Abort all SCBs that match the given description (target/channel/lun/tag),
- * setting their status to the passed in status if the status has not already
- * been modified from CAM_REQ_INPROG.  This routine assumes that the sequencer
- * is paused before it is called.
- */
-int
-ahc_abort_scbs(struct ahc_softc *ahc, int target, char channel,
-	       int lun, u_int tag, role_t role, uint32_t status)
-{
-	struct	scb *scbp;
-	struct	scb *scbp_next;
-	u_int	active_scb;
-	int	i, j;
-	int	maxtarget;
-	int	minlun;
-	int	maxlun;
-
-	int	found;
-
-	/*
-	 * Don't attempt to run any queued untagged transactions
-	 * until we are done with the abort process.
-	 */
-	ahc_freeze_untagged_queues(ahc);
-
-	/* restore this when we're done */
-	active_scb = ahc_inb(ahc, SCBPTR);
-
-	found = ahc_search_qinfifo(ahc, target, channel, lun, SCB_LIST_NULL,
-				   role, CAM_REQUEUE_REQ, SEARCH_COMPLETE);
-
-	/*
-	 * Clean out the busy target table for any untagged commands.
-	 */
-	i = 0;
-	maxtarget = 16;
-	if (target != CAM_TARGET_WILDCARD) {
-		i = target;
-		if (channel == 'B')
-			i += 8;
-		maxtarget = i + 1;
-	}
-
-	if (lun == CAM_LUN_WILDCARD) {
-
-		/*
-		 * Unless we are using an SCB based
-		 * busy targets table, there is only
-		 * one table entry for all luns of
-		 * a target.
-		 */
-		minlun = 0;
-		maxlun = 1;
-		if ((ahc->flags & AHC_SCB_BTT) != 0)
-			maxlun = AHC_NUM_LUNS;
-	} else {
-		minlun = lun;
-		maxlun = lun + 1;
-	}
-
-	if (role != ROLE_TARGET) {
-		for (;i < maxtarget; i++) {
-			for (j = minlun;j < maxlun; j++) {
-				u_int scbid;
-				u_int tcl;
-
-				tcl = BUILD_TCL(i << 4, j);
-				scbid = ahc_index_busy_tcl(ahc, tcl);
-				scbp = ahc_lookup_scb(ahc, scbid);
-				if (scbp == NULL
-				 || ahc_match_scb(ahc, scbp, target, channel,
-						  lun, tag, role) == 0)
-					continue;
-				ahc_unbusy_tcl(ahc, BUILD_TCL(i << 4, j));
-			}
-		}
-
-		/*
-		 * Go through the disconnected list and remove any entries we
-		 * have queued for completion, 0'ing their control byte too.
-		 * We save the active SCB and restore it ourselves, so there
-		 * is no reason for this search to restore it too.
-		 */
-		ahc_search_disc_list(ahc, target, channel, lun, tag,
-				     /*stop_on_first*/FALSE, /*remove*/TRUE,
-				     /*save_state*/FALSE);
-	}
-
-	/*
-	 * Go through the hardware SCB array looking for commands that
-	 * were active but not on any list.  In some cases, these remnants
-	 * might not still have mappings in the scbindex array (e.g. unexpected
-	 * bus free with the same scb queued for an abort).  Don't hold this
-	 * against them.
-	 */
-	for (i = 0; i < ahc->scb_data->maxhscbs; i++) {
-		u_int scbid;
-
-		ahc_outb(ahc, SCBPTR, i);
-		scbid = ahc_inb(ahc, SCB_TAG);
-		scbp = ahc_lookup_scb(ahc, scbid);
-		if ((scbp == NULL && scbid != SCB_LIST_NULL)
-		 || (scbp != NULL
-		  && ahc_match_scb(ahc, scbp, target, channel, lun, tag, role)))
-			ahc_add_curscb_to_free_list(ahc);
-	}
-
-	/*
-	 * Go through the pending CCB list and look for
-	 * commands for this target that are still active.
-	 * These are other tagged commands that were
-	 * disconnected when the reset occurred.
-	 */
-	scbp_next = LIST_FIRST(&ahc->pending_scbs);
-	while (scbp_next != NULL) {
-		scbp = scbp_next;
-		scbp_next = LIST_NEXT(scbp, pending_links);
-		if (ahc_match_scb(ahc, scbp, target, channel, lun, tag, role)) {
-			cam_status ostat;
-
-			ostat = ahc_get_transaction_status(scbp);
-			if (ostat == CAM_REQ_INPROG)
-				ahc_set_transaction_status(scbp, status);
-			if (ahc_get_transaction_status(scbp) != CAM_REQ_CMP)
-				ahc_freeze_scb(scbp);
-			if ((scbp->flags & SCB_ACTIVE) == 0)
-				printf("Inactive SCB on pending list\n");
-			ahc_done(ahc, scbp);
-			found++;
-		}
-	}
-	ahc_outb(ahc, SCBPTR, active_scb);
-	ahc_platform_abort_scbs(ahc, target, channel, lun, tag, role, status);
-	ahc_release_untagged_queues(ahc);
-	return found;
-}
-
-static void
-ahc_reset_current_bus(struct ahc_softc *ahc)
-{
-	uint8_t scsiseq;
-
-	ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENSCSIRST);
-	scsiseq = ahc_inb(ahc, SCSISEQ);
-	ahc_outb(ahc, SCSISEQ, scsiseq | SCSIRSTO);
-	ahc_delay(AHC_BUSRESET_DELAY);
-	/* Turn off the bus reset */
-	ahc_outb(ahc, SCSISEQ, scsiseq & ~SCSIRSTO);
-
-	ahc_clear_intstat(ahc);
-
-	/* Re-enable reset interrupts */
-	ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) | ENSCSIRST);
-}
-
-int
-ahc_reset_channel(struct ahc_softc *ahc, char channel, int initiate_reset)
-{
-	struct	ahc_devinfo devinfo;
-	u_int	initiator, target, max_scsiid;
-	u_int	sblkctl;
-	u_int	scsiseq;
-	u_int	simode1;
-	int	found;
-	int	restart_needed;
-	char	cur_channel;
-
-	ahc->pending_device = NULL;
-
-	ahc_compile_devinfo(&devinfo,
-			    CAM_TARGET_WILDCARD,
-			    CAM_TARGET_WILDCARD,
-			    CAM_LUN_WILDCARD,
-			    channel, ROLE_UNKNOWN);
-	ahc_pause(ahc);
-
-	/* Make sure the sequencer is in a safe location. */
-	ahc_clear_critical_section(ahc);
-
-	/*
-	 * Run our command complete fifos to ensure that we perform
-	 * completion processing on any commands that 'completed'
-	 * before the reset occurred.
-	 */
-	ahc_run_qoutfifo(ahc);
-#if AHC_TARGET_MODE
-	if ((ahc->flags & AHC_TARGETROLE) != 0) {
-		ahc_run_tqinfifo(ahc, /*paused*/TRUE);
-	}
-#endif
-
-	/*
-	 * Reset the bus if we are initiating this reset
-	 */
-	sblkctl = ahc_inb(ahc, SBLKCTL);
-	cur_channel = 'A';
-	if ((ahc->features & AHC_TWIN) != 0
-	 && ((sblkctl & SELBUSB) != 0))
-	    cur_channel = 'B';
-	scsiseq = ahc_inb(ahc, SCSISEQ_TEMPLATE);
-	if (cur_channel != channel) {
-		/* Case 1: Command for another bus is active
-		 * Stealthily reset the other bus without
-		 * upsetting the current bus.
-		 */
-		ahc_outb(ahc, SBLKCTL, sblkctl ^ SELBUSB);
-		simode1 = ahc_inb(ahc, SIMODE1) & ~(ENBUSFREE|ENSCSIRST);
-		ahc_outb(ahc, SIMODE1, simode1);
-		if (initiate_reset)
-			ahc_reset_current_bus(ahc);
-		ahc_clear_intstat(ahc);
-#if AHC_TARGET_MODE
-		/*
-		 * Bus resets clear ENSELI, so we cannot
-		 * defer re-enabling bus reset interrupts
-		 * if we are in target mode.
-		 */
-		if ((ahc->flags & AHC_TARGETROLE) != 0)
-			ahc_outb(ahc, SIMODE1, simode1 | ENSCSIRST);
-#endif
-		ahc_outb(ahc, SCSISEQ, scsiseq & (ENSELI|ENRSELI|ENAUTOATNP));
-		ahc_outb(ahc, SBLKCTL, sblkctl);
-		restart_needed = FALSE;
-	} else {
-		/* Case 2: A command from this bus is active or we're idle */
-		ahc_clear_msg_state(ahc);
-		simode1 = ahc_inb(ahc, SIMODE1) & ~(ENBUSFREE|ENSCSIRST);
-		ahc_outb(ahc, SIMODE1, simode1);
-		if (initiate_reset)
-			ahc_reset_current_bus(ahc);
-		ahc_clear_intstat(ahc);
-#if AHC_TARGET_MODE
-		/*
-		 * Bus resets clear ENSELI, so we cannot
-		 * defer re-enabling bus reset interrupts
-		 * if we are in target mode.
-		 */
-		if ((ahc->flags & AHC_TARGETROLE) != 0)
-			ahc_outb(ahc, SIMODE1, simode1 | ENSCSIRST);
-#endif
-		ahc_outb(ahc, SCSISEQ, scsiseq & (ENSELI|ENRSELI|ENAUTOATNP));
-		restart_needed = TRUE;
-	}
-
-	/*
-	 * Clean up all the state information for the
-	 * pending transactions on this bus.
-	 */
-	found = ahc_abort_scbs(ahc, CAM_TARGET_WILDCARD, channel,
-			       CAM_LUN_WILDCARD, SCB_LIST_NULL,
-			       ROLE_UNKNOWN, CAM_SCSI_BUS_RESET);
-
-	max_scsiid = (ahc->features & AHC_WIDE) ? 15 : 7;
-
-#ifdef AHC_TARGET_MODE
-	/*
-	 * Send an immediate notify ccb to all target more peripheral
-	 * drivers affected by this action.
-	 */
-	for (target = 0; target <= max_scsiid; target++) {
-		struct ahc_tmode_tstate* tstate;
-		u_int lun;
-
-		tstate = ahc->enabled_targets[target];
-		if (tstate == NULL)
-			continue;
-		for (lun = 0; lun < AHC_NUM_LUNS; lun++) {
-			struct ahc_tmode_lstate* lstate;
-
-			lstate = tstate->enabled_luns[lun];
-			if (lstate == NULL)
-				continue;
-
-			ahc_queue_lstate_event(ahc, lstate, CAM_TARGET_WILDCARD,
-					       EVENT_TYPE_BUS_RESET, /*arg*/0);
-			ahc_send_lstate_events(ahc, lstate);
-		}
-	}
-#endif
-	/* Notify the XPT that a bus reset occurred */
-	ahc_send_async(ahc, devinfo.channel, CAM_TARGET_WILDCARD,
-		       CAM_LUN_WILDCARD, AC_BUS_RESET, NULL);
-
-	/*
-	 * Revert to async/narrow transfers until we renegotiate.
-	 */
-	for (target = 0; target <= max_scsiid; target++) {
-
-		if (ahc->enabled_targets[target] == NULL)
-			continue;
-		for (initiator = 0; initiator <= max_scsiid; initiator++) {
-			struct ahc_devinfo devinfo;
-
-			ahc_compile_devinfo(&devinfo, target, initiator,
-					    CAM_LUN_WILDCARD,
-					    channel, ROLE_UNKNOWN);
-			ahc_set_width(ahc, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
-				      AHC_TRANS_CUR, /*paused*/TRUE);
-			ahc_set_syncrate(ahc, &devinfo, /*syncrate*/NULL,
-					 /*period*/0, /*offset*/0,
-					 /*ppr_options*/0, AHC_TRANS_CUR,
-					 /*paused*/TRUE);
-		}
-	}
-
-	if (restart_needed)
-		ahc_restart(ahc);
-	else
-		ahc_unpause(ahc);
-	return found;
-}
-
-
-/***************************** Residual Processing ****************************/
-/*
- * Calculate the residual for a just completed SCB.
- */
-void
-ahc_calc_residual(struct scb *scb)
-{
-	struct hardware_scb *hscb;
-	struct status_pkt *spkt;
-	uint32_t sgptr;
-	uint32_t resid_sgptr;
-	uint32_t resid;
-
-	/*
-	 * 5 cases.
-	 * 1) No residual.
-	 *    SG_RESID_VALID clear in sgptr.
-	 * 2) Transferless command
-	 * 3) Never performed any transfers.
-	 *    sgptr has SG_FULL_RESID set.
-	 * 4) No residual but target did not
-	 *    save data pointers after the
-	 *    last transfer, so sgptr was
-	 *    never updated.
-	 * 5) We have a partial residual.
-	 *    Use residual_sgptr to determine
-	 *    where we are.
-	 */
-
-	hscb = scb->hscb;
-	sgptr = ahc_le32toh(hscb->sgptr);
-	if ((sgptr & SG_RESID_VALID) == 0)
-		/* Case 1 */
-		return;
-	sgptr &= ~SG_RESID_VALID;
-
-	if ((sgptr & SG_LIST_NULL) != 0)
-		/* Case 2 */
-		return;
-
-	spkt = &hscb->shared_data.status;
-	resid_sgptr = ahc_le32toh(spkt->residual_sg_ptr);
-	if ((sgptr & SG_FULL_RESID) != 0) {
-		/* Case 3 */
-		resid = ahc_get_transfer_length(scb);
-	} else if ((resid_sgptr & SG_LIST_NULL) != 0) {
-		/* Case 4 */
-		return;
-	} else if ((resid_sgptr & ~SG_PTR_MASK) != 0) {
-		panic("Bogus resid sgptr value 0x%x\n", resid_sgptr);
-	} else {
-		struct ahc_dma_seg *sg;
-
-		/*
-		 * Remainder of the SG where the transfer
-		 * stopped.  
-		 */
-		resid = ahc_le32toh(spkt->residual_datacnt) & AHC_SG_LEN_MASK;
-		sg = ahc_sg_bus_to_virt(scb, resid_sgptr & SG_PTR_MASK);
-
-		/* The residual sg_ptr always points to the next sg */
-		sg--;
-
-		/*
-		 * Add up the contents of all residual
-		 * SG segments that are after the SG where
-		 * the transfer stopped.
-		 */
-		while ((ahc_le32toh(sg->len) & AHC_DMA_LAST_SEG) == 0) {
-			sg++;
-			resid += ahc_le32toh(sg->len) & AHC_SG_LEN_MASK;
-		}
-	}
-	if ((scb->flags & SCB_SENSE) == 0)
-		ahc_set_residual(scb, resid);
-	else
-		ahc_set_sense_residual(scb, resid);
-
-#ifdef AHC_DEBUG
-	if ((ahc_debug & AHC_SHOWMISC) != 0) {
-		ahc_print_path(ahc, scb);
-		printf("Handled Residual of %d bytes\n", resid);
-	}
-#endif
-}
-
-/******************************* Target Mode **********************************/
-#ifdef AHC_TARGET_MODE
-/*
- * Add a target mode event to this lun's queue
- */
-static void
-ahc_queue_lstate_event(struct ahc_softc *ahc, struct ahc_tmode_lstate *lstate,
-		       u_int initiator_id, u_int event_type, u_int event_arg)
-{
-	struct ahc_tmode_event *event;
-	int pending;
-
-	xpt_freeze_devq(lstate->path, /*count*/1);
-	if (lstate->event_w_idx >= lstate->event_r_idx)
-		pending = lstate->event_w_idx - lstate->event_r_idx;
-	else
-		pending = AHC_TMODE_EVENT_BUFFER_SIZE + 1
-			- (lstate->event_r_idx - lstate->event_w_idx);
-
-	if (event_type == EVENT_TYPE_BUS_RESET
-	 || event_type == MSG_BUS_DEV_RESET) {
-		/*
-		 * Any earlier events are irrelevant, so reset our buffer.
-		 * This has the effect of allowing us to deal with reset
-		 * floods (an external device holding down the reset line)
-		 * without losing the event that is really interesting.
-		 */
-		lstate->event_r_idx = 0;
-		lstate->event_w_idx = 0;
-		xpt_release_devq(lstate->path, pending, /*runqueue*/FALSE);
-	}
-
-	if (pending == AHC_TMODE_EVENT_BUFFER_SIZE) {
-		xpt_print_path(lstate->path);
-		printf("immediate event %x:%x lost\n",
-		       lstate->event_buffer[lstate->event_r_idx].event_type,
-		       lstate->event_buffer[lstate->event_r_idx].event_arg);
-		lstate->event_r_idx++;
-		if (lstate->event_r_idx == AHC_TMODE_EVENT_BUFFER_SIZE)
-			lstate->event_r_idx = 0;
-		xpt_release_devq(lstate->path, /*count*/1, /*runqueue*/FALSE);
-	}
-
-	event = &lstate->event_buffer[lstate->event_w_idx];
-	event->initiator_id = initiator_id;
-	event->event_type = event_type;
-	event->event_arg = event_arg;
-	lstate->event_w_idx++;
-	if (lstate->event_w_idx == AHC_TMODE_EVENT_BUFFER_SIZE)
-		lstate->event_w_idx = 0;
-}
-
-/*
- * Send any target mode events queued up waiting
- * for immediate notify resources.
- */
-void
-ahc_send_lstate_events(struct ahc_softc *ahc, struct ahc_tmode_lstate *lstate)
-{
-	struct ccb_hdr *ccbh;
-	struct ccb_immed_notify *inot;
-
-	while (lstate->event_r_idx != lstate->event_w_idx
-	    && (ccbh = SLIST_FIRST(&lstate->immed_notifies)) != NULL) {
-		struct ahc_tmode_event *event;
-
-		event = &lstate->event_buffer[lstate->event_r_idx];
-		SLIST_REMOVE_HEAD(&lstate->immed_notifies, sim_links.sle);
-		inot = (struct ccb_immed_notify *)ccbh;
-		switch (event->event_type) {
-		case EVENT_TYPE_BUS_RESET:
-			ccbh->status = CAM_SCSI_BUS_RESET|CAM_DEV_QFRZN;
-			break;
-		default:
-			ccbh->status = CAM_MESSAGE_RECV|CAM_DEV_QFRZN;
-			inot->message_args[0] = event->event_type;
-			inot->message_args[1] = event->event_arg;
-			break;
-		}
-		inot->initiator_id = event->initiator_id;
-		inot->sense_len = 0;
-		xpt_done((union ccb *)inot);
-		lstate->event_r_idx++;
-		if (lstate->event_r_idx == AHC_TMODE_EVENT_BUFFER_SIZE)
-			lstate->event_r_idx = 0;
-	}
-}
-#endif
-
-/******************** Sequencer Program Patching/Download *********************/
-
-#ifdef AHC_DUMP_SEQ
-void
-ahc_dumpseq(struct ahc_softc* ahc)
-{
-	int i;
-	int max_prog;
-
-	if ((ahc->chip & AHC_BUS_MASK) < AHC_PCI)
-		max_prog = 448;
-	else if ((ahc->features & AHC_ULTRA2) != 0)
-		max_prog = 768;
-	else
-		max_prog = 512;
-
-	ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS|FASTMODE|LOADRAM);
-	ahc_outb(ahc, SEQADDR0, 0);
-	ahc_outb(ahc, SEQADDR1, 0);
-	for (i = 0; i < max_prog; i++) {
-		uint8_t ins_bytes[4];
-
-		ahc_insb(ahc, SEQRAM, ins_bytes, 4);
-		printf("0x%08x\n", ins_bytes[0] << 24
-				 | ins_bytes[1] << 16
-				 | ins_bytes[2] << 8
-				 | ins_bytes[3]);
-	}
-}
-#endif
-
-static void
-ahc_loadseq(struct ahc_softc *ahc)
-{
-	struct	cs cs_table[num_critical_sections];
-	u_int	begin_set[num_critical_sections];
-	u_int	end_set[num_critical_sections];
-	struct	patch *cur_patch;
-	u_int	cs_count;
-	u_int	cur_cs;
-	u_int	i;
-	int	downloaded;
-	u_int	skip_addr;
-	u_int	sg_prefetch_cnt;
-	uint8_t	download_consts[7];
-
-	/*
-	 * Start out with 0 critical sections
-	 * that apply to this firmware load.
-	 */
-	cs_count = 0;
-	cur_cs = 0;
-	memset(begin_set, 0, sizeof(begin_set));
-	memset(end_set, 0, sizeof(end_set));
-
-	/* Setup downloadable constant table */
-	download_consts[QOUTFIFO_OFFSET] = 0;
-	if (ahc->targetcmds != NULL)
-		download_consts[QOUTFIFO_OFFSET] += 32;
-	download_consts[QINFIFO_OFFSET] = download_consts[QOUTFIFO_OFFSET] + 1;
-	download_consts[CACHESIZE_MASK] = ahc->pci_cachesize - 1;
-	download_consts[INVERTED_CACHESIZE_MASK] = ~(ahc->pci_cachesize - 1);
-	sg_prefetch_cnt = ahc->pci_cachesize;
-	if (sg_prefetch_cnt < (2 * sizeof(struct ahc_dma_seg)))
-		sg_prefetch_cnt = 2 * sizeof(struct ahc_dma_seg);
-	download_consts[SG_PREFETCH_CNT] = sg_prefetch_cnt;
-	download_consts[SG_PREFETCH_ALIGN_MASK] = ~(sg_prefetch_cnt - 1);
-	download_consts[SG_PREFETCH_ADDR_MASK] = (sg_prefetch_cnt - 1);
-
-	cur_patch = patches;
-	downloaded = 0;
-	skip_addr = 0;
-	ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS|FASTMODE|LOADRAM);
-	ahc_outb(ahc, SEQADDR0, 0);
-	ahc_outb(ahc, SEQADDR1, 0);
-
-	for (i = 0; i < sizeof(seqprog)/4; i++) {
-		if (ahc_check_patch(ahc, &cur_patch, i, &skip_addr) == 0) {
-			/*
-			 * Don't download this instruction as it
-			 * is in a patch that was removed.
-			 */
-			continue;
-		}
-		/*
-		 * Move through the CS table until we find a CS
-		 * that might apply to this instruction.
-		 */
-		for (; cur_cs < num_critical_sections; cur_cs++) {
-			if (critical_sections[cur_cs].end <= i) {
-				if (begin_set[cs_count] == TRUE
-				 && end_set[cs_count] == FALSE) {
-					cs_table[cs_count].end = downloaded;
-				 	end_set[cs_count] = TRUE;
-					cs_count++;
-				}
-				continue;
-			}
-			if (critical_sections[cur_cs].begin <= i
-			 && begin_set[cs_count] == FALSE) {
-				cs_table[cs_count].begin = downloaded;
-				begin_set[cs_count] = TRUE;
-			}
-			break;
-		}
-		ahc_download_instr(ahc, i, download_consts);
-		downloaded++;
-	}
-
-	ahc->num_critical_sections = cs_count;
-	if (cs_count != 0) {
-
-		cs_count *= sizeof(struct cs);
-		ahc->critical_sections = malloc(cs_count, M_DEVBUF, M_NOWAIT);
-		if (ahc->critical_sections == NULL)
-			panic("ahc_loadseq: Could not malloc");
-		memcpy(ahc->critical_sections, cs_table, cs_count);
-	}
-	ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS|FASTMODE);
-	ahc_restart(ahc);
-
-	if (bootverbose)
-		printf(" %d instructions downloaded\n", downloaded);
-}
-
-static int
-ahc_check_patch(struct ahc_softc *ahc, struct patch **start_patch,
-		u_int start_instr, u_int *skip_addr)
-{
-	struct	patch *cur_patch;
-	struct	patch *last_patch;
-	u_int	num_patches;
-
-	num_patches = sizeof(patches)/sizeof(struct patch);
-	last_patch = &patches[num_patches];
-	cur_patch = *start_patch;
-
-	while (cur_patch < last_patch && start_instr == cur_patch->begin) {
-
-		if (cur_patch->patch_func(ahc) == 0) {
-
-			/* Start rejecting code */
-			*skip_addr = start_instr + cur_patch->skip_instr;
-			cur_patch += cur_patch->skip_patch;
-		} else {
-			/* Accepted this patch.  Advance to the next
-			 * one and wait for our intruction pointer to
-			 * hit this point.
-			 */
-			cur_patch++;
-		}
-	}
-
-	*start_patch = cur_patch;
-	if (start_instr < *skip_addr)
-		/* Still skipping */
-		return (0);
-
-	return (1);
-}
-
-static void
-ahc_download_instr(struct ahc_softc *ahc, u_int instrptr, uint8_t *dconsts)
-{
-	union	ins_formats instr;
-	struct	ins_format1 *fmt1_ins;
-	struct	ins_format3 *fmt3_ins;
-	u_int	opcode;
-
-	/*
-	 * The firmware is always compiled into a little endian format.
-	 */
-	instr.integer = ahc_le32toh(*(uint32_t*)&seqprog[instrptr * 4]);
-
-	fmt1_ins = &instr.format1;
-	fmt3_ins = NULL;
-
-	/* Pull the opcode */
-	opcode = instr.format1.opcode;
-	switch (opcode) {
-	case AIC_OP_JMP:
-	case AIC_OP_JC:
-	case AIC_OP_JNC:
-	case AIC_OP_CALL:
-	case AIC_OP_JNE:
-	case AIC_OP_JNZ:
-	case AIC_OP_JE:
-	case AIC_OP_JZ:
-	{
-		struct patch *cur_patch;
-		int address_offset;
-		u_int address;
-		u_int skip_addr;
-		u_int i;
-
-		fmt3_ins = &instr.format3;
-		address_offset = 0;
-		address = fmt3_ins->address;
-		cur_patch = patches;
-		skip_addr = 0;
-
-		for (i = 0; i < address;) {
-
-			ahc_check_patch(ahc, &cur_patch, i, &skip_addr);
-
-			if (skip_addr > i) {
-				int end_addr;
-
-				end_addr = MIN(address, skip_addr);
-				address_offset += end_addr - i;
-				i = skip_addr;
-			} else {
-				i++;
-			}
-		}
-		address -= address_offset;
-		fmt3_ins->address = address;
-		/* FALLTHROUGH */
-	}
-	case AIC_OP_OR:
-	case AIC_OP_AND:
-	case AIC_OP_XOR:
-	case AIC_OP_ADD:
-	case AIC_OP_ADC:
-	case AIC_OP_BMOV:
-		if (fmt1_ins->parity != 0) {
-			fmt1_ins->immediate = dconsts[fmt1_ins->immediate];
-		}
-		fmt1_ins->parity = 0;
-		if ((ahc->features & AHC_CMD_CHAN) == 0
-		 && opcode == AIC_OP_BMOV) {
-			/*
-			 * Block move was added at the same time
-			 * as the command channel.  Verify that
-			 * this is only a move of a single element
-			 * and convert the BMOV to a MOV
-			 * (AND with an immediate of FF).
-			 */
-			if (fmt1_ins->immediate != 1)
-				panic("%s: BMOV not supported\n",
-				      ahc_name(ahc));
-			fmt1_ins->opcode = AIC_OP_AND;
-			fmt1_ins->immediate = 0xff;
-		}
-		/* FALLTHROUGH */
-	case AIC_OP_ROL:
-		if ((ahc->features & AHC_ULTRA2) != 0) {
-			int i, count;
-
-			/* Calculate odd parity for the instruction */
-			for (i = 0, count = 0; i < 31; i++) {
-				uint32_t mask;
-
-				mask = 0x01 << i;
-				if ((instr.integer & mask) != 0)
-					count++;
-			}
-			if ((count & 0x01) == 0)
-				instr.format1.parity = 1;
-		} else {
-			/* Compress the instruction for older sequencers */
-			if (fmt3_ins != NULL) {
-				instr.integer =
-					fmt3_ins->immediate
-				      | (fmt3_ins->source << 8)
-				      | (fmt3_ins->address << 16)
-				      |	(fmt3_ins->opcode << 25);
-			} else {
-				instr.integer =
-					fmt1_ins->immediate
-				      | (fmt1_ins->source << 8)
-				      | (fmt1_ins->destination << 16)
-				      |	(fmt1_ins->ret << 24)
-				      |	(fmt1_ins->opcode << 25);
-			}
-		}
-		/* The sequencer is a little endian cpu */
-		instr.integer = ahc_htole32(instr.integer);
-		ahc_outsb(ahc, SEQRAM, instr.bytes, 4);
-		break;
-	default:
-		panic("Unknown opcode encountered in seq program");
-		break;
-	}
-}
-
-void
-ahc_dump_card_state(struct ahc_softc *ahc)
-{
-	struct scb *scb;
-	struct scb_tailq *untagged_q;
-	int target;
-	int maxtarget;
-	int i;
-	uint8_t last_phase;
-	uint8_t qinpos;
-	uint8_t qintail;
-	uint8_t qoutpos;
-	uint8_t scb_index;
-	uint8_t saved_scbptr;
-
-	saved_scbptr = ahc_inb(ahc, SCBPTR);
-
-	last_phase = ahc_inb(ahc, LASTPHASE);
-	printf("%s: Dumping Card State %s, at SEQADDR 0x%x\n",
-	       ahc_name(ahc), ahc_lookup_phase_entry(last_phase)->phasemsg,
-	       ahc_inb(ahc, SEQADDR0) | (ahc_inb(ahc, SEQADDR1) << 8));
-	printf("ACCUM = 0x%x, SINDEX = 0x%x, DINDEX = 0x%x, ARG_2 = 0x%x\n",
-	       ahc_inb(ahc, ACCUM), ahc_inb(ahc, SINDEX), ahc_inb(ahc, DINDEX),
-	       ahc_inb(ahc, ARG_2));
-	printf("HCNT = 0x%x\n", ahc_inb(ahc, HCNT));
-	printf("SCSISEQ = 0x%x, SBLKCTL = 0x%x\n",
-	       ahc_inb(ahc, SCSISEQ), ahc_inb(ahc, SBLKCTL));
-	printf(" DFCNTRL = 0x%x, DFSTATUS = 0x%x\n",
-	       ahc_inb(ahc, DFCNTRL), ahc_inb(ahc, DFSTATUS));
-	printf("LASTPHASE = 0x%x, SCSISIGI = 0x%x, SXFRCTL0 = 0x%x\n",
-	       last_phase, ahc_inb(ahc, SCSISIGI), ahc_inb(ahc, SXFRCTL0));
-	printf("SSTAT0 = 0x%x, SSTAT1 = 0x%x\n",
-	       ahc_inb(ahc, SSTAT0), ahc_inb(ahc, SSTAT1));
-	if ((ahc->features & AHC_DT) != 0)
-		printf("SCSIPHASE = 0x%x\n", ahc_inb(ahc, SCSIPHASE));
-	printf("STACK == 0x%x, 0x%x, 0x%x, 0x%x\n",
-		ahc_inb(ahc, STACK) | (ahc_inb(ahc, STACK) << 8),
-		ahc_inb(ahc, STACK) | (ahc_inb(ahc, STACK) << 8),
-		ahc_inb(ahc, STACK) | (ahc_inb(ahc, STACK) << 8),
-		ahc_inb(ahc, STACK) | (ahc_inb(ahc, STACK) << 8));
-	printf("SCB count = %d\n", ahc->scb_data->numscbs);
-	printf("Kernel NEXTQSCB = %d\n", ahc->next_queued_scb->hscb->tag);
-	printf("Card NEXTQSCB = %d\n", ahc_inb(ahc, NEXT_QUEUED_SCB));
-	/* QINFIFO */
-	printf("QINFIFO entries: ");
-	if ((ahc->features & AHC_QUEUE_REGS) != 0) {
-		qinpos = ahc_inb(ahc, SNSCB_QOFF);
-		ahc_outb(ahc, SNSCB_QOFF, qinpos);
-	} else
-		qinpos = ahc_inb(ahc, QINPOS);
-	qintail = ahc->qinfifonext;
-	while (qinpos != qintail) {
-		printf("%d ", ahc->qinfifo[qinpos]);
-		qinpos++;
-	}
-	printf("\n");
-
-	printf("Waiting Queue entries: ");
-	scb_index = ahc_inb(ahc, WAITING_SCBH);
-	i = 0;
-	while (scb_index != SCB_LIST_NULL && i++ < 256) {
-		ahc_outb(ahc, SCBPTR, scb_index);
-		printf("%d:%d ", scb_index, ahc_inb(ahc, SCB_TAG));
-		scb_index = ahc_inb(ahc, SCB_NEXT);
-	}
-	printf("\n");
-
-	printf("Disconnected Queue entries: ");
-	scb_index = ahc_inb(ahc, DISCONNECTED_SCBH);
-	i = 0;
-	while (scb_index != SCB_LIST_NULL && i++ < 256) {
-		ahc_outb(ahc, SCBPTR, scb_index);
-		printf("%d:%d ", scb_index, ahc_inb(ahc, SCB_TAG));
-		scb_index = ahc_inb(ahc, SCB_NEXT);
-	}
-	printf("\n");
-		
-	ahc_sync_qoutfifo(ahc, BUS_DMASYNC_POSTREAD);
-	printf("QOUTFIFO entries: ");
-	qoutpos = ahc->qoutfifonext;
-	i = 0;
-	while (ahc->qoutfifo[qoutpos] != SCB_LIST_NULL && i++ < 256) {
-		printf("%d ", ahc->qoutfifo[qoutpos]);
-		qoutpos++;
-	}
-	printf("\n");
-
-	printf("Sequencer Free SCB List: ");
-	scb_index = ahc_inb(ahc, FREE_SCBH);
-	i = 0;
-	while (scb_index != SCB_LIST_NULL && i++ < 256) {
-		ahc_outb(ahc, SCBPTR, scb_index);
-		printf("%d ", scb_index);
-		scb_index = ahc_inb(ahc, SCB_NEXT);
-	}
-	printf("\n");
-
-	printf("Pending list: ");
-	i = 0;
-	LIST_FOREACH(scb, &ahc->pending_scbs, pending_links) {
-		if (i++ > 256)
-			break;
-		if (scb != LIST_FIRST(&ahc->pending_scbs))
-			printf(", ");
-		printf("%d", scb->hscb->tag);
-		if ((ahc->flags & AHC_PAGESCBS) == 0) {
-			ahc_outb(ahc, SCBPTR, scb->hscb->tag);
-			printf("(0x%x, 0x%x)", ahc_inb(ahc, SCB_CONTROL),
-			       ahc_inb(ahc, SCB_TAG));
-		}
-	}
-	printf("\n");
-
-	printf("Kernel Free SCB list: ");
-	i = 0;
-	SLIST_FOREACH(scb, &ahc->scb_data->free_scbs, links.sle) {
-		if (i++ > 256)
-			break;
-		printf("%d ", scb->hscb->tag);
-	}
-	printf("\n");
-
-	maxtarget = (ahc->features & (AHC_WIDE|AHC_TWIN)) ? 15 : 7;
-	for (target = 0; target <= maxtarget; target++) {
-		untagged_q = &ahc->untagged_queues[target];
-		if (TAILQ_FIRST(untagged_q) == NULL)
-			continue;
-		printf("Untagged Q(%d): ", target);
-		i = 0;
-		TAILQ_FOREACH(scb, untagged_q, links.tqe) {
-			if (i++ > 256)
-				break;
-			printf("%d ", scb->hscb->tag);
-		}
-		printf("\n");
-	}
-
-	ahc_platform_dump_card_state(ahc);
-	ahc_outb(ahc, SCBPTR, saved_scbptr);
-}
-
-/************************* Target Mode ****************************************/
-#ifdef AHC_TARGET_MODE
-cam_status
-ahc_find_tmode_devs(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb,
-		    struct ahc_tmode_tstate **tstate,
-		    struct ahc_tmode_lstate **lstate,
-		    int notfound_failure)
-{
-
-	if ((ahc->features & AHC_TARGETMODE) == 0)
-		return (CAM_REQ_INVALID);
-
-	/*
-	 * Handle the 'black hole' device that sucks up
-	 * requests to unattached luns on enabled targets.
-	 */
-	if (ccb->ccb_h.target_id == CAM_TARGET_WILDCARD
-	 && ccb->ccb_h.target_lun == CAM_LUN_WILDCARD) {
-		*tstate = NULL;
-		*lstate = ahc->black_hole;
-	} else {
-		u_int max_id;
-
-		max_id = (ahc->features & AHC_WIDE) ? 15 : 7;
-		if (ccb->ccb_h.target_id > max_id)
-			return (CAM_TID_INVALID);
-
-		if (ccb->ccb_h.target_lun >= AHC_NUM_LUNS)
-			return (CAM_LUN_INVALID);
-
-		*tstate = ahc->enabled_targets[ccb->ccb_h.target_id];
-		*lstate = NULL;
-		if (*tstate != NULL)
-			*lstate =
-			    (*tstate)->enabled_luns[ccb->ccb_h.target_lun];
-	}
-
-	if (notfound_failure != 0 && *lstate == NULL)
-		return (CAM_PATH_INVALID);
-
-	return (CAM_REQ_CMP);
-}
-
-void
-ahc_handle_en_lun(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb)
-{
-	struct	   ahc_tmode_tstate *tstate;
-	struct	   ahc_tmode_lstate *lstate;
-	struct	   ccb_en_lun *cel;
-	cam_status status;
-	u_int	   target;
-	u_int	   lun;
-	u_int	   target_mask;
-	u_long	   s;
-	char	   channel;
-
-	status = ahc_find_tmode_devs(ahc, sim, ccb, &tstate, &lstate,
-				     /*notfound_failure*/FALSE);
-
-	if (status != CAM_REQ_CMP) {
-		ccb->ccb_h.status = status;
-		return;
-	}
-
-	if ((ahc->features & AHC_MULTIROLE) != 0) {
-		u_int	   our_id;
-
-		if (cam_sim_bus(sim) == 0)
-			our_id = ahc->our_id;
-		else
-			our_id = ahc->our_id_b;
-
-		if (ccb->ccb_h.target_id != our_id) {
-			if ((ahc->features & AHC_MULTI_TID) != 0
-		   	 && (ahc->flags & AHC_INITIATORROLE) != 0) {
-				/*
-				 * Only allow additional targets if
-				 * the initiator role is disabled.
-				 * The hardware cannot handle a re-select-in
-				 * on the initiator id during a re-select-out
-				 * on a different target id.
-				 */
-				status = CAM_TID_INVALID;
-			} else if ((ahc->flags & AHC_INITIATORROLE) != 0
-				|| ahc->enabled_luns > 0) {
-				/*
-				 * Only allow our target id to change
-				 * if the initiator role is not configured
-				 * and there are no enabled luns which
-				 * are attached to the currently registered
-				 * scsi id.
-				 */
-				status = CAM_TID_INVALID;
-			}
-		}
-	}
-
-	if (status != CAM_REQ_CMP) {
-		ccb->ccb_h.status = status;
-		return;
-	}
-
-	/*
-	 * We now have an id that is valid.
-	 * If we aren't in target mode, switch modes.
-	 */
-	if ((ahc->flags & AHC_TARGETROLE) == 0
-	 && ccb->ccb_h.target_id != CAM_TARGET_WILDCARD) {
-		u_long	s;
-
-		printf("Configuring Target Mode\n");
-		ahc_lock(ahc, &s);
-		if (LIST_FIRST(&ahc->pending_scbs) != NULL) {
-			ccb->ccb_h.status = CAM_BUSY;
-			ahc_unlock(ahc, &s);
-			return;
-		}
-		ahc->flags |= AHC_TARGETROLE;
-		if ((ahc->features & AHC_MULTIROLE) == 0)
-			ahc->flags &= ~AHC_INITIATORROLE;
-		ahc_pause(ahc);
-		ahc_loadseq(ahc);
-		ahc_unlock(ahc, &s);
-	}
-	cel = &ccb->cel;
-	target = ccb->ccb_h.target_id;
-	lun = ccb->ccb_h.target_lun;
-	channel = SIM_CHANNEL(ahc, sim);
-	target_mask = 0x01 << target;
-	if (channel == 'B')
-		target_mask <<= 8;
-
-	if (cel->enable != 0) {
-		u_int scsiseq;
-
-		/* Are we already enabled?? */
-		if (lstate != NULL) {
-			xpt_print_path(ccb->ccb_h.path);
-			printf("Lun already enabled\n");
-			ccb->ccb_h.status = CAM_LUN_ALRDY_ENA;
-			return;
-		}
-
-		if (cel->grp6_len != 0
-		 || cel->grp7_len != 0) {
-			/*
-			 * Don't (yet?) support vendor
-			 * specific commands.
-			 */
-			ccb->ccb_h.status = CAM_REQ_INVALID;
-			printf("Non-zero Group Codes\n");
-			return;
-		}
-
-		/*
-		 * Seems to be okay.
-		 * Setup our data structures.
-		 */
-		if (target != CAM_TARGET_WILDCARD && tstate == NULL) {
-			tstate = ahc_alloc_tstate(ahc, target, channel);
-			if (tstate == NULL) {
-				xpt_print_path(ccb->ccb_h.path);
-				printf("Couldn't allocate tstate\n");
-				ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
-				return;
-			}
-		}
-		lstate = malloc(sizeof(*lstate), M_DEVBUF, M_NOWAIT);
-		if (lstate == NULL) {
-			xpt_print_path(ccb->ccb_h.path);
-			printf("Couldn't allocate lstate\n");
-			ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
-			return;
-		}
-		memset(lstate, 0, sizeof(*lstate));
-		status = xpt_create_path(&lstate->path, /*periph*/NULL,
-					 xpt_path_path_id(ccb->ccb_h.path),
-					 xpt_path_target_id(ccb->ccb_h.path),
-					 xpt_path_lun_id(ccb->ccb_h.path));
-		if (status != CAM_REQ_CMP) {
-			free(lstate, M_DEVBUF);
-			xpt_print_path(ccb->ccb_h.path);
-			printf("Couldn't allocate path\n");
-			ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
-			return;
-		}
-		SLIST_INIT(&lstate->accept_tios);
-		SLIST_INIT(&lstate->immed_notifies);
-		ahc_lock(ahc, &s);
-		ahc_pause(ahc);
-		if (target != CAM_TARGET_WILDCARD) {
-			tstate->enabled_luns[lun] = lstate;
-			ahc->enabled_luns++;
-
-			if ((ahc->features & AHC_MULTI_TID) != 0) {
-				u_int targid_mask;
-
-				targid_mask = ahc_inb(ahc, TARGID)
-					    | (ahc_inb(ahc, TARGID + 1) << 8);
-
-				targid_mask |= target_mask;
-				ahc_outb(ahc, TARGID, targid_mask);
-				ahc_outb(ahc, TARGID+1, (targid_mask >> 8));
-				
-				ahc_update_scsiid(ahc, targid_mask);
-			} else {
-				u_int our_id;
-				char  channel;
-
-				channel = SIM_CHANNEL(ahc, sim);
-				our_id = SIM_SCSI_ID(ahc, sim);
-
-				/*
-				 * This can only happen if selections
-				 * are not enabled
-				 */
-				if (target != our_id) {
-					u_int sblkctl;
-					char  cur_channel;
-					int   swap;
-
-					sblkctl = ahc_inb(ahc, SBLKCTL);
-					cur_channel = (sblkctl & SELBUSB)
-						    ? 'B' : 'A';
-					if ((ahc->features & AHC_TWIN) == 0)
-						cur_channel = 'A';
-					swap = cur_channel != channel;
-					if (channel == 'A')
-						ahc->our_id = target;
-					else
-						ahc->our_id_b = target;
-
-					if (swap)
-						ahc_outb(ahc, SBLKCTL,
-							 sblkctl ^ SELBUSB);
-
-					ahc_outb(ahc, SCSIID, target);
-
-					if (swap)
-						ahc_outb(ahc, SBLKCTL, sblkctl);
-				}
-			}
-		} else
-			ahc->black_hole = lstate;
-		/* Allow select-in operations */
-		if (ahc->black_hole != NULL && ahc->enabled_luns > 0) {
-			scsiseq = ahc_inb(ahc, SCSISEQ_TEMPLATE);
-			scsiseq |= ENSELI;
-			ahc_outb(ahc, SCSISEQ_TEMPLATE, scsiseq);
-			scsiseq = ahc_inb(ahc, SCSISEQ);
-			scsiseq |= ENSELI;
-			ahc_outb(ahc, SCSISEQ, scsiseq);
-		}
-		ahc_unpause(ahc);
-		ahc_unlock(ahc, &s);
-		ccb->ccb_h.status = CAM_REQ_CMP;
-		xpt_print_path(ccb->ccb_h.path);
-		printf("Lun now enabled for target mode\n");
-	} else {
-		struct scb *scb;
-		int i, empty;
-
-		if (lstate == NULL) {
-			ccb->ccb_h.status = CAM_LUN_INVALID;
-			return;
-		}
-
-		ahc_lock(ahc, &s);
-		
-		ccb->ccb_h.status = CAM_REQ_CMP;
-		LIST_FOREACH(scb, &ahc->pending_scbs, pending_links) {
-			struct ccb_hdr *ccbh;
-
-			ccbh = &scb->io_ctx->ccb_h;
-			if (ccbh->func_code == XPT_CONT_TARGET_IO
-			 && !xpt_path_comp(ccbh->path, ccb->ccb_h.path)){
-				printf("CTIO pending\n");
-				ccb->ccb_h.status = CAM_REQ_INVALID;
-				ahc_unlock(ahc, &s);
-				return;
-			}
-		}
-
-		if (SLIST_FIRST(&lstate->accept_tios) != NULL) {
-			printf("ATIOs pending\n");
-			ccb->ccb_h.status = CAM_REQ_INVALID;
-		}
-
-		if (SLIST_FIRST(&lstate->immed_notifies) != NULL) {
-			printf("INOTs pending\n");
-			ccb->ccb_h.status = CAM_REQ_INVALID;
-		}
-
-		if (ccb->ccb_h.status != CAM_REQ_CMP) {
-			ahc_unlock(ahc, &s);
-			return;
-		}
-
-		xpt_print_path(ccb->ccb_h.path);
-		printf("Target mode disabled\n");
-		xpt_free_path(lstate->path);
-		free(lstate, M_DEVBUF);
-
-		ahc_pause(ahc);
-		/* Can we clean up the target too? */
-		if (target != CAM_TARGET_WILDCARD) {
-			tstate->enabled_luns[lun] = NULL;
-			ahc->enabled_luns--;
-			for (empty = 1, i = 0; i < 8; i++)
-				if (tstate->enabled_luns[i] != NULL) {
-					empty = 0;
-					break;
-				}
-
-			if (empty) {
-				ahc_free_tstate(ahc, target, channel,
-						/*force*/FALSE);
-				if (ahc->features & AHC_MULTI_TID) {
-					u_int targid_mask;
-
-					targid_mask = ahc_inb(ahc, TARGID)
-						    | (ahc_inb(ahc, TARGID + 1)
-						       << 8);
-
-					targid_mask &= ~target_mask;
-					ahc_outb(ahc, TARGID, targid_mask);
-					ahc_outb(ahc, TARGID+1,
-					 	 (targid_mask >> 8));
-					ahc_update_scsiid(ahc, targid_mask);
-				}
-			}
-		} else {
-
-			ahc->black_hole = NULL;
-
-			/*
-			 * We can't allow selections without
-			 * our black hole device.
-			 */
-			empty = TRUE;
-		}
-		if (ahc->enabled_luns == 0) {
-			/* Disallow select-in */
-			u_int scsiseq;
-
-			scsiseq = ahc_inb(ahc, SCSISEQ_TEMPLATE);
-			scsiseq &= ~ENSELI;
-			ahc_outb(ahc, SCSISEQ_TEMPLATE, scsiseq);
-			scsiseq = ahc_inb(ahc, SCSISEQ);
-			scsiseq &= ~ENSELI;
-			ahc_outb(ahc, SCSISEQ, scsiseq);
-
-			if ((ahc->features & AHC_MULTIROLE) == 0) {
-				printf("Configuring Initiator Mode\n");
-				ahc->flags &= ~AHC_TARGETROLE;
-				ahc->flags |= AHC_INITIATORROLE;
-				ahc_pause(ahc);
-				ahc_loadseq(ahc);
-			}
-		}
-		ahc_unpause(ahc);
-		ahc_unlock(ahc, &s);
-	}
-}
-
-static void
-ahc_update_scsiid(struct ahc_softc *ahc, u_int targid_mask)
-{
-	u_int scsiid_mask;
-	u_int scsiid;
-
-	if ((ahc->features & AHC_MULTI_TID) == 0)
-		panic("ahc_update_scsiid called on non-multitid unit\n");
-
-	/*
-	 * Since we will rely on the the TARGID mask
-	 * for selection enables, ensure that OID
-	 * in SCSIID is not set to some other ID
-	 * that we don't want to allow selections on.
-	 */
-	if ((ahc->features & AHC_ULTRA2) != 0)
-		scsiid = ahc_inb(ahc, SCSIID_ULTRA2);
-	else
-		scsiid = ahc_inb(ahc, SCSIID);
-	scsiid_mask = 0x1 << (scsiid & OID);
-	if ((targid_mask & scsiid_mask) == 0) {
-		u_int our_id;
-
-		/* ffs counts from 1 */
-		our_id = ffs(targid_mask);
-		if (our_id == 0)
-			our_id = ahc->our_id;
-		else
-			our_id--;
-		scsiid &= TID;
-		scsiid |= our_id;
-	}
-	if ((ahc->features & AHC_ULTRA2) != 0)
-		ahc_outb(ahc, SCSIID_ULTRA2, scsiid);
-	else
-		ahc_outb(ahc, SCSIID, scsiid);
-}
-
-void
-ahc_run_tqinfifo(struct ahc_softc *ahc, int paused)
-{
-	struct target_cmd *cmd;
-
-	/*
-	 * If the card supports auto-access pause,
-	 * we can access the card directly regardless
-	 * of whether it is paused or not.
-	 */
-	if ((ahc->features & AHC_AUTOPAUSE) != 0)
-		paused = TRUE;
-
-	ahc_sync_tqinfifo(ahc, BUS_DMASYNC_POSTREAD);
-	while ((cmd = &ahc->targetcmds[ahc->tqinfifonext])->cmd_valid != 0) {
-
-		/*
-		 * Only advance through the queue if we
-		 * have the resources to process the command.
-		 */
-		if (ahc_handle_target_cmd(ahc, cmd) != 0)
-			break;
-
-		cmd->cmd_valid = 0;
-		ahc_dmamap_sync(ahc, ahc->shared_data_dmat,
-				ahc->shared_data_dmamap,
-				ahc_targetcmd_offset(ahc, ahc->tqinfifonext),
-				sizeof(struct target_cmd),
-				BUS_DMASYNC_PREREAD);
-		ahc->tqinfifonext++;
-
-		/*
-		 * Lazily update our position in the target mode incoming
-		 * command queue as seen by the sequencer.
-		 */
-		if ((ahc->tqinfifonext & (HOST_TQINPOS - 1)) == 1) {
-			if ((ahc->features & AHC_HS_MAILBOX) != 0) {
-				u_int hs_mailbox;
-
-				hs_mailbox = ahc_inb(ahc, HS_MAILBOX);
-				hs_mailbox &= ~HOST_TQINPOS;
-				hs_mailbox |= ahc->tqinfifonext & HOST_TQINPOS;
-				ahc_outb(ahc, HS_MAILBOX, hs_mailbox);
-			} else {
-				if (!paused)
-					ahc_pause(ahc);	
-				ahc_outb(ahc, KERNEL_TQINPOS,
-					 ahc->tqinfifonext & HOST_TQINPOS);
-				if (!paused)
-					ahc_unpause(ahc);
-			}
-		}
-	}
-}
-
-static int
-ahc_handle_target_cmd(struct ahc_softc *ahc, struct target_cmd *cmd)
-{
-	struct	  ahc_tmode_tstate *tstate;
-	struct	  ahc_tmode_lstate *lstate;
-	struct	  ccb_accept_tio *atio;
-	uint8_t *byte;
-	int	  initiator;
-	int	  target;
-	int	  lun;
-
-	initiator = SCSIID_TARGET(ahc, cmd->scsiid);
-	target = SCSIID_OUR_ID(cmd->scsiid);
-	lun    = (cmd->identify & MSG_IDENTIFY_LUNMASK);
-
-	byte = cmd->bytes;
-	tstate = ahc->enabled_targets[target];
-	lstate = NULL;
-	if (tstate != NULL)
-		lstate = tstate->enabled_luns[lun];
-
-	/*
-	 * Commands for disabled luns go to the black hole driver.
-	 */
-	if (lstate == NULL)
-		lstate = ahc->black_hole;
-
-	atio = (struct ccb_accept_tio*)SLIST_FIRST(&lstate->accept_tios);
-	if (atio == NULL) {
-		ahc->flags |= AHC_TQINFIFO_BLOCKED;
-		/*
-		 * Wait for more ATIOs from the peripheral driver for this lun.
-		 */
-		return (1);
-	} else
-		ahc->flags &= ~AHC_TQINFIFO_BLOCKED;
-#if 0
-	printf("Incoming command from %d for %d:%d%s\n",
-	       initiator, target, lun,
-	       lstate == ahc->black_hole ? "(Black Holed)" : "");
-#endif
-	SLIST_REMOVE_HEAD(&lstate->accept_tios, sim_links.sle);
-
-	if (lstate == ahc->black_hole) {
-		/* Fill in the wildcards */
-		atio->ccb_h.target_id = target;
-		atio->ccb_h.target_lun = lun;
-	}
-
-	/*
-	 * Package it up and send it off to
-	 * whomever has this lun enabled.
-	 */
-	atio->sense_len = 0;
-	atio->init_id = initiator;
-	if (byte[0] != 0xFF) {
-		/* Tag was included */
-		atio->tag_action = *byte++;
-		atio->tag_id = *byte++;
-		atio->ccb_h.flags = CAM_TAG_ACTION_VALID;
-	} else {
-		atio->ccb_h.flags = 0;
-	}
-	byte++;
-
-	/* Okay.  Now determine the cdb size based on the command code */
-	switch (*byte >> CMD_GROUP_CODE_SHIFT) {
-	case 0:
-		atio->cdb_len = 6;
-		break;
-	case 1:
-	case 2:
-		atio->cdb_len = 10;
-		break;
-	case 4:
-		atio->cdb_len = 16;
-		break;
-	case 5:
-		atio->cdb_len = 12;
-		break;
-	case 3:
-	default:
-		/* Only copy the opcode. */
-		atio->cdb_len = 1;
-		printf("Reserved or VU command code type encountered\n");
-		break;
-	}
-	
-	memcpy(atio->cdb_io.cdb_bytes, byte, atio->cdb_len);
-
-	atio->ccb_h.status |= CAM_CDB_RECVD;
-
-	if ((cmd->identify & MSG_IDENTIFY_DISCFLAG) == 0) {
-		/*
-		 * We weren't allowed to disconnect.
-		 * We're hanging on the bus until a
-		 * continue target I/O comes in response
-		 * to this accept tio.
-		 */
-#if 0
-		printf("Received Immediate Command %d:%d:%d - %p\n",
-		       initiator, target, lun, ahc->pending_device);
-#endif
-		ahc->pending_device = lstate;
-		ahc_freeze_ccb((union ccb *)atio);
-		atio->ccb_h.flags |= CAM_DIS_DISCONNECT;
-	}
-	xpt_done((union ccb*)atio);
-	return (0);
-}
-
-#endif
diff -Nru a/drivers/scsi/aic7xxx/aic7xxx_core.c b/drivers/scsi/aic7xxx/aic7xxx_core.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/scsi/aic7xxx/aic7xxx_core.c	Sun Apr 28 20:10:36 2002
@@ -0,0 +1,6988 @@
+/*
+ * Core routines and tables shareable across OS platforms.
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * 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,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.c#50 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx.c,v 1.61 2000/11/13 03:35:43 gibbs Exp $
+ */
+
+#include "aic7xxx_osm.h"
+#include "aic7xxx_inline.h"
+#include "aicasm/aicasm_insformat.h"
+
+/****************************** Softc Data ************************************/
+struct ahc_softc_tailq ahc_tailq = TAILQ_HEAD_INITIALIZER(ahc_tailq);
+
+/***************************** Lookup Tables **********************************/
+char *ahc_chip_names[] =
+{
+	"NONE",
+	"aic7770",
+	"aic7850",
+	"aic7855",
+	"aic7859",
+	"aic7860",
+	"aic7870",
+	"aic7880",
+	"aic7895",
+	"aic7895C",
+	"aic7890/91",
+	"aic7896/97",
+	"aic7892",
+	"aic7899"
+};
+static const u_int num_chip_names = NUM_ELEMENTS(ahc_chip_names);
+
+/*
+ * Hardware error codes.
+ */
+struct ahc_hard_error_entry {
+        uint8_t errno;
+	char *errmesg;
+};
+
+static struct ahc_hard_error_entry ahc_hard_errors[] = {
+	{ ILLHADDR,	"Illegal Host Access" },
+	{ ILLSADDR,	"Illegal Sequencer Address referrenced" },
+	{ ILLOPCODE,	"Illegal Opcode in sequencer program" },
+	{ SQPARERR,	"Sequencer Parity Error" },
+	{ DPARERR,	"Data-path Parity Error" },
+	{ MPARERR,	"Scratch or SCB Memory Parity Error" },
+	{ PCIERRSTAT,	"PCI Error detected" },
+	{ CIOPARERR,	"CIOBUS Parity Error" },
+};
+static const u_int num_errors = NUM_ELEMENTS(ahc_hard_errors);
+
+static struct ahc_phase_table_entry ahc_phase_table[] =
+{
+	{ P_DATAOUT,	MSG_NOOP,		"in Data-out phase"	},
+	{ P_DATAIN,	MSG_INITIATOR_DET_ERR,	"in Data-in phase"	},
+	{ P_DATAOUT_DT,	MSG_NOOP,		"in DT Data-out phase"	},
+	{ P_DATAIN_DT,	MSG_INITIATOR_DET_ERR,	"in DT Data-in phase"	},
+	{ P_COMMAND,	MSG_NOOP,		"in Command phase"	},
+	{ P_MESGOUT,	MSG_NOOP,		"in Message-out phase"	},
+	{ P_STATUS,	MSG_INITIATOR_DET_ERR,	"in Status phase"	},
+	{ P_MESGIN,	MSG_PARITY_ERROR,	"in Message-in phase"	},
+	{ P_BUSFREE,	MSG_NOOP,		"while idle"		},
+	{ 0,		MSG_NOOP,		"in unknown phase"	}
+};
+
+/*
+ * In most cases we only wish to itterate over real phases, so
+ * exclude the last element from the count.
+ */
+static const u_int num_phases = NUM_ELEMENTS(ahc_phase_table) - 1;
+
+/*
+ * Valid SCSIRATE values.  (p. 3-17)
+ * Provides a mapping of tranfer periods in ns to the proper value to
+ * stick in the scsixfer reg.
+ */
+static struct ahc_syncrate ahc_syncrates[] =
+{
+      /* ultra2    fast/ultra  period     rate */
+	{ 0x42,      0x000,      9,      "80.0" },
+	{ 0x03,      0x000,     10,      "40.0" },
+	{ 0x04,      0x000,     11,      "33.0" },
+	{ 0x05,      0x100,     12,      "20.0" },
+	{ 0x06,      0x110,     15,      "16.0" },
+	{ 0x07,      0x120,     18,      "13.4" },
+	{ 0x08,      0x000,     25,      "10.0" },
+	{ 0x19,      0x010,     31,      "8.0"  },
+	{ 0x1a,      0x020,     37,      "6.67" },
+	{ 0x1b,      0x030,     43,      "5.7"  },
+	{ 0x1c,      0x040,     50,      "5.0"  },
+	{ 0x00,      0x050,     56,      "4.4"  },
+	{ 0x00,      0x060,     62,      "4.0"  },
+	{ 0x00,      0x070,     68,      "3.6"  },
+	{ 0x00,      0x000,      0,      NULL   }
+};
+
+/* Our Sequencer Program */
+#include "aic7xxx_seq.h"
+
+/**************************** Function Declarations ***************************/
+static void		ahc_force_renegotiation(struct ahc_softc *ahc);
+static struct ahc_tmode_tstate*
+			ahc_alloc_tstate(struct ahc_softc *ahc,
+					 u_int scsi_id, char channel);
+#ifdef AHC_TARGET_MODE
+static void		ahc_free_tstate(struct ahc_softc *ahc,
+					u_int scsi_id, char channel, int force);
+#endif
+static struct ahc_syncrate*
+			ahc_devlimited_syncrate(struct ahc_softc *ahc,
+					        struct ahc_initiator_tinfo *,
+						u_int *period,
+						u_int *ppr_options,
+						role_t role);
+static void		ahc_update_pending_scbs(struct ahc_softc *ahc);
+static void		ahc_fetch_devinfo(struct ahc_softc *ahc,
+					  struct ahc_devinfo *devinfo);
+static void		ahc_scb_devinfo(struct ahc_softc *ahc,
+					struct ahc_devinfo *devinfo,
+					struct scb *scb);
+static void		ahc_assert_atn(struct ahc_softc *ahc);
+static void		ahc_setup_initiator_msgout(struct ahc_softc *ahc,
+						   struct ahc_devinfo *devinfo,
+						   struct scb *scb);
+static void		ahc_build_transfer_msg(struct ahc_softc *ahc,
+					       struct ahc_devinfo *devinfo);
+static void		ahc_construct_sdtr(struct ahc_softc *ahc,
+					   struct ahc_devinfo *devinfo,
+					   u_int period, u_int offset);
+static void		ahc_construct_wdtr(struct ahc_softc *ahc,
+					   struct ahc_devinfo *devinfo,
+					   u_int bus_width);
+static void		ahc_construct_ppr(struct ahc_softc *ahc,
+					  struct ahc_devinfo *devinfo,
+					  u_int period, u_int offset,
+					  u_int bus_width, u_int ppr_options);
+static void		ahc_clear_msg_state(struct ahc_softc *ahc);
+static void		ahc_handle_message_phase(struct ahc_softc *ahc);
+typedef enum {
+	AHCMSG_1B,
+	AHCMSG_2B,
+	AHCMSG_EXT
+} ahc_msgtype;
+static int		ahc_sent_msg(struct ahc_softc *ahc, ahc_msgtype type,
+				     u_int msgval, int full);
+static int		ahc_parse_msg(struct ahc_softc *ahc,
+				      struct ahc_devinfo *devinfo);
+static int		ahc_handle_msg_reject(struct ahc_softc *ahc,
+					      struct ahc_devinfo *devinfo);
+static void		ahc_handle_ign_wide_residue(struct ahc_softc *ahc,
+						struct ahc_devinfo *devinfo);
+static void		ahc_reinitialize_dataptrs(struct ahc_softc *ahc);
+static void		ahc_handle_devreset(struct ahc_softc *ahc,
+					    struct ahc_devinfo *devinfo,
+					    cam_status status, char *message,
+					    int verbose_level);
+#if AHC_TARGET_MODE
+static void		ahc_setup_target_msgin(struct ahc_softc *ahc,
+					       struct ahc_devinfo *devinfo,
+					       struct scb *scb);
+#endif
+
+static bus_dmamap_callback_t	ahc_dmamap_cb; 
+static void			ahc_build_free_scb_list(struct ahc_softc *ahc);
+static int			ahc_init_scbdata(struct ahc_softc *ahc);
+static void			ahc_fini_scbdata(struct ahc_softc *ahc);
+static void		ahc_qinfifo_requeue(struct ahc_softc *ahc,
+					    struct scb *prev_scb,
+					    struct scb *scb);
+static int		ahc_qinfifo_count(struct ahc_softc *ahc);
+static u_int		ahc_rem_scb_from_disc_list(struct ahc_softc *ahc,
+						   u_int prev, u_int scbptr);
+static void		ahc_add_curscb_to_free_list(struct ahc_softc *ahc);
+static u_int		ahc_rem_wscb(struct ahc_softc *ahc,
+				     u_int scbpos, u_int prev);
+static void		ahc_reset_current_bus(struct ahc_softc *ahc);
+#ifdef AHC_DUMP_SEQ
+static void		ahc_dumpseq(struct ahc_softc *ahc);
+#endif
+static void		ahc_loadseq(struct ahc_softc *ahc);
+static int		ahc_check_patch(struct ahc_softc *ahc,
+					struct patch **start_patch,
+					u_int start_instr, u_int *skip_addr);
+static void		ahc_download_instr(struct ahc_softc *ahc,
+					   u_int instrptr, uint8_t *dconsts);
+#ifdef AHC_TARGET_MODE
+static void		ahc_queue_lstate_event(struct ahc_softc *ahc,
+					       struct ahc_tmode_lstate *lstate,
+					       u_int initiator_id,
+					       u_int event_type,
+					       u_int event_arg);
+static void		ahc_update_scsiid(struct ahc_softc *ahc,
+					  u_int targid_mask);
+static int		ahc_handle_target_cmd(struct ahc_softc *ahc,
+					      struct target_cmd *cmd);
+#endif
+/************************* Sequencer Execution Control ************************/
+/*
+ * Restart the sequencer program from address zero
+ */
+void
+ahc_restart(struct ahc_softc *ahc)
+{
+
+	ahc_pause(ahc);
+
+	ahc_outb(ahc, SCSISIGO, 0);		/* De-assert BSY */
+	ahc_outb(ahc, MSG_OUT, MSG_NOOP);	/* No message to send */
+	ahc_outb(ahc, SXFRCTL1, ahc_inb(ahc, SXFRCTL1) & ~BITBUCKET);
+	ahc_outb(ahc, LASTPHASE, P_BUSFREE);
+
+	/*
+	 * Ensure that the sequencer's idea of TQINPOS
+	 * matches our own.  The sequencer increments TQINPOS
+	 * only after it sees a DMA complete and a reset could
+	 * occur before the increment leaving the kernel to believe
+	 * the command arrived but the sequencer to not.
+	 */
+	ahc_outb(ahc, TQINPOS, ahc->tqinfifonext);
+
+	/* Always allow reselection */
+	ahc_outb(ahc, SCSISEQ,
+		 ahc_inb(ahc, SCSISEQ_TEMPLATE) & (ENSELI|ENRSELI|ENAUTOATNP));
+	if ((ahc->features & AHC_CMD_CHAN) != 0) {
+		/* Ensure that no DMA operations are in progress */
+		ahc_outb(ahc, CCSCBCNT, 0);
+		ahc_outb(ahc, CCSGCTL, 0);
+		ahc_outb(ahc, CCSCBCTL, 0);
+	}
+	/*
+	 * If we were in the process of DMA'ing SCB data into
+	 * an SCB, replace that SCB on the free list.  This prevents
+	 * an SCB leak.
+	 */
+	if ((ahc_inb(ahc, SEQ_FLAGS2) & SCB_DMA) != 0) {
+		ahc_add_curscb_to_free_list(ahc);
+		ahc_outb(ahc, SEQ_FLAGS2,
+			 ahc_inb(ahc, SEQ_FLAGS2) & ~SCB_DMA);
+	}
+	ahc_outb(ahc, MWI_RESIDUAL, 0);
+	ahc_outb(ahc, SEQCTL, FASTMODE);
+	ahc_outb(ahc, SEQADDR0, 0);
+	ahc_outb(ahc, SEQADDR1, 0);
+	ahc_unpause(ahc);
+}
+
+/************************* Input/Output Queues ********************************/
+void
+ahc_run_qoutfifo(struct ahc_softc *ahc)
+{
+	struct scb *scb;
+	u_int  scb_index;
+
+	ahc_sync_qoutfifo(ahc, BUS_DMASYNC_POSTREAD);
+	while (ahc->qoutfifo[ahc->qoutfifonext] != SCB_LIST_NULL) {
+
+		scb_index = ahc->qoutfifo[ahc->qoutfifonext];
+		if ((ahc->qoutfifonext & 0x03) == 0x03) {
+			u_int modnext;
+
+			/*
+			 * Clear 32bits of QOUTFIFO at a time
+			 * so that we don't clobber an incoming
+			 * byte DMA to the array on architectures
+			 * that only support 32bit load and store
+			 * operations.
+			 */
+			modnext = ahc->qoutfifonext & ~0x3;
+			*((uint32_t *)(&ahc->qoutfifo[modnext])) = 0xFFFFFFFFUL;
+			ahc_dmamap_sync(ahc, ahc->shared_data_dmat,
+					ahc->shared_data_dmamap,
+					/*offset*/modnext, /*len*/4,
+					BUS_DMASYNC_PREREAD);
+		}
+		ahc->qoutfifonext++;
+
+		scb = ahc_lookup_scb(ahc, scb_index);
+		if (scb == NULL) {
+			printf("%s: WARNING no command for scb %d "
+			       "(cmdcmplt)\nQOUTPOS = %d\n",
+			       ahc_name(ahc), scb_index,
+			       ahc->qoutfifonext - 1);
+			continue;
+		}
+
+		/*
+		 * Save off the residual
+		 * if there is one.
+		 */
+		ahc_update_residual(scb);
+		ahc_done(ahc, scb);
+	}
+}
+
+void
+ahc_run_untagged_queues(struct ahc_softc *ahc)
+{
+	int i;
+
+	for (i = 0; i < 16; i++)
+		ahc_run_untagged_queue(ahc, &ahc->untagged_queues[i]);
+}
+
+void
+ahc_run_untagged_queue(struct ahc_softc *ahc, struct scb_tailq *queue)
+{
+	struct scb *scb;
+
+	if (ahc->untagged_queue_lock != 0)
+		return;
+
+	if ((scb = TAILQ_FIRST(queue)) != NULL
+	 && (scb->flags & SCB_ACTIVE) == 0) {
+		scb->flags |= SCB_ACTIVE;
+		ahc_queue_scb(ahc, scb);
+	}
+}
+
+/************************* Interrupt Handling *********************************/
+void
+ahc_handle_brkadrint(struct ahc_softc *ahc)
+{
+	/*
+	 * We upset the sequencer :-(
+	 * Lookup the error message
+	 */
+	int i;
+	int error;
+
+	error = ahc_inb(ahc, ERROR);
+	for (i = 0; error != 1 && i < num_errors; i++)
+		error >>= 1;
+	printf("%s: brkadrint, %s at seqaddr = 0x%x\n",
+	       ahc_name(ahc), ahc_hard_errors[i].errmesg,
+	       ahc_inb(ahc, SEQADDR0) |
+	       (ahc_inb(ahc, SEQADDR1) << 8));
+
+	ahc_dump_card_state(ahc);
+
+	/* Tell everyone that this HBA is no longer availible */
+	ahc_abort_scbs(ahc, CAM_TARGET_WILDCARD, ALL_CHANNELS,
+		       CAM_LUN_WILDCARD, SCB_LIST_NULL, ROLE_UNKNOWN,
+		       CAM_NO_HBA);
+
+	/* Disable all interrupt sources by resetting the controller */
+	ahc_shutdown(ahc);
+}
+
+void
+ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
+{
+	struct scb *scb;
+	struct ahc_devinfo devinfo;
+	
+	ahc_fetch_devinfo(ahc, &devinfo);
+
+	/*
+	 * Clear the upper byte that holds SEQINT status
+	 * codes and clear the SEQINT bit. We will unpause
+	 * the sequencer, if appropriate, after servicing
+	 * the request.
+	 */
+	ahc_outb(ahc, CLRINT, CLRSEQINT);
+	switch (intstat & SEQINT_MASK) {
+	case BAD_STATUS:
+	{
+		u_int  scb_index;
+		struct hardware_scb *hscb;
+
+		/*
+		 * Set the default return value to 0 (don't
+		 * send sense).  The sense code will change
+		 * this if needed.
+		 */
+		ahc_outb(ahc, RETURN_1, 0);
+
+		/*
+		 * The sequencer will notify us when a command
+		 * has an error that would be of interest to
+		 * the kernel.  This allows us to leave the sequencer
+		 * running in the common case of command completes
+		 * without error.  The sequencer will already have
+		 * dma'd the SCB back up to us, so we can reference
+		 * the in kernel copy directly.
+		 */
+		scb_index = ahc_inb(ahc, SCB_TAG);
+		scb = ahc_lookup_scb(ahc, scb_index);
+		if (scb == NULL) {
+			printf("%s:%c:%d: ahc_intr - referenced scb "
+			       "not valid during seqint 0x%x scb(%d)\n",
+			       ahc_name(ahc), devinfo.channel,
+			       devinfo.target, intstat, scb_index);
+			ahc_dump_card_state(ahc);
+			panic("for safety");
+			goto unpause;
+		}
+
+		hscb = scb->hscb; 
+
+		/* Don't want to clobber the original sense code */
+		if ((scb->flags & SCB_SENSE) != 0) {
+			/*
+			 * Clear the SCB_SENSE Flag and have
+			 * the sequencer do a normal command
+			 * complete.
+			 */
+			scb->flags &= ~SCB_SENSE;
+			ahc_set_transaction_status(scb, CAM_AUTOSENSE_FAIL);
+			break;
+		}
+		ahc_set_transaction_status(scb, CAM_SCSI_STATUS_ERROR);
+		/* Freeze the queue until the client sees the error. */
+		ahc_freeze_devq(ahc, scb);
+		ahc_freeze_scb(scb);
+		ahc_set_scsi_status(scb, hscb->shared_data.status.scsi_status);
+		switch (hscb->shared_data.status.scsi_status) {
+		case SCSI_STATUS_OK:
+			printf("%s: Interrupted for staus of 0???\n",
+			       ahc_name(ahc));
+			break;
+		case SCSI_STATUS_CMD_TERMINATED:
+		case SCSI_STATUS_CHECK_COND:
+		{
+			struct ahc_dma_seg *sg;
+			struct scsi_sense *sc;
+			struct ahc_initiator_tinfo *targ_info;
+			struct ahc_tmode_tstate *tstate;
+			struct ahc_transinfo *tinfo;
+#ifdef AHC_DEBUG
+			if (ahc_debug & AHC_SHOWSENSE) {
+				ahc_print_path(ahc, scb);
+				printf("SCB %d: requests Check Status\n",
+				       scb->hscb->tag);
+			}
+#endif
+
+			if (ahc_perform_autosense(scb) == 0)
+				break;
+
+			targ_info = ahc_fetch_transinfo(ahc,
+							devinfo.channel,
+							devinfo.our_scsiid,
+							devinfo.target,
+							&tstate);
+			tinfo = &targ_info->curr;
+			sg = scb->sg_list;
+			sc = (struct scsi_sense *)(&hscb->shared_data.cdb); 
+			/*
+			 * Save off the residual if there is one.
+			 */
+			ahc_update_residual(scb);
+#ifdef AHC_DEBUG
+			if (ahc_debug & AHC_SHOWSENSE) {
+				ahc_print_path(ahc, scb);
+				printf("Sending Sense\n");
+			}
+#endif
+			sg->addr = ahc_get_sense_bufaddr(ahc, scb);
+			sg->len = ahc_get_sense_bufsize(ahc, scb);
+			sg->len |= AHC_DMA_LAST_SEG;
+
+			/* Fixup byte order */
+			sg->addr = ahc_htole32(sg->addr);
+			sg->len = ahc_htole32(sg->len);
+
+			sc->opcode = REQUEST_SENSE;
+			sc->byte2 = 0;
+			if (tinfo->protocol_version <= SCSI_REV_2
+			 && SCB_GET_LUN(scb) < 8)
+				sc->byte2 = SCB_GET_LUN(scb) << 5;
+			sc->unused[0] = 0;
+			sc->unused[1] = 0;
+			sc->length = sg->len;
+			sc->control = 0;
+
+			/*
+			 * We can't allow the target to disconnect.
+			 * This will be an untagged transaction and
+			 * having the target disconnect will make this
+			 * transaction indestinguishable from outstanding
+			 * tagged transactions.
+			 */
+			hscb->control = 0;
+
+			/*
+			 * This request sense could be because the
+			 * the device lost power or in some other
+			 * way has lost our transfer negotiations.
+			 * Renegotiate if appropriate.  Unit attention
+			 * errors will be reported before any data
+			 * phases occur.
+			 */
+			if (ahc_get_residual(scb) 
+			 == ahc_get_transfer_length(scb)) {
+				ahc_update_neg_request(ahc, &devinfo,
+						       tstate, targ_info,
+						       /*force*/TRUE);
+			}
+			if (tstate->auto_negotiate & devinfo.target_mask) {
+				hscb->control |= MK_MESSAGE;
+				scb->flags &= ~SCB_NEGOTIATE;
+				scb->flags |= SCB_AUTO_NEGOTIATE;
+			}
+			hscb->cdb_len = sizeof(*sc);
+			hscb->dataptr = sg->addr; 
+			hscb->datacnt = sg->len;
+			hscb->sgptr = scb->sg_list_phys | SG_FULL_RESID;
+			hscb->sgptr = ahc_htole32(hscb->sgptr);
+			scb->sg_count = 1;
+			scb->flags |= SCB_SENSE;
+			ahc_qinfifo_requeue_tail(ahc, scb);
+			ahc_outb(ahc, RETURN_1, SEND_SENSE);
+#ifdef __FreeBSD__
+			/*
+			 * Ensure we have enough time to actually
+			 * retrieve the sense.
+			 */
+			untimeout(ahc_timeout, (caddr_t)scb,
+				  scb->io_ctx->ccb_h.timeout_ch);
+			scb->io_ctx->ccb_h.timeout_ch =
+			    timeout(ahc_timeout, (caddr_t)scb, 5 * hz);
+#endif
+			break;
+		}
+		default:
+			break;
+		}
+		break;
+	}
+	case NO_MATCH:
+	{
+		/* Ensure we don't leave the selection hardware on */
+		ahc_outb(ahc, SCSISEQ,
+			 ahc_inb(ahc, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP));
+
+		printf("%s:%c:%d: no active SCB for reconnecting "
+		       "target - issuing BUS DEVICE RESET\n",
+		       ahc_name(ahc), devinfo.channel, devinfo.target);
+		printf("SAVED_SCSIID == 0x%x, SAVED_LUN == 0x%x, "
+		       "ARG_1 == 0x%x ACCUM = 0x%x\n",
+		       ahc_inb(ahc, SAVED_SCSIID), ahc_inb(ahc, SAVED_LUN),
+		       ahc_inb(ahc, ARG_1), ahc_inb(ahc, ACCUM));
+		printf("SEQ_FLAGS == 0x%x, SCBPTR == 0x%x, BTT == 0x%x, "
+		       "SINDEX == 0x%x\n",
+		       ahc_inb(ahc, SEQ_FLAGS), ahc_inb(ahc, SCBPTR),
+		       ahc_index_busy_tcl(ahc,
+			    BUILD_TCL(ahc_inb(ahc, SAVED_SCSIID),
+				      ahc_inb(ahc, SAVED_LUN))),
+		       ahc_inb(ahc, SINDEX));
+		printf("SCSIID == 0x%x, SCB_SCSIID == 0x%x, SCB_LUN == 0x%x, "
+		       "SCB_TAG == 0x%x, SCB_CONTROL == 0x%x\n",
+		       ahc_inb(ahc, SCSIID), ahc_inb(ahc, SCB_SCSIID),
+		       ahc_inb(ahc, SCB_LUN), ahc_inb(ahc, SCB_TAG),
+		       ahc_inb(ahc, SCB_CONTROL));
+		printf("SCSIBUSL == 0x%x, SCSISIGI == 0x%x\n",
+		       ahc_inb(ahc, SCSIBUSL), ahc_inb(ahc, SCSISIGI));
+		printf("SXFRCTL0 == 0x%x\n", ahc_inb(ahc, SXFRCTL0));
+		printf("SEQCTL == 0x%x\n", ahc_inb(ahc, SEQCTL));
+		ahc_dump_card_state(ahc);
+		ahc->msgout_buf[0] = MSG_BUS_DEV_RESET;
+		ahc->msgout_len = 1;
+		ahc->msgout_index = 0;
+		ahc->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+		ahc_outb(ahc, MSG_OUT, HOST_MSG);
+		ahc_assert_atn(ahc);
+		break;
+	}
+	case SEND_REJECT: 
+	{
+		u_int rejbyte = ahc_inb(ahc, ACCUM);
+		printf("%s:%c:%d: Warning - unknown message received from "
+		       "target (0x%x).  Rejecting\n", 
+		       ahc_name(ahc), devinfo.channel, devinfo.target, rejbyte);
+		break; 
+	}
+	case NO_IDENT: 
+	{
+		/*
+		 * The reconnecting target either did not send an identify
+		 * message, or did, but we didn't find an SCB to match and
+		 * before it could respond to our ATN/abort, it hit a dataphase.
+		 * The only safe thing to do is to blow it away with a bus
+		 * reset.
+		 */
+		int found;
+
+		printf("%s:%c:%d: Target did not send an IDENTIFY message. "
+		       "LASTPHASE = 0x%x, SAVED_SCSIID == 0x%x\n",
+		       ahc_name(ahc), devinfo.channel, devinfo.target,
+		       ahc_inb(ahc, LASTPHASE), ahc_inb(ahc, SAVED_SCSIID));
+		found = ahc_reset_channel(ahc, devinfo.channel, 
+					  /*initiate reset*/TRUE);
+		printf("%s: Issued Channel %c Bus Reset. "
+		       "%d SCBs aborted\n", ahc_name(ahc), devinfo.channel,
+		       found);
+		return;
+	}
+	case IGN_WIDE_RES:
+		ahc_handle_ign_wide_residue(ahc, &devinfo);
+		break;
+	case PDATA_REINIT:
+		ahc_reinitialize_dataptrs(ahc);
+		break;
+	case BAD_PHASE:
+	{
+		u_int lastphase;
+
+		lastphase = ahc_inb(ahc, LASTPHASE);
+		printf("%s:%c:%d: unknown scsi bus phase %x, "
+		       "lastphase = 0x%x.  Attempting to continue\n",
+		       ahc_name(ahc), devinfo.channel, devinfo.target,
+		       lastphase, ahc_inb(ahc, SCSISIGI));
+		break;
+	}
+	case MISSED_BUSFREE:
+	{
+		u_int lastphase;
+
+		lastphase = ahc_inb(ahc, LASTPHASE);
+		printf("%s:%c:%d: Missed busfree. "
+		       "Lastphase = 0x%x, Curphase = 0x%x\n",
+		       ahc_name(ahc), devinfo.channel, devinfo.target,
+		       lastphase, ahc_inb(ahc, SCSISIGI));
+		ahc_restart(ahc);
+		return;
+	}
+	case HOST_MSG_LOOP:
+	{
+		/*
+		 * The sequencer has encountered a message phase
+		 * that requires host assistance for completion.
+		 * While handling the message phase(s), we will be
+		 * notified by the sequencer after each byte is
+		 * transfered so we can track bus phase changes.
+		 *
+		 * If this is the first time we've seen a HOST_MSG_LOOP
+		 * interrupt, initialize the state of the host message
+		 * loop.
+		 */
+		if (ahc->msg_type == MSG_TYPE_NONE) {
+			struct scb *scb;
+			u_int scb_index;
+			u_int bus_phase;
+
+			bus_phase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
+			if (bus_phase != P_MESGIN
+			 && bus_phase != P_MESGOUT) {
+				printf("ahc_intr: HOST_MSG_LOOP bad "
+				       "phase 0x%x\n",
+				      bus_phase);
+				/*
+				 * Probably transitioned to bus free before
+				 * we got here.  Just punt the message.
+				 */
+				ahc_clear_intstat(ahc);
+				ahc_restart(ahc);
+				return;
+			}
+
+			scb_index = ahc_inb(ahc, SCB_TAG);
+			scb = ahc_lookup_scb(ahc, scb_index);
+			if (devinfo.role == ROLE_INITIATOR) {
+				if (scb == NULL)
+					panic("HOST_MSG_LOOP with "
+					      "invalid SCB %x\n", scb_index);
+
+				if (bus_phase == P_MESGOUT)
+					ahc_setup_initiator_msgout(ahc,
+								   &devinfo,
+								   scb);
+				else {
+					ahc->msg_type =
+					    MSG_TYPE_INITIATOR_MSGIN;
+					ahc->msgin_index = 0;
+				}
+			}
+#if AHC_TARGET_MODE
+			else {
+				if (bus_phase == P_MESGOUT) {
+					ahc->msg_type =
+					    MSG_TYPE_TARGET_MSGOUT;
+					ahc->msgin_index = 0;
+				}
+				else 
+					ahc_setup_target_msgin(ahc,
+							       &devinfo,
+							       scb);
+			}
+#endif
+		}
+
+		ahc_handle_message_phase(ahc);
+		break;
+	}
+	case PERR_DETECTED:
+	{
+		/*
+		 * If we've cleared the parity error interrupt
+		 * but the sequencer still believes that SCSIPERR
+		 * is true, it must be that the parity error is
+		 * for the currently presented byte on the bus,
+		 * and we are not in a phase (data-in) where we will
+		 * eventually ack this byte.  Ack the byte and
+		 * throw it away in the hope that the target will
+		 * take us to message out to deliver the appropriate
+		 * error message.
+		 */
+		if ((intstat & SCSIINT) == 0
+		 && (ahc_inb(ahc, SSTAT1) & SCSIPERR) != 0) {
+
+			if ((ahc->features & AHC_DT) == 0) {
+				u_int curphase;
+
+				/*
+				 * The hardware will only let you ack bytes
+				 * if the expected phase in SCSISIGO matches
+				 * the current phase.  Make sure this is
+				 * currently the case.
+				 */
+				curphase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
+				ahc_outb(ahc, LASTPHASE, curphase);
+				ahc_outb(ahc, SCSISIGO, curphase);
+			}
+			ahc_inb(ahc, SCSIDATL);
+		}
+		break;
+	}
+	case DATA_OVERRUN:
+	{
+		/*
+		 * When the sequencer detects an overrun, it
+		 * places the controller in "BITBUCKET" mode
+		 * and allows the target to complete its transfer.
+		 * Unfortunately, none of the counters get updated
+		 * when the controller is in this mode, so we have
+		 * no way of knowing how large the overrun was.
+		 */
+		u_int scbindex = ahc_inb(ahc, SCB_TAG);
+		u_int lastphase = ahc_inb(ahc, LASTPHASE);
+		u_int i;
+
+		scb = ahc_lookup_scb(ahc, scbindex);
+		for (i = 0; i < num_phases; i++) {
+			if (lastphase == ahc_phase_table[i].phase)
+				break;
+		}
+		ahc_print_path(ahc, scb);
+		printf("data overrun detected %s."
+		       "  Tag == 0x%x.\n",
+		       ahc_phase_table[i].phasemsg,
+  		       scb->hscb->tag);
+		ahc_print_path(ahc, scb);
+		printf("%s seen Data Phase.  Length = %ld.  NumSGs = %d.\n",
+		       ahc_inb(ahc, SEQ_FLAGS) & DPHASE ? "Have" : "Haven't",
+		       ahc_get_transfer_length(scb), scb->sg_count);
+		if (scb->sg_count > 0) {
+			for (i = 0; i < scb->sg_count; i++) {
+
+				printf("sg[%d] - Addr 0x%x%x : Length %d\n",
+				       i,
+				       (ahc_le32toh(scb->sg_list[i].len) >> 24
+				        & SG_HIGH_ADDR_BITS),
+				       ahc_le32toh(scb->sg_list[i].addr),
+				       ahc_le32toh(scb->sg_list[i].len)
+				       & AHC_SG_LEN_MASK);
+			}
+		}
+		/*
+		 * Set this and it will take effect when the
+		 * target does a command complete.
+		 */
+		ahc_freeze_devq(ahc, scb);
+		ahc_set_transaction_status(scb, CAM_DATA_RUN_ERR);
+		ahc_freeze_scb(scb);
+
+		if ((ahc->features & AHC_ULTRA2) != 0) {
+			/*
+			 * Clear the channel in case we return
+			 * to data phase later.
+			 */
+			ahc_outb(ahc, SXFRCTL0,
+				 ahc_inb(ahc, SXFRCTL0) | CLRSTCNT|CLRCHN);
+			ahc_outb(ahc, SXFRCTL0,
+				 ahc_inb(ahc, SXFRCTL0) | CLRSTCNT|CLRCHN);
+		}
+		if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+			u_int dscommand1;
+
+			/* Ensure HHADDR is 0 for future DMA operations. */
+			dscommand1 = ahc_inb(ahc, DSCOMMAND1);
+			ahc_outb(ahc, DSCOMMAND1, dscommand1 | HADDLDSEL0);
+			ahc_outb(ahc, HADDR, 0);
+			ahc_outb(ahc, DSCOMMAND1, dscommand1);
+		}
+		break;
+	}
+	case MKMSG_FAILED:
+	{
+		u_int scbindex;
+
+		printf("%s:%c:%d:%d: Attempt to issue message failed\n",
+		       ahc_name(ahc), devinfo.channel, devinfo.target,
+		       devinfo.lun);
+		scbindex = ahc_inb(ahc, SCB_TAG);
+		scb = ahc_lookup_scb(ahc, scbindex);
+		if (scb != NULL
+		 && (scb->flags & SCB_RECOVERY_SCB) != 0)
+			/*
+			 * Ensure that we didn't put a second instance of this
+			 * SCB into the QINFIFO.
+			 */
+			ahc_search_qinfifo(ahc, SCB_GET_TARGET(ahc, scb),
+					   SCB_GET_CHANNEL(ahc, scb),
+					   SCB_GET_LUN(scb), scb->hscb->tag,
+					   ROLE_INITIATOR, /*status*/0,
+					   SEARCH_REMOVE);
+		break;
+	}
+	case NO_FREE_SCB:
+	{
+		printf("%s: No free or disconnected SCBs\n", ahc_name(ahc));
+		ahc_dump_card_state(ahc);
+		panic("for safety");
+		break;
+	}
+	case SCB_MISMATCH:
+	{
+		u_int scbptr;
+
+		scbptr = ahc_inb(ahc, SCBPTR);
+		printf("Bogus TAG after DMA.  SCBPTR %d, tag %d, our tag %d\n",
+		       scbptr, ahc_inb(ahc, ARG_1),
+		       ahc->scb_data->hscbs[scbptr].tag);
+		ahc_dump_card_state(ahc);
+		panic("for saftey");
+		break;
+	}
+	case OUT_OF_RANGE:
+	{
+		printf("%s: BTT calculation out of range\n", ahc_name(ahc));
+		printf("SAVED_SCSIID == 0x%x, SAVED_LUN == 0x%x, "
+		       "ARG_1 == 0x%x ACCUM = 0x%x\n",
+		       ahc_inb(ahc, SAVED_SCSIID), ahc_inb(ahc, SAVED_LUN),
+		       ahc_inb(ahc, ARG_1), ahc_inb(ahc, ACCUM));
+		printf("SEQ_FLAGS == 0x%x, SCBPTR == 0x%x, BTT == 0x%x, "
+		       "SINDEX == 0x%x\n, A == 0x%x\n",
+		       ahc_inb(ahc, SEQ_FLAGS), ahc_inb(ahc, SCBPTR),
+		       ahc_index_busy_tcl(ahc,
+			    BUILD_TCL(ahc_inb(ahc, SAVED_SCSIID),
+				      ahc_inb(ahc, SAVED_LUN))),
+		       ahc_inb(ahc, SINDEX),
+		       ahc_inb(ahc, ACCUM));
+		printf("SCSIID == 0x%x, SCB_SCSIID == 0x%x, SCB_LUN == 0x%x, "
+		       "SCB_TAG == 0x%x, SCB_CONTROL == 0x%x\n",
+		       ahc_inb(ahc, SCSIID), ahc_inb(ahc, SCB_SCSIID),
+		       ahc_inb(ahc, SCB_LUN), ahc_inb(ahc, SCB_TAG),
+		       ahc_inb(ahc, SCB_CONTROL));
+		printf("SCSIBUSL == 0x%x, SCSISIGI == 0x%x\n",
+		       ahc_inb(ahc, SCSIBUSL), ahc_inb(ahc, SCSISIGI));
+		ahc_dump_card_state(ahc);
+		panic("for safety");
+		break;
+	}
+	default:
+		printf("ahc_intr: seqint, "
+		       "intstat == 0x%x, scsisigi = 0x%x\n",
+		       intstat, ahc_inb(ahc, SCSISIGI));
+		break;
+	}
+unpause:
+	/*
+	 *  The sequencer is paused immediately on
+	 *  a SEQINT, so we should restart it when
+	 *  we're done.
+	 */
+	ahc_unpause(ahc);
+}
+
+void
+ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
+{
+	u_int	scb_index;
+	u_int	status0;
+	u_int	status;
+	struct	scb *scb;
+	char	cur_channel;
+	char	intr_channel;
+
+	/* Make sure the sequencer is in a safe location. */
+	ahc_clear_critical_section(ahc);
+
+	if ((ahc->features & AHC_TWIN) != 0
+	 && ((ahc_inb(ahc, SBLKCTL) & SELBUSB) != 0))
+		cur_channel = 'B';
+	else
+		cur_channel = 'A';
+	intr_channel = cur_channel;
+
+	if ((ahc->features & AHC_ULTRA2) != 0)
+		status0 = ahc_inb(ahc, SSTAT0) & IOERR;
+	else
+		status0 = 0;
+	status = ahc_inb(ahc, SSTAT1) & (SELTO|SCSIRSTI|BUSFREE|SCSIPERR);
+	if (status == 0 && status0 == 0) {
+		if ((ahc->features & AHC_TWIN) != 0) {
+			/* Try the other channel */
+		 	ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) ^ SELBUSB);
+			status = ahc_inb(ahc, SSTAT1)
+			       & (SELTO|SCSIRSTI|BUSFREE|SCSIPERR);
+			intr_channel = (cur_channel == 'A') ? 'B' : 'A';
+		}
+		if (status == 0) {
+			printf("%s: Spurious SCSI interrupt\n", ahc_name(ahc));
+			ahc_outb(ahc, CLRINT, CLRSCSIINT);
+			ahc_unpause(ahc);
+			return;
+		}
+	}
+
+	scb_index = ahc_inb(ahc, SCB_TAG);
+	scb = ahc_lookup_scb(ahc, scb_index);
+	if (scb != NULL
+	 && (ahc_inb(ahc, SEQ_FLAGS) & IDENTIFY_SEEN) == 0)
+		scb = NULL;
+
+	if ((ahc->features & AHC_ULTRA2) != 0
+	 && (status0 & IOERR) != 0) {
+		int now_lvd;
+
+		now_lvd = ahc_inb(ahc, SBLKCTL) & ENAB40;
+		printf("%s: Transceiver State Has Changed to %s mode\n",
+		       ahc_name(ahc), now_lvd ? "LVD" : "SE");
+		ahc_outb(ahc, CLRSINT0, CLRIOERR);
+		/*
+		 * When transitioning to SE mode, the reset line
+		 * glitches, triggering an arbitration bug in some
+		 * Ultra2 controllers.  This bug is cleared when we
+		 * assert the reset line.  Since a reset glitch has
+		 * already occurred with this transition and a
+		 * transceiver state change is handled just like
+		 * a bus reset anyway, asserting the reset line
+		 * ourselves is safe.
+		 */
+		ahc_reset_channel(ahc, intr_channel,
+				 /*Initiate Reset*/now_lvd == 0);
+	} else if ((status & SCSIRSTI) != 0) {
+		printf("%s: Someone reset channel %c\n",
+			ahc_name(ahc), intr_channel);
+		if (intr_channel != cur_channel)
+		 	ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) ^ SELBUSB);
+		ahc_reset_channel(ahc, intr_channel, /*Initiate Reset*/FALSE);
+	} else if ((status & SCSIPERR) != 0) {
+		/*
+		 * Determine the bus phase and queue an appropriate message.
+		 * SCSIPERR is latched true as soon as a parity error
+		 * occurs.  If the sequencer acked the transfer that
+		 * caused the parity error and the currently presented
+		 * transfer on the bus has correct parity, SCSIPERR will
+		 * be cleared by CLRSCSIPERR.  Use this to determine if
+		 * we should look at the last phase the sequencer recorded,
+		 * or the current phase presented on the bus.
+		 */
+		u_int mesg_out;
+		u_int curphase;
+		u_int errorphase;
+		u_int lastphase;
+		u_int scsirate;
+		u_int i;
+		u_int sstat2;
+
+		lastphase = ahc_inb(ahc, LASTPHASE);
+		curphase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
+		sstat2 = ahc_inb(ahc, SSTAT2);
+		ahc_outb(ahc, CLRSINT1, CLRSCSIPERR);
+		/*
+		 * For all phases save DATA, the sequencer won't
+		 * automatically ack a byte that has a parity error
+		 * in it.  So the only way that the current phase
+		 * could be 'data-in' is if the parity error is for
+		 * an already acked byte in the data phase.  During
+		 * synchronous data-in transfers, we may actually
+		 * ack bytes before latching the current phase in
+		 * LASTPHASE, leading to the discrepancy between
+		 * curphase and lastphase.
+		 */
+		if ((ahc_inb(ahc, SSTAT1) & SCSIPERR) != 0
+		 || curphase == P_DATAIN || curphase == P_DATAIN_DT)
+			errorphase = curphase;
+		else
+			errorphase = lastphase;
+
+		for (i = 0; i < num_phases; i++) {
+			if (errorphase == ahc_phase_table[i].phase)
+				break;
+		}
+		mesg_out = ahc_phase_table[i].mesg_out;
+		if (scb != NULL)
+			ahc_print_path(ahc, scb);
+		else
+			printf("%s:%c:%d: ", ahc_name(ahc), intr_channel,
+			       SCSIID_TARGET(ahc, ahc_inb(ahc, SAVED_SCSIID)));
+		scsirate = ahc_inb(ahc, SCSIRATE);
+		printf("parity error detected %s. "
+		       "SEQADDR(0x%x) SCSIRATE(0x%x)\n",
+		       ahc_phase_table[i].phasemsg,
+		       ahc_inb(ahc, SEQADDR0) | (ahc_inb(ahc, SEQADDR1) << 8),
+		       scsirate);
+
+		if ((ahc->features & AHC_DT) != 0) {
+
+			if ((sstat2 & CRCVALERR) != 0)
+				printf("\tCRC Value Mismatch\n");
+			if ((sstat2 & CRCENDERR) != 0)
+				printf("\tNo terminal CRC packet recevied\n");
+			if ((sstat2 & CRCREQERR) != 0)
+				printf("\tIllegal CRC packet request\n");
+			if ((sstat2 & DUAL_EDGE_ERR) != 0)
+				printf("\tUnexpected %sDT Data Phase\n",
+				       (scsirate & SINGLE_EDGE) ? "" : "non-");
+		}
+
+		/*
+		 * We've set the hardware to assert ATN if we   
+		 * get a parity error on "in" phases, so all we  
+		 * need to do is stuff the message buffer with
+		 * the appropriate message.  "In" phases have set
+		 * mesg_out to something other than MSG_NOP.
+		 */
+		if (mesg_out != MSG_NOOP) {
+			if (ahc->msg_type != MSG_TYPE_NONE)
+				ahc->send_msg_perror = TRUE;
+			else
+				ahc_outb(ahc, MSG_OUT, mesg_out);
+		}
+		/*
+		 * Force a renegotiation with this target just in
+		 * case we are out of sync for some external reason
+		 * unknown (or unreported) by the target.
+		 */
+		ahc_force_renegotiation(ahc);
+		ahc_outb(ahc, CLRINT, CLRSCSIINT);
+		ahc_unpause(ahc);
+	} else if ((status & SELTO) != 0) {
+		u_int	scbptr;
+
+		/* Stop the selection */
+		ahc_outb(ahc, SCSISEQ, 0);
+
+		/* No more pending messages */
+		ahc_clear_msg_state(ahc);
+
+		/* Clear interrupt state */
+		ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENBUSFREE);
+		ahc_outb(ahc, CLRSINT1, CLRSELTIMEO|CLRBUSFREE|CLRSCSIPERR);
+
+		/*
+		 * Although the driver does not care about the
+		 * 'Selection in Progress' status bit, the busy
+		 * LED does.  SELINGO is only cleared by a sucessfull
+		 * selection, so we must manually clear it to insure
+		 * the LED turns off just incase no future successful
+		 * selections occur (e.g. no devices on the bus).
+		 */
+		ahc_outb(ahc, CLRSINT0, CLRSELINGO);
+
+		scbptr = ahc_inb(ahc, WAITING_SCBH);
+		ahc_outb(ahc, SCBPTR, scbptr);
+		scb_index = ahc_inb(ahc, SCB_TAG);
+
+		scb = ahc_lookup_scb(ahc, scb_index);
+		if (scb == NULL) {
+			printf("%s: ahc_intr - referenced scb not "
+			       "valid during SELTO scb(%d, %d)\n",
+			       ahc_name(ahc), scbptr, scb_index);
+		} else {
+			ahc_set_transaction_status(scb, CAM_SEL_TIMEOUT);
+			ahc_freeze_devq(ahc, scb);
+		}
+		ahc_outb(ahc, CLRINT, CLRSCSIINT);
+		/*
+		 * Force a renegotiation with this target just in
+		 * case the cable was pulled and will later be
+		 * re-attached.  The target may forget its negotiation
+		 * settings with us should it attempt to reselect
+		 * during the interruption.  The target will not issue
+		 * a unit attention in this case, so we must always
+		 * renegotiate.
+		 */
+		ahc_force_renegotiation(ahc);
+		ahc_restart(ahc);
+	} else if ((status & BUSFREE) != 0
+		&& (ahc_inb(ahc, SIMODE1) & ENBUSFREE) != 0) {
+		u_int lastphase;
+		u_int saved_scsiid;
+		u_int saved_lun;
+		u_int target;
+		u_int initiator_role_id;
+		char channel;
+		int printerror;
+
+		/*
+		 * Clear our selection hardware as soon as possible.
+		 * We may have an entry in the waiting Q for this target,
+		 * that is affected by this busfree and we don't want to
+		 * go about selecting the target while we handle the event.
+		 */
+		ahc_outb(ahc, SCSISEQ,
+			 ahc_inb(ahc, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP));
+
+		/*
+		 * Disable busfree interrupts and clear the busfree
+		 * interrupt status.  We do this here so that several
+		 * bus transactions occur prior to clearing the SCSIINT
+		 * latch.  It can take a bit for the clearing to take effect.
+		 */
+		ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENBUSFREE);
+		ahc_outb(ahc, CLRSINT1, CLRBUSFREE|CLRSCSIPERR);
+
+		/*
+		 * Look at what phase we were last in.
+		 * If its message out, chances are pretty good
+		 * that the busfree was in response to one of
+		 * our abort requests.
+		 */
+		lastphase = ahc_inb(ahc, LASTPHASE);
+		saved_scsiid = ahc_inb(ahc, SAVED_SCSIID);
+		saved_lun = ahc_inb(ahc, SAVED_LUN);
+		target = SCSIID_TARGET(ahc, saved_scsiid);
+		initiator_role_id = SCSIID_OUR_ID(saved_scsiid);
+		channel = SCSIID_CHANNEL(ahc, saved_scsiid);
+		printerror = 1;
+
+		if (lastphase == P_MESGOUT) {
+			struct ahc_devinfo devinfo;
+			u_int tag;
+
+			ahc_fetch_devinfo(ahc, &devinfo);
+			tag = SCB_LIST_NULL;
+			if (ahc_sent_msg(ahc, AHCMSG_1B, MSG_ABORT_TAG, TRUE)
+			 || ahc_sent_msg(ahc, AHCMSG_1B, MSG_ABORT, TRUE)) {
+				if (ahc->msgout_buf[ahc->msgout_index - 1]
+				 == MSG_ABORT_TAG)
+					tag = scb->hscb->tag;
+				ahc_print_path(ahc, scb);
+				printf("SCB %d - Abort%s Completed.\n",
+				       scb->hscb->tag, tag == SCB_LIST_NULL ?
+				       "" : " Tag");
+				ahc_abort_scbs(ahc, target, channel,
+					       saved_lun, tag,
+					       ROLE_INITIATOR,
+					       CAM_REQ_ABORTED);
+				printerror = 0;
+			} else if (ahc_sent_msg(ahc, AHCMSG_1B,
+						MSG_BUS_DEV_RESET, TRUE)) {
+#ifdef __FreeBSD__
+				/*
+				 * Don't mark the user's request for this BDR
+				 * as completing with CAM_BDR_SENT.  CAM3
+				 * specifies CAM_REQ_CMP.
+				 */
+				if (scb != NULL
+				 && scb->io_ctx->ccb_h.func_code== XPT_RESET_DEV
+				 && ahc_match_scb(ahc, scb, target, channel,
+						  CAM_LUN_WILDCARD,
+						  SCB_LIST_NULL,
+						  ROLE_INITIATOR)) {
+					ahc_set_transaction_status(scb, CAM_REQ_CMP);
+				}
+#endif
+				ahc_compile_devinfo(&devinfo,
+						    initiator_role_id,
+						    target,
+						    CAM_LUN_WILDCARD,
+						    channel,
+						    ROLE_INITIATOR);
+				ahc_handle_devreset(ahc, &devinfo,
+						    CAM_BDR_SENT,
+						    "Bus Device Reset",
+						    /*verbose_level*/0);
+				printerror = 0;
+			} else if (ahc_sent_msg(ahc, AHCMSG_EXT,
+						MSG_EXT_PPR, FALSE)) {
+				struct ahc_initiator_tinfo *tinfo;
+				struct ahc_tmode_tstate *tstate;
+
+				/*
+				 * PPR Rejected.  Try non-ppr negotiation
+				 * and retry command.
+				 */
+				tinfo = ahc_fetch_transinfo(ahc,
+							    devinfo.channel,
+							    devinfo.our_scsiid,
+							    devinfo.target,
+							    &tstate);
+				tinfo->curr.transport_version = 2;
+				tinfo->goal.transport_version = 2;
+				tinfo->goal.ppr_options = 0;
+				ahc_qinfifo_requeue_tail(ahc, scb);
+				printerror = 0;
+			} else if (ahc_sent_msg(ahc, AHCMSG_EXT,
+						MSG_EXT_WDTR, FALSE)
+				|| ahc_sent_msg(ahc, AHCMSG_EXT,
+						MSG_EXT_SDTR, FALSE)) {
+				/*
+				 * Negotiation Rejected.  Go-async and
+				 * retry command.
+				 */
+				ahc_set_width(ahc, &devinfo,
+					      MSG_EXT_WDTR_BUS_8_BIT,
+					      AHC_TRANS_CUR|AHC_TRANS_GOAL,
+					      /*paused*/TRUE);
+				ahc_set_syncrate(ahc, &devinfo,
+						/*syncrate*/NULL,
+						/*period*/0, /*offset*/0,
+						/*ppr_options*/0,
+						AHC_TRANS_CUR|AHC_TRANS_GOAL,
+						/*paused*/TRUE);
+				ahc_qinfifo_requeue_tail(ahc, scb);
+				printerror = 0;
+			}
+		}
+		if (printerror != 0) {
+			u_int i;
+
+			if (scb != NULL) {
+				u_int tag;
+
+				if ((scb->hscb->control & TAG_ENB) != 0)
+					tag = scb->hscb->tag;
+				else
+					tag = SCB_LIST_NULL;
+				ahc_print_path(ahc, scb);
+				ahc_abort_scbs(ahc, target, channel,
+					       SCB_GET_LUN(scb), tag,
+					       ROLE_INITIATOR,
+					       CAM_UNEXP_BUSFREE);
+			} else {
+				/*
+				 * We had not fully identified this connection,
+				 * so we cannot abort anything.
+				 */
+				printf("%s: ", ahc_name(ahc));
+			}
+			for (i = 0; i < num_phases; i++) {
+				if (lastphase == ahc_phase_table[i].phase)
+					break;
+			}
+			printf("Unexpected busfree %s\n"
+			       "SEQADDR == 0x%x\n",
+			       ahc_phase_table[i].phasemsg,
+			       ahc_inb(ahc, SEQADDR0)
+				| (ahc_inb(ahc, SEQADDR1) << 8));
+		}
+		ahc_clear_msg_state(ahc);
+		ahc_outb(ahc, CLRINT, CLRSCSIINT);
+		ahc_restart(ahc);
+	} else {
+		printf("%s: Missing case in ahc_handle_scsiint. status = %x\n",
+		       ahc_name(ahc), status);
+		ahc_outb(ahc, CLRINT, CLRSCSIINT);
+	}
+}
+
+/*
+ * Force renegotiation to occur the next time we initiate
+ * a command to the current device.
+ */
+static void
+ahc_force_renegotiation(struct ahc_softc *ahc)
+{
+	struct	ahc_devinfo devinfo;
+	struct	ahc_initiator_tinfo *targ_info;
+	struct	ahc_tmode_tstate *tstate;
+
+	ahc_fetch_devinfo(ahc, &devinfo);
+	targ_info = ahc_fetch_transinfo(ahc,
+					devinfo.channel,
+					devinfo.our_scsiid,
+					devinfo.target,
+					&tstate);
+	ahc_update_neg_request(ahc, &devinfo, tstate,
+			       targ_info, /*force*/TRUE);
+}
+
+#define AHC_MAX_STEPS 2000
+void
+ahc_clear_critical_section(struct ahc_softc *ahc)
+{
+	int	stepping;
+	int	steps;
+	u_int	simode0;
+	u_int	simode1;
+
+	if (ahc->num_critical_sections == 0)
+		return;
+
+	stepping = FALSE;
+	steps = 0;
+	simode0 = 0;
+	simode1 = 0;
+	for (;;) {
+		struct	cs *cs;
+		u_int	seqaddr;
+		u_int	i;
+
+		seqaddr = ahc_inb(ahc, SEQADDR0)
+			| (ahc_inb(ahc, SEQADDR1) << 8);
+
+		/*
+		 * Seqaddr represents the next instruction to execute, 
+		 * so we are really executing the instruction just
+		 * before it.
+		 */
+		if (seqaddr != 0)
+			seqaddr -= 1;
+		cs = ahc->critical_sections;
+		for (i = 0; i < ahc->num_critical_sections; i++, cs++) {
+			
+			if (cs->begin < seqaddr && cs->end >= seqaddr)
+				break;
+		}
+
+		if (i == ahc->num_critical_sections)
+			break;
+
+		if (steps > AHC_MAX_STEPS) {
+			printf("%s: Infinite loop in critical section\n",
+			       ahc_name(ahc));
+			ahc_dump_card_state(ahc);
+			panic("critical section loop");
+		}
+
+		steps++;
+		if (stepping == FALSE) {
+
+			/*
+			 * Disable all interrupt sources so that the
+			 * sequencer will not be stuck by a pausing
+			 * interrupt condition while we attempt to
+			 * leave a critical section.
+			 */
+			simode0 = ahc_inb(ahc, SIMODE0);
+			ahc_outb(ahc, SIMODE0, 0);
+			simode1 = ahc_inb(ahc, SIMODE1);
+			ahc_outb(ahc, SIMODE1, 0);
+			ahc_outb(ahc, CLRINT, CLRSCSIINT);
+			ahc_outb(ahc, SEQCTL, ahc_inb(ahc, SEQCTL) | STEP);
+			stepping = TRUE;
+		}
+		ahc_outb(ahc, HCNTRL, ahc->unpause);
+		do {
+			ahc_delay(200);
+		} while (!ahc_is_paused(ahc));
+	}
+	if (stepping) {
+		ahc_outb(ahc, SIMODE0, simode0);
+		ahc_outb(ahc, SIMODE1, simode1);
+		ahc_outb(ahc, SEQCTL, ahc_inb(ahc, SEQCTL) & ~STEP);
+	}
+}
+
+/*
+ * Clear any pending interrupt status.
+ */
+void
+ahc_clear_intstat(struct ahc_softc *ahc)
+{
+	/* Clear any interrupt conditions this may have caused */
+	ahc_outb(ahc, CLRSINT1, CLRSELTIMEO|CLRATNO|CLRSCSIRSTI
+				|CLRBUSFREE|CLRSCSIPERR|CLRPHASECHG|
+				CLRREQINIT);
+	ahc_outb(ahc, CLRSINT0, CLRSELDO|CLRSELDI|CLRSELINGO);
+	ahc_outb(ahc, CLRINT, CLRSCSIINT);
+}
+
+/**************************** Debugging Routines ******************************/
+void
+ahc_print_scb(struct scb *scb)
+{
+	int i;
+
+	struct hardware_scb *hscb = scb->hscb;
+
+	printf("scb:%p control:0x%x scsiid:0x%x lun:%d cdb_len:%d\n",
+	       (void *)scb,
+	       hscb->control,
+	       hscb->scsiid,
+	       hscb->lun,
+	       hscb->cdb_len);
+	printf("Shared Data: ");
+	for (i = 0; i < sizeof(hscb->shared_data.cdb); i++)
+		printf("%#02x", hscb->shared_data.cdb[i]);
+	printf("        dataptr:%#x datacnt:%#x sgptr:%#x tag:%#x\n",
+		ahc_le32toh(hscb->dataptr),
+		ahc_le32toh(hscb->datacnt),
+		ahc_le32toh(hscb->sgptr),
+		hscb->tag);
+	if (scb->sg_count > 0) {
+		for (i = 0; i < scb->sg_count; i++) {
+			printf("sg[%d] - Addr 0x%x%x : Length %d\n",
+			       i,
+			       (ahc_le32toh(scb->sg_list[i].len) >> 24
+			        & SG_HIGH_ADDR_BITS),
+			       ahc_le32toh(scb->sg_list[i].addr),
+			       ahc_le32toh(scb->sg_list[i].len));
+		}
+	}
+}
+
+/************************* Transfer Negotiation *******************************/
+/*
+ * Allocate per target mode instance (ID we respond to as a target)
+ * transfer negotiation data structures.
+ */
+static struct ahc_tmode_tstate *
+ahc_alloc_tstate(struct ahc_softc *ahc, u_int scsi_id, char channel)
+{
+	struct ahc_tmode_tstate *master_tstate;
+	struct ahc_tmode_tstate *tstate;
+	int i;
+
+	master_tstate = ahc->enabled_targets[ahc->our_id];
+	if (channel == 'B') {
+		scsi_id += 8;
+		master_tstate = ahc->enabled_targets[ahc->our_id_b + 8];
+	}
+	if (ahc->enabled_targets[scsi_id] != NULL
+	 && ahc->enabled_targets[scsi_id] != master_tstate)
+		panic("%s: ahc_alloc_tstate - Target already allocated",
+		      ahc_name(ahc));
+	tstate = malloc(sizeof(*tstate), M_DEVBUF, M_NOWAIT);
+	if (tstate == NULL)
+		return (NULL);
+
+	/*
+	 * If we have allocated a master tstate, copy user settings from
+	 * the master tstate (taken from SRAM or the EEPROM) for this
+	 * channel, but reset our current and goal settings to async/narrow
+	 * until an initiator talks to us.
+	 */
+	if (master_tstate != NULL) {
+		memcpy(tstate, master_tstate, sizeof(*tstate));
+		memset(tstate->enabled_luns, 0, sizeof(tstate->enabled_luns));
+		tstate->ultraenb = 0;
+		for (i = 0; i < 16; i++) {
+			memset(&tstate->transinfo[i].curr, 0,
+			      sizeof(tstate->transinfo[i].curr));
+			memset(&tstate->transinfo[i].goal, 0,
+			      sizeof(tstate->transinfo[i].goal));
+		}
+	} else
+		memset(tstate, 0, sizeof(*tstate));
+	ahc->enabled_targets[scsi_id] = tstate;
+	return (tstate);
+}
+
+#ifdef AHC_TARGET_MODE
+/*
+ * Free per target mode instance (ID we respond to as a target)
+ * transfer negotiation data structures.
+ */
+static void
+ahc_free_tstate(struct ahc_softc *ahc, u_int scsi_id, char channel, int force)
+{
+	struct ahc_tmode_tstate *tstate;
+
+	/*
+	 * Don't clean up our "master" tstate.
+	 * It has our default user settings.
+	 */
+	if (((channel == 'B' && scsi_id == ahc->our_id_b)
+	  || (channel == 'A' && scsi_id == ahc->our_id))
+	 && force == FALSE)
+		return;
+
+	if (channel == 'B')
+		scsi_id += 8;
+	tstate = ahc->enabled_targets[scsi_id];
+	if (tstate != NULL)
+		free(tstate, M_DEVBUF);
+	ahc->enabled_targets[scsi_id] = NULL;
+}
+#endif
+
+/*
+ * Called when we have an active connection to a target on the bus,
+ * this function finds the nearest syncrate to the input period limited
+ * by the capabilities of the bus connectivity of and sync settings for
+ * the target.
+ */
+struct ahc_syncrate *
+ahc_devlimited_syncrate(struct ahc_softc *ahc,
+			struct ahc_initiator_tinfo *tinfo,
+			u_int *period, u_int *ppr_options, role_t role) {
+	struct	ahc_transinfo *transinfo;
+	u_int	maxsync;
+
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		if ((ahc_inb(ahc, SBLKCTL) & ENAB40) != 0
+		 && (ahc_inb(ahc, SSTAT2) & EXP_ACTIVE) == 0) {
+			maxsync = AHC_SYNCRATE_DT;
+		} else {
+			maxsync = AHC_SYNCRATE_ULTRA;
+			/* Can't do DT on an SE bus */
+			*ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+		}
+	} else if ((ahc->features & AHC_ULTRA) != 0) {
+		maxsync = AHC_SYNCRATE_ULTRA;
+	} else {
+		maxsync = AHC_SYNCRATE_FAST;
+	}
+	/*
+	 * Never allow a value higher than our current goal
+	 * period otherwise we may allow a target initiated
+	 * negotiation to go above the limit as set by the
+	 * user.  In the case of an initiator initiated
+	 * sync negotiation, we limit based on the user
+	 * setting.  This allows the system to still accept
+	 * incoming negotiations even if target initiated
+	 * negotiation is not performed.
+	 */
+	if (role == ROLE_TARGET)
+		transinfo = &tinfo->user;
+	else 
+		transinfo = &tinfo->goal;
+	*ppr_options &= transinfo->ppr_options;
+	if (transinfo->period == 0) {
+		*period = 0;
+		*ppr_options = 0;
+		return (NULL);
+	}
+	*period = MAX(*period, transinfo->period);
+	return (ahc_find_syncrate(ahc, period, ppr_options, maxsync));
+}
+
+/*
+ * Look up the valid period to SCSIRATE conversion in our table.
+ * Return the period and offset that should be sent to the target
+ * if this was the beginning of an SDTR.
+ */
+struct ahc_syncrate *
+ahc_find_syncrate(struct ahc_softc *ahc, u_int *period,
+		  u_int *ppr_options, u_int maxsync)
+{
+	struct ahc_syncrate *syncrate;
+
+	if ((ahc->features & AHC_DT) == 0)
+		*ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+
+	/* Skip all DT only entries if DT is not available */
+	if ((*ppr_options & MSG_EXT_PPR_DT_REQ) == 0
+	 && maxsync < AHC_SYNCRATE_ULTRA2)
+		maxsync = AHC_SYNCRATE_ULTRA2;
+	
+	for (syncrate = &ahc_syncrates[maxsync];
+	     syncrate->rate != NULL;
+	     syncrate++) {
+
+		/*
+		 * The Ultra2 table doesn't go as low
+		 * as for the Fast/Ultra cards.
+		 */
+		if ((ahc->features & AHC_ULTRA2) != 0
+		 && (syncrate->sxfr_u2 == 0))
+			break;
+
+		if (*period <= syncrate->period) {
+			/*
+			 * When responding to a target that requests
+			 * sync, the requested rate may fall between
+			 * two rates that we can output, but still be
+			 * a rate that we can receive.  Because of this,
+			 * we want to respond to the target with
+			 * the same rate that it sent to us even
+			 * if the period we use to send data to it
+			 * is lower.  Only lower the response period
+			 * if we must.
+			 */
+			if (syncrate == &ahc_syncrates[maxsync])
+				*period = syncrate->period;
+
+			/*
+			 * At some speeds, we only support
+			 * ST transfers.
+			 */
+		 	if ((syncrate->sxfr_u2 & ST_SXFR) != 0)
+				*ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+			break;
+		}
+	}
+
+	if ((*period == 0)
+	 || (syncrate->rate == NULL)
+	 || ((ahc->features & AHC_ULTRA2) != 0
+	  && (syncrate->sxfr_u2 == 0))) {
+		/* Use asynchronous transfers. */
+		*period = 0;
+		syncrate = NULL;
+		*ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+	}
+	return (syncrate);
+}
+
+/*
+ * Convert from an entry in our syncrate table to the SCSI equivalent
+ * sync "period" factor.
+ */
+u_int
+ahc_find_period(struct ahc_softc *ahc, u_int scsirate, u_int maxsync)
+{
+	struct ahc_syncrate *syncrate;
+
+	if ((ahc->features & AHC_ULTRA2) != 0)
+		scsirate &= SXFR_ULTRA2;
+	else
+		scsirate &= SXFR;
+
+	syncrate = &ahc_syncrates[maxsync];
+	while (syncrate->rate != NULL) {
+
+		if ((ahc->features & AHC_ULTRA2) != 0) {
+			if (syncrate->sxfr_u2 == 0)
+				break;
+			else if (scsirate == (syncrate->sxfr_u2 & SXFR_ULTRA2))
+				return (syncrate->period);
+		} else if (scsirate == (syncrate->sxfr & SXFR)) {
+				return (syncrate->period);
+		}
+		syncrate++;
+	}
+	return (0); /* async */
+}
+
+/*
+ * Truncate the given synchronous offset to a value the
+ * current adapter type and syncrate are capable of.
+ */
+void
+ahc_validate_offset(struct ahc_softc *ahc,
+		    struct ahc_initiator_tinfo *tinfo,
+		    struct ahc_syncrate *syncrate,
+		    u_int *offset, int wide, role_t role)
+{
+	u_int maxoffset;
+
+	/* Limit offset to what we can do */
+	if (syncrate == NULL) {
+		maxoffset = 0;
+	} else if ((ahc->features & AHC_ULTRA2) != 0) {
+		maxoffset = MAX_OFFSET_ULTRA2;
+	} else {
+		if (wide)
+			maxoffset = MAX_OFFSET_16BIT;
+		else
+			maxoffset = MAX_OFFSET_8BIT;
+	}
+	*offset = MIN(*offset, maxoffset);
+	if (tinfo != NULL) {
+		if (role == ROLE_TARGET)
+			*offset = MIN(*offset, tinfo->user.offset);
+		else
+			*offset = MIN(*offset, tinfo->goal.offset);
+	}
+}
+
+/*
+ * Truncate the given transfer width parameter to a value the
+ * current adapter type is capable of.
+ */
+void
+ahc_validate_width(struct ahc_softc *ahc, struct ahc_initiator_tinfo *tinfo,
+		   u_int *bus_width, role_t role)
+{
+	switch (*bus_width) {
+	default:
+		if (ahc->features & AHC_WIDE) {
+			/* Respond Wide */
+			*bus_width = MSG_EXT_WDTR_BUS_16_BIT;
+			break;
+		}
+		/* FALLTHROUGH */
+	case MSG_EXT_WDTR_BUS_8_BIT:
+		*bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+		break;
+	}
+	if (tinfo != NULL) {
+		if (role == ROLE_TARGET)
+			*bus_width = MIN(tinfo->user.width, *bus_width);
+		else
+			*bus_width = MIN(tinfo->goal.width, *bus_width);
+	}
+}
+
+/*
+ * Update the bitmask of targets for which the controller should
+ * negotiate with at the next convenient oportunity.  This currently
+ * means the next time we send the initial identify messages for
+ * a new transaction.
+ */
+int
+ahc_update_neg_request(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+		       struct ahc_tmode_tstate *tstate,
+		       struct ahc_initiator_tinfo *tinfo, int force)
+{
+	u_int auto_negotiate_orig;
+
+	auto_negotiate_orig = tstate->auto_negotiate;
+	if (tinfo->curr.period != tinfo->goal.period
+	 || tinfo->curr.width != tinfo->goal.width
+	 || tinfo->curr.offset != tinfo->goal.offset
+	 || tinfo->curr.ppr_options != tinfo->goal.ppr_options
+	 || (force
+	  && (tinfo->goal.period != 0
+	   || tinfo->goal.width != MSG_EXT_WDTR_BUS_8_BIT
+	   || tinfo->goal.ppr_options != 0)))
+		tstate->auto_negotiate |= devinfo->target_mask;
+	else
+		tstate->auto_negotiate &= ~devinfo->target_mask;
+
+	return (auto_negotiate_orig != tstate->auto_negotiate);
+}
+
+/*
+ * Update the user/goal/curr tables of synchronous negotiation
+ * parameters as well as, in the case of a current or active update,
+ * any data structures on the host controller.  In the case of an
+ * active update, the specified target is currently talking to us on
+ * the bus, so the transfer parameter update must take effect
+ * immediately.
+ */
+void
+ahc_set_syncrate(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+		 struct ahc_syncrate *syncrate, u_int period,
+		 u_int offset, u_int ppr_options, u_int type, int paused)
+{
+	struct	ahc_initiator_tinfo *tinfo;
+	struct	ahc_tmode_tstate *tstate;
+	u_int	old_period;
+	u_int	old_offset;
+	u_int	old_ppr;
+	int	active;
+	int	update_needed;
+
+	active = (type & AHC_TRANS_ACTIVE) == AHC_TRANS_ACTIVE;
+	update_needed = 0;
+
+	if (syncrate == NULL) {
+		period = 0;
+		offset = 0;
+	}
+
+	tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
+				    devinfo->target, &tstate);
+
+	if ((type & AHC_TRANS_USER) != 0) {
+		tinfo->user.period = period;
+		tinfo->user.offset = offset;
+		tinfo->user.ppr_options = ppr_options;
+	}
+
+	if ((type & AHC_TRANS_GOAL) != 0) {
+		tinfo->goal.period = period;
+		tinfo->goal.offset = offset;
+		tinfo->goal.ppr_options = ppr_options;
+	}
+
+	old_period = tinfo->curr.period;
+	old_offset = tinfo->curr.offset;
+	old_ppr	   = tinfo->curr.ppr_options;
+
+	if ((type & AHC_TRANS_CUR) != 0
+	 && (old_period != period
+	  || old_offset != offset
+	  || old_ppr != ppr_options)) {
+		u_int	scsirate;
+
+		update_needed++;
+		scsirate = tinfo->scsirate;
+		if ((ahc->features & AHC_ULTRA2) != 0) {
+
+			scsirate &= ~(SXFR_ULTRA2|SINGLE_EDGE|ENABLE_CRC);
+			if (syncrate != NULL) {
+				scsirate |= syncrate->sxfr_u2;
+				if ((ppr_options & MSG_EXT_PPR_DT_REQ) != 0)
+					scsirate |= ENABLE_CRC;
+				else
+					scsirate |= SINGLE_EDGE;
+			}
+		} else {
+
+			scsirate &= ~(SXFR|SOFS);
+			/*
+			 * Ensure Ultra mode is set properly for
+			 * this target.
+			 */
+			tstate->ultraenb &= ~devinfo->target_mask;
+			if (syncrate != NULL) {
+				if (syncrate->sxfr & ULTRA_SXFR) {
+					tstate->ultraenb |=
+						devinfo->target_mask;
+				}
+				scsirate |= syncrate->sxfr & SXFR;
+				scsirate |= offset & SOFS;
+			}
+			if (active) {
+				u_int sxfrctl0;
+
+				sxfrctl0 = ahc_inb(ahc, SXFRCTL0);
+				sxfrctl0 &= ~FAST20;
+				if (tstate->ultraenb & devinfo->target_mask)
+					sxfrctl0 |= FAST20;
+				ahc_outb(ahc, SXFRCTL0, sxfrctl0);
+			}
+		}
+		if (active) {
+			ahc_outb(ahc, SCSIRATE, scsirate);
+			if ((ahc->features & AHC_ULTRA2) != 0)
+				ahc_outb(ahc, SCSIOFFSET, offset);
+		}
+
+		tinfo->scsirate = scsirate;
+		tinfo->curr.period = period;
+		tinfo->curr.offset = offset;
+		tinfo->curr.ppr_options = ppr_options;
+
+		ahc_send_async(ahc, devinfo->channel, devinfo->target,
+			       CAM_LUN_WILDCARD, AC_TRANSFER_NEG, NULL);
+		if (bootverbose) {
+			if (offset != 0) {
+				printf("%s: target %d synchronous at %sMHz%s, "
+				       "offset = 0x%x\n", ahc_name(ahc),
+				       devinfo->target, syncrate->rate,
+				       (ppr_options & MSG_EXT_PPR_DT_REQ)
+				       ? " DT" : "", offset);
+			} else {
+				printf("%s: target %d using "
+				       "asynchronous transfers\n",
+				       ahc_name(ahc), devinfo->target);
+			}
+		}
+	}
+
+	update_needed += ahc_update_neg_request(ahc, devinfo, tstate,
+						tinfo, /*force*/FALSE);
+
+	if (update_needed)
+		ahc_update_pending_scbs(ahc);
+}
+
+/*
+ * Update the user/goal/curr tables of wide negotiation
+ * parameters as well as, in the case of a current or active update,
+ * any data structures on the host controller.  In the case of an
+ * active update, the specified target is currently talking to us on
+ * the bus, so the transfer parameter update must take effect
+ * immediately.
+ */
+void
+ahc_set_width(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+	      u_int width, u_int type, int paused)
+{
+	struct	ahc_initiator_tinfo *tinfo;
+	struct	ahc_tmode_tstate *tstate;
+	u_int	oldwidth;
+	int	active;
+	int	update_needed;
+
+	active = (type & AHC_TRANS_ACTIVE) == AHC_TRANS_ACTIVE;
+	update_needed = 0;
+	tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
+				    devinfo->target, &tstate);
+
+	if ((type & AHC_TRANS_USER) != 0)
+		tinfo->user.width = width;
+
+	if ((type & AHC_TRANS_GOAL) != 0)
+		tinfo->goal.width = width;
+
+	oldwidth = tinfo->curr.width;
+	if ((type & AHC_TRANS_CUR) != 0 && oldwidth != width) {
+		u_int	scsirate;
+
+		update_needed++;
+		scsirate =  tinfo->scsirate;
+		scsirate &= ~WIDEXFER;
+		if (width == MSG_EXT_WDTR_BUS_16_BIT)
+			scsirate |= WIDEXFER;
+
+		tinfo->scsirate = scsirate;
+
+		if (active)
+			ahc_outb(ahc, SCSIRATE, scsirate);
+
+		tinfo->curr.width = width;
+
+		ahc_send_async(ahc, devinfo->channel, devinfo->target,
+			       CAM_LUN_WILDCARD, AC_TRANSFER_NEG, NULL);
+		if (bootverbose) {
+			printf("%s: target %d using %dbit transfers\n",
+			       ahc_name(ahc), devinfo->target,
+			       8 * (0x01 << width));
+		}
+	}
+
+	update_needed += ahc_update_neg_request(ahc, devinfo, tstate,
+						tinfo, /*force*/FALSE);
+	if (update_needed)
+		ahc_update_pending_scbs(ahc);
+}
+
+/*
+ * Update the current state of tagged queuing for a given target.
+ */
+void
+ahc_set_tags(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+	     ahc_queue_alg alg)
+{
+ 	ahc_platform_set_tags(ahc, devinfo, alg);
+ 	ahc_send_async(ahc, devinfo->channel, devinfo->target,
+ 		       devinfo->lun, AC_TRANSFER_NEG, &alg);
+}
+
+/*
+ * When the transfer settings for a connection change, update any
+ * in-transit SCBs to contain the new data so the hardware will
+ * be set correctly during future (re)selections.
+ */
+static void
+ahc_update_pending_scbs(struct ahc_softc *ahc)
+{
+	struct	scb *pending_scb;
+	int	pending_scb_count;
+	int	i;
+	int	paused;
+	u_int	saved_scbptr;
+
+	/*
+	 * Traverse the pending SCB list and ensure that all of the
+	 * SCBs there have the proper settings.
+	 */
+	pending_scb_count = 0;
+	LIST_FOREACH(pending_scb, &ahc->pending_scbs, pending_links) {
+		struct ahc_devinfo devinfo;
+		struct hardware_scb *pending_hscb;
+		struct ahc_initiator_tinfo *tinfo;
+		struct ahc_tmode_tstate *tstate;
+
+		ahc_scb_devinfo(ahc, &devinfo, pending_scb);
+		tinfo = ahc_fetch_transinfo(ahc, devinfo.channel,
+					    devinfo.our_scsiid,
+					    devinfo.target, &tstate);
+		pending_hscb = pending_scb->hscb;
+		pending_hscb->control &= ~ULTRAENB;
+		if ((tstate->ultraenb & devinfo.target_mask) != 0)
+			pending_hscb->control |= ULTRAENB;
+		pending_hscb->scsirate = tinfo->scsirate;
+		pending_hscb->scsioffset = tinfo->curr.offset;
+		if ((tstate->auto_negotiate & devinfo.target_mask) == 0
+		 && (pending_scb->flags & SCB_AUTO_NEGOTIATE) != 0) {
+			pending_scb->flags &= ~SCB_AUTO_NEGOTIATE;
+			pending_hscb->control &= ~MK_MESSAGE;
+		}
+		ahc_sync_scb(ahc, pending_scb,
+			     BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+		pending_scb_count++;
+	}
+
+	if (pending_scb_count == 0)
+		return;
+
+	if (ahc_is_paused(ahc)) {
+		paused = 1;
+	} else {
+		paused = 0;
+		ahc_pause(ahc);
+	}
+
+	saved_scbptr = ahc_inb(ahc, SCBPTR);
+	/* Ensure that the hscbs down on the card match the new information */
+	for (i = 0; i < ahc->scb_data->maxhscbs; i++) {
+		struct	hardware_scb *pending_hscb;
+		u_int	control;
+		u_int	scb_tag;
+
+		ahc_outb(ahc, SCBPTR, i);
+		scb_tag = ahc_inb(ahc, SCB_TAG);
+		pending_scb = ahc_lookup_scb(ahc, scb_tag);
+		if (pending_scb == NULL)
+			continue;
+
+		pending_hscb = pending_scb->hscb;
+		control = ahc_inb(ahc, SCB_CONTROL);
+		control &= ~(ULTRAENB|MK_MESSAGE);
+		control |= pending_hscb->control & (ULTRAENB|MK_MESSAGE);
+		ahc_outb(ahc, SCB_CONTROL, control);
+		ahc_outb(ahc, SCB_SCSIRATE, pending_hscb->scsirate);
+		ahc_outb(ahc, SCB_SCSIOFFSET, pending_hscb->scsioffset);
+	}
+	ahc_outb(ahc, SCBPTR, saved_scbptr);
+
+	if (paused == 0)
+		ahc_unpause(ahc);
+}
+
+/**************************** Pathing Information *****************************/
+static void
+ahc_fetch_devinfo(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+	u_int	saved_scsiid;
+	role_t	role;
+	int	our_id;
+
+	if (ahc_inb(ahc, SSTAT0) & TARGET)
+		role = ROLE_TARGET;
+	else
+		role = ROLE_INITIATOR;
+
+	if (role == ROLE_TARGET
+	 && (ahc->features & AHC_MULTI_TID) != 0
+	 && (ahc_inb(ahc, SEQ_FLAGS) & CMDPHASE_PENDING) != 0) {
+		/* We were selected, so pull our id from TARGIDIN */
+		our_id = ahc_inb(ahc, TARGIDIN) & OID;
+	} else if ((ahc->features & AHC_ULTRA2) != 0)
+		our_id = ahc_inb(ahc, SCSIID_ULTRA2) & OID;
+	else
+		our_id = ahc_inb(ahc, SCSIID) & OID;
+
+	saved_scsiid = ahc_inb(ahc, SAVED_SCSIID);
+	ahc_compile_devinfo(devinfo,
+			    our_id,
+			    SCSIID_TARGET(ahc, saved_scsiid),
+			    ahc_inb(ahc, SAVED_LUN),
+			    SCSIID_CHANNEL(ahc, saved_scsiid),
+			    role);
+}
+
+struct ahc_phase_table_entry*
+ahc_lookup_phase_entry(int phase)
+{
+	struct ahc_phase_table_entry *entry;
+	struct ahc_phase_table_entry *last_entry;
+
+	/*
+	 * num_phases doesn't include the default entry which
+	 * will be returned if the phase doesn't match.
+	 */
+	last_entry = &ahc_phase_table[num_phases];
+	for (entry = ahc_phase_table; entry < last_entry; entry++) {
+		if (phase == entry->phase)
+			break;
+	}
+	return (entry);
+}
+
+void
+ahc_compile_devinfo(struct ahc_devinfo *devinfo, u_int our_id, u_int target,
+		    u_int lun, char channel, role_t role)
+{
+	devinfo->our_scsiid = our_id;
+	devinfo->target = target;
+	devinfo->lun = lun;
+	devinfo->target_offset = target;
+	devinfo->channel = channel;
+	devinfo->role = role;
+	if (channel == 'B')
+		devinfo->target_offset += 8;
+	devinfo->target_mask = (0x01 << devinfo->target_offset);
+}
+
+static void
+ahc_scb_devinfo(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+		struct scb *scb)
+{
+	role_t	role;
+	int	our_id;
+
+	our_id = SCSIID_OUR_ID(scb->hscb->scsiid);
+	role = ROLE_INITIATOR;
+	if ((scb->hscb->control & TARGET_SCB) != 0)
+		role = ROLE_TARGET;
+	ahc_compile_devinfo(devinfo, our_id, SCB_GET_TARGET(ahc, scb),
+			    SCB_GET_LUN(scb), SCB_GET_CHANNEL(ahc, scb), role);
+}
+
+
+/************************ Message Phase Processing ****************************/
+static void
+ahc_assert_atn(struct ahc_softc *ahc)
+{
+	u_int scsisigo;
+
+	scsisigo = ATNO;
+	if ((ahc->features & AHC_DT) == 0)
+		scsisigo |= ahc_inb(ahc, SCSISIGI);
+	ahc_outb(ahc, SCSISIGO, scsisigo);
+}
+
+/*
+ * When an initiator transaction with the MK_MESSAGE flag either reconnects
+ * or enters the initial message out phase, we are interrupted.  Fill our
+ * outgoing message buffer with the appropriate message and beging handing
+ * the message phase(s) manually.
+ */
+static void
+ahc_setup_initiator_msgout(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+			   struct scb *scb)
+{
+	/*
+	 * To facilitate adding multiple messages together,
+	 * each routine should increment the index and len
+	 * variables instead of setting them explicitly.
+	 */
+	ahc->msgout_index = 0;
+	ahc->msgout_len = 0;
+
+	if ((scb->flags & SCB_DEVICE_RESET) == 0
+	 && ahc_inb(ahc, MSG_OUT) == MSG_IDENTIFYFLAG) {
+		u_int identify_msg;
+
+		identify_msg = MSG_IDENTIFYFLAG | SCB_GET_LUN(scb);
+		if ((scb->hscb->control & DISCENB) != 0)
+			identify_msg |= MSG_IDENTIFY_DISCFLAG;
+		ahc->msgout_buf[ahc->msgout_index++] = identify_msg;
+		ahc->msgout_len++;
+
+		if ((scb->hscb->control & TAG_ENB) != 0) {
+			ahc->msgout_buf[ahc->msgout_index++] =
+			    scb->hscb->control & (TAG_ENB|SCB_TAG_TYPE);
+			ahc->msgout_buf[ahc->msgout_index++] = scb->hscb->tag;
+			ahc->msgout_len += 2;
+		}
+	}
+
+	if (scb->flags & SCB_DEVICE_RESET) {
+		ahc->msgout_buf[ahc->msgout_index++] = MSG_BUS_DEV_RESET;
+		ahc->msgout_len++;
+		ahc_print_path(ahc, scb);
+		printf("Bus Device Reset Message Sent\n");
+		/*
+		 * Clear our selection hardware in advance of
+		 * the busfree.  We may have an entry in the waiting
+		 * Q for this target, and we don't want to go about
+		 * selecting while we handle the busfree and blow it
+		 * away.
+		 */
+		ahc_outb(ahc, SCSISEQ, (ahc_inb(ahc, SCSISEQ) & ~ENSELO));
+	} else if ((scb->flags & SCB_ABORT) != 0) {
+		if ((scb->hscb->control & TAG_ENB) != 0)
+			ahc->msgout_buf[ahc->msgout_index++] = MSG_ABORT_TAG;
+		else
+			ahc->msgout_buf[ahc->msgout_index++] = MSG_ABORT;
+		ahc->msgout_len++;
+		ahc_print_path(ahc, scb);
+		printf("Abort%s Message Sent\n",
+		       (scb->hscb->control & TAG_ENB) != 0 ? " Tag" : "");
+		/*
+		 * Clear our selection hardware in advance of
+		 * the busfree.  We may have an entry in the waiting
+		 * Q for this target, and we don't want to go about
+		 * selecting while we handle the busfree and blow it
+		 * away.
+		 */
+		ahc_outb(ahc, SCSISEQ, (ahc_inb(ahc, SCSISEQ) & ~ENSELO));
+	} else if ((scb->flags & (SCB_AUTO_NEGOTIATE|SCB_NEGOTIATE)) != 0) {
+		ahc_build_transfer_msg(ahc, devinfo);
+	} else {
+		printf("ahc_intr: AWAITING_MSG for an SCB that "
+		       "does not have a waiting message\n");
+		printf("SCSIID = %x, target_mask = %x\n", scb->hscb->scsiid,
+		       devinfo->target_mask);
+		panic("SCB = %d, SCB Control = %x, MSG_OUT = %x "
+		      "SCB flags = %x", scb->hscb->tag, scb->hscb->control,
+		      ahc_inb(ahc, MSG_OUT), scb->flags);
+	}
+
+	/*
+	 * Clear the MK_MESSAGE flag from the SCB so we aren't
+	 * asked to send this message again.
+	 */
+	ahc_outb(ahc, SCB_CONTROL, ahc_inb(ahc, SCB_CONTROL) & ~MK_MESSAGE);
+	scb->hscb->control &= ~MK_MESSAGE;
+	ahc->msgout_index = 0;
+	ahc->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+}
+
+/*
+ * Build an appropriate transfer negotiation message for the
+ * currently active target.
+ */
+static void
+ahc_build_transfer_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+	/*
+	 * We need to initiate transfer negotiations.
+	 * If our current and goal settings are identical,
+	 * we want to renegotiate due to a check condition.
+	 */
+	struct	ahc_initiator_tinfo *tinfo;
+	struct	ahc_tmode_tstate *tstate;
+	struct	ahc_syncrate *rate;
+	int	dowide;
+	int	dosync;
+	int	doppr;
+	int	use_ppr;
+	u_int	period;
+	u_int	ppr_options;
+	u_int	offset;
+
+	tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
+				    devinfo->target, &tstate);
+	/*
+	 * Filter our period based on the current connection.
+	 * If we can't perform DT transfers on this segment (not in LVD
+	 * mode for instance), then our decision to issue a PPR message
+	 * may change.
+	 */
+	period = tinfo->goal.period;
+	ppr_options = tinfo->goal.ppr_options;
+	/* Target initiated PPR is not allowed in the SCSI spec */
+	if (devinfo->role == ROLE_TARGET)
+		ppr_options = 0;
+	rate = ahc_devlimited_syncrate(ahc, tinfo, &period,
+				       &ppr_options, devinfo->role);
+	dowide = tinfo->curr.width != tinfo->goal.width;
+	dosync = tinfo->curr.period != period;
+	doppr = tinfo->curr.ppr_options != ppr_options;
+
+	if (!dowide && !dosync && !doppr) {
+		dowide = tinfo->goal.width != MSG_EXT_WDTR_BUS_8_BIT;
+		dosync = tinfo->goal.period != 0;
+		doppr = tinfo->goal.ppr_options != 0;
+	}
+
+	if (!dowide && !dosync && !doppr) {
+		panic("ahc_intr: AWAITING_MSG for negotiation, "
+		      "but no negotiation needed\n");	
+	}
+
+	use_ppr = (tinfo->curr.transport_version >= 3) || doppr;
+	/* Target initiated PPR is not allowed in the SCSI spec */
+	if (devinfo->role == ROLE_TARGET)
+		use_ppr = 0;
+
+	/*
+	 * Both the PPR message and SDTR message require the
+	 * goal syncrate to be limited to what the target device
+	 * is capable of handling (based on whether an LVD->SE
+	 * expander is on the bus), so combine these two cases.
+	 * Regardless, guarantee that if we are using WDTR and SDTR
+	 * messages that WDTR comes first.
+	 */
+	if (use_ppr || (dosync && !dowide)) {
+
+		offset = tinfo->goal.offset;
+		ahc_validate_offset(ahc, tinfo, rate, &offset,
+				    use_ppr ? tinfo->goal.width
+					    : tinfo->curr.width,
+				    devinfo->role);
+		if (use_ppr) {
+			ahc_construct_ppr(ahc, devinfo, period, offset,
+					  tinfo->goal.width, ppr_options);
+		} else {
+			ahc_construct_sdtr(ahc, devinfo, period, offset);
+		}
+	} else {
+		ahc_construct_wdtr(ahc, devinfo, tinfo->goal.width);
+	}
+}
+
+/*
+ * Build a synchronous negotiation message in our message
+ * buffer based on the input parameters.
+ */
+static void
+ahc_construct_sdtr(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+		   u_int period, u_int offset)
+{
+	ahc->msgout_buf[ahc->msgout_index++] = MSG_EXTENDED;
+	ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_SDTR_LEN;
+	ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_SDTR;
+	ahc->msgout_buf[ahc->msgout_index++] = period;
+	ahc->msgout_buf[ahc->msgout_index++] = offset;
+	ahc->msgout_len += 5;
+	if (bootverbose) {
+		printf("(%s:%c:%d:%d): Sending SDTR period %x, offset %x\n",
+		       ahc_name(ahc), devinfo->channel, devinfo->target,
+		       devinfo->lun, period, offset);
+	}
+}
+
+/*
+ * Build a wide negotiation message in our message
+ * buffer based on the input parameters.
+ */
+static void
+ahc_construct_wdtr(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+		   u_int bus_width)
+{
+	ahc->msgout_buf[ahc->msgout_index++] = MSG_EXTENDED;
+	ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_WDTR_LEN;
+	ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_WDTR;
+	ahc->msgout_buf[ahc->msgout_index++] = bus_width;
+	ahc->msgout_len += 4;
+	if (bootverbose) {
+		printf("(%s:%c:%d:%d): Sending WDTR %x\n",
+		       ahc_name(ahc), devinfo->channel, devinfo->target,
+		       devinfo->lun, bus_width);
+	}
+}
+
+/*
+ * Build a parallel protocol request message in our message
+ * buffer based on the input parameters.
+ */
+static void
+ahc_construct_ppr(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+		  u_int period, u_int offset, u_int bus_width,
+		  u_int ppr_options)
+{
+	ahc->msgout_buf[ahc->msgout_index++] = MSG_EXTENDED;
+	ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_PPR_LEN;
+	ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_PPR;
+	ahc->msgout_buf[ahc->msgout_index++] = period;
+	ahc->msgout_buf[ahc->msgout_index++] = 0;
+	ahc->msgout_buf[ahc->msgout_index++] = offset;
+	ahc->msgout_buf[ahc->msgout_index++] = bus_width;
+	ahc->msgout_buf[ahc->msgout_index++] = ppr_options;
+	ahc->msgout_len += 8;
+	if (bootverbose) {
+		printf("(%s:%c:%d:%d): Sending PPR bus_width %x, period %x, "
+		       "offset %x, ppr_options %x\n", ahc_name(ahc),
+		       devinfo->channel, devinfo->target, devinfo->lun,
+		       bus_width, period, offset, ppr_options);
+	}
+}
+
+/*
+ * Clear any active message state.
+ */
+static void
+ahc_clear_msg_state(struct ahc_softc *ahc)
+{
+	ahc->msgout_len = 0;
+	ahc->msgin_index = 0;
+	ahc->msg_type = MSG_TYPE_NONE;
+	if ((ahc_inb(ahc, SCSISIGI) & ATNI) != 0) {
+		/*
+		 * The target didn't care to respond to our
+		 * message request, so clear ATN.
+		 */
+		ahc_outb(ahc, CLRSINT1, CLRATNO);
+	}
+	ahc_outb(ahc, MSG_OUT, MSG_NOOP);
+	ahc_outb(ahc, SEQ_FLAGS2,
+		 ahc_inb(ahc, SEQ_FLAGS2) & ~TARGET_MSG_PENDING);
+}
+
+/*
+ * Manual message loop handler.
+ */
+static void
+ahc_handle_message_phase(struct ahc_softc *ahc)
+{ 
+	struct	ahc_devinfo devinfo;
+	u_int	bus_phase;
+	int	end_session;
+
+	ahc_fetch_devinfo(ahc, &devinfo);
+	end_session = FALSE;
+	bus_phase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
+
+reswitch:
+	switch (ahc->msg_type) {
+	case MSG_TYPE_INITIATOR_MSGOUT:
+	{
+		int lastbyte;
+		int phasemis;
+		int msgdone;
+
+		if (ahc->msgout_len == 0)
+			panic("HOST_MSG_LOOP interrupt with no active message");
+
+		phasemis = bus_phase != P_MESGOUT;
+		if (phasemis) {
+			if (bus_phase == P_MESGIN) {
+				/*
+				 * Change gears and see if
+				 * this messages is of interest to
+				 * us or should be passed back to
+				 * the sequencer.
+				 */
+				ahc_outb(ahc, CLRSINT1, CLRATNO);
+				ahc->send_msg_perror = FALSE;
+				ahc->msg_type = MSG_TYPE_INITIATOR_MSGIN;
+				ahc->msgin_index = 0;
+				goto reswitch;
+			}
+			end_session = TRUE;
+			break;
+		}
+
+		if (ahc->send_msg_perror) {
+			ahc_outb(ahc, CLRSINT1, CLRATNO);
+			ahc_outb(ahc, CLRSINT1, CLRREQINIT);
+			ahc_outb(ahc, SCSIDATL, MSG_PARITY_ERROR);
+			break;
+		}
+
+		msgdone	= ahc->msgout_index == ahc->msgout_len;
+		if (msgdone) {
+			/*
+			 * The target has requested a retry.
+			 * Re-assert ATN, reset our message index to
+			 * 0, and try again.
+			 */
+			ahc->msgout_index = 0;
+			ahc_assert_atn(ahc);
+		}
+
+		lastbyte = ahc->msgout_index == (ahc->msgout_len - 1);
+		if (lastbyte) {
+			/* Last byte is signified by dropping ATN */
+			ahc_outb(ahc, CLRSINT1, CLRATNO);
+		}
+
+		/*
+		 * Clear our interrupt status and present
+		 * the next byte on the bus.
+		 */
+		ahc_outb(ahc, CLRSINT1, CLRREQINIT);
+		ahc_outb(ahc, SCSIDATL, ahc->msgout_buf[ahc->msgout_index++]);
+		break;
+	}
+	case MSG_TYPE_INITIATOR_MSGIN:
+	{
+		int phasemis;
+		int message_done;
+
+		phasemis = bus_phase != P_MESGIN;
+
+		if (phasemis) {
+			ahc->msgin_index = 0;
+			if (bus_phase == P_MESGOUT
+			 && (ahc->send_msg_perror == TRUE
+			  || (ahc->msgout_len != 0
+			   && ahc->msgout_index == 0))) {
+				ahc->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+				goto reswitch;
+			}
+			end_session = TRUE;
+			break;
+		}
+
+		/* Pull the byte in without acking it */
+		ahc->msgin_buf[ahc->msgin_index] = ahc_inb(ahc, SCSIBUSL);
+
+		message_done = ahc_parse_msg(ahc, &devinfo);
+
+		if (message_done) {
+			/*
+			 * Clear our incoming message buffer in case there
+			 * is another message following this one.
+			 */
+			ahc->msgin_index = 0;
+
+			/*
+			 * If this message illicited a response,
+			 * assert ATN so the target takes us to the
+			 * message out phase.
+			 */
+			if (ahc->msgout_len != 0)
+				ahc_assert_atn(ahc);
+		} else 
+			ahc->msgin_index++;
+
+		/* Ack the byte */
+		ahc_outb(ahc, CLRSINT1, CLRREQINIT);
+		ahc_inb(ahc, SCSIDATL);
+		break;
+	}
+	case MSG_TYPE_TARGET_MSGIN:
+	{
+		int msgdone;
+		int msgout_request;
+
+		if (ahc->msgout_len == 0)
+			panic("Target MSGIN with no active message");
+
+		/*
+		 * If we interrupted a mesgout session, the initiator
+		 * will not know this until our first REQ.  So, we
+		 * only honor mesgout requests after we've sent our
+		 * first byte.
+		 */
+		if ((ahc_inb(ahc, SCSISIGI) & ATNI) != 0
+		 && ahc->msgout_index > 0)
+			msgout_request = TRUE;
+		else
+			msgout_request = FALSE;
+
+		if (msgout_request) {
+
+			/*
+			 * Change gears and see if
+			 * this messages is of interest to
+			 * us or should be passed back to
+			 * the sequencer.
+			 */
+			ahc->msg_type = MSG_TYPE_TARGET_MSGOUT;
+			ahc_outb(ahc, SCSISIGO, P_MESGOUT | BSYO);
+			ahc->msgin_index = 0;
+			/* Dummy read to REQ for first byte */
+			ahc_inb(ahc, SCSIDATL);
+			ahc_outb(ahc, SXFRCTL0,
+				 ahc_inb(ahc, SXFRCTL0) | SPIOEN);
+			break;
+		}
+
+		msgdone = ahc->msgout_index == ahc->msgout_len;
+		if (msgdone) {
+			ahc_outb(ahc, SXFRCTL0,
+				 ahc_inb(ahc, SXFRCTL0) & ~SPIOEN);
+			end_session = TRUE;
+			break;
+		}
+
+		/*
+		 * Present the next byte on the bus.
+		 */
+		ahc_outb(ahc, SXFRCTL0, ahc_inb(ahc, SXFRCTL0) | SPIOEN);
+		ahc_outb(ahc, SCSIDATL, ahc->msgout_buf[ahc->msgout_index++]);
+		break;
+	}
+	case MSG_TYPE_TARGET_MSGOUT:
+	{
+		int lastbyte;
+		int msgdone;
+
+		/*
+		 * The initiator signals that this is
+		 * the last byte by dropping ATN.
+		 */
+		lastbyte = (ahc_inb(ahc, SCSISIGI) & ATNI) == 0;
+
+		/*
+		 * Read the latched byte, but turn off SPIOEN first
+		 * so that we don't inadvertently cause a REQ for the
+		 * next byte.
+		 */
+		ahc_outb(ahc, SXFRCTL0, ahc_inb(ahc, SXFRCTL0) & ~SPIOEN);
+		ahc->msgin_buf[ahc->msgin_index] = ahc_inb(ahc, SCSIDATL);
+		msgdone = ahc_parse_msg(ahc, &devinfo);
+		if (msgdone == MSGLOOP_TERMINATED) {
+			/*
+			 * The message is *really* done in that it caused
+			 * us to go to bus free.  The sequencer has already
+			 * been reset at this point, so pull the ejection
+			 * handle.
+			 */
+			return;
+		}
+		
+		ahc->msgin_index++;
+
+		/*
+		 * XXX Read spec about initiator dropping ATN too soon
+		 *     and use msgdone to detect it.
+		 */
+		if (msgdone == MSGLOOP_MSGCOMPLETE) {
+			ahc->msgin_index = 0;
+
+			/*
+			 * If this message illicited a response, transition
+			 * to the Message in phase and send it.
+			 */
+			if (ahc->msgout_len != 0) {
+				ahc_outb(ahc, SCSISIGO, P_MESGIN | BSYO);
+				ahc_outb(ahc, SXFRCTL0,
+					 ahc_inb(ahc, SXFRCTL0) | SPIOEN);
+				ahc->msg_type = MSG_TYPE_TARGET_MSGIN;
+				ahc->msgin_index = 0;
+				break;
+			}
+		}
+
+		if (lastbyte)
+			end_session = TRUE;
+		else {
+			/* Ask for the next byte. */
+			ahc_outb(ahc, SXFRCTL0,
+				 ahc_inb(ahc, SXFRCTL0) | SPIOEN);
+		}
+
+		break;
+	}
+	default:
+		panic("Unknown REQINIT message type");
+	}
+
+	if (end_session) {
+		ahc_clear_msg_state(ahc);
+		ahc_outb(ahc, RETURN_1, EXIT_MSG_LOOP);
+	} else
+		ahc_outb(ahc, RETURN_1, CONT_MSG_LOOP);
+}
+
+/*
+ * See if we sent a particular extended message to the target.
+ * If "full" is true, return true only if the target saw the full
+ * message.  If "full" is false, return true if the target saw at
+ * least the first byte of the message.
+ */
+static int
+ahc_sent_msg(struct ahc_softc *ahc, ahc_msgtype type, u_int msgval, int full)
+{
+	int found;
+	u_int index;
+
+	found = FALSE;
+	index = 0;
+
+	while (index < ahc->msgout_len) {
+		if (ahc->msgout_buf[index] == MSG_EXTENDED) {
+			u_int end_index;
+
+			end_index = index + 1 + ahc->msgout_buf[index + 1];
+			if (ahc->msgout_buf[index+2] == msgval
+			 && type == AHCMSG_EXT) {
+
+				if (full) {
+					if (ahc->msgout_index > end_index)
+						found = TRUE;
+				} else if (ahc->msgout_index > index)
+					found = TRUE;
+			}
+			index = end_index;
+		} else if (ahc->msgout_buf[index] >= MSG_SIMPLE_TASK
+			&& ahc->msgout_buf[index] <= MSG_IGN_WIDE_RESIDUE) {
+
+			/* Skip tag type and tag id or residue param*/
+			index += 2;
+		} else {
+			/* Single byte message */
+			if (type == AHCMSG_1B
+			 && ahc->msgout_buf[index] == msgval
+			 && ahc->msgout_index > index)
+				found = TRUE;
+			index++;
+		}
+
+		if (found)
+			break;
+	}
+	return (found);
+}
+
+/*
+ * Wait for a complete incoming message, parse it, and respond accordingly.
+ */
+static int
+ahc_parse_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+	struct	ahc_initiator_tinfo *tinfo;
+	struct	ahc_tmode_tstate *tstate;
+	int	reject;
+	int	done;
+	int	response;
+	u_int	targ_scsirate;
+
+	done = MSGLOOP_IN_PROG;
+	response = FALSE;
+	reject = FALSE;
+	tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
+				    devinfo->target, &tstate);
+	targ_scsirate = tinfo->scsirate;
+
+	/*
+	 * Parse as much of the message as is availible,
+	 * rejecting it if we don't support it.  When
+	 * the entire message is availible and has been
+	 * handled, return MSGLOOP_MSGCOMPLETE, indicating
+	 * that we have parsed an entire message.
+	 *
+	 * In the case of extended messages, we accept the length
+	 * byte outright and perform more checking once we know the
+	 * extended message type.
+	 */
+	switch (ahc->msgin_buf[0]) {
+	case MSG_MESSAGE_REJECT:
+		response = ahc_handle_msg_reject(ahc, devinfo);
+		/* FALLTHROUGH */
+	case MSG_NOOP:
+		done = MSGLOOP_MSGCOMPLETE;
+		break;
+	case MSG_EXTENDED:
+	{
+		/* Wait for enough of the message to begin validation */
+		if (ahc->msgin_index < 2)
+			break;
+		switch (ahc->msgin_buf[2]) {
+		case MSG_EXT_SDTR:
+		{
+			struct	 ahc_syncrate *syncrate;
+			u_int	 period;
+			u_int	 ppr_options;
+			u_int	 offset;
+			u_int	 saved_offset;
+			
+			if (ahc->msgin_buf[1] != MSG_EXT_SDTR_LEN) {
+				reject = TRUE;
+				break;
+			}
+
+			/*
+			 * Wait until we have both args before validating
+			 * and acting on this message.
+			 *
+			 * Add one to MSG_EXT_SDTR_LEN to account for
+			 * the extended message preamble.
+			 */
+			if (ahc->msgin_index < (MSG_EXT_SDTR_LEN + 1))
+				break;
+
+			period = ahc->msgin_buf[3];
+			ppr_options = 0;
+			saved_offset = offset = ahc->msgin_buf[4];
+			syncrate = ahc_devlimited_syncrate(ahc, tinfo, &period,
+							   &ppr_options,
+							   devinfo->role);
+			ahc_validate_offset(ahc, tinfo, syncrate, &offset,
+					    targ_scsirate & WIDEXFER,
+					    devinfo->role);
+			if (bootverbose) {
+				printf("(%s:%c:%d:%d): Received "
+				       "SDTR period %x, offset %x\n\t"
+				       "Filtered to period %x, offset %x\n",
+				       ahc_name(ahc), devinfo->channel,
+				       devinfo->target, devinfo->lun,
+				       ahc->msgin_buf[3], saved_offset,
+				       period, offset);
+			}
+			ahc_set_syncrate(ahc, devinfo, 
+					 syncrate, period,
+					 offset, ppr_options,
+					 AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+					 /*paused*/TRUE);
+
+			/*
+			 * See if we initiated Sync Negotiation
+			 * and didn't have to fall down to async
+			 * transfers.
+			 */
+			if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_SDTR, TRUE)) {
+				/* We started it */
+				if (saved_offset != offset) {
+					/* Went too low - force async */
+					reject = TRUE;
+				}
+			} else {
+				/*
+				 * Send our own SDTR in reply
+				 */
+				if (bootverbose
+				 && devinfo->role == ROLE_INITIATOR) {
+					printf("(%s:%c:%d:%d): Target "
+					       "Initiated SDTR\n",
+					       ahc_name(ahc), devinfo->channel,
+					       devinfo->target, devinfo->lun);
+				}
+				ahc->msgout_index = 0;
+				ahc->msgout_len = 0;
+				ahc_construct_sdtr(ahc, devinfo,
+						   period, offset);
+				ahc->msgout_index = 0;
+				response = TRUE;
+			}
+			done = MSGLOOP_MSGCOMPLETE;
+			break;
+		}
+		case MSG_EXT_WDTR:
+		{
+			u_int bus_width;
+			u_int saved_width;
+			u_int sending_reply;
+
+			sending_reply = FALSE;
+			if (ahc->msgin_buf[1] != MSG_EXT_WDTR_LEN) {
+				reject = TRUE;
+				break;
+			}
+
+			/*
+			 * Wait until we have our arg before validating
+			 * and acting on this message.
+			 *
+			 * Add one to MSG_EXT_WDTR_LEN to account for
+			 * the extended message preamble.
+			 */
+			if (ahc->msgin_index < (MSG_EXT_WDTR_LEN + 1))
+				break;
+
+			bus_width = ahc->msgin_buf[3];
+			saved_width = bus_width;
+			ahc_validate_width(ahc, tinfo, &bus_width,
+					   devinfo->role);
+			if (bootverbose) {
+				printf("(%s:%c:%d:%d): Received WDTR "
+				       "%x filtered to %x\n",
+				       ahc_name(ahc), devinfo->channel,
+				       devinfo->target, devinfo->lun,
+				       saved_width, bus_width);
+			}
+
+			if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_WDTR, TRUE)) {
+				/*
+				 * Don't send a WDTR back to the
+				 * target, since we asked first.
+				 * If the width went higher than our
+				 * request, reject it.
+				 */
+				if (saved_width > bus_width) {
+					reject = TRUE;
+					printf("(%s:%c:%d:%d): requested %dBit "
+					       "transfers.  Rejecting...\n",
+					       ahc_name(ahc), devinfo->channel,
+					       devinfo->target, devinfo->lun,
+					       8 * (0x01 << bus_width));
+					bus_width = 0;
+				}
+			} else {
+				/*
+				 * Send our own WDTR in reply
+				 */
+				if (bootverbose
+				 && devinfo->role == ROLE_INITIATOR) {
+					printf("(%s:%c:%d:%d): Target "
+					       "Initiated WDTR\n",
+					       ahc_name(ahc), devinfo->channel,
+					       devinfo->target, devinfo->lun);
+				}
+				ahc->msgout_index = 0;
+				ahc->msgout_len = 0;
+				ahc_construct_wdtr(ahc, devinfo, bus_width);
+				ahc->msgout_index = 0;
+				response = TRUE;
+				sending_reply = TRUE;
+			}
+			ahc_set_width(ahc, devinfo, bus_width,
+				      AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+				      /*paused*/TRUE);
+			/* After a wide message, we are async */
+			ahc_set_syncrate(ahc, devinfo,
+					 /*syncrate*/NULL, /*period*/0,
+					 /*offset*/0, /*ppr_options*/0,
+					 AHC_TRANS_ACTIVE, /*paused*/TRUE);
+			if (sending_reply == FALSE && reject == FALSE) {
+
+				if (tinfo->goal.period) {
+					ahc->msgout_index = 0;
+					ahc->msgout_len = 0;
+					ahc_build_transfer_msg(ahc, devinfo);
+					ahc->msgout_index = 0;
+					response = TRUE;
+				}
+			}
+			done = MSGLOOP_MSGCOMPLETE;
+			break;
+		}
+		case MSG_EXT_PPR:
+		{
+			struct	ahc_syncrate *syncrate;
+			u_int	period;
+			u_int	offset;
+			u_int	bus_width;
+			u_int	ppr_options;
+			u_int	saved_width;
+			u_int	saved_offset;
+			u_int	saved_ppr_options;
+
+			if (ahc->msgin_buf[1] != MSG_EXT_PPR_LEN) {
+				reject = TRUE;
+				break;
+			}
+
+			/*
+			 * Wait until we have all args before validating
+			 * and acting on this message.
+			 *
+			 * Add one to MSG_EXT_PPR_LEN to account for
+			 * the extended message preamble.
+			 */
+			if (ahc->msgin_index < (MSG_EXT_PPR_LEN + 1))
+				break;
+
+			period = ahc->msgin_buf[3];
+			offset = ahc->msgin_buf[5];
+			bus_width = ahc->msgin_buf[6];
+			saved_width = bus_width;
+			ppr_options = ahc->msgin_buf[7];
+			/*
+			 * According to the spec, a DT only
+			 * period factor with no DT option
+			 * set implies async.
+			 */
+			if ((ppr_options & MSG_EXT_PPR_DT_REQ) == 0
+			 && period == 9)
+				offset = 0;
+			saved_ppr_options = ppr_options;
+			saved_offset = offset;
+
+			/*
+			 * Mask out any options we don't support
+			 * on any controller.  Transfer options are
+			 * only available if we are negotiating wide.
+			 */
+			ppr_options &= MSG_EXT_PPR_DT_REQ;
+			if (bus_width == 0)
+				ppr_options = 0;
+
+			ahc_validate_width(ahc, tinfo, &bus_width,
+					   devinfo->role);
+			syncrate = ahc_devlimited_syncrate(ahc, tinfo, &period,
+							   &ppr_options,
+							   devinfo->role);
+			ahc_validate_offset(ahc, tinfo, syncrate,
+					    &offset, bus_width,
+					    devinfo->role);
+
+			if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_PPR, TRUE)) {
+				/*
+				 * If we are unable to do any of the
+				 * requested options (we went too low),
+				 * then we'll have to reject the message.
+				 */
+				if (saved_width > bus_width
+				 || saved_offset != offset
+				 || saved_ppr_options != ppr_options) {
+					reject = TRUE;
+					period = 0;
+					offset = 0;
+					bus_width = 0;
+					ppr_options = 0;
+					syncrate = NULL;
+				}
+			} else {
+				if (devinfo->role != ROLE_TARGET)
+					printf("(%s:%c:%d:%d): Target "
+					       "Initiated PPR\n",
+					       ahc_name(ahc), devinfo->channel,
+					       devinfo->target, devinfo->lun);
+				else
+					printf("(%s:%c:%d:%d): Initiator "
+					       "Initiated PPR\n",
+					       ahc_name(ahc), devinfo->channel,
+					       devinfo->target, devinfo->lun);
+				ahc->msgout_index = 0;
+				ahc->msgout_len = 0;
+				ahc_construct_ppr(ahc, devinfo, period, offset,
+						  bus_width, ppr_options);
+				ahc->msgout_index = 0;
+				response = TRUE;
+			}
+			if (bootverbose) {
+				printf("(%s:%c:%d:%d): Received PPR width %x, "
+				       "period %x, offset %x,options %x\n"
+				       "\tFiltered to width %x, period %x, "
+				       "offset %x, options %x\n",
+				       ahc_name(ahc), devinfo->channel,
+				       devinfo->target, devinfo->lun,
+				       saved_width, ahc->msgin_buf[3],
+				       saved_offset, saved_ppr_options,
+				       bus_width, period, offset, ppr_options);
+			}
+			ahc_set_width(ahc, devinfo, bus_width,
+				      AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+				      /*paused*/TRUE);
+			ahc_set_syncrate(ahc, devinfo,
+					 syncrate, period,
+					 offset, ppr_options,
+					 AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+					 /*paused*/TRUE);
+			done = MSGLOOP_MSGCOMPLETE;
+			break;
+		}
+		default:
+			/* Unknown extended message.  Reject it. */
+			reject = TRUE;
+			break;
+		}
+		break;
+	}
+#ifdef AHC_TARGET_MODE
+	case MSG_BUS_DEV_RESET:
+		ahc_handle_devreset(ahc, devinfo,
+				    CAM_BDR_SENT,
+				    "Bus Device Reset Received",
+				    /*verbose_level*/0);
+		ahc_restart(ahc);
+		done = MSGLOOP_TERMINATED;
+		break;
+	case MSG_ABORT_TAG:
+	case MSG_ABORT:
+	case MSG_CLEAR_QUEUE:
+	{
+		int tag;
+
+		/* Target mode messages */
+		if (devinfo->role != ROLE_TARGET) {
+			reject = TRUE;
+			break;
+		}
+		tag = SCB_LIST_NULL;
+		if (ahc->msgin_buf[0] == MSG_ABORT_TAG)
+			tag = ahc_inb(ahc, INITIATOR_TAG);
+		ahc_abort_scbs(ahc, devinfo->target, devinfo->channel,
+			       devinfo->lun, tag, ROLE_TARGET,
+			       CAM_REQ_ABORTED);
+
+		tstate = ahc->enabled_targets[devinfo->our_scsiid];
+		if (tstate != NULL) {
+			struct ahc_tmode_lstate* lstate;
+
+			lstate = tstate->enabled_luns[devinfo->lun];
+			if (lstate != NULL) {
+				ahc_queue_lstate_event(ahc, lstate,
+						       devinfo->our_scsiid,
+						       ahc->msgin_buf[0],
+						       /*arg*/tag);
+				ahc_send_lstate_events(ahc, lstate);
+			}
+		}
+		ahc_restart(ahc);
+		done = MSGLOOP_TERMINATED;
+		break;
+	}
+#endif
+	case MSG_TERM_IO_PROC:
+	default:
+		reject = TRUE;
+		break;
+	}
+
+	if (reject) {
+		/*
+		 * Setup to reject the message.
+		 */
+		ahc->msgout_index = 0;
+		ahc->msgout_len = 1;
+		ahc->msgout_buf[0] = MSG_MESSAGE_REJECT;
+		done = MSGLOOP_MSGCOMPLETE;
+		response = TRUE;
+	}
+
+	if (done != MSGLOOP_IN_PROG && !response)
+		/* Clear the outgoing message buffer */
+		ahc->msgout_len = 0;
+
+	return (done);
+}
+
+/*
+ * Process a message reject message.
+ */
+static int
+ahc_handle_msg_reject(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+	/*
+	 * What we care about here is if we had an
+	 * outstanding SDTR or WDTR message for this
+	 * target.  If we did, this is a signal that
+	 * the target is refusing negotiation.
+	 */
+	struct scb *scb;
+	struct ahc_initiator_tinfo *tinfo;
+	struct ahc_tmode_tstate *tstate;
+	u_int scb_index;
+	u_int last_msg;
+	int   response = 0;
+
+	scb_index = ahc_inb(ahc, SCB_TAG);
+	scb = ahc_lookup_scb(ahc, scb_index);
+	tinfo = ahc_fetch_transinfo(ahc, devinfo->channel,
+				    devinfo->our_scsiid,
+				    devinfo->target, &tstate);
+	/* Might be necessary */
+	last_msg = ahc_inb(ahc, LAST_MSG);
+
+	if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_PPR, /*full*/FALSE)) {
+		/*
+		 * Target does not support the PPR message.
+		 * Attempt to negotiate SPI-2 style.
+		 */
+		if (bootverbose) {
+			printf("(%s:%c:%d:%d): PPR Rejected. "
+			       "Trying WDTR/SDTR\n",
+			       ahc_name(ahc), devinfo->channel,
+			       devinfo->target, devinfo->lun);
+		}
+		tinfo->goal.ppr_options = 0;
+		tinfo->curr.transport_version = 2;
+		tinfo->goal.transport_version = 2;
+		ahc->msgout_index = 0;
+		ahc->msgout_len = 0;
+		ahc_build_transfer_msg(ahc, devinfo);
+		ahc->msgout_index = 0;
+		response = 1;
+	} else if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_WDTR, /*full*/FALSE)) {
+
+		/* note 8bit xfers */
+		printf("(%s:%c:%d:%d): refuses WIDE negotiation.  Using "
+		       "8bit transfers\n", ahc_name(ahc),
+		       devinfo->channel, devinfo->target, devinfo->lun);
+		ahc_set_width(ahc, devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+			      AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+			      /*paused*/TRUE);
+		/*
+		 * No need to clear the sync rate.  If the target
+		 * did not accept the command, our syncrate is
+		 * unaffected.  If the target started the negotiation,
+		 * but rejected our response, we already cleared the
+		 * sync rate before sending our WDTR.
+		 */
+		if (tinfo->goal.period) {
+
+			/* Start the sync negotiation */
+			ahc->msgout_index = 0;
+			ahc->msgout_len = 0;
+			ahc_build_transfer_msg(ahc, devinfo);
+			ahc->msgout_index = 0;
+			response = 1;
+		}
+	} else if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_SDTR, /*full*/FALSE)) {
+		/* note asynch xfers and clear flag */
+		ahc_set_syncrate(ahc, devinfo, /*syncrate*/NULL, /*period*/0,
+				 /*offset*/0, /*ppr_options*/0,
+				 AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+				 /*paused*/TRUE);
+		printf("(%s:%c:%d:%d): refuses synchronous negotiation. "
+		       "Using asynchronous transfers\n",
+		       ahc_name(ahc), devinfo->channel,
+		       devinfo->target, devinfo->lun);
+	} else if ((scb->hscb->control & MSG_SIMPLE_TASK) != 0) {
+		int tag_type;
+		int mask;
+
+		tag_type = (scb->hscb->control & MSG_SIMPLE_TASK);
+
+		if (tag_type == MSG_SIMPLE_TASK) {
+			printf("(%s:%c:%d:%d): refuses tagged commands.  "
+			       "Performing non-tagged I/O\n", ahc_name(ahc),
+			       devinfo->channel, devinfo->target, devinfo->lun);
+			ahc_set_tags(ahc, devinfo, AHC_QUEUE_NONE);
+			mask = ~0x23;
+		} else {
+			printf("(%s:%c:%d:%d): refuses %s tagged commands.  "
+			       "Performing simple queue tagged I/O only\n",
+			       ahc_name(ahc), devinfo->channel, devinfo->target,
+			       devinfo->lun, tag_type == MSG_ORDERED_TASK
+			       ? "ordered" : "head of queue");
+			ahc_set_tags(ahc, devinfo, AHC_QUEUE_BASIC);
+			mask = ~0x03;
+		}
+
+		/*
+		 * Resend the identify for this CCB as the target
+		 * may believe that the selection is invalid otherwise.
+		 */
+		ahc_outb(ahc, SCB_CONTROL,
+			 ahc_inb(ahc, SCB_CONTROL) & mask);
+	 	scb->hscb->control &= mask;
+		ahc_set_transaction_tag(scb, /*enabled*/FALSE,
+					/*type*/MSG_SIMPLE_TASK);
+		ahc_outb(ahc, MSG_OUT, MSG_IDENTIFYFLAG);
+		ahc_assert_atn(ahc);
+
+		/*
+		 * This transaction is now at the head of
+		 * the untagged queue for this target.
+		 */
+		if ((ahc->flags & AHC_SCB_BTT) == 0) {
+			struct scb_tailq *untagged_q;
+
+			untagged_q =
+			    &(ahc->untagged_queues[devinfo->target_offset]);
+			TAILQ_INSERT_HEAD(untagged_q, scb, links.tqe);
+			scb->flags |= SCB_UNTAGGEDQ;
+		}
+		ahc_busy_tcl(ahc, BUILD_TCL(scb->hscb->scsiid, devinfo->lun),
+			     scb->hscb->tag);
+
+		/*
+		 * Requeue all tagged commands for this target
+		 * currently in our posession so they can be
+		 * converted to untagged commands.
+		 */
+		ahc_search_qinfifo(ahc, SCB_GET_TARGET(ahc, scb),
+				   SCB_GET_CHANNEL(ahc, scb),
+				   SCB_GET_LUN(scb), /*tag*/SCB_LIST_NULL,
+				   ROLE_INITIATOR, CAM_REQUEUE_REQ,
+				   SEARCH_COMPLETE);
+	} else {
+		/*
+		 * Otherwise, we ignore it.
+		 */
+		printf("%s:%c:%d: Message reject for %x -- ignored\n",
+		       ahc_name(ahc), devinfo->channel, devinfo->target,
+		       last_msg);
+	}
+	return (response);
+}
+
+/*
+ * Process an ingnore wide residue message.
+ */
+static void
+ahc_handle_ign_wide_residue(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+	u_int scb_index;
+	struct scb *scb;
+
+	scb_index = ahc_inb(ahc, SCB_TAG);
+	scb = ahc_lookup_scb(ahc, scb_index);
+	/*
+	 * XXX Actually check data direction in the sequencer?
+	 * Perhaps add datadir to some spare bits in the hscb?
+	 */
+	if ((ahc_inb(ahc, SEQ_FLAGS) & DPHASE) == 0
+	 || ahc_get_transfer_dir(scb) != CAM_DIR_IN) {
+		/*
+		 * Ignore the message if we haven't
+		 * seen an appropriate data phase yet.
+		 */
+	} else {
+		/*
+		 * If the residual occurred on the last
+		 * transfer and the transfer request was
+		 * expected to end on an odd count, do
+		 * nothing.  Otherwise, subtract a byte
+		 * and update the residual count accordingly.
+		 */
+		uint32_t sgptr;
+
+		sgptr = ahc_inb(ahc, SCB_RESIDUAL_SGPTR);
+		if ((sgptr & SG_LIST_NULL) != 0
+		 && ahc_inb(ahc, DATA_COUNT_ODD) == 1) {
+			/*
+			 * If the residual occurred on the last
+			 * transfer and the transfer request was
+			 * expected to end on an odd count, do
+			 * nothing.
+			 */
+		} else {
+			struct ahc_dma_seg *sg;
+			uint32_t data_cnt;
+			uint32_t data_addr;
+			uint32_t sglen;
+
+			/* Pull in the rest of the sgptr */
+			sgptr |= (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 3) << 24)
+			      | (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 2) << 16)
+			      | (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 1) << 8);
+			sgptr &= SG_PTR_MASK;
+			data_cnt = (ahc_inb(ahc, SCB_RESIDUAL_DATACNT+3) << 24)
+				 | (ahc_inb(ahc, SCB_RESIDUAL_DATACNT+2) << 16)
+				 | (ahc_inb(ahc, SCB_RESIDUAL_DATACNT+1) << 8)
+				 | (ahc_inb(ahc, SCB_RESIDUAL_DATACNT));
+
+			data_addr = (ahc_inb(ahc, SHADDR + 3) << 24)
+				  | (ahc_inb(ahc, SHADDR + 2) << 16)
+				  | (ahc_inb(ahc, SHADDR + 1) << 8)
+				  | (ahc_inb(ahc, SHADDR));
+
+			data_cnt += 1;
+			data_addr -= 1;
+
+			sg = ahc_sg_bus_to_virt(scb, sgptr);
+			/*
+			 * The residual sg ptr points to the next S/G
+			 * to load so we must go back one.
+			 */
+			sg--;
+			sglen = ahc_le32toh(sg->len) & AHC_SG_LEN_MASK;
+			if (sg != scb->sg_list
+			 && sglen < (data_cnt & AHC_SG_LEN_MASK)) {
+
+				sg--;
+				sglen = ahc_le32toh(sg->len);
+				/*
+				 * Preserve High Address and SG_LIST bits
+				 * while setting the count to 1.
+				 */
+				data_cnt = 1 | (sglen & (~AHC_SG_LEN_MASK));
+				data_addr = ahc_le32toh(sg->addr)
+					  + (sglen & AHC_SG_LEN_MASK) - 1;
+
+				/*
+				 * Increment sg so it points to the
+				 * "next" sg.
+				 */
+				sg++;
+				sgptr = ahc_sg_virt_to_bus(scb, sg);
+				ahc_outb(ahc, SCB_RESIDUAL_SGPTR + 3,
+					 sgptr >> 24);
+				ahc_outb(ahc, SCB_RESIDUAL_SGPTR + 2,
+					 sgptr >> 16);
+				ahc_outb(ahc, SCB_RESIDUAL_SGPTR + 1,
+					 sgptr >> 8);
+				ahc_outb(ahc, SCB_RESIDUAL_SGPTR, sgptr);
+			}
+
+			ahc_outb(ahc, SCB_RESIDUAL_DATACNT + 3, data_cnt >> 24);
+			ahc_outb(ahc, SCB_RESIDUAL_DATACNT + 2, data_cnt >> 16);
+			ahc_outb(ahc, SCB_RESIDUAL_DATACNT + 1, data_cnt >> 8);
+			ahc_outb(ahc, SCB_RESIDUAL_DATACNT, data_cnt);
+		}
+	}
+}
+
+
+/*
+ * Reinitialize the data pointers for the active transfer
+ * based on its current residual.
+ */
+static void
+ahc_reinitialize_dataptrs(struct ahc_softc *ahc)
+{
+	struct	 scb *scb;
+	struct	 ahc_dma_seg *sg;
+	u_int	 scb_index;
+	uint32_t sgptr;
+	uint32_t resid;
+	uint32_t dataptr;
+
+	scb_index = ahc_inb(ahc, SCB_TAG);
+	scb = ahc_lookup_scb(ahc, scb_index);
+	sgptr = (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 3) << 24)
+	      | (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 2) << 16)
+	      | (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 1) << 8)
+	      |	ahc_inb(ahc, SCB_RESIDUAL_SGPTR);
+
+	sgptr &= SG_PTR_MASK;
+	sg = ahc_sg_bus_to_virt(scb, sgptr);
+
+	/* The residual sg_ptr always points to the next sg */
+	sg--;
+
+	resid = (ahc_inb(ahc, SCB_RESIDUAL_DATACNT + 2) << 16)
+	      | (ahc_inb(ahc, SCB_RESIDUAL_DATACNT + 1) << 8)
+	      | ahc_inb(ahc, SCB_RESIDUAL_DATACNT);
+
+	dataptr = ahc_le32toh(sg->addr)
+		+ (ahc_le32toh(sg->len) & AHC_SG_LEN_MASK)
+		- resid;
+	if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+		u_int dscommand1;
+
+		dscommand1 = ahc_inb(ahc, DSCOMMAND1);
+		ahc_outb(ahc, DSCOMMAND1, dscommand1 | HADDLDSEL0);
+		ahc_outb(ahc, HADDR,
+			 (ahc_le32toh(sg->len) >> 24) & SG_HIGH_ADDR_BITS);
+		ahc_outb(ahc, DSCOMMAND1, dscommand1);
+	}
+	ahc_outb(ahc, HADDR + 3, dataptr >> 24);
+	ahc_outb(ahc, HADDR + 2, dataptr >> 16);
+	ahc_outb(ahc, HADDR + 1, dataptr >> 8);
+	ahc_outb(ahc, HADDR, dataptr);
+	ahc_outb(ahc, HCNT + 2, resid >> 16);
+	ahc_outb(ahc, HCNT + 1, resid >> 8);
+	ahc_outb(ahc, HCNT, resid);
+	if ((ahc->features & AHC_ULTRA2) == 0) {
+		ahc_outb(ahc, STCNT + 2, resid >> 16);
+		ahc_outb(ahc, STCNT + 1, resid >> 8);
+		ahc_outb(ahc, STCNT, resid);
+	}
+}
+
+/*
+ * Handle the effects of issuing a bus device reset message.
+ */
+static void
+ahc_handle_devreset(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+		    cam_status status, char *message, int verbose_level)
+{
+#ifdef AHC_TARGET_MODE
+	struct ahc_tmode_tstate* tstate;
+	u_int lun;
+#endif
+	int found;
+
+	found = ahc_abort_scbs(ahc, devinfo->target, devinfo->channel,
+			       CAM_LUN_WILDCARD, SCB_LIST_NULL, devinfo->role,
+			       status);
+
+#ifdef AHC_TARGET_MODE
+	/*
+	 * Send an immediate notify ccb to all target mord peripheral
+	 * drivers affected by this action.
+	 */
+	tstate = ahc->enabled_targets[devinfo->our_scsiid];
+	if (tstate != NULL) {
+		for (lun = 0; lun < AHC_NUM_LUNS; lun++) {
+			struct ahc_tmode_lstate* lstate;
+
+			lstate = tstate->enabled_luns[lun];
+			if (lstate == NULL)
+				continue;
+
+			ahc_queue_lstate_event(ahc, lstate, devinfo->our_scsiid,
+					       MSG_BUS_DEV_RESET, /*arg*/0);
+			ahc_send_lstate_events(ahc, lstate);
+		}
+	}
+#endif
+
+	/*
+	 * Go back to async/narrow transfers and renegotiate.
+	 */
+	ahc_set_width(ahc, devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+		      AHC_TRANS_CUR, /*paused*/TRUE);
+	ahc_set_syncrate(ahc, devinfo, /*syncrate*/NULL,
+			 /*period*/0, /*offset*/0, /*ppr_options*/0,
+			 AHC_TRANS_CUR, /*paused*/TRUE);
+	
+	ahc_send_async(ahc, devinfo->channel, devinfo->target,
+		       CAM_LUN_WILDCARD, AC_SENT_BDR, NULL);
+
+	if (message != NULL
+	 && (verbose_level <= bootverbose))
+		printf("%s: %s on %c:%d. %d SCBs aborted\n", ahc_name(ahc),
+		       message, devinfo->channel, devinfo->target, found);
+}
+
+#ifdef AHC_TARGET_MODE
+static void
+ahc_setup_target_msgin(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+		       struct scb *scb)
+{
+
+	/*              
+	 * To facilitate adding multiple messages together,
+	 * each routine should increment the index and len
+	 * variables instead of setting them explicitly.
+	 */             
+	ahc->msgout_index = 0;
+	ahc->msgout_len = 0;
+
+	if (scb != NULL && (scb->flags & SCB_AUTO_NEGOTIATE) != 0)
+		ahc_build_transfer_msg(ahc, devinfo);
+	else
+		panic("ahc_intr: AWAITING target message with no message");
+
+	ahc->msgout_index = 0;
+	ahc->msg_type = MSG_TYPE_TARGET_MSGIN;
+}
+#endif
+/**************************** Initialization **********************************/
+/*
+ * Allocate a controller structure for a new device
+ * and perform initial initializion.
+ */
+struct ahc_softc *
+ahc_alloc(void *platform_arg, char *name)
+{
+	struct  ahc_softc *ahc;
+	int	i;
+
+#ifndef	__FreeBSD__
+	ahc = malloc(sizeof(*ahc), M_DEVBUF, M_NOWAIT);
+	if (!ahc) {
+		printf("aic7xxx: cannot malloc softc!\n");
+		free(name, M_DEVBUF);
+		return NULL;
+	}
+#else
+	ahc = device_get_softc((device_t)platform_arg);
+#endif
+	memset(ahc, 0, sizeof(*ahc));
+	LIST_INIT(&ahc->pending_scbs);
+	/* We don't know our unit number until the OSM sets it */
+	ahc->name = name;
+	ahc->unit = -1;
+	ahc->description = NULL;
+	ahc->channel = 'A';
+	ahc->channel_b = 'B';
+	ahc->chip = AHC_NONE;
+	ahc->features = AHC_FENONE;
+	ahc->bugs = AHC_BUGNONE;
+	ahc->flags = AHC_FNONE;
+
+	for (i = 0; i < 16; i++)
+		TAILQ_INIT(&ahc->untagged_queues[i]);
+	if (ahc_platform_alloc(ahc, platform_arg) != 0) {
+		ahc_free(ahc);
+		ahc = NULL;
+	}
+	return (ahc);
+}
+
+int
+ahc_softc_init(struct ahc_softc *ahc)
+{
+
+	/* The IRQMS bit is only valid on VL and EISA chips */
+	if ((ahc->chip & AHC_PCI) == 0)
+		ahc->unpause = ahc_inb(ahc, HCNTRL) & IRQMS;
+	else
+		ahc->unpause = 0;
+	ahc->pause = ahc->unpause | PAUSE; 
+	/* XXX The shared scb data stuff should be deprecated */
+	if (ahc->scb_data == NULL) {
+		ahc->scb_data = malloc(sizeof(*ahc->scb_data),
+				       M_DEVBUF, M_NOWAIT);
+		if (ahc->scb_data == NULL)
+			return (ENOMEM);
+		memset(ahc->scb_data, 0, sizeof(*ahc->scb_data));
+	}
+
+	return (0);
+}
+
+void
+ahc_softc_insert(struct ahc_softc *ahc)
+{
+	struct ahc_softc *list_ahc;
+
+#if AHC_PCI_CONFIG > 0
+	/*
+	 * Second Function PCI devices need to inherit some
+	 * settings from function 0.
+	 */
+	if ((ahc->chip & AHC_BUS_MASK) == AHC_PCI
+	 && (ahc->features & AHC_MULTI_FUNC) != 0) {
+		TAILQ_FOREACH(list_ahc, &ahc_tailq, links) {
+			ahc_dev_softc_t list_pci;
+			ahc_dev_softc_t pci;
+
+			list_pci = list_ahc->dev_softc;
+			pci = ahc->dev_softc;
+			if (ahc_get_pci_slot(list_pci) == ahc_get_pci_slot(pci)
+			 && ahc_get_pci_bus(list_pci) == ahc_get_pci_bus(pci)) {
+				struct ahc_softc *master;
+				struct ahc_softc *slave;
+
+				if (ahc_get_pci_function(list_pci) == 0) {
+					master = list_ahc;
+					slave = ahc;
+				} else {
+					master = ahc;
+					slave = list_ahc;
+				}
+				slave->flags &= ~AHC_BIOS_ENABLED; 
+				slave->flags |=
+				    master->flags & AHC_BIOS_ENABLED;
+				slave->flags &= ~AHC_PRIMARY_CHANNEL; 
+				slave->flags |=
+				    master->flags & AHC_PRIMARY_CHANNEL;
+				break;
+			}
+		}
+	}
+#endif
+
+	/*
+	 * Insertion sort into our list of softcs.
+	 */
+	list_ahc = TAILQ_FIRST(&ahc_tailq);
+	while (list_ahc != NULL
+	    && ahc_softc_comp(list_ahc, ahc) <= 0)
+		list_ahc = TAILQ_NEXT(list_ahc, links);
+	if (list_ahc != NULL)
+		TAILQ_INSERT_BEFORE(list_ahc, ahc, links);
+	else
+		TAILQ_INSERT_TAIL(&ahc_tailq, ahc, links);
+	ahc->init_level++;
+}
+
+void
+ahc_set_unit(struct ahc_softc *ahc, int unit)
+{
+	ahc->unit = unit;
+}
+
+void
+ahc_set_name(struct ahc_softc *ahc, char *name)
+{
+	if (ahc->name != NULL)
+		free(ahc->name, M_DEVBUF);
+	ahc->name = name;
+}
+
+void
+ahc_free(struct ahc_softc *ahc)
+{
+	int i;
+
+	ahc_fini_scbdata(ahc);
+	switch (ahc->init_level) {
+	default:
+	case 5:
+		ahc_shutdown(ahc);
+		TAILQ_REMOVE(&ahc_tailq, ahc, links);
+		/* FALLTHROUGH */
+	case 4:
+		ahc_dmamap_unload(ahc, ahc->shared_data_dmat,
+				  ahc->shared_data_dmamap);
+		/* FALLTHROUGH */
+	case 3:
+		ahc_dmamem_free(ahc, ahc->shared_data_dmat, ahc->qoutfifo,
+				ahc->shared_data_dmamap);
+		ahc_dmamap_destroy(ahc, ahc->shared_data_dmat,
+				   ahc->shared_data_dmamap);
+		/* FALLTHROUGH */
+	case 2:
+		ahc_dma_tag_destroy(ahc, ahc->shared_data_dmat);
+	case 1:
+#ifndef __linux__
+		ahc_dma_tag_destroy(ahc, ahc->buffer_dmat);
+#endif
+		break;
+	case 0:
+		break;
+	}
+
+#ifndef __linux__
+	ahc_dma_tag_destroy(ahc, ahc->parent_dmat);
+#endif
+	ahc_platform_free(ahc);
+	for (i = 0; i < AHC_NUM_TARGETS; i++) {
+		struct ahc_tmode_tstate *tstate;
+
+		tstate = ahc->enabled_targets[i];
+		if (tstate != NULL) {
+#if AHC_TARGET_MODE
+			int j;
+
+			for (j = 0; j < AHC_NUM_LUNS; j++) {
+				struct ahc_tmode_lstate *lstate;
+
+				lstate = tstate->enabled_luns[j];
+				if (lstate != NULL) {
+					xpt_free_path(lstate->path);
+					free(lstate, M_DEVBUF);
+				}
+			}
+#endif
+			free(tstate, M_DEVBUF);
+		}
+	}
+#if AHC_TARGET_MODE
+	if (ahc->black_hole != NULL) {
+		xpt_free_path(ahc->black_hole->path);
+		free(ahc->black_hole, M_DEVBUF);
+	}
+#endif
+	if (ahc->name != NULL)
+		free(ahc->name, M_DEVBUF);
+#ifndef __FreeBSD__
+	free(ahc, M_DEVBUF);
+#endif
+	return;
+}
+
+void
+ahc_shutdown(void *arg)
+{
+	struct	ahc_softc *ahc;
+	int	i;
+
+	ahc = (struct ahc_softc *)arg;
+
+	/* This will reset most registers to 0, but not all */
+	ahc_reset(ahc);
+	ahc_outb(ahc, SCSISEQ, 0);
+	ahc_outb(ahc, SXFRCTL0, 0);
+	ahc_outb(ahc, DSPCISTATUS, 0);
+
+	for (i = TARG_SCSIRATE; i < HA_274_BIOSCTRL; i++)
+		ahc_outb(ahc, i, 0);
+}
+
+/*
+ * Reset the controller and record some information about it
+ * that is only availabel just after a reset.
+ */
+int
+ahc_reset(struct ahc_softc *ahc)
+{
+	u_int	sblkctl;
+	u_int	sxfrctl1_a, sxfrctl1_b;
+	int	wait;
+	
+	/*
+	 * Preserve the value of the SXFRCTL1 register for all channels.
+	 * It contains settings that affect termination and we don't want
+	 * to disturb the integrity of the bus.
+	 */
+	ahc_pause(ahc);
+	sxfrctl1_b = 0;
+	if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7770) {
+		u_int sblkctl;
+
+		/*
+		 * Save channel B's settings in case this chip
+		 * is setup for TWIN channel operation.
+		 */
+		sblkctl = ahc_inb(ahc, SBLKCTL);
+		ahc_outb(ahc, SBLKCTL, sblkctl | SELBUSB);
+		sxfrctl1_b = ahc_inb(ahc, SXFRCTL1);
+		ahc_outb(ahc, SBLKCTL, sblkctl & ~SELBUSB);
+	}
+	sxfrctl1_a = ahc_inb(ahc, SXFRCTL1);
+
+	ahc_outb(ahc, HCNTRL, CHIPRST | ahc->pause);
+
+	/*
+	 * Ensure that the reset has finished
+	 */
+	wait = 1000;
+	do {
+		ahc_delay(1000);
+	} while (--wait && !(ahc_inb(ahc, HCNTRL) & CHIPRSTACK));
+
+	if (wait == 0) {
+		printf("%s: WARNING - Failed chip reset!  "
+		       "Trying to initialize anyway.\n", ahc_name(ahc));
+	}
+	ahc_outb(ahc, HCNTRL, ahc->pause);
+
+	/* Determine channel configuration */
+	sblkctl = ahc_inb(ahc, SBLKCTL) & (SELBUSB|SELWIDE);
+	/* No Twin Channel PCI cards */
+	if ((ahc->chip & AHC_PCI) != 0)
+		sblkctl &= ~SELBUSB;
+	switch (sblkctl) {
+	case 0:
+		/* Single Narrow Channel */
+		break;
+	case 2:
+		/* Wide Channel */
+		ahc->features |= AHC_WIDE;
+		break;
+	case 8:
+		/* Twin Channel */
+		ahc->features |= AHC_TWIN;
+		break;
+	default:
+		printf(" Unsupported adapter type.  Ignoring\n");
+		return(-1);
+	}
+
+	/*
+	 * Reload sxfrctl1.
+	 *
+	 * We must always initialize STPWEN to 1 before we
+	 * restore the saved values.  STPWEN is initialized
+	 * to a tri-state condition which can only be cleared
+	 * by turning it on.
+	 */
+	if ((ahc->features & AHC_TWIN) != 0) {
+		u_int sblkctl;
+
+		sblkctl = ahc_inb(ahc, SBLKCTL);
+		ahc_outb(ahc, SBLKCTL, sblkctl | SELBUSB);
+		ahc_outb(ahc, SXFRCTL1, sxfrctl1_b);
+		ahc_outb(ahc, SBLKCTL, sblkctl & ~SELBUSB);
+	}
+	ahc_outb(ahc, SXFRCTL1, sxfrctl1_a);
+
+#ifdef AHC_DUMP_SEQ
+	if (ahc->init_level == 0)
+		ahc_dumpseq(ahc);
+#endif
+
+	return (0);
+}
+
+/*
+ * Determine the number of SCBs available on the controller
+ */
+int
+ahc_probe_scbs(struct ahc_softc *ahc) {
+	int i;
+
+	for (i = 0; i < AHC_SCB_MAX; i++) {
+
+		ahc_outb(ahc, SCBPTR, i);
+		ahc_outb(ahc, SCB_BASE, i);
+		if (ahc_inb(ahc, SCB_BASE) != i)
+			break;
+		ahc_outb(ahc, SCBPTR, 0);
+		if (ahc_inb(ahc, SCB_BASE) != 0)
+			break;
+	}
+	return (i);
+}
+
+static void
+ahc_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) 
+{
+	bus_addr_t *baddr;
+
+	baddr = (bus_addr_t *)arg;
+	*baddr = segs->ds_addr;
+}
+
+static void
+ahc_build_free_scb_list(struct ahc_softc *ahc)
+{
+	int i;
+
+	for (i = 0; i < ahc->scb_data->maxhscbs; i++) {
+		ahc_outb(ahc, SCBPTR, i);
+
+		/* Clear the control byte. */
+		ahc_outb(ahc, SCB_CONTROL, 0);
+
+		/* Set the next pointer */
+		if ((ahc->flags & AHC_PAGESCBS) != 0)
+			ahc_outb(ahc, SCB_NEXT, i+1);
+		else 
+			ahc_outb(ahc, SCB_NEXT, SCB_LIST_NULL);
+
+		/* Make the tag number invalid */
+		ahc_outb(ahc, SCB_TAG, SCB_LIST_NULL);
+	}
+
+	/* Make sure that the last SCB terminates the free list */
+	ahc_outb(ahc, SCBPTR, i-1);
+	ahc_outb(ahc, SCB_NEXT, SCB_LIST_NULL);
+
+	/* Ensure we clear the 0 SCB's control byte. */
+	ahc_outb(ahc, SCBPTR, 0);
+	ahc_outb(ahc, SCB_CONTROL, 0);
+}
+
+static int
+ahc_init_scbdata(struct ahc_softc *ahc)
+{
+	struct scb_data *scb_data;
+
+	scb_data = ahc->scb_data;
+	SLIST_INIT(&scb_data->free_scbs);
+	SLIST_INIT(&scb_data->sg_maps);
+
+	/* Allocate SCB resources */
+	scb_data->scbarray =
+	    (struct scb *)malloc(sizeof(struct scb) * AHC_SCB_MAX_ALLOC,
+				 M_DEVBUF, M_NOWAIT);
+	if (scb_data->scbarray == NULL)
+		return (ENOMEM);
+	memset(scb_data->scbarray, 0, sizeof(struct scb) * AHC_SCB_MAX_ALLOC);
+
+	/* Determine the number of hardware SCBs and initialize them */
+
+	scb_data->maxhscbs = ahc_probe_scbs(ahc);
+	if ((ahc->flags & AHC_PAGESCBS) != 0) {
+		/* SCB 0 heads the free list */
+		ahc_outb(ahc, FREE_SCBH, 0);
+	} else {
+		ahc_outb(ahc, FREE_SCBH, SCB_LIST_NULL);
+	}
+
+	if (ahc->scb_data->maxhscbs == 0) {
+		printf("%s: No SCB space found\n", ahc_name(ahc));
+		return (ENXIO);
+	}
+
+	ahc_build_free_scb_list(ahc);
+
+	/*
+	 * Create our DMA tags.  These tags define the kinds of device
+	 * accessible memory allocations and memory mappings we will
+	 * need to perform during normal operation.
+	 *
+	 * Unless we need to further restrict the allocation, we rely
+	 * on the restrictions of the parent dmat, hence the common
+	 * use of MAXADDR and MAXSIZE.
+	 */
+
+	/* DMA tag for our hardware scb structures */
+	if (ahc_dma_tag_create(ahc, ahc->parent_dmat, /*alignment*/1,
+			       /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+			       /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+			       /*highaddr*/BUS_SPACE_MAXADDR,
+			       /*filter*/NULL, /*filterarg*/NULL,
+			       AHC_SCB_MAX_ALLOC * sizeof(struct hardware_scb),
+			       /*nsegments*/1,
+			       /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+			       /*flags*/0, &scb_data->hscb_dmat) != 0) {
+		goto error_exit;
+	}
+
+	scb_data->init_level++;
+
+	/* Allocation for our hscbs */
+	if (ahc_dmamem_alloc(ahc, scb_data->hscb_dmat,
+			     (void **)&scb_data->hscbs,
+			     BUS_DMA_NOWAIT, &scb_data->hscb_dmamap) != 0) {
+		goto error_exit;
+	}
+
+	scb_data->init_level++;
+
+	/* And permanently map them */
+	ahc_dmamap_load(ahc, scb_data->hscb_dmat, scb_data->hscb_dmamap,
+			scb_data->hscbs,
+			AHC_SCB_MAX_ALLOC * sizeof(struct hardware_scb),
+			ahc_dmamap_cb, &scb_data->hscb_busaddr, /*flags*/0);
+
+	scb_data->init_level++;
+
+	/* DMA tag for our sense buffers */
+	if (ahc_dma_tag_create(ahc, ahc->parent_dmat, /*alignment*/1,
+			       /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+			       /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+			       /*highaddr*/BUS_SPACE_MAXADDR,
+			       /*filter*/NULL, /*filterarg*/NULL,
+			       AHC_SCB_MAX_ALLOC * sizeof(struct scsi_sense_data),
+			       /*nsegments*/1,
+			       /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+			       /*flags*/0, &scb_data->sense_dmat) != 0) {
+		goto error_exit;
+	}
+
+	scb_data->init_level++;
+
+	/* Allocate them */
+	if (ahc_dmamem_alloc(ahc, scb_data->sense_dmat,
+			     (void **)&scb_data->sense,
+			     BUS_DMA_NOWAIT, &scb_data->sense_dmamap) != 0) {
+		goto error_exit;
+	}
+
+	scb_data->init_level++;
+
+	/* And permanently map them */
+	ahc_dmamap_load(ahc, scb_data->sense_dmat, scb_data->sense_dmamap,
+			scb_data->sense,
+			AHC_SCB_MAX_ALLOC * sizeof(struct scsi_sense_data),
+			ahc_dmamap_cb, &scb_data->sense_busaddr, /*flags*/0);
+
+	scb_data->init_level++;
+
+	/* DMA tag for our S/G structures.  We allocate in page sized chunks */
+	if (ahc_dma_tag_create(ahc, ahc->parent_dmat, /*alignment*/1,
+			       /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+			       /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+			       /*highaddr*/BUS_SPACE_MAXADDR,
+			       /*filter*/NULL, /*filterarg*/NULL,
+			       PAGE_SIZE, /*nsegments*/1,
+			       /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+			       /*flags*/0, &scb_data->sg_dmat) != 0) {
+		goto error_exit;
+	}
+
+	scb_data->init_level++;
+
+	/* Perform initial CCB allocation */
+	memset(scb_data->hscbs, 0,
+	       AHC_SCB_MAX_ALLOC * sizeof(struct hardware_scb));
+	ahc_alloc_scbs(ahc);
+
+	if (scb_data->numscbs == 0) {
+		printf("%s: ahc_init_scbdata - "
+		       "Unable to allocate initial scbs\n",
+		       ahc_name(ahc));
+		goto error_exit;
+	}
+
+	/*
+	 * Tell the sequencer which SCB will be the next one it receives.
+	 */
+	ahc->next_queued_scb = ahc_get_scb(ahc);
+	ahc_outb(ahc, NEXT_QUEUED_SCB, ahc->next_queued_scb->hscb->tag);
+
+	/*
+	 * Note that we were successfull
+	 */
+	return (0); 
+
+error_exit:
+
+	return (ENOMEM);
+}
+
+static void
+ahc_fini_scbdata(struct ahc_softc *ahc)
+{
+	struct scb_data *scb_data;
+
+	scb_data = ahc->scb_data;
+	if (scb_data == NULL)
+		return;
+
+	switch (scb_data->init_level) {
+	default:
+	case 7:
+	{
+		struct sg_map_node *sg_map;
+
+		while ((sg_map = SLIST_FIRST(&scb_data->sg_maps))!= NULL) {
+			SLIST_REMOVE_HEAD(&scb_data->sg_maps, links);
+			ahc_dmamap_unload(ahc, scb_data->sg_dmat,
+					  sg_map->sg_dmamap);
+			ahc_dmamem_free(ahc, scb_data->sg_dmat,
+					sg_map->sg_vaddr,
+					sg_map->sg_dmamap);
+			free(sg_map, M_DEVBUF);
+		}
+		ahc_dma_tag_destroy(ahc, scb_data->sg_dmat);
+	}
+	case 6:
+		ahc_dmamap_unload(ahc, scb_data->sense_dmat,
+				  scb_data->sense_dmamap);
+	case 5:
+		ahc_dmamem_free(ahc, scb_data->sense_dmat, scb_data->sense,
+				scb_data->sense_dmamap);
+		ahc_dmamap_destroy(ahc, scb_data->sense_dmat,
+				   scb_data->sense_dmamap);
+	case 4:
+		ahc_dma_tag_destroy(ahc, scb_data->sense_dmat);
+	case 3:
+		ahc_dmamap_unload(ahc, scb_data->hscb_dmat,
+				  scb_data->hscb_dmamap);
+	case 2:
+		ahc_dmamem_free(ahc, scb_data->hscb_dmat, scb_data->hscbs,
+				scb_data->hscb_dmamap);
+		ahc_dmamap_destroy(ahc, scb_data->hscb_dmat,
+				   scb_data->hscb_dmamap);
+	case 1:
+		ahc_dma_tag_destroy(ahc, scb_data->hscb_dmat);
+		break;
+	case 0:
+		break;
+	}
+	if (scb_data->scbarray != NULL)
+		free(scb_data->scbarray, M_DEVBUF);
+}
+
+void
+ahc_alloc_scbs(struct ahc_softc *ahc)
+{
+	struct scb_data *scb_data;
+	struct scb *next_scb;
+	struct sg_map_node *sg_map;
+	bus_addr_t physaddr;
+	struct ahc_dma_seg *segs;
+	int newcount;
+	int i;
+
+	scb_data = ahc->scb_data;
+	if (scb_data->numscbs >= AHC_SCB_MAX_ALLOC)
+		/* Can't allocate any more */
+		return;
+
+	next_scb = &scb_data->scbarray[scb_data->numscbs];
+
+	sg_map = malloc(sizeof(*sg_map), M_DEVBUF, M_NOWAIT);
+
+	if (sg_map == NULL)
+		return;
+
+	/* Allocate S/G space for the next batch of SCBS */
+	if (ahc_dmamem_alloc(ahc, scb_data->sg_dmat,
+			     (void **)&sg_map->sg_vaddr,
+			     BUS_DMA_NOWAIT, &sg_map->sg_dmamap) != 0) {
+		free(sg_map, M_DEVBUF);
+		return;
+	}
+
+	SLIST_INSERT_HEAD(&scb_data->sg_maps, sg_map, links);
+
+	ahc_dmamap_load(ahc, scb_data->sg_dmat, sg_map->sg_dmamap,
+			sg_map->sg_vaddr, PAGE_SIZE, ahc_dmamap_cb,
+			&sg_map->sg_physaddr, /*flags*/0);
+
+	segs = sg_map->sg_vaddr;
+	physaddr = sg_map->sg_physaddr;
+
+	newcount = (PAGE_SIZE / (AHC_NSEG * sizeof(struct ahc_dma_seg)));
+	newcount = MIN(newcount, (AHC_SCB_MAX_ALLOC - scb_data->numscbs));
+	for (i = 0; i < newcount; i++) {
+		struct scb_platform_data *pdata;
+#ifndef __linux__
+		int error;
+#endif
+		pdata = (struct scb_platform_data *)malloc(sizeof(*pdata),
+							   M_DEVBUF, M_NOWAIT);
+		if (pdata == NULL)
+			break;
+		next_scb->platform_data = pdata;
+		next_scb->sg_map = sg_map;
+		next_scb->sg_list = segs;
+		/*
+		 * The sequencer always starts with the second entry.
+		 * The first entry is embedded in the scb.
+		 */
+		next_scb->sg_list_phys = physaddr + sizeof(struct ahc_dma_seg);
+		next_scb->ahc_softc = ahc;
+		next_scb->flags = SCB_FREE;
+#ifndef __linux__
+		error = ahc_dmamap_create(ahc, ahc->buffer_dmat, /*flags*/0,
+					  &next_scb->dmamap);
+		if (error != 0)
+			break;
+#endif
+		next_scb->hscb = &scb_data->hscbs[scb_data->numscbs];
+		next_scb->hscb->tag = ahc->scb_data->numscbs;
+		SLIST_INSERT_HEAD(&ahc->scb_data->free_scbs,
+				  next_scb, links.sle);
+		segs += AHC_NSEG;
+		physaddr += (AHC_NSEG * sizeof(struct ahc_dma_seg));
+		next_scb++;
+		ahc->scb_data->numscbs++;
+	}
+}
+
+void
+ahc_controller_info(struct ahc_softc *ahc, char *buf)
+{
+	int len;
+
+	len = sprintf(buf, "%s: ", ahc_chip_names[ahc->chip & AHC_CHIPID_MASK]);
+	buf += len;
+	if ((ahc->features & AHC_TWIN) != 0)
+ 		len = sprintf(buf, "Twin Channel, A SCSI Id=%d, "
+			      "B SCSI Id=%d, primary %c, ",
+			      ahc->our_id, ahc->our_id_b,
+			      (ahc->flags & AHC_PRIMARY_CHANNEL) + 'A');
+	else {
+		const char *speed;
+		const char *type;
+
+		speed = "";
+		if ((ahc->features & AHC_ULTRA) != 0) {
+			speed = "Ultra ";
+		} else if ((ahc->features & AHC_DT) != 0) {
+			speed = "Ultra160 ";
+		} else if ((ahc->features & AHC_ULTRA2) != 0) {
+			speed = "Ultra2 ";
+		}
+		if ((ahc->features & AHC_WIDE) != 0) {
+			type = "Wide";
+		} else {
+			type = "Single";
+		}
+		len = sprintf(buf, "%s%s Channel %c, SCSI Id=%d, ",
+			      speed, type, ahc->channel, ahc->our_id);
+	}
+	buf += len;
+
+	if ((ahc->flags & AHC_PAGESCBS) != 0)
+		sprintf(buf, "%d/%d SCBs",
+			ahc->scb_data->maxhscbs, AHC_MAX_QUEUE);
+	else
+		sprintf(buf, "%d SCBs", ahc->scb_data->maxhscbs);
+}
+
+/*
+ * Start the board, ready for normal operation
+ */
+int
+ahc_init(struct ahc_softc *ahc)
+{
+	int	 max_targ;
+	int	 i;
+	int	 term;
+	u_int	 scsi_conf;
+	u_int	 scsiseq_template;
+	u_int	 ultraenb;
+	u_int	 discenable;
+	u_int	 tagenable;
+	size_t	 driver_data_size;
+	uint32_t physaddr;
+
+#ifdef AHC_DEBUG_SEQUENCER
+	ahc->flags |= AHC_SEQUENCER_DEBUG;
+#endif
+
+#ifdef AHC_PRINT_SRAM
+	printf("Scratch Ram:");
+	for (i = 0x20; i < 0x5f; i++) {
+		if (((i % 8) == 0) && (i != 0)) {
+			printf ("\n              ");
+		}
+		printf (" 0x%x", ahc_inb(ahc, i));
+	}
+	if ((ahc->features & AHC_MORE_SRAM) != 0) {
+		for (i = 0x70; i < 0x7f; i++) {
+			if (((i % 8) == 0) && (i != 0)) {
+				printf ("\n              ");
+			}
+			printf (" 0x%x", ahc_inb(ahc, i));
+		}
+	}
+	printf ("\n");
+#endif
+	max_targ = 15;
+
+	/*
+	 * Assume we have a board at this stage and it has been reset.
+	 */
+	if ((ahc->flags & AHC_USEDEFAULTS) != 0)
+		ahc->our_id = ahc->our_id_b = 7;
+	
+	/*
+	 * Default to allowing initiator operations.
+	 */
+	ahc->flags |= AHC_INITIATORROLE;
+
+	/*
+	 * Only allow target mode features if this unit has them enabled.
+	 */
+	if ((AHC_TMODE_ENABLE & (0x1 << ahc->unit)) == 0)
+		ahc->features &= ~AHC_TARGETMODE;
+
+#ifndef __linux__
+	/* DMA tag for mapping buffers into device visible space. */
+	if (ahc_dma_tag_create(ahc, ahc->parent_dmat, /*alignment*/1,
+			       /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+			       /*lowaddr*/BUS_SPACE_MAXADDR,
+			       /*highaddr*/BUS_SPACE_MAXADDR,
+			       /*filter*/NULL, /*filterarg*/NULL,
+			       /*maxsize*/MAXBSIZE, /*nsegments*/AHC_NSEG,
+			       /*maxsegsz*/AHC_MAXTRANSFER_SIZE,
+			       /*flags*/BUS_DMA_ALLOCNOW,
+			       &ahc->buffer_dmat) != 0) {
+		return (ENOMEM);
+	}
+#endif
+
+	ahc->init_level++;
+
+	/*
+	 * DMA tag for our command fifos and other data in system memory
+	 * the card's sequencer must be able to access.  For initiator
+	 * roles, we need to allocate space for the the qinfifo and qoutfifo.
+	 * The qinfifo and qoutfifo are composed of 256 1 byte elements. 
+	 * When providing for the target mode role, we must additionally
+	 * provide space for the incoming target command fifo and an extra
+	 * byte to deal with a dma bug in some chip versions.
+	 */
+	driver_data_size = 2 * 256 * sizeof(uint8_t);
+	if ((ahc->features & AHC_TARGETMODE) != 0)
+		driver_data_size += AHC_TMODE_CMDS * sizeof(struct target_cmd)
+				 + /*DMA WideOdd Bug Buffer*/1;
+	if (ahc_dma_tag_create(ahc, ahc->parent_dmat, /*alignment*/1,
+			       /*boundary*/BUS_SPACE_MAXADDR_32BIT + 1,
+			       /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+			       /*highaddr*/BUS_SPACE_MAXADDR,
+			       /*filter*/NULL, /*filterarg*/NULL,
+			       driver_data_size,
+			       /*nsegments*/1,
+			       /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+			       /*flags*/0, &ahc->shared_data_dmat) != 0) {
+		return (ENOMEM);
+	}
+
+	ahc->init_level++;
+
+	/* Allocation of driver data */
+	if (ahc_dmamem_alloc(ahc, ahc->shared_data_dmat,
+			     (void **)&ahc->qoutfifo,
+			     BUS_DMA_NOWAIT, &ahc->shared_data_dmamap) != 0) {
+		return (ENOMEM);
+	}
+
+	ahc->init_level++;
+
+	/* And permanently map it in */
+	ahc_dmamap_load(ahc, ahc->shared_data_dmat, ahc->shared_data_dmamap,
+			ahc->qoutfifo, driver_data_size, ahc_dmamap_cb,
+			&ahc->shared_data_busaddr, /*flags*/0);
+
+	if ((ahc->features & AHC_TARGETMODE) != 0) {
+		ahc->targetcmds = (struct target_cmd *)ahc->qoutfifo;
+		ahc->qoutfifo = (uint8_t *)&ahc->targetcmds[AHC_TMODE_CMDS];
+		ahc->dma_bug_buf = ahc->shared_data_busaddr
+				 + driver_data_size - 1;
+		/* All target command blocks start out invalid. */
+		for (i = 0; i < AHC_TMODE_CMDS; i++)
+			ahc->targetcmds[i].cmd_valid = 0;
+		ahc_sync_tqinfifo(ahc, BUS_DMASYNC_PREREAD);
+		ahc->tqinfifonext = 1;
+		ahc_outb(ahc, KERNEL_TQINPOS, ahc->tqinfifonext - 1);
+		ahc_outb(ahc, TQINPOS, ahc->tqinfifonext);
+		ahc->qoutfifo = (uint8_t *)&ahc->targetcmds[256];
+	}
+	ahc->qinfifo = &ahc->qoutfifo[256];
+
+	ahc->init_level++;
+
+	/* Allocate SCB data now that buffer_dmat is initialized */
+	if (ahc->scb_data->maxhscbs == 0)
+		if (ahc_init_scbdata(ahc) != 0)
+			return (ENOMEM);
+
+	/*
+	 * Allocate a tstate to house information for our
+	 * initiator presence on the bus as well as the user
+	 * data for any target mode initiator.
+	 */
+	if (ahc_alloc_tstate(ahc, ahc->our_id, 'A') == NULL) {
+		printf("%s: unable to allocate ahc_tmode_tstate.  "
+		       "Failing attach\n", ahc_name(ahc));
+		return (ENOMEM);
+	}
+
+	if ((ahc->features & AHC_TWIN) != 0) {
+		if (ahc_alloc_tstate(ahc, ahc->our_id_b, 'B') == NULL) {
+			printf("%s: unable to allocate ahc_tmode_tstate.  "
+			       "Failing attach\n", ahc_name(ahc));
+			return (ENOMEM);
+		}
+	}
+
+	ahc_outb(ahc, SEQ_FLAGS, 0);
+	ahc_outb(ahc, SEQ_FLAGS2, 0);
+
+	if (ahc->scb_data->maxhscbs < AHC_SCB_MAX_ALLOC) {
+		ahc->flags |= AHC_PAGESCBS;
+	} else {
+		ahc->flags &= ~AHC_PAGESCBS;
+	}
+
+#ifdef AHC_DEBUG
+	if (ahc_debug & AHC_SHOWMISC) {
+		printf("%s: hardware scb %d bytes; kernel scb %d bytes; "
+		       "ahc_dma %d bytes\n",
+			ahc_name(ahc),
+			sizeof(struct hardware_scb),
+			sizeof(struct scb),
+			sizeof(struct ahc_dma_seg));
+	}
+#endif /* AHC_DEBUG */
+
+	/* Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels*/
+	if (ahc->features & AHC_TWIN) {
+
+		/*
+		 * The device is gated to channel B after a chip reset,
+		 * so set those values first
+		 */
+		ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) | SELBUSB);
+		term = (ahc->flags & AHC_TERM_ENB_B) != 0 ? STPWEN : 0;
+		ahc_outb(ahc, SCSIID, ahc->our_id_b);
+		scsi_conf = ahc_inb(ahc, SCSICONF + 1);
+		ahc_outb(ahc, SXFRCTL1, (scsi_conf & (ENSPCHK|STIMESEL))
+					|term|ahc->seltime_b|ENSTIMER|ACTNEGEN);
+		if ((ahc->features & AHC_ULTRA2) != 0)
+			ahc_outb(ahc, SIMODE0, ahc_inb(ahc, SIMODE0)|ENIOERR);
+		ahc_outb(ahc, SIMODE1, ENSELTIMO|ENSCSIRST|ENSCSIPERR);
+		ahc_outb(ahc, SXFRCTL0, DFON|SPIOEN);
+
+		if ((scsi_conf & RESET_SCSI) != 0
+		 && (ahc->flags & AHC_INITIATORROLE) != 0)
+			ahc->flags |= AHC_RESET_BUS_B;
+
+		/* Select Channel A */
+		ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) & ~SELBUSB);
+	}
+	term = (ahc->flags & AHC_TERM_ENB_A) != 0 ? STPWEN : 0;
+	if ((ahc->features & AHC_ULTRA2) != 0)
+		ahc_outb(ahc, SCSIID_ULTRA2, ahc->our_id);
+	else
+		ahc_outb(ahc, SCSIID, ahc->our_id);
+	scsi_conf = ahc_inb(ahc, SCSICONF);
+	ahc_outb(ahc, SXFRCTL1, (scsi_conf & (ENSPCHK|STIMESEL))
+				|term|ahc->seltime
+				|ENSTIMER|ACTNEGEN);
+	if ((ahc->features & AHC_ULTRA2) != 0)
+		ahc_outb(ahc, SIMODE0, ahc_inb(ahc, SIMODE0)|ENIOERR);
+	ahc_outb(ahc, SIMODE1, ENSELTIMO|ENSCSIRST|ENSCSIPERR);
+	ahc_outb(ahc, SXFRCTL0, DFON|SPIOEN);
+
+	if ((scsi_conf & RESET_SCSI) != 0
+	 && (ahc->flags & AHC_INITIATORROLE) != 0)
+		ahc->flags |= AHC_RESET_BUS_A;
+
+	/*
+	 * Look at the information that board initialization or
+	 * the board bios has left us.
+	 */
+	ultraenb = 0;	
+	tagenable = ALL_TARGETS_MASK;
+
+	/* Grab the disconnection disable table and invert it for our needs */
+	if (ahc->flags & AHC_USEDEFAULTS) {
+		printf("%s: Host Adapter Bios disabled.  Using default SCSI "
+			"device parameters\n", ahc_name(ahc));
+		ahc->flags |= AHC_EXTENDED_TRANS_A|AHC_EXTENDED_TRANS_B|
+			      AHC_TERM_ENB_A|AHC_TERM_ENB_B;
+		discenable = ALL_TARGETS_MASK;
+		if ((ahc->features & AHC_ULTRA) != 0)
+			ultraenb = ALL_TARGETS_MASK;
+	} else {
+		discenable = ~((ahc_inb(ahc, DISC_DSB + 1) << 8)
+			   | ahc_inb(ahc, DISC_DSB));
+		if ((ahc->features & (AHC_ULTRA|AHC_ULTRA2)) != 0)
+			ultraenb = (ahc_inb(ahc, ULTRA_ENB + 1) << 8)
+				      | ahc_inb(ahc, ULTRA_ENB);
+	}
+
+	if ((ahc->features & (AHC_WIDE|AHC_TWIN)) == 0)
+		max_targ = 7;
+
+	for (i = 0; i <= max_targ; i++) {
+		struct ahc_initiator_tinfo *tinfo;
+		struct ahc_tmode_tstate *tstate;
+		u_int our_id;
+		u_int target_id;
+		char channel;
+
+		channel = 'A';
+		our_id = ahc->our_id;
+		target_id = i;
+		if (i > 7 && (ahc->features & AHC_TWIN) != 0) {
+			channel = 'B';
+			our_id = ahc->our_id_b;
+			target_id = i % 8;
+		}
+		tinfo = ahc_fetch_transinfo(ahc, channel, our_id,
+					    target_id, &tstate);
+		/* Default to async narrow across the board */
+		memset(tinfo, 0, sizeof(*tinfo));
+		if (ahc->flags & AHC_USEDEFAULTS) {
+			if ((ahc->features & AHC_WIDE) != 0)
+				tinfo->user.width = MSG_EXT_WDTR_BUS_16_BIT;
+
+			/*
+			 * These will be truncated when we determine the
+			 * connection type we have with the target.
+			 */
+			tinfo->user.period = ahc_syncrates->period;
+			tinfo->user.offset = ~0;
+		} else {
+			u_int scsirate;
+			uint16_t mask;
+
+			/* Take the settings leftover in scratch RAM. */
+			scsirate = ahc_inb(ahc, TARG_SCSIRATE + i);
+			mask = (0x01 << i);
+			if ((ahc->features & AHC_ULTRA2) != 0) {
+				u_int offset;
+				u_int maxsync;
+
+				if ((scsirate & SOFS) == 0x0F) {
+					/*
+					 * Haven't negotiated yet,
+					 * so the format is different.
+					 */
+					scsirate = (scsirate & SXFR) >> 4
+						 | (ultraenb & mask)
+						  ? 0x08 : 0x0
+						 | (scsirate & WIDEXFER);
+					offset = MAX_OFFSET_ULTRA2;
+				} else
+					offset = ahc_inb(ahc, TARG_OFFSET + i);
+				if ((scsirate & ~WIDEXFER) == 0 && offset != 0)
+					/* Set to the lowest sync rate, 5MHz */
+					scsirate |= 0x1c;
+				maxsync = AHC_SYNCRATE_ULTRA2;
+				if ((ahc->features & AHC_DT) != 0)
+					maxsync = AHC_SYNCRATE_DT;
+				tinfo->user.period =
+				    ahc_find_period(ahc, scsirate, maxsync);
+				if (offset == 0)
+					tinfo->user.period = 0;
+				else
+					tinfo->user.offset = ~0;
+				if ((scsirate & SXFR_ULTRA2) <= 8/*10MHz*/
+				 && (ahc->features & AHC_DT) != 0)
+					tinfo->user.ppr_options =
+					    MSG_EXT_PPR_DT_REQ;
+			} else if ((scsirate & SOFS) != 0) {
+				if ((scsirate & SXFR) == 0x40
+				 && (ultraenb & mask) != 0) {
+					/* Treat 10MHz as a non-ultra speed */
+					scsirate &= ~SXFR;
+				 	ultraenb &= ~mask;
+				}
+				tinfo->user.period = 
+				    ahc_find_period(ahc, scsirate,
+						    (ultraenb & mask)
+						   ? AHC_SYNCRATE_ULTRA
+						   : AHC_SYNCRATE_FAST);
+				if (tinfo->user.period != 0)
+					tinfo->user.offset = ~0;
+			}
+			if (tinfo->user.period == 0)
+				tinfo->user.offset = 0;
+			if ((scsirate & WIDEXFER) != 0
+			 && (ahc->features & AHC_WIDE) != 0)
+				tinfo->user.width = MSG_EXT_WDTR_BUS_16_BIT;
+			tinfo->user.protocol_version = 4;
+			if ((ahc->features & AHC_DT) != 0)
+				tinfo->user.transport_version = 3;
+			else
+				tinfo->user.transport_version = 2;
+			tinfo->goal.protocol_version = 2;
+			tinfo->goal.transport_version = 2;
+			tinfo->curr.protocol_version = 2;
+			tinfo->curr.transport_version = 2;
+		}
+		tstate->ultraenb = ultraenb;
+	}
+	ahc->user_discenable = discenable;
+	ahc->user_tagenable = tagenable;
+
+	/* There are no untagged SCBs active yet. */
+	for (i = 0; i < 16; i++) {
+		ahc_unbusy_tcl(ahc, BUILD_TCL(i << 4, 0));
+		if ((ahc->flags & AHC_SCB_BTT) != 0) {
+			int lun;
+
+			/*
+			 * The SCB based BTT allows an entry per
+			 * target and lun pair.
+			 */
+			for (lun = 1; lun < AHC_NUM_LUNS; lun++)
+				ahc_unbusy_tcl(ahc, BUILD_TCL(i << 4, lun));
+		}
+	}
+
+	/* All of our queues are empty */
+	for (i = 0; i < 256; i++)
+		ahc->qoutfifo[i] = SCB_LIST_NULL;
+	ahc_sync_qoutfifo(ahc, BUS_DMASYNC_PREREAD);
+
+	for (i = 0; i < 256; i++)
+		ahc->qinfifo[i] = SCB_LIST_NULL;
+
+	if ((ahc->features & AHC_MULTI_TID) != 0) {
+		ahc_outb(ahc, TARGID, 0);
+		ahc_outb(ahc, TARGID + 1, 0);
+	}
+
+	/*
+	 * Tell the sequencer where it can find our arrays in memory.
+	 */
+	physaddr = ahc->scb_data->hscb_busaddr;
+	ahc_outb(ahc, HSCB_ADDR, physaddr & 0xFF);
+	ahc_outb(ahc, HSCB_ADDR + 1, (physaddr >> 8) & 0xFF);
+	ahc_outb(ahc, HSCB_ADDR + 2, (physaddr >> 16) & 0xFF);
+	ahc_outb(ahc, HSCB_ADDR + 3, (physaddr >> 24) & 0xFF);
+
+	physaddr = ahc->shared_data_busaddr;
+	ahc_outb(ahc, SHARED_DATA_ADDR, physaddr & 0xFF);
+	ahc_outb(ahc, SHARED_DATA_ADDR + 1, (physaddr >> 8) & 0xFF);
+	ahc_outb(ahc, SHARED_DATA_ADDR + 2, (physaddr >> 16) & 0xFF);
+	ahc_outb(ahc, SHARED_DATA_ADDR + 3, (physaddr >> 24) & 0xFF);
+
+	/*
+	 * Initialize the group code to command length table.
+	 * This overrides the values in TARG_SCSIRATE, so only
+	 * setup the table after we have processed that information.
+	 */
+	ahc_outb(ahc, CMDSIZE_TABLE, 5);
+	ahc_outb(ahc, CMDSIZE_TABLE + 1, 9);
+	ahc_outb(ahc, CMDSIZE_TABLE + 2, 9);
+	ahc_outb(ahc, CMDSIZE_TABLE + 3, 0);
+	ahc_outb(ahc, CMDSIZE_TABLE + 4, 15);
+	ahc_outb(ahc, CMDSIZE_TABLE + 5, 11);
+	ahc_outb(ahc, CMDSIZE_TABLE + 6, 0);
+	ahc_outb(ahc, CMDSIZE_TABLE + 7, 0);
+		
+	/* Tell the sequencer of our initial queue positions */
+	ahc_outb(ahc, KERNEL_QINPOS, 0);
+	ahc_outb(ahc, QINPOS, 0);
+	ahc_outb(ahc, QOUTPOS, 0);
+
+	/*
+	 * Use the built in queue management registers
+	 * if they are available.
+	 */
+	if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+		ahc_outb(ahc, QOFF_CTLSTA, SCB_QSIZE_256);
+		ahc_outb(ahc, SDSCB_QOFF, 0);
+		ahc_outb(ahc, SNSCB_QOFF, 0);
+		ahc_outb(ahc, HNSCB_QOFF, 0);
+	}
+
+
+	/* We don't have any waiting selections */
+	ahc_outb(ahc, WAITING_SCBH, SCB_LIST_NULL);
+
+	/* Our disconnection list is empty too */
+	ahc_outb(ahc, DISCONNECTED_SCBH, SCB_LIST_NULL);
+
+	/* Message out buffer starts empty */
+	ahc_outb(ahc, MSG_OUT, MSG_NOOP);
+
+	/*
+	 * Setup the allowed SCSI Sequences based on operational mode.
+	 * If we are a target, we'll enalbe select in operations once
+	 * we've had a lun enabled.
+	 */
+	scsiseq_template = ENSELO|ENAUTOATNO|ENAUTOATNP;
+	if ((ahc->flags & AHC_INITIATORROLE) != 0)
+		scsiseq_template |= ENRSELI;
+	ahc_outb(ahc, SCSISEQ_TEMPLATE, scsiseq_template);
+
+	/*
+	 * Load the Sequencer program and Enable the adapter
+	 * in "fast" mode.
+	 */
+	if (bootverbose)
+		printf("%s: Downloading Sequencer Program...",
+		       ahc_name(ahc));
+
+	ahc_loadseq(ahc);
+
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		int wait;
+
+		/*
+		 * Wait for up to 500ms for our transceivers
+		 * to settle.  If the adapter does not have
+		 * a cable attached, the tranceivers may
+		 * never settle, so don't complain if we
+		 * fail here.
+		 */
+		ahc_pause(ahc);
+		for (wait = 5000;
+		     (ahc_inb(ahc, SBLKCTL) & (ENAB40|ENAB20)) == 0 && wait;
+		     wait--)
+			ahc_delay(100);
+		ahc_unpause(ahc);
+	}
+	return (0);
+}
+
+void
+ahc_intr_enable(struct ahc_softc *ahc, int enable)
+{
+	u_int hcntrl;
+
+	hcntrl = ahc_inb(ahc, HCNTRL);
+	hcntrl &= ~INTEN;
+	ahc->pause &= ~INTEN;
+	ahc->unpause &= ~INTEN;
+	if (enable) {
+		hcntrl |= INTEN;
+		ahc->pause |= INTEN;
+		ahc->unpause |= INTEN;
+	}
+	ahc_outb(ahc, HCNTRL, hcntrl);
+}
+
+/*
+ * Ensure that the card is paused in a location
+ * outside of all critical sections and that all
+ * pending work is completed prior to returning.
+ * This routine should only be called from outside
+ * an interrupt context.
+ */
+void
+ahc_pause_and_flushwork(struct ahc_softc *ahc)
+{
+	int intstat;
+	int maxloops;
+
+	maxloops = 1000;
+	ahc->flags |= AHC_ALL_INTERRUPTS;
+	intstat = 0;
+	do {
+		ahc_intr(ahc);
+		ahc_pause(ahc);
+		ahc_clear_critical_section(ahc);
+		if (intstat == 0xFF && (ahc->features & AHC_REMOVABLE) != 0)
+			break;
+		maxloops--;
+	} while (((intstat = ahc_inb(ahc, INTSTAT)) & INT_PEND) && --maxloops);
+	if (maxloops == 0) {
+		printf("Infinite interrupt loop, INTSTAT = %x",
+		      ahc_inb(ahc, INTSTAT));
+	}
+	ahc_platform_flushwork(ahc);
+	ahc->flags &= ~AHC_ALL_INTERRUPTS;
+}
+
+int
+ahc_suspend(struct ahc_softc *ahc)
+{
+	uint8_t *ptr;
+	int	 i;
+
+	ahc_pause_and_flushwork(ahc);
+
+	if (LIST_FIRST(&ahc->pending_scbs) != NULL)
+		return (EBUSY);
+
+#if AHC_TARGET_MODE
+	/*
+	 * XXX What about ATIOs that have not yet been serviced?
+	 * Perhaps we should just refuse to be suspended if we
+	 * are acting in a target role.
+	 */
+	if (ahc->pending_device != NULL)
+		return (EBUSY);
+#endif
+
+	/* Save volatile registers */
+	if ((ahc->features & AHC_TWIN) != 0) {
+		ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) | SELBUSB);
+		ahc->suspend_state.channel[1].scsiseq = ahc_inb(ahc, SCSISEQ);
+		ahc->suspend_state.channel[1].sxfrctl0 = ahc_inb(ahc, SXFRCTL0);
+		ahc->suspend_state.channel[1].sxfrctl1 = ahc_inb(ahc, SXFRCTL1);
+		ahc->suspend_state.channel[1].simode0 = ahc_inb(ahc, SIMODE0);
+		ahc->suspend_state.channel[1].simode1 = ahc_inb(ahc, SIMODE1);
+		ahc->suspend_state.channel[1].seltimer = ahc_inb(ahc, SELTIMER);
+		ahc->suspend_state.channel[1].seqctl = ahc_inb(ahc, SEQCTL);
+		ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) & ~SELBUSB);
+	}
+	ahc->suspend_state.channel[0].scsiseq = ahc_inb(ahc, SCSISEQ);
+	ahc->suspend_state.channel[0].sxfrctl0 = ahc_inb(ahc, SXFRCTL0);
+	ahc->suspend_state.channel[0].sxfrctl1 = ahc_inb(ahc, SXFRCTL1);
+	ahc->suspend_state.channel[0].simode0 = ahc_inb(ahc, SIMODE0);
+	ahc->suspend_state.channel[0].simode1 = ahc_inb(ahc, SIMODE1);
+	ahc->suspend_state.channel[0].seltimer = ahc_inb(ahc, SELTIMER);
+	ahc->suspend_state.channel[0].seqctl = ahc_inb(ahc, SEQCTL);
+
+	if ((ahc->chip & AHC_PCI) != 0) {
+		ahc->suspend_state.dscommand0 = ahc_inb(ahc, DSCOMMAND0);
+		ahc->suspend_state.dspcistatus = ahc_inb(ahc, DSPCISTATUS);
+	}
+
+	if ((ahc->features & AHC_DT) != 0) {
+		u_int sfunct;
+
+		sfunct = ahc_inb(ahc, SFUNCT) & ~ALT_MODE;
+		ahc_outb(ahc, SFUNCT, sfunct | ALT_MODE);
+		ahc->suspend_state.optionmode = ahc_inb(ahc, OPTIONMODE);
+		ahc_outb(ahc, SFUNCT, sfunct);
+		ahc->suspend_state.crccontrol1 = ahc_inb(ahc, CRCCONTROL1);
+	}
+
+	if ((ahc->features & AHC_MULTI_FUNC) != 0)
+		ahc->suspend_state.scbbaddr = ahc_inb(ahc, SCBBADDR);
+
+	if ((ahc->features & AHC_ULTRA2) != 0)
+		ahc->suspend_state.dff_thrsh = ahc_inb(ahc, DFF_THRSH);
+
+	ptr = ahc->suspend_state.scratch_ram;
+	for (i = 0; i < 64; i++)
+		*ptr++ = ahc_inb(ahc, SRAM_BASE + i);
+
+	if ((ahc->features & AHC_MORE_SRAM) != 0) {
+		for (i = 0; i < 16; i++)
+			*ptr++ = ahc_inb(ahc, TARG_OFFSET + i);
+	}
+
+	ptr = ahc->suspend_state.btt;
+	if ((ahc->flags & AHC_SCB_BTT) != 0) {
+		for (i = 0;i < AHC_NUM_TARGETS; i++) {
+			int j;
+
+			for (j = 0;j < AHC_NUM_LUNS; j++) {
+				u_int tcl;
+
+				tcl = BUILD_TCL(i << 4, j);
+				*ptr = ahc_index_busy_tcl(ahc, tcl);
+			}
+		}
+	}
+	ahc_shutdown(ahc);
+	return (0);
+}
+
+int
+ahc_resume(struct ahc_softc *ahc)
+{
+	uint8_t *ptr;
+	int	 i;
+
+	ahc_reset(ahc);
+
+	ahc_build_free_scb_list(ahc);
+
+	/* Restore volatile registers */
+	if ((ahc->features & AHC_TWIN) != 0) {
+		ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) | SELBUSB);
+		ahc_outb(ahc, SCSIID, ahc->our_id);
+		ahc_outb(ahc, SCSISEQ, ahc->suspend_state.channel[1].scsiseq);
+		ahc_outb(ahc, SXFRCTL0, ahc->suspend_state.channel[1].sxfrctl0);
+		ahc_outb(ahc, SXFRCTL1, ahc->suspend_state.channel[1].sxfrctl1);
+		ahc_outb(ahc, SIMODE0, ahc->suspend_state.channel[1].simode0);
+		ahc_outb(ahc, SIMODE1, ahc->suspend_state.channel[1].simode1);
+		ahc_outb(ahc, SELTIMER, ahc->suspend_state.channel[1].seltimer);
+		ahc_outb(ahc, SEQCTL, ahc->suspend_state.channel[1].seqctl);
+		ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) & ~SELBUSB);
+	}
+	ahc_outb(ahc, SCSISEQ, ahc->suspend_state.channel[0].scsiseq);
+	ahc_outb(ahc, SXFRCTL0, ahc->suspend_state.channel[0].sxfrctl0);
+	ahc_outb(ahc, SXFRCTL1, ahc->suspend_state.channel[0].sxfrctl1);
+	ahc_outb(ahc, SIMODE0, ahc->suspend_state.channel[0].simode0);
+	ahc_outb(ahc, SIMODE1, ahc->suspend_state.channel[0].simode1);
+	ahc_outb(ahc, SELTIMER, ahc->suspend_state.channel[0].seltimer);
+	ahc_outb(ahc, SEQCTL, ahc->suspend_state.channel[0].seqctl);
+	if ((ahc->features & AHC_ULTRA2) != 0)
+		ahc_outb(ahc, SCSIID_ULTRA2, ahc->our_id);
+	else
+		ahc_outb(ahc, SCSIID, ahc->our_id);
+
+	if ((ahc->chip & AHC_PCI) != 0) {
+		ahc_outb(ahc, DSCOMMAND0, ahc->suspend_state.dscommand0);
+		ahc_outb(ahc, DSPCISTATUS, ahc->suspend_state.dspcistatus);
+	}
+
+	if ((ahc->features & AHC_DT) != 0) {
+		u_int sfunct;
+
+		sfunct = ahc_inb(ahc, SFUNCT) & ~ALT_MODE;
+		ahc_outb(ahc, SFUNCT, sfunct | ALT_MODE);
+		ahc_outb(ahc, OPTIONMODE, ahc->suspend_state.optionmode);
+		ahc_outb(ahc, SFUNCT, sfunct);
+		ahc_outb(ahc, CRCCONTROL1, ahc->suspend_state.crccontrol1);
+	}
+
+	if ((ahc->features & AHC_MULTI_FUNC) != 0)
+		ahc_outb(ahc, SCBBADDR, ahc->suspend_state.scbbaddr);
+
+	if ((ahc->features & AHC_ULTRA2) != 0)
+		ahc_outb(ahc, DFF_THRSH, ahc->suspend_state.dff_thrsh);
+
+	ptr = ahc->suspend_state.scratch_ram;
+	for (i = 0; i < 64; i++)
+		ahc_outb(ahc, SRAM_BASE + i, *ptr++);
+
+	if ((ahc->features & AHC_MORE_SRAM) != 0) {
+		for (i = 0; i < 16; i++)
+			ahc_outb(ahc, TARG_OFFSET + i, *ptr++);
+	}
+
+	ptr = ahc->suspend_state.btt;
+	if ((ahc->flags & AHC_SCB_BTT) != 0) {
+		for (i = 0;i < AHC_NUM_TARGETS; i++) {
+			int j;
+
+			for (j = 0;j < AHC_NUM_LUNS; j++) {
+				u_int tcl;
+
+				tcl = BUILD_TCL(i << 4, j);
+				ahc_busy_tcl(ahc, tcl, *ptr);
+			}
+		}
+	}
+	return (0);
+}
+
+/************************** Busy Target Table *********************************/
+/*
+ * Return the untagged transaction id for a given target/channel lun.
+ * Optionally, clear the entry.
+ */
+u_int
+ahc_index_busy_tcl(struct ahc_softc *ahc, u_int tcl)
+{
+	u_int scbid;
+	u_int target_offset;
+
+	if ((ahc->flags & AHC_SCB_BTT) != 0) {
+		u_int saved_scbptr;
+		
+		saved_scbptr = ahc_inb(ahc, SCBPTR);
+		ahc_outb(ahc, SCBPTR, TCL_LUN(tcl));
+		scbid = ahc_inb(ahc, SCB_64_BTT + TCL_TARGET_OFFSET(tcl));
+		ahc_outb(ahc, SCBPTR, saved_scbptr);
+	} else {
+		target_offset = TCL_TARGET_OFFSET(tcl);
+		scbid = ahc_inb(ahc, BUSY_TARGETS + target_offset);
+	}
+
+	return (scbid);
+}
+
+void
+ahc_unbusy_tcl(struct ahc_softc *ahc, u_int tcl)
+{
+	u_int target_offset;
+
+	if ((ahc->flags & AHC_SCB_BTT) != 0) {
+		u_int saved_scbptr;
+		
+		saved_scbptr = ahc_inb(ahc, SCBPTR);
+		ahc_outb(ahc, SCBPTR, TCL_LUN(tcl));
+		ahc_outb(ahc, SCB_64_BTT+TCL_TARGET_OFFSET(tcl), SCB_LIST_NULL);
+		ahc_outb(ahc, SCBPTR, saved_scbptr);
+	} else {
+		target_offset = TCL_TARGET_OFFSET(tcl);
+		ahc_outb(ahc, BUSY_TARGETS + target_offset, SCB_LIST_NULL);
+	}
+}
+
+void
+ahc_busy_tcl(struct ahc_softc *ahc, u_int tcl, u_int scbid)
+{
+	u_int target_offset;
+
+	if ((ahc->flags & AHC_SCB_BTT) != 0) {
+		u_int saved_scbptr;
+		
+		saved_scbptr = ahc_inb(ahc, SCBPTR);
+		ahc_outb(ahc, SCBPTR, TCL_LUN(tcl));
+		ahc_outb(ahc, SCB_64_BTT + TCL_TARGET_OFFSET(tcl), scbid);
+		ahc_outb(ahc, SCBPTR, saved_scbptr);
+	} else {
+		target_offset = TCL_TARGET_OFFSET(tcl);
+		ahc_outb(ahc, BUSY_TARGETS + target_offset, scbid);
+	}
+}
+
+/************************** SCB and SCB queue management **********************/
+int
+ahc_match_scb(struct ahc_softc *ahc, struct scb *scb, int target,
+	      char channel, int lun, u_int tag, role_t role)
+{
+	int targ = SCB_GET_TARGET(ahc, scb);
+	char chan = SCB_GET_CHANNEL(ahc, scb);
+	int slun = SCB_GET_LUN(scb);
+	int match;
+
+	match = ((chan == channel) || (channel == ALL_CHANNELS));
+	if (match != 0)
+		match = ((targ == target) || (target == CAM_TARGET_WILDCARD));
+	if (match != 0)
+		match = ((lun == slun) || (lun == CAM_LUN_WILDCARD));
+	if (match != 0) {
+#if AHC_TARGET_MODE
+		int group;
+
+		group = XPT_FC_GROUP(scb->io_ctx->ccb_h.func_code);
+		if (role == ROLE_INITIATOR) {
+			match = (group != XPT_FC_GROUP_TMODE)
+			      && ((tag == scb->hscb->tag)
+			       || (tag == SCB_LIST_NULL));
+		} else if (role == ROLE_TARGET) {
+			match = (group == XPT_FC_GROUP_TMODE)
+			      && ((tag == scb->io_ctx->csio.tag_id)
+			       || (tag == SCB_LIST_NULL));
+		}
+#else /* !AHC_TARGET_MODE */
+		match = ((tag == scb->hscb->tag) || (tag == SCB_LIST_NULL));
+#endif /* AHC_TARGET_MODE */
+	}
+
+	return match;
+}
+
+void
+ahc_freeze_devq(struct ahc_softc *ahc, struct scb *scb)
+{
+	int	target;
+	char	channel;
+	int	lun;
+
+	target = SCB_GET_TARGET(ahc, scb);
+	lun = SCB_GET_LUN(scb);
+	channel = SCB_GET_CHANNEL(ahc, scb);
+	
+	ahc_search_qinfifo(ahc, target, channel, lun,
+			   /*tag*/SCB_LIST_NULL, ROLE_UNKNOWN,
+			   CAM_REQUEUE_REQ, SEARCH_COMPLETE);
+
+	ahc_platform_freeze_devq(ahc, scb);
+}
+
+void
+ahc_qinfifo_requeue_tail(struct ahc_softc *ahc, struct scb *scb)
+{
+	struct scb *prev_scb;
+
+	prev_scb = NULL;
+	if (ahc_qinfifo_count(ahc) != 0) {
+		u_int prev_tag;
+		uint8_t prev_pos;
+
+		prev_pos = ahc->qinfifonext - 1;
+		prev_tag = ahc->qinfifo[prev_pos];
+		prev_scb = ahc_lookup_scb(ahc, prev_tag);
+	}
+	ahc_qinfifo_requeue(ahc, prev_scb, scb);
+	if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+		ahc_outb(ahc, HNSCB_QOFF, ahc->qinfifonext);
+	} else {
+		ahc_outb(ahc, KERNEL_QINPOS, ahc->qinfifonext);
+	}
+}
+
+static void
+ahc_qinfifo_requeue(struct ahc_softc *ahc, struct scb *prev_scb,
+		    struct scb *scb)
+{
+	if (prev_scb == NULL) {
+		ahc_outb(ahc, NEXT_QUEUED_SCB, scb->hscb->tag);
+	} else {
+		prev_scb->hscb->next = scb->hscb->tag;
+		ahc_sync_scb(ahc, prev_scb, 
+			     BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+	}
+	ahc->qinfifo[ahc->qinfifonext++] = scb->hscb->tag;
+	scb->hscb->next = ahc->next_queued_scb->hscb->tag;
+	ahc_sync_scb(ahc, scb, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+}
+
+static int
+ahc_qinfifo_count(struct ahc_softc *ahc)
+{
+	u_int8_t qinpos;
+	u_int8_t diff;
+
+	if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+		qinpos = ahc_inb(ahc, SNSCB_QOFF);
+		ahc_outb(ahc, SNSCB_QOFF, qinpos);
+	} else
+		qinpos = ahc_inb(ahc, QINPOS);
+	diff = ahc->qinfifonext - qinpos;
+	return (diff);
+}
+
+int
+ahc_search_qinfifo(struct ahc_softc *ahc, int target, char channel,
+		   int lun, u_int tag, role_t role, uint32_t status,
+		   ahc_search_action action)
+{
+	struct	scb *scb;
+	struct	scb *prev_scb;
+	uint8_t qinstart;
+	uint8_t qinpos;
+	uint8_t qintail;
+	uint8_t next, prev;
+	uint8_t curscbptr;
+	int	found;
+	int	maxtarget;
+	int	i;
+	int	have_qregs;
+
+	qintail = ahc->qinfifonext;
+	have_qregs = (ahc->features & AHC_QUEUE_REGS) != 0;
+	if (have_qregs) {
+		qinstart = ahc_inb(ahc, SNSCB_QOFF);
+		ahc_outb(ahc, SNSCB_QOFF, qinstart);
+	} else
+		qinstart = ahc_inb(ahc, QINPOS);
+	qinpos = qinstart;
+	next = ahc_inb(ahc, NEXT_QUEUED_SCB);
+	found = 0;
+	prev_scb = NULL;
+
+	if (action == SEARCH_COMPLETE) {
+		/*
+		 * Don't attempt to run any queued untagged transactions
+		 * until we are done with the abort process.
+		 */
+		ahc_freeze_untagged_queues(ahc);
+	}
+
+	/*
+	 * Start with an empty queue.  Entries that are not chosen
+	 * for removal will be re-added to the queue as we go.
+	 */
+	ahc->qinfifonext = qinpos;
+	ahc_outb(ahc, NEXT_QUEUED_SCB, ahc->next_queued_scb->hscb->tag);
+
+	while (qinpos != qintail) {
+		scb = ahc_lookup_scb(ahc, ahc->qinfifo[qinpos]);
+		if (scb == NULL) {
+			printf("qinpos = %d, SCB index = %d\n",
+				qinpos, ahc->qinfifo[qinpos]);
+			panic("Loop 1\n");
+		}
+
+		if (ahc_match_scb(ahc, scb, target, channel, lun, tag, role)) {
+			/*
+			 * We found an scb that needs to be acted on.
+			 */
+			found++;
+			switch (action) {
+			case SEARCH_COMPLETE:
+			{
+				cam_status ostat;
+				cam_status cstat;
+
+				ostat = ahc_get_transaction_status(scb);
+				if (ostat == CAM_REQ_INPROG)
+					ahc_set_transaction_status(scb,
+								   status);
+				cstat = ahc_get_transaction_status(scb);
+				if (cstat != CAM_REQ_CMP)
+					ahc_freeze_scb(scb);
+				if ((scb->flags & SCB_ACTIVE) == 0)
+					printf("Inactive SCB in qinfifo\n");
+				ahc_done(ahc, scb);
+
+				/* FALLTHROUGH */
+			case SEARCH_REMOVE:
+				break;
+			}
+			case SEARCH_COUNT:
+				ahc_qinfifo_requeue(ahc, prev_scb, scb);
+				prev_scb = scb;
+				break;
+			}
+		} else {
+			ahc_qinfifo_requeue(ahc, prev_scb, scb);
+			prev_scb = scb;
+		}
+		qinpos++;
+	}
+
+	if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+		ahc_outb(ahc, HNSCB_QOFF, ahc->qinfifonext);
+	} else {
+		ahc_outb(ahc, KERNEL_QINPOS, ahc->qinfifonext);
+	}
+
+	if (action != SEARCH_COUNT
+	 && (found != 0)
+	 && (qinstart != ahc->qinfifonext)) {
+		/*
+		 * The sequencer may be in the process of dmaing
+		 * down the SCB at the beginning of the queue.
+		 * This could be problematic if either the first,
+		 * or the second SCB is removed from the queue
+		 * (the first SCB includes a pointer to the "next"
+		 * SCB to dma). If we have removed any entries, swap
+		 * the first element in the queue with the next HSCB
+		 * so the sequencer will notice that NEXT_QUEUED_SCB
+		 * has changed during its dma attempt and will retry
+		 * the DMA.
+		 */
+		scb = ahc_lookup_scb(ahc, ahc->qinfifo[qinstart]);
+
+		if (scb == NULL) {
+			printf("found = %d, qinstart = %d, qinfifionext = %d\n",
+				found, qinstart, ahc->qinfifonext);
+			panic("First/Second Qinfifo fixup\n");
+		}
+		/*
+		 * ahc_swap_with_next_hscb forces our next pointer to
+		 * point to the reserved SCB for future commands.  Save
+		 * and restore our original next pointer to maintain
+		 * queue integrity.
+		 */
+		next = scb->hscb->next;
+		ahc->scb_data->scbindex[scb->hscb->tag] = NULL;
+		ahc_swap_with_next_hscb(ahc, scb);
+		scb->hscb->next = next;
+		ahc->qinfifo[qinstart] = scb->hscb->tag;
+
+		/* Tell the card about the new head of the qinfifo. */
+		ahc_outb(ahc, NEXT_QUEUED_SCB, scb->hscb->tag);
+
+		/* Fixup the tail "next" pointer. */
+		qintail = ahc->qinfifonext - 1;
+		scb = ahc_lookup_scb(ahc, ahc->qinfifo[qintail]);
+		scb->hscb->next = ahc->next_queued_scb->hscb->tag;
+	}
+
+	/*
+	 * Search waiting for selection list.
+	 */
+	curscbptr = ahc_inb(ahc, SCBPTR);
+	next = ahc_inb(ahc, WAITING_SCBH);  /* Start at head of list. */
+	prev = SCB_LIST_NULL;
+
+	while (next != SCB_LIST_NULL) {
+		uint8_t scb_index;
+
+		ahc_outb(ahc, SCBPTR, next);
+		scb_index = ahc_inb(ahc, SCB_TAG);
+		if (scb_index >= ahc->scb_data->numscbs) {
+			printf("Waiting List inconsistency. "
+			       "SCB index == %d, yet numscbs == %d.",
+			       scb_index, ahc->scb_data->numscbs);
+			ahc_dump_card_state(ahc);
+			panic("for safety");
+		}
+		scb = ahc_lookup_scb(ahc, scb_index);
+		if (scb == NULL) {
+			printf("scb_index = %d, next = %d\n",
+				scb_index, next);
+			panic("Waiting List traversal\n");
+		}
+		if (ahc_match_scb(ahc, scb, target, channel,
+				  lun, SCB_LIST_NULL, role)) {
+			/*
+			 * We found an scb that needs to be acted on.
+			 */
+			found++;
+			switch (action) {
+			case SEARCH_COMPLETE:
+			{
+				cam_status ostat;
+				cam_status cstat;
+
+				ostat = ahc_get_transaction_status(scb);
+				if (ostat == CAM_REQ_INPROG)
+					ahc_set_transaction_status(scb,
+								   status);
+				cstat = ahc_get_transaction_status(scb);
+				if (cstat != CAM_REQ_CMP)
+					ahc_freeze_scb(scb);
+				if ((scb->flags & SCB_ACTIVE) == 0)
+					printf("Inactive SCB in Waiting List\n");
+				ahc_done(ahc, scb);
+				/* FALLTHROUGH */
+			}
+			case SEARCH_REMOVE:
+				next = ahc_rem_wscb(ahc, next, prev);
+				break;
+			case SEARCH_COUNT:
+				prev = next;
+				next = ahc_inb(ahc, SCB_NEXT);
+				break;
+			}
+		} else {
+			
+			prev = next;
+			next = ahc_inb(ahc, SCB_NEXT);
+		}
+	}
+	ahc_outb(ahc, SCBPTR, curscbptr);
+
+	/*
+	 * And lastly, the untagged holding queues.
+	 */
+	i = 0;
+	if ((ahc->flags & AHC_SCB_BTT) == 0) {
+
+		maxtarget = 16;
+		if (target != CAM_TARGET_WILDCARD) {
+
+			i = target;
+			if (channel == 'B')
+				i += 8;
+			maxtarget = i + 1;
+		}
+	} else {
+		maxtarget = 0;
+	}
+
+	for (; i < maxtarget; i++) {
+		struct scb_tailq *untagged_q;
+		struct scb *next_scb;
+
+		untagged_q = &(ahc->untagged_queues[i]);
+		next_scb = TAILQ_FIRST(untagged_q);
+		while (next_scb != NULL) {
+
+			scb = next_scb;
+			next_scb = TAILQ_NEXT(scb, links.tqe);
+
+			/*
+			 * The head of the list may be the currently
+			 * active untagged command for a device.
+			 * We're only searching for commands that
+			 * have not been started.  A transaction
+			 * marked active but still in the qinfifo
+			 * is removed by the qinfifo scanning code
+			 * above.
+			 */
+			if ((scb->flags & SCB_ACTIVE) != 0)
+				continue;
+
+			if (ahc_match_scb(ahc, scb, target, channel,
+					  lun, SCB_LIST_NULL, role)) {
+				/*
+				 * We found an scb that needs to be acted on.
+				 */
+				found++;
+				switch (action) {
+				case SEARCH_COMPLETE:
+				{
+					cam_status ostat;
+					cam_status cstat;
+
+					ostat = ahc_get_transaction_status(scb);
+					if (ostat == CAM_REQ_INPROG)
+						ahc_set_transaction_status(scb,
+								   status);
+					cstat = ahc_get_transaction_status(scb);
+					if (cstat != CAM_REQ_CMP)
+						ahc_freeze_scb(scb);
+					if ((scb->flags & SCB_ACTIVE) == 0)
+						printf("Inactive SCB in untaggedQ\n");
+					ahc_done(ahc, scb);
+					break;
+				}
+				case SEARCH_REMOVE:
+					TAILQ_REMOVE(untagged_q, scb,
+						     links.tqe);
+					break;
+				case SEARCH_COUNT:
+					break;
+				}
+			}
+		}
+	}
+
+	if (action == SEARCH_COMPLETE)
+		ahc_release_untagged_queues(ahc);
+	return (found);
+}
+
+int
+ahc_search_disc_list(struct ahc_softc *ahc, int target, char channel,
+		     int lun, u_int tag, int stop_on_first, int remove,
+		     int save_state)
+{
+	struct	scb *scbp;
+	u_int	next;
+	u_int	prev;
+	u_int	count;
+	u_int	active_scb;
+
+	count = 0;
+	next = ahc_inb(ahc, DISCONNECTED_SCBH);
+	prev = SCB_LIST_NULL;
+
+	if (save_state) {
+		/* restore this when we're done */
+		active_scb = ahc_inb(ahc, SCBPTR);
+	} else
+		/* Silence compiler */
+		active_scb = SCB_LIST_NULL;
+
+	while (next != SCB_LIST_NULL) {
+		u_int scb_index;
+
+		ahc_outb(ahc, SCBPTR, next);
+		scb_index = ahc_inb(ahc, SCB_TAG);
+		if (scb_index >= ahc->scb_data->numscbs) {
+			printf("Disconnected List inconsistency. "
+			       "SCB index == %d, yet numscbs == %d.",
+			       scb_index, ahc->scb_data->numscbs);
+			ahc_dump_card_state(ahc);
+			panic("for safety");
+		}
+
+		if (next == prev) {
+			panic("Disconnected List Loop. "
+			      "cur SCBPTR == %x, prev SCBPTR == %x.",
+			      next, prev);
+		}
+		scbp = ahc_lookup_scb(ahc, scb_index);
+		if (ahc_match_scb(ahc, scbp, target, channel, lun,
+				  tag, ROLE_INITIATOR)) {
+			count++;
+			if (remove) {
+				next =
+				    ahc_rem_scb_from_disc_list(ahc, prev, next);
+			} else {
+				prev = next;
+				next = ahc_inb(ahc, SCB_NEXT);
+			}
+			if (stop_on_first)
+				break;
+		} else {
+			prev = next;
+			next = ahc_inb(ahc, SCB_NEXT);
+		}
+	}
+	if (save_state)
+		ahc_outb(ahc, SCBPTR, active_scb);
+	return (count);
+}
+
+/*
+ * Remove an SCB from the on chip list of disconnected transactions.
+ * This is empty/unused if we are not performing SCB paging.
+ */
+static u_int
+ahc_rem_scb_from_disc_list(struct ahc_softc *ahc, u_int prev, u_int scbptr)
+{
+	u_int next;
+
+	ahc_outb(ahc, SCBPTR, scbptr);
+	next = ahc_inb(ahc, SCB_NEXT);
+
+	ahc_outb(ahc, SCB_CONTROL, 0);
+
+	ahc_add_curscb_to_free_list(ahc);
+
+	if (prev != SCB_LIST_NULL) {
+		ahc_outb(ahc, SCBPTR, prev);
+		ahc_outb(ahc, SCB_NEXT, next);
+	} else
+		ahc_outb(ahc, DISCONNECTED_SCBH, next);
+
+	return (next);
+}
+
+/*
+ * Add the SCB as selected by SCBPTR onto the on chip list of
+ * free hardware SCBs.  This list is empty/unused if we are not
+ * performing SCB paging.
+ */
+static void
+ahc_add_curscb_to_free_list(struct ahc_softc *ahc)
+{
+	/*
+	 * Invalidate the tag so that our abort
+	 * routines don't think it's active.
+	 */
+	ahc_outb(ahc, SCB_TAG, SCB_LIST_NULL);
+
+	if ((ahc->flags & AHC_PAGESCBS) != 0) {
+		ahc_outb(ahc, SCB_NEXT, ahc_inb(ahc, FREE_SCBH));
+		ahc_outb(ahc, FREE_SCBH, ahc_inb(ahc, SCBPTR));
+	}
+}
+
+/*
+ * Manipulate the waiting for selection list and return the
+ * scb that follows the one that we remove.
+ */
+static u_int
+ahc_rem_wscb(struct ahc_softc *ahc, u_int scbpos, u_int prev)
+{       
+	u_int curscb, next;
+
+	/*
+	 * Select the SCB we want to abort and
+	 * pull the next pointer out of it.
+	 */
+	curscb = ahc_inb(ahc, SCBPTR);
+	ahc_outb(ahc, SCBPTR, scbpos);
+	next = ahc_inb(ahc, SCB_NEXT);
+
+	/* Clear the necessary fields */
+	ahc_outb(ahc, SCB_CONTROL, 0);
+
+	ahc_add_curscb_to_free_list(ahc);
+
+	/* update the waiting list */
+	if (prev == SCB_LIST_NULL) {
+		/* First in the list */
+		ahc_outb(ahc, WAITING_SCBH, next); 
+
+		/*
+		 * Ensure we aren't attempting to perform
+		 * selection for this entry.
+		 */
+		ahc_outb(ahc, SCSISEQ, (ahc_inb(ahc, SCSISEQ) & ~ENSELO));
+	} else {
+		/*
+		 * Select the scb that pointed to us 
+		 * and update its next pointer.
+		 */
+		ahc_outb(ahc, SCBPTR, prev);
+		ahc_outb(ahc, SCB_NEXT, next);
+	}
+
+	/*
+	 * Point us back at the original scb position.
+	 */
+	ahc_outb(ahc, SCBPTR, curscb);
+	return next;
+}
+
+/******************************** Error Handling ******************************/
+/*
+ * Abort all SCBs that match the given description (target/channel/lun/tag),
+ * setting their status to the passed in status if the status has not already
+ * been modified from CAM_REQ_INPROG.  This routine assumes that the sequencer
+ * is paused before it is called.
+ */
+int
+ahc_abort_scbs(struct ahc_softc *ahc, int target, char channel,
+	       int lun, u_int tag, role_t role, uint32_t status)
+{
+	struct	scb *scbp;
+	struct	scb *scbp_next;
+	u_int	active_scb;
+	int	i, j;
+	int	maxtarget;
+	int	minlun;
+	int	maxlun;
+
+	int	found;
+
+	/*
+	 * Don't attempt to run any queued untagged transactions
+	 * until we are done with the abort process.
+	 */
+	ahc_freeze_untagged_queues(ahc);
+
+	/* restore this when we're done */
+	active_scb = ahc_inb(ahc, SCBPTR);
+
+	found = ahc_search_qinfifo(ahc, target, channel, lun, SCB_LIST_NULL,
+				   role, CAM_REQUEUE_REQ, SEARCH_COMPLETE);
+
+	/*
+	 * Clean out the busy target table for any untagged commands.
+	 */
+	i = 0;
+	maxtarget = 16;
+	if (target != CAM_TARGET_WILDCARD) {
+		i = target;
+		if (channel == 'B')
+			i += 8;
+		maxtarget = i + 1;
+	}
+
+	if (lun == CAM_LUN_WILDCARD) {
+
+		/*
+		 * Unless we are using an SCB based
+		 * busy targets table, there is only
+		 * one table entry for all luns of
+		 * a target.
+		 */
+		minlun = 0;
+		maxlun = 1;
+		if ((ahc->flags & AHC_SCB_BTT) != 0)
+			maxlun = AHC_NUM_LUNS;
+	} else {
+		minlun = lun;
+		maxlun = lun + 1;
+	}
+
+	if (role != ROLE_TARGET) {
+		for (;i < maxtarget; i++) {
+			for (j = minlun;j < maxlun; j++) {
+				u_int scbid;
+				u_int tcl;
+
+				tcl = BUILD_TCL(i << 4, j);
+				scbid = ahc_index_busy_tcl(ahc, tcl);
+				scbp = ahc_lookup_scb(ahc, scbid);
+				if (scbp == NULL
+				 || ahc_match_scb(ahc, scbp, target, channel,
+						  lun, tag, role) == 0)
+					continue;
+				ahc_unbusy_tcl(ahc, BUILD_TCL(i << 4, j));
+			}
+		}
+
+		/*
+		 * Go through the disconnected list and remove any entries we
+		 * have queued for completion, 0'ing their control byte too.
+		 * We save the active SCB and restore it ourselves, so there
+		 * is no reason for this search to restore it too.
+		 */
+		ahc_search_disc_list(ahc, target, channel, lun, tag,
+				     /*stop_on_first*/FALSE, /*remove*/TRUE,
+				     /*save_state*/FALSE);
+	}
+
+	/*
+	 * Go through the hardware SCB array looking for commands that
+	 * were active but not on any list.  In some cases, these remnants
+	 * might not still have mappings in the scbindex array (e.g. unexpected
+	 * bus free with the same scb queued for an abort).  Don't hold this
+	 * against them.
+	 */
+	for (i = 0; i < ahc->scb_data->maxhscbs; i++) {
+		u_int scbid;
+
+		ahc_outb(ahc, SCBPTR, i);
+		scbid = ahc_inb(ahc, SCB_TAG);
+		scbp = ahc_lookup_scb(ahc, scbid);
+		if ((scbp == NULL && scbid != SCB_LIST_NULL)
+		 || (scbp != NULL
+		  && ahc_match_scb(ahc, scbp, target, channel, lun, tag, role)))
+			ahc_add_curscb_to_free_list(ahc);
+	}
+
+	/*
+	 * Go through the pending CCB list and look for
+	 * commands for this target that are still active.
+	 * These are other tagged commands that were
+	 * disconnected when the reset occurred.
+	 */
+	scbp_next = LIST_FIRST(&ahc->pending_scbs);
+	while (scbp_next != NULL) {
+		scbp = scbp_next;
+		scbp_next = LIST_NEXT(scbp, pending_links);
+		if (ahc_match_scb(ahc, scbp, target, channel, lun, tag, role)) {
+			cam_status ostat;
+
+			ostat = ahc_get_transaction_status(scbp);
+			if (ostat == CAM_REQ_INPROG)
+				ahc_set_transaction_status(scbp, status);
+			if (ahc_get_transaction_status(scbp) != CAM_REQ_CMP)
+				ahc_freeze_scb(scbp);
+			if ((scbp->flags & SCB_ACTIVE) == 0)
+				printf("Inactive SCB on pending list\n");
+			ahc_done(ahc, scbp);
+			found++;
+		}
+	}
+	ahc_outb(ahc, SCBPTR, active_scb);
+	ahc_platform_abort_scbs(ahc, target, channel, lun, tag, role, status);
+	ahc_release_untagged_queues(ahc);
+	return found;
+}
+
+static void
+ahc_reset_current_bus(struct ahc_softc *ahc)
+{
+	uint8_t scsiseq;
+
+	ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENSCSIRST);
+	scsiseq = ahc_inb(ahc, SCSISEQ);
+	ahc_outb(ahc, SCSISEQ, scsiseq | SCSIRSTO);
+	ahc_delay(AHC_BUSRESET_DELAY);
+	/* Turn off the bus reset */
+	ahc_outb(ahc, SCSISEQ, scsiseq & ~SCSIRSTO);
+
+	ahc_clear_intstat(ahc);
+
+	/* Re-enable reset interrupts */
+	ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) | ENSCSIRST);
+}
+
+int
+ahc_reset_channel(struct ahc_softc *ahc, char channel, int initiate_reset)
+{
+	struct	ahc_devinfo devinfo;
+	u_int	initiator, target, max_scsiid;
+	u_int	sblkctl;
+	u_int	scsiseq;
+	u_int	simode1;
+	int	found;
+	int	restart_needed;
+	char	cur_channel;
+
+	ahc->pending_device = NULL;
+
+	ahc_compile_devinfo(&devinfo,
+			    CAM_TARGET_WILDCARD,
+			    CAM_TARGET_WILDCARD,
+			    CAM_LUN_WILDCARD,
+			    channel, ROLE_UNKNOWN);
+	ahc_pause(ahc);
+
+	/* Make sure the sequencer is in a safe location. */
+	ahc_clear_critical_section(ahc);
+
+	/*
+	 * Run our command complete fifos to ensure that we perform
+	 * completion processing on any commands that 'completed'
+	 * before the reset occurred.
+	 */
+	ahc_run_qoutfifo(ahc);
+#if AHC_TARGET_MODE
+	if ((ahc->flags & AHC_TARGETROLE) != 0) {
+		ahc_run_tqinfifo(ahc, /*paused*/TRUE);
+	}
+#endif
+
+	/*
+	 * Reset the bus if we are initiating this reset
+	 */
+	sblkctl = ahc_inb(ahc, SBLKCTL);
+	cur_channel = 'A';
+	if ((ahc->features & AHC_TWIN) != 0
+	 && ((sblkctl & SELBUSB) != 0))
+	    cur_channel = 'B';
+	scsiseq = ahc_inb(ahc, SCSISEQ_TEMPLATE);
+	if (cur_channel != channel) {
+		/* Case 1: Command for another bus is active
+		 * Stealthily reset the other bus without
+		 * upsetting the current bus.
+		 */
+		ahc_outb(ahc, SBLKCTL, sblkctl ^ SELBUSB);
+		simode1 = ahc_inb(ahc, SIMODE1) & ~(ENBUSFREE|ENSCSIRST);
+		ahc_outb(ahc, SIMODE1, simode1);
+		if (initiate_reset)
+			ahc_reset_current_bus(ahc);
+		ahc_clear_intstat(ahc);
+#if AHC_TARGET_MODE
+		/*
+		 * Bus resets clear ENSELI, so we cannot
+		 * defer re-enabling bus reset interrupts
+		 * if we are in target mode.
+		 */
+		if ((ahc->flags & AHC_TARGETROLE) != 0)
+			ahc_outb(ahc, SIMODE1, simode1 | ENSCSIRST);
+#endif
+		ahc_outb(ahc, SCSISEQ, scsiseq & (ENSELI|ENRSELI|ENAUTOATNP));
+		ahc_outb(ahc, SBLKCTL, sblkctl);
+		restart_needed = FALSE;
+	} else {
+		/* Case 2: A command from this bus is active or we're idle */
+		ahc_clear_msg_state(ahc);
+		simode1 = ahc_inb(ahc, SIMODE1) & ~(ENBUSFREE|ENSCSIRST);
+		ahc_outb(ahc, SIMODE1, simode1);
+		if (initiate_reset)
+			ahc_reset_current_bus(ahc);
+		ahc_clear_intstat(ahc);
+#if AHC_TARGET_MODE
+		/*
+		 * Bus resets clear ENSELI, so we cannot
+		 * defer re-enabling bus reset interrupts
+		 * if we are in target mode.
+		 */
+		if ((ahc->flags & AHC_TARGETROLE) != 0)
+			ahc_outb(ahc, SIMODE1, simode1 | ENSCSIRST);
+#endif
+		ahc_outb(ahc, SCSISEQ, scsiseq & (ENSELI|ENRSELI|ENAUTOATNP));
+		restart_needed = TRUE;
+	}
+
+	/*
+	 * Clean up all the state information for the
+	 * pending transactions on this bus.
+	 */
+	found = ahc_abort_scbs(ahc, CAM_TARGET_WILDCARD, channel,
+			       CAM_LUN_WILDCARD, SCB_LIST_NULL,
+			       ROLE_UNKNOWN, CAM_SCSI_BUS_RESET);
+
+	max_scsiid = (ahc->features & AHC_WIDE) ? 15 : 7;
+
+#ifdef AHC_TARGET_MODE
+	/*
+	 * Send an immediate notify ccb to all target more peripheral
+	 * drivers affected by this action.
+	 */
+	for (target = 0; target <= max_scsiid; target++) {
+		struct ahc_tmode_tstate* tstate;
+		u_int lun;
+
+		tstate = ahc->enabled_targets[target];
+		if (tstate == NULL)
+			continue;
+		for (lun = 0; lun < AHC_NUM_LUNS; lun++) {
+			struct ahc_tmode_lstate* lstate;
+
+			lstate = tstate->enabled_luns[lun];
+			if (lstate == NULL)
+				continue;
+
+			ahc_queue_lstate_event(ahc, lstate, CAM_TARGET_WILDCARD,
+					       EVENT_TYPE_BUS_RESET, /*arg*/0);
+			ahc_send_lstate_events(ahc, lstate);
+		}
+	}
+#endif
+	/* Notify the XPT that a bus reset occurred */
+	ahc_send_async(ahc, devinfo.channel, CAM_TARGET_WILDCARD,
+		       CAM_LUN_WILDCARD, AC_BUS_RESET, NULL);
+
+	/*
+	 * Revert to async/narrow transfers until we renegotiate.
+	 */
+	for (target = 0; target <= max_scsiid; target++) {
+
+		if (ahc->enabled_targets[target] == NULL)
+			continue;
+		for (initiator = 0; initiator <= max_scsiid; initiator++) {
+			struct ahc_devinfo devinfo;
+
+			ahc_compile_devinfo(&devinfo, target, initiator,
+					    CAM_LUN_WILDCARD,
+					    channel, ROLE_UNKNOWN);
+			ahc_set_width(ahc, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+				      AHC_TRANS_CUR, /*paused*/TRUE);
+			ahc_set_syncrate(ahc, &devinfo, /*syncrate*/NULL,
+					 /*period*/0, /*offset*/0,
+					 /*ppr_options*/0, AHC_TRANS_CUR,
+					 /*paused*/TRUE);
+		}
+	}
+
+	if (restart_needed)
+		ahc_restart(ahc);
+	else
+		ahc_unpause(ahc);
+	return found;
+}
+
+
+/***************************** Residual Processing ****************************/
+/*
+ * Calculate the residual for a just completed SCB.
+ */
+void
+ahc_calc_residual(struct scb *scb)
+{
+	struct hardware_scb *hscb;
+	struct status_pkt *spkt;
+	uint32_t sgptr;
+	uint32_t resid_sgptr;
+	uint32_t resid;
+
+	/*
+	 * 5 cases.
+	 * 1) No residual.
+	 *    SG_RESID_VALID clear in sgptr.
+	 * 2) Transferless command
+	 * 3) Never performed any transfers.
+	 *    sgptr has SG_FULL_RESID set.
+	 * 4) No residual but target did not
+	 *    save data pointers after the
+	 *    last transfer, so sgptr was
+	 *    never updated.
+	 * 5) We have a partial residual.
+	 *    Use residual_sgptr to determine
+	 *    where we are.
+	 */
+
+	hscb = scb->hscb;
+	sgptr = ahc_le32toh(hscb->sgptr);
+	if ((sgptr & SG_RESID_VALID) == 0)
+		/* Case 1 */
+		return;
+	sgptr &= ~SG_RESID_VALID;
+
+	if ((sgptr & SG_LIST_NULL) != 0)
+		/* Case 2 */
+		return;
+
+	spkt = &hscb->shared_data.status;
+	resid_sgptr = ahc_le32toh(spkt->residual_sg_ptr);
+	if ((sgptr & SG_FULL_RESID) != 0) {
+		/* Case 3 */
+		resid = ahc_get_transfer_length(scb);
+	} else if ((resid_sgptr & SG_LIST_NULL) != 0) {
+		/* Case 4 */
+		return;
+	} else if ((resid_sgptr & ~SG_PTR_MASK) != 0) {
+		panic("Bogus resid sgptr value 0x%x\n", resid_sgptr);
+	} else {
+		struct ahc_dma_seg *sg;
+
+		/*
+		 * Remainder of the SG where the transfer
+		 * stopped.  
+		 */
+		resid = ahc_le32toh(spkt->residual_datacnt) & AHC_SG_LEN_MASK;
+		sg = ahc_sg_bus_to_virt(scb, resid_sgptr & SG_PTR_MASK);
+
+		/* The residual sg_ptr always points to the next sg */
+		sg--;
+
+		/*
+		 * Add up the contents of all residual
+		 * SG segments that are after the SG where
+		 * the transfer stopped.
+		 */
+		while ((ahc_le32toh(sg->len) & AHC_DMA_LAST_SEG) == 0) {
+			sg++;
+			resid += ahc_le32toh(sg->len) & AHC_SG_LEN_MASK;
+		}
+	}
+	if ((scb->flags & SCB_SENSE) == 0)
+		ahc_set_residual(scb, resid);
+	else
+		ahc_set_sense_residual(scb, resid);
+
+#ifdef AHC_DEBUG
+	if ((ahc_debug & AHC_SHOWMISC) != 0) {
+		ahc_print_path(ahc, scb);
+		printf("Handled Residual of %d bytes\n", resid);
+	}
+#endif
+}
+
+/******************************* Target Mode **********************************/
+#ifdef AHC_TARGET_MODE
+/*
+ * Add a target mode event to this lun's queue
+ */
+static void
+ahc_queue_lstate_event(struct ahc_softc *ahc, struct ahc_tmode_lstate *lstate,
+		       u_int initiator_id, u_int event_type, u_int event_arg)
+{
+	struct ahc_tmode_event *event;
+	int pending;
+
+	xpt_freeze_devq(lstate->path, /*count*/1);
+	if (lstate->event_w_idx >= lstate->event_r_idx)
+		pending = lstate->event_w_idx - lstate->event_r_idx;
+	else
+		pending = AHC_TMODE_EVENT_BUFFER_SIZE + 1
+			- (lstate->event_r_idx - lstate->event_w_idx);
+
+	if (event_type == EVENT_TYPE_BUS_RESET
+	 || event_type == MSG_BUS_DEV_RESET) {
+		/*
+		 * Any earlier events are irrelevant, so reset our buffer.
+		 * This has the effect of allowing us to deal with reset
+		 * floods (an external device holding down the reset line)
+		 * without losing the event that is really interesting.
+		 */
+		lstate->event_r_idx = 0;
+		lstate->event_w_idx = 0;
+		xpt_release_devq(lstate->path, pending, /*runqueue*/FALSE);
+	}
+
+	if (pending == AHC_TMODE_EVENT_BUFFER_SIZE) {
+		xpt_print_path(lstate->path);
+		printf("immediate event %x:%x lost\n",
+		       lstate->event_buffer[lstate->event_r_idx].event_type,
+		       lstate->event_buffer[lstate->event_r_idx].event_arg);
+		lstate->event_r_idx++;
+		if (lstate->event_r_idx == AHC_TMODE_EVENT_BUFFER_SIZE)
+			lstate->event_r_idx = 0;
+		xpt_release_devq(lstate->path, /*count*/1, /*runqueue*/FALSE);
+	}
+
+	event = &lstate->event_buffer[lstate->event_w_idx];
+	event->initiator_id = initiator_id;
+	event->event_type = event_type;
+	event->event_arg = event_arg;
+	lstate->event_w_idx++;
+	if (lstate->event_w_idx == AHC_TMODE_EVENT_BUFFER_SIZE)
+		lstate->event_w_idx = 0;
+}
+
+/*
+ * Send any target mode events queued up waiting
+ * for immediate notify resources.
+ */
+void
+ahc_send_lstate_events(struct ahc_softc *ahc, struct ahc_tmode_lstate *lstate)
+{
+	struct ccb_hdr *ccbh;
+	struct ccb_immed_notify *inot;
+
+	while (lstate->event_r_idx != lstate->event_w_idx
+	    && (ccbh = SLIST_FIRST(&lstate->immed_notifies)) != NULL) {
+		struct ahc_tmode_event *event;
+
+		event = &lstate->event_buffer[lstate->event_r_idx];
+		SLIST_REMOVE_HEAD(&lstate->immed_notifies, sim_links.sle);
+		inot = (struct ccb_immed_notify *)ccbh;
+		switch (event->event_type) {
+		case EVENT_TYPE_BUS_RESET:
+			ccbh->status = CAM_SCSI_BUS_RESET|CAM_DEV_QFRZN;
+			break;
+		default:
+			ccbh->status = CAM_MESSAGE_RECV|CAM_DEV_QFRZN;
+			inot->message_args[0] = event->event_type;
+			inot->message_args[1] = event->event_arg;
+			break;
+		}
+		inot->initiator_id = event->initiator_id;
+		inot->sense_len = 0;
+		xpt_done((union ccb *)inot);
+		lstate->event_r_idx++;
+		if (lstate->event_r_idx == AHC_TMODE_EVENT_BUFFER_SIZE)
+			lstate->event_r_idx = 0;
+	}
+}
+#endif
+
+/******************** Sequencer Program Patching/Download *********************/
+
+#ifdef AHC_DUMP_SEQ
+void
+ahc_dumpseq(struct ahc_softc* ahc)
+{
+	int i;
+	int max_prog;
+
+	if ((ahc->chip & AHC_BUS_MASK) < AHC_PCI)
+		max_prog = 448;
+	else if ((ahc->features & AHC_ULTRA2) != 0)
+		max_prog = 768;
+	else
+		max_prog = 512;
+
+	ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS|FASTMODE|LOADRAM);
+	ahc_outb(ahc, SEQADDR0, 0);
+	ahc_outb(ahc, SEQADDR1, 0);
+	for (i = 0; i < max_prog; i++) {
+		uint8_t ins_bytes[4];
+
+		ahc_insb(ahc, SEQRAM, ins_bytes, 4);
+		printf("0x%08x\n", ins_bytes[0] << 24
+				 | ins_bytes[1] << 16
+				 | ins_bytes[2] << 8
+				 | ins_bytes[3]);
+	}
+}
+#endif
+
+static void
+ahc_loadseq(struct ahc_softc *ahc)
+{
+	struct	cs cs_table[num_critical_sections];
+	u_int	begin_set[num_critical_sections];
+	u_int	end_set[num_critical_sections];
+	struct	patch *cur_patch;
+	u_int	cs_count;
+	u_int	cur_cs;
+	u_int	i;
+	int	downloaded;
+	u_int	skip_addr;
+	u_int	sg_prefetch_cnt;
+	uint8_t	download_consts[7];
+
+	/*
+	 * Start out with 0 critical sections
+	 * that apply to this firmware load.
+	 */
+	cs_count = 0;
+	cur_cs = 0;
+	memset(begin_set, 0, sizeof(begin_set));
+	memset(end_set, 0, sizeof(end_set));
+
+	/* Setup downloadable constant table */
+	download_consts[QOUTFIFO_OFFSET] = 0;
+	if (ahc->targetcmds != NULL)
+		download_consts[QOUTFIFO_OFFSET] += 32;
+	download_consts[QINFIFO_OFFSET] = download_consts[QOUTFIFO_OFFSET] + 1;
+	download_consts[CACHESIZE_MASK] = ahc->pci_cachesize - 1;
+	download_consts[INVERTED_CACHESIZE_MASK] = ~(ahc->pci_cachesize - 1);
+	sg_prefetch_cnt = ahc->pci_cachesize;
+	if (sg_prefetch_cnt < (2 * sizeof(struct ahc_dma_seg)))
+		sg_prefetch_cnt = 2 * sizeof(struct ahc_dma_seg);
+	download_consts[SG_PREFETCH_CNT] = sg_prefetch_cnt;
+	download_consts[SG_PREFETCH_ALIGN_MASK] = ~(sg_prefetch_cnt - 1);
+	download_consts[SG_PREFETCH_ADDR_MASK] = (sg_prefetch_cnt - 1);
+
+	cur_patch = patches;
+	downloaded = 0;
+	skip_addr = 0;
+	ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS|FASTMODE|LOADRAM);
+	ahc_outb(ahc, SEQADDR0, 0);
+	ahc_outb(ahc, SEQADDR1, 0);
+
+	for (i = 0; i < sizeof(seqprog)/4; i++) {
+		if (ahc_check_patch(ahc, &cur_patch, i, &skip_addr) == 0) {
+			/*
+			 * Don't download this instruction as it
+			 * is in a patch that was removed.
+			 */
+			continue;
+		}
+		/*
+		 * Move through the CS table until we find a CS
+		 * that might apply to this instruction.
+		 */
+		for (; cur_cs < num_critical_sections; cur_cs++) {
+			if (critical_sections[cur_cs].end <= i) {
+				if (begin_set[cs_count] == TRUE
+				 && end_set[cs_count] == FALSE) {
+					cs_table[cs_count].end = downloaded;
+				 	end_set[cs_count] = TRUE;
+					cs_count++;
+				}
+				continue;
+			}
+			if (critical_sections[cur_cs].begin <= i
+			 && begin_set[cs_count] == FALSE) {
+				cs_table[cs_count].begin = downloaded;
+				begin_set[cs_count] = TRUE;
+			}
+			break;
+		}
+		ahc_download_instr(ahc, i, download_consts);
+		downloaded++;
+	}
+
+	ahc->num_critical_sections = cs_count;
+	if (cs_count != 0) {
+
+		cs_count *= sizeof(struct cs);
+		ahc->critical_sections = malloc(cs_count, M_DEVBUF, M_NOWAIT);
+		if (ahc->critical_sections == NULL)
+			panic("ahc_loadseq: Could not malloc");
+		memcpy(ahc->critical_sections, cs_table, cs_count);
+	}
+	ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS|FASTMODE);
+	ahc_restart(ahc);
+
+	if (bootverbose)
+		printf(" %d instructions downloaded\n", downloaded);
+}
+
+static int
+ahc_check_patch(struct ahc_softc *ahc, struct patch **start_patch,
+		u_int start_instr, u_int *skip_addr)
+{
+	struct	patch *cur_patch;
+	struct	patch *last_patch;
+	u_int	num_patches;
+
+	num_patches = sizeof(patches)/sizeof(struct patch);
+	last_patch = &patches[num_patches];
+	cur_patch = *start_patch;
+
+	while (cur_patch < last_patch && start_instr == cur_patch->begin) {
+
+		if (cur_patch->patch_func(ahc) == 0) {
+
+			/* Start rejecting code */
+			*skip_addr = start_instr + cur_patch->skip_instr;
+			cur_patch += cur_patch->skip_patch;
+		} else {
+			/* Accepted this patch.  Advance to the next
+			 * one and wait for our intruction pointer to
+			 * hit this point.
+			 */
+			cur_patch++;
+		}
+	}
+
+	*start_patch = cur_patch;
+	if (start_instr < *skip_addr)
+		/* Still skipping */
+		return (0);
+
+	return (1);
+}
+
+static void
+ahc_download_instr(struct ahc_softc *ahc, u_int instrptr, uint8_t *dconsts)
+{
+	union	ins_formats instr;
+	struct	ins_format1 *fmt1_ins;
+	struct	ins_format3 *fmt3_ins;
+	u_int	opcode;
+
+	/*
+	 * The firmware is always compiled into a little endian format.
+	 */
+	instr.integer = ahc_le32toh(*(uint32_t*)&seqprog[instrptr * 4]);
+
+	fmt1_ins = &instr.format1;
+	fmt3_ins = NULL;
+
+	/* Pull the opcode */
+	opcode = instr.format1.opcode;
+	switch (opcode) {
+	case AIC_OP_JMP:
+	case AIC_OP_JC:
+	case AIC_OP_JNC:
+	case AIC_OP_CALL:
+	case AIC_OP_JNE:
+	case AIC_OP_JNZ:
+	case AIC_OP_JE:
+	case AIC_OP_JZ:
+	{
+		struct patch *cur_patch;
+		int address_offset;
+		u_int address;
+		u_int skip_addr;
+		u_int i;
+
+		fmt3_ins = &instr.format3;
+		address_offset = 0;
+		address = fmt3_ins->address;
+		cur_patch = patches;
+		skip_addr = 0;
+
+		for (i = 0; i < address;) {
+
+			ahc_check_patch(ahc, &cur_patch, i, &skip_addr);
+
+			if (skip_addr > i) {
+				int end_addr;
+
+				end_addr = MIN(address, skip_addr);
+				address_offset += end_addr - i;
+				i = skip_addr;
+			} else {
+				i++;
+			}
+		}
+		address -= address_offset;
+		fmt3_ins->address = address;
+		/* FALLTHROUGH */
+	}
+	case AIC_OP_OR:
+	case AIC_OP_AND:
+	case AIC_OP_XOR:
+	case AIC_OP_ADD:
+	case AIC_OP_ADC:
+	case AIC_OP_BMOV:
+		if (fmt1_ins->parity != 0) {
+			fmt1_ins->immediate = dconsts[fmt1_ins->immediate];
+		}
+		fmt1_ins->parity = 0;
+		if ((ahc->features & AHC_CMD_CHAN) == 0
+		 && opcode == AIC_OP_BMOV) {
+			/*
+			 * Block move was added at the same time
+			 * as the command channel.  Verify that
+			 * this is only a move of a single element
+			 * and convert the BMOV to a MOV
+			 * (AND with an immediate of FF).
+			 */
+			if (fmt1_ins->immediate != 1)
+				panic("%s: BMOV not supported\n",
+				      ahc_name(ahc));
+			fmt1_ins->opcode = AIC_OP_AND;
+			fmt1_ins->immediate = 0xff;
+		}
+		/* FALLTHROUGH */
+	case AIC_OP_ROL:
+		if ((ahc->features & AHC_ULTRA2) != 0) {
+			int i, count;
+
+			/* Calculate odd parity for the instruction */
+			for (i = 0, count = 0; i < 31; i++) {
+				uint32_t mask;
+
+				mask = 0x01 << i;
+				if ((instr.integer & mask) != 0)
+					count++;
+			}
+			if ((count & 0x01) == 0)
+				instr.format1.parity = 1;
+		} else {
+			/* Compress the instruction for older sequencers */
+			if (fmt3_ins != NULL) {
+				instr.integer =
+					fmt3_ins->immediate
+				      | (fmt3_ins->source << 8)
+				      | (fmt3_ins->address << 16)
+				      |	(fmt3_ins->opcode << 25);
+			} else {
+				instr.integer =
+					fmt1_ins->immediate
+				      | (fmt1_ins->source << 8)
+				      | (fmt1_ins->destination << 16)
+				      |	(fmt1_ins->ret << 24)
+				      |	(fmt1_ins->opcode << 25);
+			}
+		}
+		/* The sequencer is a little endian cpu */
+		instr.integer = ahc_htole32(instr.integer);
+		ahc_outsb(ahc, SEQRAM, instr.bytes, 4);
+		break;
+	default:
+		panic("Unknown opcode encountered in seq program");
+		break;
+	}
+}
+
+void
+ahc_dump_card_state(struct ahc_softc *ahc)
+{
+	struct scb *scb;
+	struct scb_tailq *untagged_q;
+	int target;
+	int maxtarget;
+	int i;
+	uint8_t last_phase;
+	uint8_t qinpos;
+	uint8_t qintail;
+	uint8_t qoutpos;
+	uint8_t scb_index;
+	uint8_t saved_scbptr;
+
+	saved_scbptr = ahc_inb(ahc, SCBPTR);
+
+	last_phase = ahc_inb(ahc, LASTPHASE);
+	printf("%s: Dumping Card State %s, at SEQADDR 0x%x\n",
+	       ahc_name(ahc), ahc_lookup_phase_entry(last_phase)->phasemsg,
+	       ahc_inb(ahc, SEQADDR0) | (ahc_inb(ahc, SEQADDR1) << 8));
+	printf("ACCUM = 0x%x, SINDEX = 0x%x, DINDEX = 0x%x, ARG_2 = 0x%x\n",
+	       ahc_inb(ahc, ACCUM), ahc_inb(ahc, SINDEX), ahc_inb(ahc, DINDEX),
+	       ahc_inb(ahc, ARG_2));
+	printf("HCNT = 0x%x\n", ahc_inb(ahc, HCNT));
+	printf("SCSISEQ = 0x%x, SBLKCTL = 0x%x\n",
+	       ahc_inb(ahc, SCSISEQ), ahc_inb(ahc, SBLKCTL));
+	printf(" DFCNTRL = 0x%x, DFSTATUS = 0x%x\n",
+	       ahc_inb(ahc, DFCNTRL), ahc_inb(ahc, DFSTATUS));
+	printf("LASTPHASE = 0x%x, SCSISIGI = 0x%x, SXFRCTL0 = 0x%x\n",
+	       last_phase, ahc_inb(ahc, SCSISIGI), ahc_inb(ahc, SXFRCTL0));
+	printf("SSTAT0 = 0x%x, SSTAT1 = 0x%x\n",
+	       ahc_inb(ahc, SSTAT0), ahc_inb(ahc, SSTAT1));
+	if ((ahc->features & AHC_DT) != 0)
+		printf("SCSIPHASE = 0x%x\n", ahc_inb(ahc, SCSIPHASE));
+	printf("STACK == 0x%x, 0x%x, 0x%x, 0x%x\n",
+		ahc_inb(ahc, STACK) | (ahc_inb(ahc, STACK) << 8),
+		ahc_inb(ahc, STACK) | (ahc_inb(ahc, STACK) << 8),
+		ahc_inb(ahc, STACK) | (ahc_inb(ahc, STACK) << 8),
+		ahc_inb(ahc, STACK) | (ahc_inb(ahc, STACK) << 8));
+	printf("SCB count = %d\n", ahc->scb_data->numscbs);
+	printf("Kernel NEXTQSCB = %d\n", ahc->next_queued_scb->hscb->tag);
+	printf("Card NEXTQSCB = %d\n", ahc_inb(ahc, NEXT_QUEUED_SCB));
+	/* QINFIFO */
+	printf("QINFIFO entries: ");
+	if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+		qinpos = ahc_inb(ahc, SNSCB_QOFF);
+		ahc_outb(ahc, SNSCB_QOFF, qinpos);
+	} else
+		qinpos = ahc_inb(ahc, QINPOS);
+	qintail = ahc->qinfifonext;
+	while (qinpos != qintail) {
+		printf("%d ", ahc->qinfifo[qinpos]);
+		qinpos++;
+	}
+	printf("\n");
+
+	printf("Waiting Queue entries: ");
+	scb_index = ahc_inb(ahc, WAITING_SCBH);
+	i = 0;
+	while (scb_index != SCB_LIST_NULL && i++ < 256) {
+		ahc_outb(ahc, SCBPTR, scb_index);
+		printf("%d:%d ", scb_index, ahc_inb(ahc, SCB_TAG));
+		scb_index = ahc_inb(ahc, SCB_NEXT);
+	}
+	printf("\n");
+
+	printf("Disconnected Queue entries: ");
+	scb_index = ahc_inb(ahc, DISCONNECTED_SCBH);
+	i = 0;
+	while (scb_index != SCB_LIST_NULL && i++ < 256) {
+		ahc_outb(ahc, SCBPTR, scb_index);
+		printf("%d:%d ", scb_index, ahc_inb(ahc, SCB_TAG));
+		scb_index = ahc_inb(ahc, SCB_NEXT);
+	}
+	printf("\n");
+		
+	ahc_sync_qoutfifo(ahc, BUS_DMASYNC_POSTREAD);
+	printf("QOUTFIFO entries: ");
+	qoutpos = ahc->qoutfifonext;
+	i = 0;
+	while (ahc->qoutfifo[qoutpos] != SCB_LIST_NULL && i++ < 256) {
+		printf("%d ", ahc->qoutfifo[qoutpos]);
+		qoutpos++;
+	}
+	printf("\n");
+
+	printf("Sequencer Free SCB List: ");
+	scb_index = ahc_inb(ahc, FREE_SCBH);
+	i = 0;
+	while (scb_index != SCB_LIST_NULL && i++ < 256) {
+		ahc_outb(ahc, SCBPTR, scb_index);
+		printf("%d ", scb_index);
+		scb_index = ahc_inb(ahc, SCB_NEXT);
+	}
+	printf("\n");
+
+	printf("Pending list: ");
+	i = 0;
+	LIST_FOREACH(scb, &ahc->pending_scbs, pending_links) {
+		if (i++ > 256)
+			break;
+		if (scb != LIST_FIRST(&ahc->pending_scbs))
+			printf(", ");
+		printf("%d", scb->hscb->tag);
+		if ((ahc->flags & AHC_PAGESCBS) == 0) {
+			ahc_outb(ahc, SCBPTR, scb->hscb->tag);
+			printf("(0x%x, 0x%x)", ahc_inb(ahc, SCB_CONTROL),
+			       ahc_inb(ahc, SCB_TAG));
+		}
+	}
+	printf("\n");
+
+	printf("Kernel Free SCB list: ");
+	i = 0;
+	SLIST_FOREACH(scb, &ahc->scb_data->free_scbs, links.sle) {
+		if (i++ > 256)
+			break;
+		printf("%d ", scb->hscb->tag);
+	}
+	printf("\n");
+
+	maxtarget = (ahc->features & (AHC_WIDE|AHC_TWIN)) ? 15 : 7;
+	for (target = 0; target <= maxtarget; target++) {
+		untagged_q = &ahc->untagged_queues[target];
+		if (TAILQ_FIRST(untagged_q) == NULL)
+			continue;
+		printf("Untagged Q(%d): ", target);
+		i = 0;
+		TAILQ_FOREACH(scb, untagged_q, links.tqe) {
+			if (i++ > 256)
+				break;
+			printf("%d ", scb->hscb->tag);
+		}
+		printf("\n");
+	}
+
+	ahc_platform_dump_card_state(ahc);
+	ahc_outb(ahc, SCBPTR, saved_scbptr);
+}
+
+/************************* Target Mode ****************************************/
+#ifdef AHC_TARGET_MODE
+cam_status
+ahc_find_tmode_devs(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb,
+		    struct ahc_tmode_tstate **tstate,
+		    struct ahc_tmode_lstate **lstate,
+		    int notfound_failure)
+{
+
+	if ((ahc->features & AHC_TARGETMODE) == 0)
+		return (CAM_REQ_INVALID);
+
+	/*
+	 * Handle the 'black hole' device that sucks up
+	 * requests to unattached luns on enabled targets.
+	 */
+	if (ccb->ccb_h.target_id == CAM_TARGET_WILDCARD
+	 && ccb->ccb_h.target_lun == CAM_LUN_WILDCARD) {
+		*tstate = NULL;
+		*lstate = ahc->black_hole;
+	} else {
+		u_int max_id;
+
+		max_id = (ahc->features & AHC_WIDE) ? 15 : 7;
+		if (ccb->ccb_h.target_id > max_id)
+			return (CAM_TID_INVALID);
+
+		if (ccb->ccb_h.target_lun >= AHC_NUM_LUNS)
+			return (CAM_LUN_INVALID);
+
+		*tstate = ahc->enabled_targets[ccb->ccb_h.target_id];
+		*lstate = NULL;
+		if (*tstate != NULL)
+			*lstate =
+			    (*tstate)->enabled_luns[ccb->ccb_h.target_lun];
+	}
+
+	if (notfound_failure != 0 && *lstate == NULL)
+		return (CAM_PATH_INVALID);
+
+	return (CAM_REQ_CMP);
+}
+
+void
+ahc_handle_en_lun(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb)
+{
+	struct	   ahc_tmode_tstate *tstate;
+	struct	   ahc_tmode_lstate *lstate;
+	struct	   ccb_en_lun *cel;
+	cam_status status;
+	u_int	   target;
+	u_int	   lun;
+	u_int	   target_mask;
+	u_long	   s;
+	char	   channel;
+
+	status = ahc_find_tmode_devs(ahc, sim, ccb, &tstate, &lstate,
+				     /*notfound_failure*/FALSE);
+
+	if (status != CAM_REQ_CMP) {
+		ccb->ccb_h.status = status;
+		return;
+	}
+
+	if ((ahc->features & AHC_MULTIROLE) != 0) {
+		u_int	   our_id;
+
+		if (cam_sim_bus(sim) == 0)
+			our_id = ahc->our_id;
+		else
+			our_id = ahc->our_id_b;
+
+		if (ccb->ccb_h.target_id != our_id) {
+			if ((ahc->features & AHC_MULTI_TID) != 0
+		   	 && (ahc->flags & AHC_INITIATORROLE) != 0) {
+				/*
+				 * Only allow additional targets if
+				 * the initiator role is disabled.
+				 * The hardware cannot handle a re-select-in
+				 * on the initiator id during a re-select-out
+				 * on a different target id.
+				 */
+				status = CAM_TID_INVALID;
+			} else if ((ahc->flags & AHC_INITIATORROLE) != 0
+				|| ahc->enabled_luns > 0) {
+				/*
+				 * Only allow our target id to change
+				 * if the initiator role is not configured
+				 * and there are no enabled luns which
+				 * are attached to the currently registered
+				 * scsi id.
+				 */
+				status = CAM_TID_INVALID;
+			}
+		}
+	}
+
+	if (status != CAM_REQ_CMP) {
+		ccb->ccb_h.status = status;
+		return;
+	}
+
+	/*
+	 * We now have an id that is valid.
+	 * If we aren't in target mode, switch modes.
+	 */
+	if ((ahc->flags & AHC_TARGETROLE) == 0
+	 && ccb->ccb_h.target_id != CAM_TARGET_WILDCARD) {
+		u_long	s;
+
+		printf("Configuring Target Mode\n");
+		ahc_lock(ahc, &s);
+		if (LIST_FIRST(&ahc->pending_scbs) != NULL) {
+			ccb->ccb_h.status = CAM_BUSY;
+			ahc_unlock(ahc, &s);
+			return;
+		}
+		ahc->flags |= AHC_TARGETROLE;
+		if ((ahc->features & AHC_MULTIROLE) == 0)
+			ahc->flags &= ~AHC_INITIATORROLE;
+		ahc_pause(ahc);
+		ahc_loadseq(ahc);
+		ahc_unlock(ahc, &s);
+	}
+	cel = &ccb->cel;
+	target = ccb->ccb_h.target_id;
+	lun = ccb->ccb_h.target_lun;
+	channel = SIM_CHANNEL(ahc, sim);
+	target_mask = 0x01 << target;
+	if (channel == 'B')
+		target_mask <<= 8;
+
+	if (cel->enable != 0) {
+		u_int scsiseq;
+
+		/* Are we already enabled?? */
+		if (lstate != NULL) {
+			xpt_print_path(ccb->ccb_h.path);
+			printf("Lun already enabled\n");
+			ccb->ccb_h.status = CAM_LUN_ALRDY_ENA;
+			return;
+		}
+
+		if (cel->grp6_len != 0
+		 || cel->grp7_len != 0) {
+			/*
+			 * Don't (yet?) support vendor
+			 * specific commands.
+			 */
+			ccb->ccb_h.status = CAM_REQ_INVALID;
+			printf("Non-zero Group Codes\n");
+			return;
+		}
+
+		/*
+		 * Seems to be okay.
+		 * Setup our data structures.
+		 */
+		if (target != CAM_TARGET_WILDCARD && tstate == NULL) {
+			tstate = ahc_alloc_tstate(ahc, target, channel);
+			if (tstate == NULL) {
+				xpt_print_path(ccb->ccb_h.path);
+				printf("Couldn't allocate tstate\n");
+				ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+				return;
+			}
+		}
+		lstate = malloc(sizeof(*lstate), M_DEVBUF, M_NOWAIT);
+		if (lstate == NULL) {
+			xpt_print_path(ccb->ccb_h.path);
+			printf("Couldn't allocate lstate\n");
+			ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+			return;
+		}
+		memset(lstate, 0, sizeof(*lstate));
+		status = xpt_create_path(&lstate->path, /*periph*/NULL,
+					 xpt_path_path_id(ccb->ccb_h.path),
+					 xpt_path_target_id(ccb->ccb_h.path),
+					 xpt_path_lun_id(ccb->ccb_h.path));
+		if (status != CAM_REQ_CMP) {
+			free(lstate, M_DEVBUF);
+			xpt_print_path(ccb->ccb_h.path);
+			printf("Couldn't allocate path\n");
+			ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+			return;
+		}
+		SLIST_INIT(&lstate->accept_tios);
+		SLIST_INIT(&lstate->immed_notifies);
+		ahc_lock(ahc, &s);
+		ahc_pause(ahc);
+		if (target != CAM_TARGET_WILDCARD) {
+			tstate->enabled_luns[lun] = lstate;
+			ahc->enabled_luns++;
+
+			if ((ahc->features & AHC_MULTI_TID) != 0) {
+				u_int targid_mask;
+
+				targid_mask = ahc_inb(ahc, TARGID)
+					    | (ahc_inb(ahc, TARGID + 1) << 8);
+
+				targid_mask |= target_mask;
+				ahc_outb(ahc, TARGID, targid_mask);
+				ahc_outb(ahc, TARGID+1, (targid_mask >> 8));
+				
+				ahc_update_scsiid(ahc, targid_mask);
+			} else {
+				u_int our_id;
+				char  channel;
+
+				channel = SIM_CHANNEL(ahc, sim);
+				our_id = SIM_SCSI_ID(ahc, sim);
+
+				/*
+				 * This can only happen if selections
+				 * are not enabled
+				 */
+				if (target != our_id) {
+					u_int sblkctl;
+					char  cur_channel;
+					int   swap;
+
+					sblkctl = ahc_inb(ahc, SBLKCTL);
+					cur_channel = (sblkctl & SELBUSB)
+						    ? 'B' : 'A';
+					if ((ahc->features & AHC_TWIN) == 0)
+						cur_channel = 'A';
+					swap = cur_channel != channel;
+					if (channel == 'A')
+						ahc->our_id = target;
+					else
+						ahc->our_id_b = target;
+
+					if (swap)
+						ahc_outb(ahc, SBLKCTL,
+							 sblkctl ^ SELBUSB);
+
+					ahc_outb(ahc, SCSIID, target);
+
+					if (swap)
+						ahc_outb(ahc, SBLKCTL, sblkctl);
+				}
+			}
+		} else
+			ahc->black_hole = lstate;
+		/* Allow select-in operations */
+		if (ahc->black_hole != NULL && ahc->enabled_luns > 0) {
+			scsiseq = ahc_inb(ahc, SCSISEQ_TEMPLATE);
+			scsiseq |= ENSELI;
+			ahc_outb(ahc, SCSISEQ_TEMPLATE, scsiseq);
+			scsiseq = ahc_inb(ahc, SCSISEQ);
+			scsiseq |= ENSELI;
+			ahc_outb(ahc, SCSISEQ, scsiseq);
+		}
+		ahc_unpause(ahc);
+		ahc_unlock(ahc, &s);
+		ccb->ccb_h.status = CAM_REQ_CMP;
+		xpt_print_path(ccb->ccb_h.path);
+		printf("Lun now enabled for target mode\n");
+	} else {
+		struct scb *scb;
+		int i, empty;
+
+		if (lstate == NULL) {
+			ccb->ccb_h.status = CAM_LUN_INVALID;
+			return;
+		}
+
+		ahc_lock(ahc, &s);
+		
+		ccb->ccb_h.status = CAM_REQ_CMP;
+		LIST_FOREACH(scb, &ahc->pending_scbs, pending_links) {
+			struct ccb_hdr *ccbh;
+
+			ccbh = &scb->io_ctx->ccb_h;
+			if (ccbh->func_code == XPT_CONT_TARGET_IO
+			 && !xpt_path_comp(ccbh->path, ccb->ccb_h.path)){
+				printf("CTIO pending\n");
+				ccb->ccb_h.status = CAM_REQ_INVALID;
+				ahc_unlock(ahc, &s);
+				return;
+			}
+		}
+
+		if (SLIST_FIRST(&lstate->accept_tios) != NULL) {
+			printf("ATIOs pending\n");
+			ccb->ccb_h.status = CAM_REQ_INVALID;
+		}
+
+		if (SLIST_FIRST(&lstate->immed_notifies) != NULL) {
+			printf("INOTs pending\n");
+			ccb->ccb_h.status = CAM_REQ_INVALID;
+		}
+
+		if (ccb->ccb_h.status != CAM_REQ_CMP) {
+			ahc_unlock(ahc, &s);
+			return;
+		}
+
+		xpt_print_path(ccb->ccb_h.path);
+		printf("Target mode disabled\n");
+		xpt_free_path(lstate->path);
+		free(lstate, M_DEVBUF);
+
+		ahc_pause(ahc);
+		/* Can we clean up the target too? */
+		if (target != CAM_TARGET_WILDCARD) {
+			tstate->enabled_luns[lun] = NULL;
+			ahc->enabled_luns--;
+			for (empty = 1, i = 0; i < 8; i++)
+				if (tstate->enabled_luns[i] != NULL) {
+					empty = 0;
+					break;
+				}
+
+			if (empty) {
+				ahc_free_tstate(ahc, target, channel,
+						/*force*/FALSE);
+				if (ahc->features & AHC_MULTI_TID) {
+					u_int targid_mask;
+
+					targid_mask = ahc_inb(ahc, TARGID)
+						    | (ahc_inb(ahc, TARGID + 1)
+						       << 8);
+
+					targid_mask &= ~target_mask;
+					ahc_outb(ahc, TARGID, targid_mask);
+					ahc_outb(ahc, TARGID+1,
+					 	 (targid_mask >> 8));
+					ahc_update_scsiid(ahc, targid_mask);
+				}
+			}
+		} else {
+
+			ahc->black_hole = NULL;
+
+			/*
+			 * We can't allow selections without
+			 * our black hole device.
+			 */
+			empty = TRUE;
+		}
+		if (ahc->enabled_luns == 0) {
+			/* Disallow select-in */
+			u_int scsiseq;
+
+			scsiseq = ahc_inb(ahc, SCSISEQ_TEMPLATE);
+			scsiseq &= ~ENSELI;
+			ahc_outb(ahc, SCSISEQ_TEMPLATE, scsiseq);
+			scsiseq = ahc_inb(ahc, SCSISEQ);
+			scsiseq &= ~ENSELI;
+			ahc_outb(ahc, SCSISEQ, scsiseq);
+
+			if ((ahc->features & AHC_MULTIROLE) == 0) {
+				printf("Configuring Initiator Mode\n");
+				ahc->flags &= ~AHC_TARGETROLE;
+				ahc->flags |= AHC_INITIATORROLE;
+				ahc_pause(ahc);
+				ahc_loadseq(ahc);
+			}
+		}
+		ahc_unpause(ahc);
+		ahc_unlock(ahc, &s);
+	}
+}
+
+static void
+ahc_update_scsiid(struct ahc_softc *ahc, u_int targid_mask)
+{
+	u_int scsiid_mask;
+	u_int scsiid;
+
+	if ((ahc->features & AHC_MULTI_TID) == 0)
+		panic("ahc_update_scsiid called on non-multitid unit\n");
+
+	/*
+	 * Since we will rely on the the TARGID mask
+	 * for selection enables, ensure that OID
+	 * in SCSIID is not set to some other ID
+	 * that we don't want to allow selections on.
+	 */
+	if ((ahc->features & AHC_ULTRA2) != 0)
+		scsiid = ahc_inb(ahc, SCSIID_ULTRA2);
+	else
+		scsiid = ahc_inb(ahc, SCSIID);
+	scsiid_mask = 0x1 << (scsiid & OID);
+	if ((targid_mask & scsiid_mask) == 0) {
+		u_int our_id;
+
+		/* ffs counts from 1 */
+		our_id = ffs(targid_mask);
+		if (our_id == 0)
+			our_id = ahc->our_id;
+		else
+			our_id--;
+		scsiid &= TID;
+		scsiid |= our_id;
+	}
+	if ((ahc->features & AHC_ULTRA2) != 0)
+		ahc_outb(ahc, SCSIID_ULTRA2, scsiid);
+	else
+		ahc_outb(ahc, SCSIID, scsiid);
+}
+
+void
+ahc_run_tqinfifo(struct ahc_softc *ahc, int paused)
+{
+	struct target_cmd *cmd;
+
+	/*
+	 * If the card supports auto-access pause,
+	 * we can access the card directly regardless
+	 * of whether it is paused or not.
+	 */
+	if ((ahc->features & AHC_AUTOPAUSE) != 0)
+		paused = TRUE;
+
+	ahc_sync_tqinfifo(ahc, BUS_DMASYNC_POSTREAD);
+	while ((cmd = &ahc->targetcmds[ahc->tqinfifonext])->cmd_valid != 0) {
+
+		/*
+		 * Only advance through the queue if we
+		 * have the resources to process the command.
+		 */
+		if (ahc_handle_target_cmd(ahc, cmd) != 0)
+			break;
+
+		cmd->cmd_valid = 0;
+		ahc_dmamap_sync(ahc, ahc->shared_data_dmat,
+				ahc->shared_data_dmamap,
+				ahc_targetcmd_offset(ahc, ahc->tqinfifonext),
+				sizeof(struct target_cmd),
+				BUS_DMASYNC_PREREAD);
+		ahc->tqinfifonext++;
+
+		/*
+		 * Lazily update our position in the target mode incoming
+		 * command queue as seen by the sequencer.
+		 */
+		if ((ahc->tqinfifonext & (HOST_TQINPOS - 1)) == 1) {
+			if ((ahc->features & AHC_HS_MAILBOX) != 0) {
+				u_int hs_mailbox;
+
+				hs_mailbox = ahc_inb(ahc, HS_MAILBOX);
+				hs_mailbox &= ~HOST_TQINPOS;
+				hs_mailbox |= ahc->tqinfifonext & HOST_TQINPOS;
+				ahc_outb(ahc, HS_MAILBOX, hs_mailbox);
+			} else {
+				if (!paused)
+					ahc_pause(ahc);	
+				ahc_outb(ahc, KERNEL_TQINPOS,
+					 ahc->tqinfifonext & HOST_TQINPOS);
+				if (!paused)
+					ahc_unpause(ahc);
+			}
+		}
+	}
+}
+
+static int
+ahc_handle_target_cmd(struct ahc_softc *ahc, struct target_cmd *cmd)
+{
+	struct	  ahc_tmode_tstate *tstate;
+	struct	  ahc_tmode_lstate *lstate;
+	struct	  ccb_accept_tio *atio;
+	uint8_t *byte;
+	int	  initiator;
+	int	  target;
+	int	  lun;
+
+	initiator = SCSIID_TARGET(ahc, cmd->scsiid);
+	target = SCSIID_OUR_ID(cmd->scsiid);
+	lun    = (cmd->identify & MSG_IDENTIFY_LUNMASK);
+
+	byte = cmd->bytes;
+	tstate = ahc->enabled_targets[target];
+	lstate = NULL;
+	if (tstate != NULL)
+		lstate = tstate->enabled_luns[lun];
+
+	/*
+	 * Commands for disabled luns go to the black hole driver.
+	 */
+	if (lstate == NULL)
+		lstate = ahc->black_hole;
+
+	atio = (struct ccb_accept_tio*)SLIST_FIRST(&lstate->accept_tios);
+	if (atio == NULL) {
+		ahc->flags |= AHC_TQINFIFO_BLOCKED;
+		/*
+		 * Wait for more ATIOs from the peripheral driver for this lun.
+		 */
+		return (1);
+	} else
+		ahc->flags &= ~AHC_TQINFIFO_BLOCKED;
+#if 0
+	printf("Incoming command from %d for %d:%d%s\n",
+	       initiator, target, lun,
+	       lstate == ahc->black_hole ? "(Black Holed)" : "");
+#endif
+	SLIST_REMOVE_HEAD(&lstate->accept_tios, sim_links.sle);
+
+	if (lstate == ahc->black_hole) {
+		/* Fill in the wildcards */
+		atio->ccb_h.target_id = target;
+		atio->ccb_h.target_lun = lun;
+	}
+
+	/*
+	 * Package it up and send it off to
+	 * whomever has this lun enabled.
+	 */
+	atio->sense_len = 0;
+	atio->init_id = initiator;
+	if (byte[0] != 0xFF) {
+		/* Tag was included */
+		atio->tag_action = *byte++;
+		atio->tag_id = *byte++;
+		atio->ccb_h.flags = CAM_TAG_ACTION_VALID;
+	} else {
+		atio->ccb_h.flags = 0;
+	}
+	byte++;
+
+	/* Okay.  Now determine the cdb size based on the command code */
+	switch (*byte >> CMD_GROUP_CODE_SHIFT) {
+	case 0:
+		atio->cdb_len = 6;
+		break;
+	case 1:
+	case 2:
+		atio->cdb_len = 10;
+		break;
+	case 4:
+		atio->cdb_len = 16;
+		break;
+	case 5:
+		atio->cdb_len = 12;
+		break;
+	case 3:
+	default:
+		/* Only copy the opcode. */
+		atio->cdb_len = 1;
+		printf("Reserved or VU command code type encountered\n");
+		break;
+	}
+	
+	memcpy(atio->cdb_io.cdb_bytes, byte, atio->cdb_len);
+
+	atio->ccb_h.status |= CAM_CDB_RECVD;
+
+	if ((cmd->identify & MSG_IDENTIFY_DISCFLAG) == 0) {
+		/*
+		 * We weren't allowed to disconnect.
+		 * We're hanging on the bus until a
+		 * continue target I/O comes in response
+		 * to this accept tio.
+		 */
+#if 0
+		printf("Received Immediate Command %d:%d:%d - %p\n",
+		       initiator, target, lun, ahc->pending_device);
+#endif
+		ahc->pending_device = lstate;
+		ahc_freeze_ccb((union ccb *)atio);
+		atio->ccb_h.flags |= CAM_DIS_DISCONNECT;
+	}
+	xpt_done((union ccb*)atio);
+	return (0);
+}
+
+#endif
diff -Nru a/drivers/scsi/cpqfcTSstructs.h b/drivers/scsi/cpqfcTSstructs.h
--- a/drivers/scsi/cpqfcTSstructs.h	Sun Apr 28 20:10:35 2002
+++ b/drivers/scsi/cpqfcTSstructs.h	Sun Apr 28 20:10:35 2002
@@ -27,7 +27,7 @@
 
 #define DbgDelay(secs) { int wait_time; printk( " DbgDelay %ds ", secs); \
                          for( wait_time=jiffies + (secs*HZ); \
-		         wait_time > jiffies ;) ; }
+		         time_before(jiffies, wait_time) ;) ; }
 
 #define CPQFCTS_DRIVER_VER(maj,min,submin) ((maj<<16)|(min<<8)|(submin))
 // don't forget to also change MODULE_DESCRIPTION in cpqfcTSinit.c
diff -Nru a/drivers/scsi/gdth_proc.c b/drivers/scsi/gdth_proc.c
--- a/drivers/scsi/gdth_proc.c	Sun Apr 28 20:10:36 2002
+++ b/drivers/scsi/gdth_proc.c	Sun Apr 28 20:10:36 2002
@@ -1464,7 +1464,7 @@
             timer_table[SCSI_TIMER].expires = jiffies + timeout;
             timer_active |= 1 << SCSI_TIMER;
         } else {
-            if (jiffies + timeout < timer_table[SCSI_TIMER].expires)
+            if (time_before(jiffies + timeout, timer_table[SCSI_TIMER].expires))
                 timer_table[SCSI_TIMER].expires = jiffies + timeout;
         }
     }
diff -Nru a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c
--- a/drivers/scsi/ide-scsi.c	Sun Apr 28 20:10:30 2002
+++ b/drivers/scsi/ide-scsi.c	Sun Apr 28 20:10:30 2002
@@ -458,9 +458,9 @@
 }
 
 /*
- *	idescsi_do_request is our request handling function.
+ * This is our request handling function.
  */
-static ide_startstop_t idescsi_do_request (ide_drive_t *drive, struct request *rq, unsigned long block)
+static ide_startstop_t idescsi_do_request(struct ata_device *drive, struct request *rq, sector_t block)
 {
 #if IDESCSI_DEBUG_LOG
 	printk (KERN_INFO "rq_status: %d, rq_dev: %u, cmd: %d, errors: %d\n",rq->rq_status,(unsigned int) rq->rq_dev,rq->cmd,rq->errors);
@@ -468,20 +468,20 @@
 #endif /* IDESCSI_DEBUG_LOG */
 
 	if (rq->flags & REQ_SPECIAL) {
-		return idescsi_issue_pc (drive, (idescsi_pc_t *) rq->special);
+		return idescsi_issue_pc(drive, (idescsi_pc_t *) rq->special);
 	}
 	blk_dump_rq_flags(rq, "ide-scsi: unsup command");
 	idescsi_end_request(drive, 0);
 	return ide_stopped;
 }
 
-static int idescsi_open (struct inode *inode, struct file *filp, ide_drive_t *drive)
+static int idescsi_open(struct inode *inode, struct file *filp, struct ata_device *drive)
 {
 	MOD_INC_USE_COUNT;
 	return 0;
 }
 
-static void idescsi_ide_release (struct inode *inode, struct file *filp, ide_drive_t *drive)
+static void idescsi_ide_release(struct inode *inode, struct file *filp, struct ata_device *drive)
 {
 	MOD_DEC_USE_COUNT;
 }
@@ -556,9 +556,7 @@
 	release:		idescsi_ide_release,
 	check_media_change:	NULL,
 	revalidate:		idescsi_revalidate,
-	pre_reset:		NULL,
 	capacity:		NULL,
-	special:		NULL,
 	proc:			NULL
 };
 
diff -Nru a/drivers/scsi/qlogicfas.c b/drivers/scsi/qlogicfas.c
--- a/drivers/scsi/qlogicfas.c	Sun Apr 28 20:10:36 2002
+++ b/drivers/scsi/qlogicfas.c	Sun Apr 28 20:10:36 2002
@@ -271,11 +271,11 @@
 int	i,k;
 	k = 0;
 	i = jiffies + WATCHDOG;
-	while ( i > jiffies && !qabort && !((k = inb(qbase + 4)) & 0xe0)) {
+	while (time_before(jiffies, i) && !qabort && !((k = inb(qbase + 4)) & 0xe0)) {
 		barrier();
 		cpu_relax();
 	}
-	if (i <= jiffies)
+	if (time_after_eq(jiffies, i))
 		return (DID_TIME_OUT);
 	if (qabort)
 		return (qabort == 1 ? DID_ABORT : DID_RESET);
@@ -407,8 +407,8 @@
 	}
 /*** Enter Status (and Message In) Phase ***/
 	k = jiffies + WATCHDOG;
-	while ( k > jiffies && !qabort && !(inb(qbase + 4) & 6));	/* wait for status phase */
-	if ( k <= jiffies ) {
+	while ( time_before(jiffies, k) && !qabort && !(inb(qbase + 4) & 6));	/* wait for status phase */
+	if ( time_after_eq(jiffies, k) ) {
 		ql_zap();
 		return (DID_TIME_OUT << 16);
 	}
diff -Nru a/drivers/scsi/qlogicfc.c b/drivers/scsi/qlogicfc.c
--- a/drivers/scsi/qlogicfc.c	Sun Apr 28 20:10:32 2002
+++ b/drivers/scsi/qlogicfc.c	Sun Apr 28 20:10:32 2002
@@ -802,7 +802,7 @@
 			outw(HCCR_CLEAR_RISC_INTR, host->io_port + HOST_HCCR);
 			isp2x00_enable_irqs(host);
 			/* wait for the loop to come up */
-			for (wait_time = jiffies + 10 * HZ; wait_time > jiffies && hostdata->adapter_state == AS_LOOP_DOWN;) {
+			for (wait_time = jiffies + 10 * HZ; time_before(jiffies, wait_time) && hostdata->adapter_state == AS_LOOP_DOWN;) {
 			        barrier();
 				cpu_relax();
 			}
@@ -819,7 +819,7 @@
 	   some time before recognizing it is attached to a fabric */
 
 #if ISP2x00_FABRIC
-	for (wait_time = jiffies + 5 * HZ; wait_time > jiffies;) {
+	for (wait_time = jiffies + 5 * HZ; time_before(jiffies, wait_time);) {
 		barrier();
 		cpu_relax();
 	}
diff -Nru a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
--- a/drivers/scsi/scsi.c	Sun Apr 28 20:10:37 2002
+++ b/drivers/scsi/scsi.c	Sun Apr 28 20:10:37 2002
@@ -158,6 +158,8 @@
 	"Enclosure        ",
 };
 
+static char * scsi_null_device_strs = "nullnullnullnull";
+
 /* 
  * Function prototypes.
  */
@@ -1831,6 +1833,8 @@
 				HBA_ptr->host_queue = scd->next;
 			}
 			blk_cleanup_queue(&scd->request_queue);
+			if (scd->inquiry)
+				kfree(scd->inquiry);
 			kfree((char *) scd);
 		} else {
 			goto out;
@@ -2129,6 +2133,8 @@
 			blk_cleanup_queue(&SDpnt->request_queue);
 			/* Next free up the Scsi_Device structures for this host */
 			shpnt->host_queue = SDpnt->next;
+			if (SDpnt->inquiry)
+				kfree(SDpnt->inquiry);
 			kfree((char *) SDpnt);
 
 		}
@@ -2618,6 +2624,9 @@
         	return NULL;
         	
         memset(SDpnt, 0, sizeof(Scsi_Device));
+	SDpnt->vendor = scsi_null_device_strs;
+	SDpnt->model = scsi_null_device_strs;
+	SDpnt->rev = scsi_null_device_strs;
 
         SDpnt->host = SHpnt;
         SDpnt->id = SHpnt->this_id;
@@ -2664,6 +2673,8 @@
          * it now.
          */
 	scsi_release_commandblocks(SDpnt);
+	if (SDpnt->inquiry)
+		kfree(SDpnt->inquiry);
         kfree(SDpnt);
 }
 
diff -Nru a/drivers/scsi/scsi.h b/drivers/scsi/scsi.h
--- a/drivers/scsi/scsi.h	Sun Apr 28 20:10:32 2002
+++ b/drivers/scsi/scsi.h	Sun Apr 28 20:10:32 2002
@@ -575,7 +575,11 @@
 	devfs_handle_t de;      /* directory for the device      */
 	char type;
 	char scsi_level;
-	char vendor[8], model[16], rev[4];
+	unsigned char inquiry_len;	/* valid bytes in 'inquiry' */
+	unsigned char * inquiry;	/* INQUIRY response data */
+	char * vendor;		/* [back_compat] point into 'inquiry' ... */
+	char * model;		/* ... after scan; point to static string */
+	char * rev;		/* ... "nullnullnullnull" before scan */
 	unsigned char current_tag;	/* current tag */
 	unsigned char sync_min_period;	/* Not less than this period */
 	unsigned char sync_max_offset;	/* Not greater than this offset */
@@ -666,6 +670,8 @@
 	unsigned short sr_sglist_len;	/* size of malloc'd scatter-gather list */
 	unsigned sr_underflow;	/* Return error if less than
 				   this amount is transferred */
+ 	void * upper_private_data;	/* reserved for owner (usually upper
+ 					   level driver) of this request */
 };
 
 /*
diff -Nru a/drivers/scsi/scsi_mid_low_api.txt b/drivers/scsi/scsi_mid_low_api.txt
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/scsi/scsi_mid_low_api.txt	Sun Apr 28 20:10:39 2002
@@ -0,0 +1,535 @@
+                          Linux Kernel 2.5 series
+                    SCSI mid_level - lower_level interface
+                    ======================================
+
+Introduction
+============
+This document outlines the interface between the Linux scsi mid level
+and lower level drivers. Lower level drivers are variously called HBA
+(host bus adapter) drivers, host drivers (HD) or pseudo adapter drivers.
+The latter alludes to the fact that a lower level driver may be a
+bridge to another IO subsystem (and the "ide-scsi" driver is an example
+of this). There can be many lower level drivers active in a running
+system, but only one per hardware type. For example, the aic7xxx driver
+controls adaptec controllers based on the 7xxx chip series. Most lower 
+level drivers can control one or more scsi hosts (a.k.a. scsi initiators).
+
+The Linux kernel source Documentation/DocBook/scsidrivers.tmpl file
+refers to this file. With the appropriate DocBook toolset, this permits
+users to generate html, ps and pdf renderings of information within this
+file (e.g. the interface functions).
+
+Driver structure
+================
+Traditionally a lower level driver for the scsi subsystem has been
+at least two files in the drivers/scsi directory. For example, a
+driver called "xyz" has a header file "xyz.h" and a source file
+"xyz.c". [Actually there is no good reason why this couldn't all
+be in one file.] Some drivers that have been ported to several operating
+systems (e.g. aic7xxx which has separate  files for generic and 
+OS-specific code) have more than two files. Such drivers tend to have 
+their own directory under the drivers/scsi directory.
+
+scsi_module.c is normally included at the end of a lower
+level driver. For it to work a declaration like this is needed before
+it is included:
+    static Scsi_Host_Template driver_template = DRIVER_TEMPLATE;
+    /* DRIVER_TEMPLATE should contain pointers to supported interface
+       functions. Scsi_Host_Template is defined in hosts.h */
+    #include "scsi_module.c"
+
+The scsi_module.c assumes the name "driver_template" is appropriately
+defined. It contains 2 functions:
+  1) init_this_scsi_driver() called during builtin and module driver
+     initialization: invokes mid level's scsi_register_host()
+  2) exit_this_scsi_driver() called during closedown: invokes
+     mid level's scsi_unregister_host()
+
+When a new, lower level driver is being added to Linux, the following 
+files (all found in the drivers/scsi directory) will need some attention: 
+Makefile, Config.help and Config.in . It is probably best to look at what 
+an existing lower level driver does in this regard.
+
+
+
+Interface Functions
+===================
+Interface functions should be declared static. The accepted convention
+is that driver "xyz" will declare its detect() function as:
+    static int xyz_detect(Scsi_Host_Template * shtp);
+
+A pointer to this function should be placed in the 'detect' member of
+a Scsi_Host_Template instance. A pointer to such an instance should
+passed to the mid level's scsi_register_host().
+
+The interface functions are listed below in alphabetical order.
+
+
+/**
+ *      bios_param - fetch head, sector, cylinder info for a disk
+ *      @sdkp: pointer to disk structure (defined in sd.h)
+ *      @dev: corresponds to dev_t of device file name (e.g. /dev/sdb)
+ *      @params: three element array to place output:
+ *              params[0] number of heads
+ *              params[1] number of sectors
+ *              params[2] number of cylinders
+ *
+ *      Return value is ignored
+ *
+ *      Required: no
+ *
+ *      Locks: none
+ *
+ *      Notes: sd driver will make up geometry (based on READ CAPACITY)
+ *      if this function is not provided. The params array is
+ *      pre-initialized with made up values just in case this function 
+ *      doesn't output anything.
+ **/
+    int bios_param(Scsi_Disk * sdkp, kdev_t dev, int params[3]);
+
+
+/**
+ *      command - send scsi command to device, wait for reply
+ *      @scp: pointer to scsi command object
+ *
+ *      Returns an int with four component bytes: scsi_byte, msg_byte, 
+ *      host_byte, driver_byte (status_byte is in the lsb). A value of
+ *      0 is an unqualified success.
+ *
+ *      Required: if Scsi_Host::can_queue can ever by cleared (zero)
+ *                then this function is required.
+ *
+ *      Locks: Scsi_Host::host_lock held on entry (with "irqsave") and
+ *             is expected to be held on return.
+ *
+ *      Notes: Drivers tend to be dropping support for this function and
+ *      rather supporting queuecommand().
+ **/
+    int command(Scsi_Cmnd * scp);
+
+
+/**
+ *      detect - detects HBAs this driver wants to control
+ *      @shtp: host template for this driver.
+ *
+ *      Returns number of hosts this driver wants to control. 0 means no
+ *      suitable hosts found.
+ *
+ *      Required: yes
+ *
+ *      Locks: none held
+ *
+ *      Notes: First function called from the scsi mid level on this
+ *      driver. Upper level drivers (e.g. sd) may not (yet) be present.
+ *      For each host found, this method should call scsi_register() 
+ *      [see hosts.c].
+ **/
+    int detect(Scsi_Host_Template * shtp);
+
+
+/**
+ *      eh_abort_handler - abort command associated with scp
+ *      @scp: identifies command to be aborted
+ *
+ *      Returns SUCCESS if command aborted else FAILED
+ *
+ *      Required: no
+ *
+ *      Locks: Scsi_Host::host_lock held (with irqsave) on entry and assumed
+ *      to be held on return.
+ *
+ *      Notes: Invoked from scsi_eh thread. No other commands will be
+ *      queued on current host during eh.
+ **/
+     int eh_abort_handler(Scsi_Cmnd * scp);
+
+
+/**
+ *      eh_device_reset_handler - issue scsi device reset
+ *      @scp: identifies scsi device to be reset
+ *
+ *      Returns SUCCESS if command aborted else FAILED
+ *
+ *      Required: no
+ *
+ *      Locks: Scsi_Host::host_lock held (with irqsave) on entry and assumed
+ *      to be held on return.
+ *
+ *      Notes: Invoked from scsi_eh thread. No other commands will be
+ *      queued on current host during eh.
+ **/
+     int eh_device_reset_handler(Scsi_Cmnd * scp);
+
+
+/**
+ *      eh_bus_reset_handler - issue scsi bus reset
+ *      @scp: scsi bus that contains this device should be reset
+ *
+ *      Returns SUCCESS if command aborted else FAILED
+ *
+ *      Required: no
+ *
+ *      Locks: Scsi_Host::host_lock held (with irqsave) on entry and assumed
+ *      to be held on return.
+ *
+ *      Notes: Invoked from scsi_eh thread. No other commands will be
+ *      queued on current host during eh.
+ **/
+     int eh_bus_reset_handler(Scsi_Cmnd * scp);
+
+
+/**
+ *      eh_host_reset_handler - reset host (host bus adapter)
+ *      @scp: scsi host that contains this device should be reset
+ *
+ *      Returns SUCCESS if command aborted else FAILED
+ *
+ *      Required: no
+ *
+ *      Locks: Scsi_Host::host_lock held (with irqsave) on entry and assumed
+ *      to be held on return.
+ *
+ *      Notes: Invoked from scsi_eh thread. No other commands will be
+ *      queued on current host during eh. 
+ *      With the default eh_strategy in place, if none of the _abort_, 
+ *      _device_reset_, _bus_reset_ or this eh handler function are 
+ *      defined (or they all return FAILED) then the device in question 
+ *      will be set offline whenever eh is invoked.
+ **/
+     int eh_host_reset_handler(Scsi_Cmnd * scp);
+
+
+/**
+ *      eh_strategy_handler - driver supplied alternate to scsi_unjam_host()
+ *      @shp: host on which error has occurred
+ *
+ *      Returns TRUE if host unjammed, else FALSE.
+ *
+ *      Required: no
+ *
+ *      Locks: none
+ *
+ *      Notes: Invoked from scsi_eh thread. Driver supplied alternate to 
+ *      scsi_unjam_host() found in scsi_error.c
+ **/
+     int eh_strategy_handler(struct Scsi_Host * shp);
+
+
+/**
+ *      info - supply information about given host: driver name plus data
+ *             to distinguish given host
+ *      @shp: host to supply information about
+ *
+ *      Return ASCII null terminated string. [This driver is assumed to
+ *      manage the memory pointed to and maintain it, typically for the
+ *      lifetime of this host.]
+ *
+ *      Required: no
+ *
+ *      Locks: none
+ *
+ *      Notes: Often supplies PCI or ISA information such as IO addresses
+ *      and interrupt numbers. If not supplied Scsi_Host::name used
+ *      instead. It is assumed the returned information fits on one line 
+ *      (i.e. does not included embedded newlines).
+ *      The SCSI_IOCTL_PROBE_HOST ioctl yields the string returned by this
+ *      function (or Scsi_Host::name if this function is not available).
+ *      In a similar manner, scsi_register_host() outputs to the console
+ *      each host's "info" (or name) for the driver it is registering.
+ *      Also if proc_info() is not supplied, the output of this function
+ *      is used instead.
+ **/
+    const char * info(struct Scsi_Host * shp);
+
+
+/**
+ *      ioctl - driver can respond to ioctls
+ *      @sdp: device that ioctl was issued for
+ *      @cmd: ioctl number
+ *      @arg: pointer to read or write data from. Since it points to
+ *            user space, should use appropriate kernel functions
+ *            (e.g. copy_from_user() ). In the Unix style this argument
+ *            can also be viewed as an unsigned long.
+ *
+ *      Returns negative "errno" value when there is a problem. 0 or a
+ *      positive value indicates success and is returned to the user space.
+ *
+ *      Required: no
+ *
+ *      Locks: none
+ *
+ *      Notes: The scsi subsystem uses a "trickle down" ioctl model.
+ *      The user issues an ioctl() against an upper level driver
+ *      (e.g. /dev/sdc) and if the upper level driver doesn't recognize
+ *      the 'cmd' then it is passed to the scsi mid level. If the scsi
+ *      mid level does not recognize it, then the lower driver that controls
+ *      the device receives the ioctl. According to recent Unix standards
+ *      unsupported ioctl() 'cmd' numbers should return -ENOTTY.
+ *      However the mid level returns -EINVAL for unrecognized 'cmd'
+ *      numbers when this function is not supplied by the driver.
+ **/
+    int ioctl(Scsi_Device *sdp, int cmd, void *arg);
+
+
+/**
+ *      proc_info - supports /proc/scsi/{driver_name}/{host_no}
+ *      @buffer: anchor point to output to (0==writeto1_read0) or fetch from
+ *               (1==writeto1_read0).
+ *      @start: where "interesting" data is written to. Ignored when
+ *              1==writeto1_read0.
+ *      @offset: offset within buffer 0==writeto1_read0 is actually
+ *               interested in. Ignored when 1==writeto1_read0 .
+ *      @length: maximum (or actual) extent of buffer
+ *      @host_no: host number of interest (Scsi_Host::host_no)
+ *      @writeto1_read0: 1 -> data coming from user space towards driver
+ *                            (e.g. "echo some_string > /proc/scsi/xyz/2")
+ *                       0 -> user what data from this driver
+ *                            (e.g. "cat /proc/scsi/xyz/2")
+ *
+ *      Returns length when 1==writeto1_read0. Otherwise number of chars
+ *      output to buffer past offset.
+ *
+ *      Required: no
+ *
+ *      Locks: none held
+ *
+ *      Notes: Driven from scsi_proc.c which interfaces to proc_fs
+ **/
+int proc_info(char * buffer, char ** start, off_t offset, 
+              int length, int hostno, int writeto1_read0);
+
+
+/**
+ *      queuecommand - queue scsi command, invoke 'done' on completion
+ *      @scp: pointer to scsi command object
+ *      @done: function pointer to be invoked on completion
+ *
+ *      Returns 1 if the adapter is busy, else returns 0.
+ *
+ *      Required: if Scsi_Host::can_queue is ever non-zero
+ *                then this function is required.
+ *
+ *      Locks: Scsi_Host::host_lock held on entry (with "irqsave") and
+ *             is expected to be held on return.
+ *
+ *      Notes: This function should be relatively fast. Normally it will
+ *      not wait for IO to complete. Hence the 'done' callback is invoked 
+ *      (often directly from an interrupt service routine) sometime after
+ *      this command has returned. In some cases (e.g. pseudo adapter 
+ *      drivers that manufacture the response to a scsi INQUIRY)
+ *      the 'done' callback may be invoked before this function returns.
+ *      If the 'done' callback is not invoked within a certain period
+ *      the scsi mid level will commence error processing.
+ *      The integer with 4 component bytes that command() uses as its
+ *      return value should be generated by this function. However, in
+ *      this case, it should be placed in scp->result before this function 
+ *      returns.
+ **/
+    int queuecommand(Scsi_Cmnd * scp, void (*done)(Scsi_Cmnd *));
+
+
+/**
+ *      release - release all resources associated with given host
+ *      @shp: host to be released.
+ *
+ *      Return value ignored.
+ *
+ *      Required: no
+ *
+ *      Locks: lock_kernel() active on entry and expected to be active
+ *      on return.
+ *
+ *      Notes: Invoked from mid level's scsi_unregister_host(). When a
+ *      host is being unregistered the mid level does not bother to
+ *      call revoke() on the devices it controls.
+ *      This function should call scsi_unregister(shp) [found in hosts.c]
+ *      prior to returning.
+ **/
+    int release(struct Scsi_Host * shp);
+
+
+/**
+ *      revoke - indicate disinterest in a scsi device
+ *      @sdp: host template for this driver.
+ *
+ *      Return value ignored.
+ *
+ *      Required: no
+ *
+ *      Locks: none held
+ *
+ *      Notes: Called when "scsi remove-single-device <h> <b> <t> <l>"
+ *      is written to /proc/scsi/scsi to indicate the device is no longer 
+ *      required. It is called after the upper level drivers have detached 
+ *      this device and before the device name  (e.g. /dev/sdc) is 
+ *      unregistered and the resources associated with it are freed.
+ **/
+    int revoke(Scsi_device * sdp);
+
+
+/**
+ *      select_queue_depths - calculate allowable number of scsi commands
+ *                            that can be queued on each device (disk)
+ *                            on the given host
+ *      @shp: host containing device
+ *      @sdp: first device to examine [start of list]
+ *
+ *      Returns nothing
+ *
+ *      Required: no
+ *
+ *      Locks: none
+ *
+ *      Notes: This function should examine all devices on the given host.
+ *      The next device can be fetched with sdp->next (NULL when finished).
+ *      Queue depths should be placed in Scsi_Device::queue_depth . 
+ **/
+    void select_queue_depths(struct Scsi_Host * shp, Scsi_Device * sdp);
+
+
+Data Structures
+===============
+Scsi_Host_Template
+------------------
+There is one Scsi_Host_Template instance per lower level driver. It is
+typically initialized as a file scope static in a driver's header file. That
+way members that are not explicitly initialized will be set to 0 or NULL.
+Member of interest:
+    name         - name of driver (should only use characters that are
+                   permitted in a unix file name)
+    proc_name    - name used in "/proc/scsi/<proc_name>/<host_no>"
+The structure is defined and commented in hosts.h
+
+Scsi_Host
+---------
+There is one Scsi_Host instance per host (HBA) that a lower level driver
+controls. The Scsi_Host structure has many members in common with the
+Scsi_Host_Template. When a new Scsi_Host instance is created (in
+scsi_register() in hosts.c) those common members are initialized from
+the driver's Scsi_Host_Template instance. Members of interest:
+    host_no      - system wide unique number that is used for identifying
+                   this host. Issued in ascending order from 0 (and the
+                   positioning can be influenced by the scsihosts
+                   kernel boot (or module) parameter)
+    can_queue    - 0->use command(), greater than 0->use queuecommand() and do
+                   not send more than can_queue commands to the adapter.
+    this_id      - scsi id of host (scsi initiator) or -1 if not known
+    sg_tablesize - maximum scatter gather elements allowed by host.
+                   0 implies scatter gather not supported by host
+    max_sectors  - maximum number of sectors (usually 512 bytes) allowed
+                   in a single scsi command. 0 implies no maximum.
+    cmd_per_lun  - maximum number of command that can be queued on devices
+                   controlled by the host. Used if select_queue_depths()
+                   not defined or yields 0.
+    unchecked_isa_dma - 1->only use bottom 16 MB of ram (ISA DMA addressing
+                   restriction), 0->can use full 32 bit (or better) DMA
+                   address space
+    use_clustering - 1->scsi commands in mid level's queue can be merged,
+                     0->disallow scsi command merging
+    highmem_io   - 1->can DMA in to or out of high memory,
+                   0->use bounce buffers if data is in high memory
+    hostt        - pointer to driver's Scsi_Host_Template from which this
+                   Scsi_Host instance was spawned
+    host_queue   - deceptively named pointer to the start of a double linked
+                   list of Scsi_Device instances that belong to this host.
+The structure is defined in hosts.h
+
+Scsi_Device
+-----------
+Generally, there is one instance of this structure for each scsi logical unit
+on a host. Scsi devices are uniquely identified within a host by bus number,
+target id and logical unit number (lun).
+The structure is defined in scsi.h
+
+Scsi_Cmnd
+---------
+Instances of this structure convey scsi commands to the lower level
+driver. Each scsi device has a pool of Scsi_Cmnd instances whose size
+is determined by select_queue_depths (or Scsi_Host::cmd_per_lun). There
+will be at least one instance of Scsi_Cmnd for each scsi device.
+The structure is defined in scsi.h
+
+Scsi_Disk
+---------
+It is a bit strange that this structure is exposed to a lower level
+driver in the bios_param() function. It is defined in sd.h and is only
+relevant to the sd upper level driver. The sd driver invokes bios_param()
+is response to the HD_GETGEO and HD_GETGEO_BIG ioctls.
+
+
+Flow of control during initialization
+=====================================
+
+Lower level driver                        mid level
+------------------                        ---------
+
+init_this_scsi_driver() [1]              -----+
+  <scsi_modules.c>                            |
+                                              v
+                                         scsi_register_host()
+                                              |
+detect() [2]                               <--+
+                                              |
+queuecommand()** [3]                       <--+
+                                              |
+select_queue_depths()*                     <--+
+
+
+Notes
+-----
+[1] invoked during kernel initialization when built in or during module
+    initialization. The scsi mid level will already be initialized.
+[2] detect() calls scsi_register() for each HBA it wants to control
+    (if not, scsi_register_host() will do it instead)
+[3] called as part of scsi bus scan during discovery of devices. This scan
+    will be done for each host. Scsi commands such as INQUIRY, MODE SENSE
+    and REPORT LUNS will be sent during device discovery
+*   invoked for each host controlled by the lower level driver
+**  invoked for each device of each host controlled by the driver
+
+
+Locks
+=====
+Each Scsi_Host instance has a spin_lock called Scsi_Host::default_lock
+which is initialized in scsi_register() [found in hosts.c]. Within the
+same function the Scsi_Host::host_lock pointer is initialized to point
+at default_lock with the scsi_assign_lock() function. Thereafter 
+lock and unlock operations performed by the mid level use the
+Scsi_Host::host_lock pointer.
+
+Lower level drivers can override the use of Scsi_Host::default_lock by
+using scsi_assign_lock(). The earliest opportunity to do this would
+be in the detect() function after it has invoked scsi_register(). It
+could be replaced by a coarser grain lock (e.g. per driver) or a
+lock of equal granularity (i.e. per host). Using finer grain locks 
+(e.g. per scsi device) may be possible by juggling locks in
+queuecommand().
+
+
+Changes since lk 2.4 series
+===========================
+io_request_lock has been replaced by several finer grained locks. The lock 
+relevant to lower level drivers is Scsi_Host::host_lock and there is one 
+per scsi host.
+
+The older error handling mechanism has been removed. This means the
+lower level interface functions abort() and reset() have been removed.
+
+In the 2.4 series the scsi subsystem configuration descriptions were 
+aggregated with the configuration descriptions from all other Linux 
+subsystems in the Documentation/Configure.help file. In the 2.5 series, 
+the scsi subsystem now has its own (much smaller) drivers/scsi/Config.help
+file.
+
+
+Credits
+=======
+The following people have contributed to this document:
+        Mike Anderson <andmike@us.ibm.com>
+        James Bottomley <James.Bottomley@steeleye.com> 
+        Patrick Mansfield <patmans@us.ibm.com> 
+
+
+Douglas Gilbert
+dgilbert@interlog.com
+27th April 2002
diff -Nru a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
--- a/drivers/scsi/scsi_scan.c	Sun Apr 28 20:10:37 2002
+++ b/drivers/scsi/scsi_scan.c	Sun Apr 28 20:10:37 2002
@@ -22,27 +22,36 @@
 #include <linux/kmod.h>
 #endif
 
-/* The following devices are known not to tolerate a lun != 0 scan for
- * one reason or another.  Some will respond to all luns, others will
- * lock up.
+/*
+ * Flags for irregular SCSI devices that need special treatment
  */
+#define BLIST_NOLUN     	0x001	/* Don't scan for LUNs */
+#define BLIST_FORCELUN  	0x002	/* Known to have LUNs, force sanning */
+#define BLIST_BORKEN    	0x004	/* Flag for broken handshaking */
+#define BLIST_KEY       	0x008	/* Needs to be unlocked by special command */
+#define BLIST_SINGLELUN 	0x010	/* LUNs should better not be used in parallel */
+#define BLIST_NOTQ		0x020	/* Buggy Tagged Command Queuing */
+#define BLIST_SPARSELUN 	0x040	/* Non consecutive LUN numbering */
+#define BLIST_MAX5LUN		0x080	/* Avoid LUNS >= 5 */
+#define BLIST_ISDISK    	0x100	/* Treat as (removable) disk */
+#define BLIST_ISROM     	0x200	/* Treat as (removable) CD-ROM */
+#define BLIST_LARGELUN		0x400	/* LUNs larger than 7 despite reporting as SCSI 2 */
+#define BLIST_INQUIRY_36	0x800	/* override additional length field */
+#define BLIST_INQUIRY_58	0x1000	/* ... for broken inquiry responses */
 
-#define BLIST_NOLUN     	0x001
-#define BLIST_FORCELUN  	0x002
-#define BLIST_BORKEN    	0x004
-#define BLIST_KEY       	0x008
-#define BLIST_SINGLELUN 	0x010
-#define BLIST_NOTQ		0x020
-#define BLIST_SPARSELUN 	0x040
-#define BLIST_MAX5LUN		0x080
-#define BLIST_ISDISK    	0x100
-#define BLIST_ISROM     	0x200
+/*
+ * scan_scsis_single() return values.
+ */
+#define SCSI_SCAN_NO_RESPONSE      0
+#define SCSI_SCAN_DEVICE_PRESENT   1
+#define SCSI_SCAN_DEVICE_ADDED     2
 
 static void print_inquiry(unsigned char *data);
 static int scan_scsis_single(unsigned int channel, unsigned int dev,
-		unsigned int lun, int lun0_scsi_level, 
-		unsigned int *max_scsi_dev, unsigned int *sparse_lun, 
-		Scsi_Device ** SDpnt, struct Scsi_Host *shpnt, 
+		unsigned int lun, int scsi_level, Scsi_Device ** SDpnt2,
+		struct Scsi_Host *shpnt, char *scsi_result);
+static void scan_scsis_target(unsigned int channel, unsigned int dev,
+		Scsi_Device ** SDpnt2, struct Scsi_Host *shpnt,
 		char *scsi_result);
 static int find_lun0_scsi_level(unsigned int channel, unsigned int dev,
 				struct Scsi_Host *shpnt);
@@ -61,6 +70,10 @@
  */
 static struct dev_info device_list[] =
 {
+/* The following devices are known not to tolerate a lun != 0 scan for
+ * one reason or another.  Some will respond to all luns, others will
+ * lock up.
+ */
 	{"Aashima", "IMAGERY 2400SP", "1.03", BLIST_NOLUN},	/* Locks up if polled for lun != 0 */
 	{"CHINON", "CD-ROM CDS-431", "H42", BLIST_NOLUN},	/* Locks up if polled for lun != 0 */
 	{"CHINON", "CD-ROM CDS-535", "Q14", BLIST_NOLUN},	/* Locks up if polled for lun != 0 */
@@ -74,7 +87,6 @@
 	{"MAXTOR", "XT-4170S", "B5A", BLIST_NOLUN},		/* Locks-up sometimes when LUN>0 polled. */
 	{"MAXTOR", "XT-8760S", "B7B", BLIST_NOLUN},		/* guess what? */
 	{"MEDIAVIS", "RENO CD-ROMX2A", "2.03", BLIST_NOLUN},	/*Responds to all lun */
-	{"MICROP", "4110", "*", BLIST_NOTQ},			/* Buggy Tagged Queuing */
 	{"NEC", "CD-ROM DRIVE:841", "1.0", BLIST_NOLUN},	/* Locks-up when LUN>0 polled. */
 	{"PHILIPS", "PCA80SC", "V4-2", BLIST_NOLUN},		/* Responds to all lun */
 	{"RODIME", "RO3000S", "2.33", BLIST_NOLUN},		/* Locks up if polled for lun != 0 */
@@ -125,6 +137,7 @@
 	{"INSITE", "Floptical   F*8I", "*", BLIST_KEY},
 	{"INSITE", "I325VM", "*", BLIST_KEY},
 	{"LASOUND","CDX7405","3.10", BLIST_MAX5LUN | BLIST_SINGLELUN},
+	{"MICROP", "4110", "*", BLIST_NOTQ},			/* Buggy Tagged Queuing */
 	{"NRC", "MBR-7", "*", BLIST_FORCELUN | BLIST_SINGLELUN},
 	{"NRC", "MBR-7.4", "*", BLIST_FORCELUN | BLIST_SINGLELUN},
 	{"REGAL", "CDC-4X", "*", BLIST_MAX5LUN | BLIST_SINGLELUN},
@@ -151,7 +164,8 @@
 	{"DELL", "PV660F   PSEUDO",   "*", BLIST_SPARSELUN},
 	{"DELL", "PSEUDO DEVICE .",   "*", BLIST_SPARSELUN}, // Dell PV 530F
 	{"DELL", "PV530F",    "*", BLIST_SPARSELUN}, // Dell PV 530F
-	{"EMC", "SYMMETRIX", "*", BLIST_SPARSELUN},
+	{"EMC", "SYMMETRIX", "*", BLIST_SPARSELUN | BLIST_LARGELUN | BLIST_FORCELUN},
+	{"HP", "A6189A", "*", BLIST_SPARSELUN |  BLIST_LARGELUN}, // HP VA7400, by Alar Aun
 	{"CMD", "CRA-7280", "*", BLIST_SPARSELUN},   // CMD RAID Controller
 	{"CNSI", "G7324", "*", BLIST_SPARSELUN},     // Chaparral G7324 RAID
 	{"CNSi", "G8324", "*", BLIST_SPARSELUN},     // Chaparral G8324 RAID
@@ -162,6 +176,9 @@
 	{"HP", "NetRAID-4M", "*", BLIST_FORCELUN},
 	{"ADAPTEC", "AACRAID", "*", BLIST_FORCELUN},
 	{"ADAPTEC", "Adaptec 5400S", "*", BLIST_FORCELUN},
+	{"COMPAQ", "MSA1000", "*", BLIST_FORCELUN},
+	{"HP", "C1557A", "*", BLIST_FORCELUN},
+	{"IBM", "AuSaV1S2", "*", BLIST_FORCELUN},
 
 	/*
 	 * Must be at end of list...
@@ -169,6 +186,8 @@
 	{NULL, NULL, NULL}
 };
 
+static char * scsi_null_device_strs = "nullnullnullnull";
+
 #define MAX_SCSI_LUNS 0xFFFFFFFF
 
 #ifdef CONFIG_SCSI_MULTI_LUN
@@ -177,11 +196,27 @@
 static unsigned int max_scsi_luns = 1;
 #endif
 
+#ifdef CONFIG_SCSI_REPORT_LUNS
+/* 
+ * max_scsi_report_luns: the maximum number of LUNS that will be
+ * returned from the REPORT LUNS command. 8 times this value must
+ * be allocated. In theory this could be up to an 8 byte value, but
+ * in practice, the maximum number of LUNs suppored by any device
+ * is about 16k.
+ */
+static unsigned int max_scsi_report_luns = 128;
+#endif
+
 #ifdef MODULE
 
 MODULE_PARM(max_scsi_luns, "i");
 MODULE_PARM_DESC(max_scsi_luns, "last scsi LUN (should be between 1 and 2^32-1)");
 
+#ifdef CONFIG_SCSI_REPORT_LUNS
+MODULE_PARM(max_scsi_report_luns, "i");
+MODULE_PARM_DESC(max_scsi_report_luns, "REPORT LUNS maximum number of LUNS received (should be between 1 and 16384)");
+#endif
+
 #else
 
 static int __init scsi_luns_setup(char *str)
@@ -200,6 +235,59 @@
 
 __setup("max_scsi_luns=", scsi_luns_setup);
 
+#ifdef CONFIG_SCSI_REPORT_LUNS
+static int __init max_scsi_report_luns_setup(char *str)
+{
+	unsigned int tmp;
+
+	if (get_option(&str, &tmp) == 1) {
+		max_scsi_report_luns = tmp;
+		return 1;
+	} else {
+		printk("scsi_report_luns_setup : usage max_scsi_report_luns=n "
+		       "(n should be between 1 and 16384)\n");
+		return 0;
+	}
+}
+
+__setup("max_scsi_report_luns=", max_scsi_report_luns_setup);
+#endif /* CONFIG_SCSI_REPORT_LUNS */
+
+#endif
+
+#ifdef CONFIG_SCSI_REPORT_LUNS
+/*
+ * Function:    scsilun_to_int
+ *
+ * Purpose:     Convert ScsiLun (8 byte LUN) to an int.
+ *
+ * Arguments:   scsilun_pnt - pointer to a ScsiLun to be converted
+ *
+ * Lock status: None
+ *
+ * Returns:     cpu ordered integer containing the truncated LUN value
+ *
+ * Notes:       The ScsiLun is assumed to be four levels, with each level
+ * 		effectively containing a SCSI byte-ordered (big endidan)
+ * 		short; the addressing bits of each level are ignored (the
+ * 		highest two bits). For a description of the LUN format, post
+ * 		SCSI-3 see the SCSI Architecture Model, for SCSI-3 see the
+ * 		SCSI Controller Commands.
+ *
+ * 		Given a ScsiLun of: 0a 04 0b 03 00 00 00 00, this function
+ * 		returns the integer: 0x0b030a04
+ */
+static int scsilun_to_int(ScsiLun *scsilun_pnt) 
+{
+	int i;
+	unsigned int lun;
+
+	lun = 0;
+	for (i = 0; i < sizeof(lun); i += 2)
+		lun = lun | (((scsilun_pnt->scsi_lun[i] << 8) |
+			scsilun_pnt->scsi_lun[i + 1]) << (i * 8));
+	return lun;
+}
 #endif
 
 static void print_inquiry(unsigned char *data)
@@ -243,23 +331,20 @@
 		printk("\n");
 }
 
-static int get_device_flags(unsigned char *response_data)
+static int get_device_flags(unsigned char *vendor_pnt, unsigned char *model_pnt)
 {
 	int i = 0;
-	unsigned char *pnt;
 	for (i = 0; 1; i++) {
 		if (device_list[i].vendor == NULL)
 			return 0;
-		pnt = &response_data[8];
-		while (*pnt && *pnt == ' ')
-			pnt++;
-		if (memcmp(device_list[i].vendor, pnt,
+		while (*vendor_pnt && *vendor_pnt == ' ')
+			vendor_pnt++;
+		if (memcmp(device_list[i].vendor, vendor_pnt,
 			   strlen(device_list[i].vendor)))
 			continue;
-		pnt = &response_data[16];
-		while (*pnt && *pnt == ' ')
-			pnt++;
-		if (memcmp(device_list[i].model, pnt,
+		while (*model_pnt && *model_pnt == ' ')
+			model_pnt++;
+		if (memcmp(device_list[i].model, model_pnt,
 			   strlen(device_list[i].model)))
 			continue;
 		return device_list[i].flags;
@@ -283,13 +368,10 @@
 	uint channel;
 	unsigned int dev;
 	unsigned int lun;
-	unsigned int max_dev_lun;
 	unsigned char *scsi_result;
 	unsigned char scsi_result0[256];
 	Scsi_Device *SDpnt;
 	Scsi_Device *SDtail;
-	unsigned int sparse_lun;
-	int lun0_sl;
 
 	scsi_result = NULL;
 
@@ -297,6 +379,9 @@
 					GFP_ATOMIC);
 	if (SDpnt) {
 		memset(SDpnt, 0, sizeof(Scsi_Device));
+		SDpnt->vendor = scsi_null_device_strs;
+		SDpnt->model = scsi_null_device_strs;
+		SDpnt->rev = scsi_null_device_strs;
 		/*
 		 * Register the queue for the device.  All I/O requests will
 		 * come in through here.  We also need to register a pointer to
@@ -352,6 +437,8 @@
 	if (hardcoded == 1) {
 		Scsi_Device *oldSDpnt = SDpnt;
 		struct Scsi_Device_Template *sdtpnt;
+		unsigned int lun0_sl;
+
 		channel = hchannel;
 		if (channel > shpnt->max_channel)
 			goto leave;
@@ -365,8 +452,8 @@
 			lun0_sl = SCSI_3; /* actually don't care for 0 == lun */
 		else
 			lun0_sl = find_lun0_scsi_level(channel, dev, shpnt);
-		scan_scsis_single(channel, dev, lun, lun0_sl, &max_dev_lun, 
-				  &sparse_lun, &SDpnt, shpnt, scsi_result);
+		scan_scsis_single(channel, dev, lun, lun0_sl, &SDpnt, shpnt,
+			       	  scsi_result);
 		if (SDpnt != oldSDpnt) {
 
 			/* it could happen the blockdevice hasn't yet been inited */
@@ -411,31 +498,11 @@
 					order_dev = dev;
 
 				if (shpnt->this_id != order_dev) {
-
-					/*
-					 * We need the for so our continue, etc. work fine. We put this in
-					 * a variable so that we can override it during the scan if we
-					 * detect a device *KNOWN* to have multiple logical units.
-					 */
-					max_dev_lun = (max_scsi_luns < shpnt->max_lun ?
-					 max_scsi_luns : shpnt->max_lun);
-					sparse_lun = 0;
-					for (lun = 0, lun0_sl = SCSI_2; lun < max_dev_lun; ++lun) {
-						/* don't probe further for luns > 7 for targets <= SCSI_2 */
-						if ((lun0_sl < SCSI_3) && (lun > 7))
-							break;
-
-						if (!scan_scsis_single(channel, order_dev, lun, lun0_sl,
-							 	       &max_dev_lun, &sparse_lun, &SDpnt, shpnt,
-								       scsi_result)
-						    && !sparse_lun)
-							break;	/* break means don't probe further for luns!=0 */
-						if (SDpnt && (0 == lun))
-							lun0_sl = SDpnt->scsi_level;
-					}	/* for lun ends */
-				}	/* if this_id != id ends */
-			}	/* for dev ends */
-		}		/* for channel ends */
+					scan_scsis_target(channel, order_dev,
+						&SDpnt, shpnt, scsi_result);
+				}
+			}
+		}
 	}			/* if/else hardcoded */
 
       leave:
@@ -461,6 +528,8 @@
 	/* Last device block does not exist.  Free memory. */
 	if (SDpnt != NULL) {
 		blk_cleanup_queue(&SDpnt->request_queue);
+		if (SDpnt->inquiry)
+			kfree(SDpnt->inquiry);
 		kfree((char *) SDpnt);
 	}
 
@@ -483,15 +552,38 @@
 }
 
 /*
- * The worker for scan_scsis.
- * Returning 0 means Please don't ask further for lun!=0, 1 means OK go on.
- * Global variables used : scsi_devices(linked list)
+ * Function:    scan_scsis_single
+ *
+ * Purpose:     Determine if a SCSI device (a single LUN) exists, and
+ * 		configure it into the system.
+ *
+ * Arguments:   channel    - the host's channel
+ * 		dev        - target dev (target id)
+ * 		lun        - LUN
+ * 		scsi_level - SCSI 1, 2 or 3
+ * 		SDpnt2     - pointer to pointer of a preallocated Scsi_Device
+ * 		shpnt      - host device to use
+ * 		scsi_result - preallocated buffer for the SCSI command result
+ *
+ * Lock status: None
+ *
+ * Returns:     SCSI_SCAN_NO_RESPONSE - no valid response received from the
+ * 		device, this includes allocation failures preventing IO from
+ * 		being sent, or any general failures.
+ *
+ *		SCSI_SCAN_DEVICE_PRESENT - device responded, SDpnt2 has all
+ *		values needed to send IO set, plus scsi_level is set, but no
+ *		new Scsi_Device was added/allocated.
+ *
+ *   		SCSI_SCAN_DEVICE_ADDED - device responded, and added to list;
+ *   		SDpnt2 filled, and pointed to new allocated Scsi_Device.
+ *
+ * Notes:       This could be cleaned up more by moving SDpnt2 and Scsi_Device
+ * 		allocation into scan_scsis_target.
  */
 static int scan_scsis_single(unsigned int channel, unsigned int dev,
-		unsigned int lun, int lun0_scsi_level,
-		unsigned int *max_dev_lun, unsigned int *sparse_lun, 
-		Scsi_Device ** SDpnt2, struct Scsi_Host *shpnt, 
-		char *scsi_result)
+		unsigned int lun, int scsi_level, Scsi_Device ** SDpnt2,
+		struct Scsi_Host *shpnt, char *scsi_result)
 {
 	char devname[64];
 	unsigned char scsi_cmd[MAX_COMMAND_SIZE];
@@ -499,6 +591,7 @@
 	Scsi_Device *SDtail, *SDpnt = *SDpnt2;
 	Scsi_Request * SRpnt;
 	int bflags, type = -1;
+	int possible_inq_resp_len;
 	extern devfs_handle_t scsi_devfs_handle;
 
 	SDpnt->host = shpnt;
@@ -523,7 +616,8 @@
 
 	if (NULL == (SRpnt = scsi_allocate_request(SDpnt))) {
 		printk("scan_scsis_single: no memory\n");
-		return 0;
+		scsi_release_commandblocks(SDpnt);
+		return SCSI_SCAN_NO_RESPONSE;
 	}
 
 	/*
@@ -532,25 +626,26 @@
 	 * devices (and TEST_UNIT_READY to poll for media change). - Paul G.
 	 */
 
-	SCSI_LOG_SCAN_BUS(3, printk("scsi: performing INQUIRY\n"));
+	SCSI_LOG_SCAN_BUS(3,
+		printk("scsi scan: INQUIRY to host %d channel %d id %d lun %d\n",
+		       shpnt->host_no, channel, dev, lun)
+	);
+
 	/*
 	 * Build an INQUIRY command block.
 	 */
+	memset(scsi_cmd, 0, 6);
 	scsi_cmd[0] = INQUIRY;
-	if ((lun > 0) && (lun0_scsi_level <= SCSI_2))
+	if ((lun > 0) && (scsi_level <= SCSI_2))
 		scsi_cmd[1] = (lun << 5) & 0xe0;
-	else	
-		scsi_cmd[1] = 0;	/* SCSI_3 and higher, don't touch */
-	scsi_cmd[2] = 0;
-	scsi_cmd[3] = 0;
-	scsi_cmd[4] = 255;
-	scsi_cmd[5] = 0;
+	scsi_cmd[4] = 36;	/* issue conservative alloc_length */
 	SRpnt->sr_cmd_len = 0;
 	SRpnt->sr_data_direction = SCSI_DATA_READ;
 
+	memset(scsi_result, 0, 36);
 	scsi_wait_req (SRpnt, (void *) scsi_cmd,
 	          (void *) scsi_result,
-	          256, SCSI_TIMEOUT+4*HZ, 3);
+	          36, SCSI_TIMEOUT+4*HZ, 3);
 
 	SCSI_LOG_SCAN_BUS(3, printk("scsi: INQUIRY %s with code 0x%x\n",
 		SRpnt->sr_result ? "failed" : "successful", SRpnt->sr_result));
@@ -565,6 +660,7 @@
 		    SRpnt->sr_sense_buffer[12] == 0x28 &&
 		    SRpnt->sr_sense_buffer[13] == 0) {
 			/* not-ready to ready transition - good */
+	 		/* dpg: bogus? INQUIRY never returns UNIT_ATTENTION */
 		} else {
 			/* assume no peripheral if any other sort of error */
 			scsi_release_request(SRpnt);
@@ -573,25 +669,63 @@
 	}
 
 	/*
-	 * Check for SPARSELUN before checking the peripheral qualifier,
-	 * so sparse lun devices are completely scanned.
-	 */
-
-	/*
 	 * Get any flags for this device.  
 	 */
-	bflags = get_device_flags (scsi_result);
+	bflags = get_device_flags (&scsi_result[8], &scsi_result[16]);
 
-	if (bflags & BLIST_SPARSELUN) {
-	  *sparse_lun = 1;
+	possible_inq_resp_len = (unsigned char)scsi_result[4] + 5;
+	if (BLIST_INQUIRY_36 & bflags)
+		possible_inq_resp_len = 36;
+	else if (BLIST_INQUIRY_58 & bflags)
+		possible_inq_resp_len = 58;
+	else if (possible_inq_resp_len > 255)
+		possible_inq_resp_len = 36;	/* sanity */
+
+	if (possible_inq_resp_len > 36) { /* do additional INQUIRY */
+		memset(scsi_cmd, 0, 6);
+		scsi_cmd[0] = INQUIRY;
+		if ((lun > 0) && (scsi_level <= SCSI_2))
+			scsi_cmd[1] = (lun << 5) & 0xe0;
+		scsi_cmd[4] = (unsigned char)possible_inq_resp_len;
+		SRpnt->sr_cmd_len = 0;
+		SRpnt->sr_data_direction = SCSI_DATA_READ;
+
+		scsi_wait_req (SRpnt, (void *) scsi_cmd,
+			  (void *) scsi_result,
+			  256, SCSI_TIMEOUT+4*HZ, 3);
+		/* assume successful */
+	}
+	SDpnt->inquiry_len = possible_inq_resp_len;
+	SDpnt->inquiry = kmalloc(possible_inq_resp_len, GFP_ATOMIC);
+	if (NULL == SDpnt->inquiry) {
+		scsi_release_commandblocks(SDpnt);
+		scsi_release_request(SRpnt);
+		return SCSI_SCAN_NO_RESPONSE;
 	}
+	memcpy(SDpnt->inquiry, scsi_result, possible_inq_resp_len);
+	SDpnt->vendor = (char *)(SDpnt->inquiry + 8);
+	SDpnt->model = (char *)(SDpnt->inquiry + 16);
+	SDpnt->rev = (char *)(SDpnt->inquiry + 32);
+
+	SDpnt->scsi_level = scsi_result[2] & 0x07;
+	if (SDpnt->scsi_level >= 2 ||
+	    (SDpnt->scsi_level == 1 &&
+	     (scsi_result[3] & 0x0f) == 1))
+		SDpnt->scsi_level++;
+
 	/*
 	 * Check the peripheral qualifier field - this tells us whether LUNS
 	 * are supported here or not.
 	 */
 	if ((scsi_result[0] >> 5) == 3) {
-		scsi_release_request(SRpnt);
-		return 0;	/* assume no peripheral if any sort of error */
+		/*
+		 * Peripheral qualifier 3 (011b): The device server is not
+		 * capable of supporting a physical device on this logical
+		 * unit.
+		 */
+		scsi_release_commandblocks(SDpnt);
+  		scsi_release_request(SRpnt);
+		return SCSI_SCAN_DEVICE_PRESENT;
 	}
 	 /*   The Toshiba ROM was "gender-changed" here as an inline hack.
 	      This is now much more generic.
@@ -609,9 +743,6 @@
 		scsi_result[1] |= 0x80;     /* removable */
 	}
     
-	memcpy(SDpnt->vendor, scsi_result + 8, 8);
-	memcpy(SDpnt->model, scsi_result + 16, 16);
-	memcpy(SDpnt->rev, scsi_result + 32, 4);
 
 	SDpnt->removable = (0x80 & scsi_result[1]) >> 7;
 	/* Use the peripheral qualifier field to determine online/offline */
@@ -630,6 +761,7 @@
 	switch (type = (scsi_result[0] & 0x1f)) {
 	case TYPE_TAPE:
 	case TYPE_DISK:
+	case TYPE_PRINTER:
 	case TYPE_MOD:
 	case TYPE_PROCESSOR:
 	case TYPE_SCANNER:
@@ -667,12 +799,6 @@
 			SDpnt->attached +=
 			    (*sdtpnt->detect) (SDpnt);
 
-	SDpnt->scsi_level = scsi_result[2] & 0x07;
-	if (SDpnt->scsi_level >= 2 ||
-	    (SDpnt->scsi_level == 1 &&
-	     (scsi_result[3] & 0x0f) == 1))
-		SDpnt->scsi_level++;
-
 	/*
 	 * Accommodate drivers that want to sleep when they should be in a polling
 	 * loop.
@@ -726,6 +852,9 @@
 		scsi_wait_req (SRpnt, (void *) scsi_cmd,
 	        	(void *) scsi_result, 0x2a,
 	        	SCSI_TIMEOUT, 3);
+		/*
+		 * scsi_result no longer holds inquiry data.
+		 */
 	}
 
 	scsi_release_request(SRpnt);
@@ -741,14 +870,18 @@
 	SDpnt = (Scsi_Device *) kmalloc(sizeof(Scsi_Device), GFP_ATOMIC);
 	if (!SDpnt) {
 		printk("scsi: scan_scsis_single: Cannot malloc\n");
-		return 0;
+		return SCSI_SCAN_NO_RESPONSE;
 	}
         memset(SDpnt, 0, sizeof(Scsi_Device));
+	SDpnt->vendor = scsi_null_device_strs;
+	SDpnt->model = scsi_null_device_strs;
+	SDpnt->rev = scsi_null_device_strs;
 
 	*SDpnt2 = SDpnt;
 	SDpnt->queue_depth = 1;
 	SDpnt->host = shpnt;
 	SDpnt->online = TRUE;
+	SDpnt->scsi_level = scsi_level;
 
 	/*
 	 * Register the queue for the device.  All I/O requests will come
@@ -788,62 +921,375 @@
 	SDpnt->prev = SDtail;
 	SDpnt->next = NULL;
 
+	return SCSI_SCAN_DEVICE_ADDED;
+}
+
+/*
+ * Function:    scsi_report_lun_scan
+ *
+ * Purpose:     Use a SCSI REPORT LUN to determine what LUNs to scan.
+ *
+ * Arguments:   SDlun0_pnt - pointer to a Scsi_Device for LUN 0
+ * 		channel    - the host's channel
+ * 		dev        - target dev (target id)
+ * 		SDpnt2     - pointer to pointer of a preallocated Scsi_Device
+ * 		shpnt      - host device to use
+ * 		scsi_result - preallocated buffer for the SCSI command result
+ *
+ * Lock status: None
+ *
+ * Returns:     If the LUNs for device at shpnt/channel/dev are scanned,
+ * 		return 0, else return 1.
+ *
+ * Notes:       This code copies and truncates the 8 byte LUN into the
+ * 		current 4 byte (int) lun.
+ */
+static int scsi_report_lun_scan(Scsi_Device *SDlun0_pnt, unsigned
+		int channel, unsigned int dev, Scsi_Device **SDpnt2,
+		struct Scsi_Host *shpnt, char *scsi_result)
+{
+#ifdef CONFIG_SCSI_REPORT_LUNS
+
+	char devname[64];
+	unsigned char scsi_cmd[MAX_COMMAND_SIZE];
+	unsigned int length;
+	unsigned int lun;
+	unsigned int num_luns;
+	unsigned int retries;
+	ScsiLun *fcp_cur_lun_pnt, *lun_data_pnt;
+	Scsi_Request *SRpnt;
+	int scsi_level;
+	char *byte_pnt;
+	int got_command_blocks = 0;
+
 	/*
-	 * Some scsi devices cannot be polled for lun != 0 due to firmware bugs
+	 * Only support SCSI-3 and up devices.
 	 */
-	if (bflags & BLIST_NOLUN)
-		return 0;	/* break; */
+	if (SDlun0_pnt->scsi_level < SCSI_3)
+		return 1;
 
 	/*
-	 * If this device is known to support sparse multiple units, override the
-	 * other settings, and scan all of them.
+	 * Note SDlun0_pnt might be invalid after scan_scsis_single is called.
 	 */
-	if (bflags & BLIST_SPARSELUN) {
-		*max_dev_lun = shpnt->max_lun;
-		*sparse_lun = 1;
-		return 1;
-	}
+
 	/*
-	 * If this device is known to support multiple units, override the other
-	 * settings, and scan all of them.
+	 * Command blocks might be built depending on whether LUN 0 was
+	 * configured or not. Checking has_cmdblocks here is ugly.
 	 */
-	if (bflags & BLIST_FORCELUN) {
-		/* 
-		 * Scanning MAX_SCSI_LUNS units would be a bad idea.
-		 * Any better idea?
-		 * I think we need REPORT LUNS in future to avoid scanning
-		 * of unused LUNs. But, that is another item.
-		 */
-		if (*max_dev_lun < shpnt->max_lun)
-			*max_dev_lun = shpnt->max_lun;
-		else 	if ((max_scsi_luns >> 1) >= *max_dev_lun)
-				*max_dev_lun += shpnt->max_lun;
-			else	*max_dev_lun = max_scsi_luns;
+	if (SDlun0_pnt->has_cmdblocks == 0) {
+		got_command_blocks = 1;
+		scsi_build_commandblocks(SDlun0_pnt);
+	}
+	SRpnt = scsi_allocate_request(SDlun0_pnt);
+
+	sprintf (devname, "host %d channel %d id %d",
+		 SDlun0_pnt->host->host_no, SDlun0_pnt->channel,
+		 SDlun0_pnt->id);
+	/*
+	 * Allocate enough to hold the header (the same size as one ScsiLun)
+	 * plus the max number of luns we are requesting.
+	 *
+	 * XXX: Maybe allocate this once, like scsi_result, and pass it down.
+	 * scsi_result can't be used, as it is needed for the scan INQUIRY
+	 * data. In addition, reallocating and trying again (with the exact
+	 * amount we need) would be nice, but then we need to somehow limit the
+	 * size allocated based on the available memory (and limits of kmalloc).
+	 */
+	length = (max_scsi_report_luns + 1) * sizeof(ScsiLun);
+	lun_data_pnt = (ScsiLun *) kmalloc(length,
+			(shpnt->unchecked_isa_dma ?  GFP_DMA : GFP_ATOMIC));
+	if (lun_data_pnt == NULL) {
+		printk("scsi: scsi_report_lun_scan: Cannot malloc\n");
+		if (got_command_blocks)
+			scsi_release_commandblocks(SDlun0_pnt);
 		return 1;
 	}
+
+	scsi_cmd[0] = REPORT_LUNS;
 	/*
-	 * REGAL CDC-4X: avoid hang after LUN 4
+	 * bytes 1 - 5: reserved, set to zero.
 	 */
-	if (bflags & BLIST_MAX5LUN) {
-		*max_dev_lun = 5;
+	memset(&scsi_cmd[1], 0, 5);
+	/*
+	 * bytes 6 - 9: length of the command.
+	 */
+	scsi_cmd[6] = (unsigned char) (length >> 24) & 0xff;
+	scsi_cmd[7] = (unsigned char) (length >> 16) & 0xff;
+	scsi_cmd[8] = (unsigned char) (length >> 8) & 0xff;
+	scsi_cmd[9] = (unsigned char) length & 0xff;
+
+	scsi_cmd[10] = 0; /* reserved */
+	scsi_cmd[11] = 0; /* control */
+	SRpnt->sr_cmd_len = 0;
+	SRpnt->sr_data_direction = SCSI_DATA_READ;
+
+	/*
+	 * We can get a UNIT ATTENTION, for example a power on/reset, so retry
+	 * a few times (like sd.c does for TEST UNIT READY). Experience shows
+	 * some combinations of adapter/devices get at least two power
+	 * on/resets.
+	 *
+	 * Illegal requests (for devices that do not support REPORT LUNS)
+	 * should come through as a check condition, and will not generate a
+	 * retry.
+	 */
+	retries = 0;
+	while (retries++ < 3) {
+		SCSI_LOG_SCAN_BUS(3,
+			printk("scsi: Sending REPORT LUNS to %s (try %d)\n",
+				devname, retries));
+
+		scsi_wait_req (SRpnt, (void *) scsi_cmd, (void *) lun_data_pnt,
+			  length, SCSI_TIMEOUT+4*HZ, 3);
+
+		SCSI_LOG_SCAN_BUS(3,
+			printk("scsi: REPORT LUNS %s (try %d) result 0x%x\n",
+			SRpnt->sr_result ? "failed" : "successful", retries,
+			SRpnt->sr_result));
+
+		if (SRpnt->sr_result == 0
+		    || SRpnt->sr_sense_buffer[2] != UNIT_ATTENTION)
+			break;
+	}
+
+	scsi_release_request(SRpnt);
+	if (got_command_blocks)
+		scsi_release_commandblocks(SDlun0_pnt);
+
+	if (SRpnt->sr_result) {
+		kfree((char *) lun_data_pnt);
 		return 1;
 	}
 
 	/*
-	 * We assume the device can't handle lun!=0 if: - it reports scsi-0
-	 * (ANSI SCSI Revision 0) (old drives like MAXTOR XT-3280) or - it
-	 * reports scsi-1 (ANSI SCSI Revision 1) and Response Data Format 0
-	 */
-	if (((scsi_result[2] & 0x07) == 0)
-	    ||
-	    ((scsi_result[2] & 0x07) == 1 &&
-	     (scsi_result[3] & 0x0f) == 0))
-		return 0;
+	 * Get the length from the first four bytes of lun_data_pnt.
+	 */
+	byte_pnt = (char*) lun_data_pnt->scsi_lun;
+	length = ((byte_pnt[0] << 24) | (byte_pnt[1] << 16) |
+			 (byte_pnt[2] << 8) | (byte_pnt[3] << 0));
+	if ((length / sizeof(ScsiLun)) > max_scsi_report_luns) {
+		printk("scsi: On %s only %d (max_scsi_report_luns) of %d luns"
+			" reported, try increasing max_scsi_report_luns.\n",
+			devname, max_scsi_report_luns,
+			length / sizeof(ScsiLun));
+		num_luns = max_scsi_report_luns;
+	} else
+		num_luns = (length / sizeof(ScsiLun));
+
+	scsi_level = SDlun0_pnt->scsi_level;
+
+	SCSI_LOG_SCAN_BUS(3,
+		printk("scsi: REPORT LUN scan of host %d channel %d id %d\n",
+		 SDlun0_pnt->host->host_no, SDlun0_pnt->channel,
+		 SDlun0_pnt->id));
+	/*
+	 * Scan the luns in lun_data_pnt. The entry at offset 0 is really
+	 * the header, so start at 1 and go up to and including num_luns.
+	 */
+	for (fcp_cur_lun_pnt = &lun_data_pnt[1];
+	     fcp_cur_lun_pnt <= &lun_data_pnt[num_luns];
+	     fcp_cur_lun_pnt++) {
+		lun = scsilun_to_int(fcp_cur_lun_pnt);
+		/*
+		 * Check if the unused part of fcp_cur_lun_pnt is non-zero,
+		 * and so does not fit in lun.
+		 */
+		if (memcmp(&fcp_cur_lun_pnt->scsi_lun[sizeof(lun)],
+			   "\0\0\0\0", 4) != 0) {
+			int i;
+
+			/*
+			 * Output an error displaying the LUN in byte order,
+			 * this differs from what linux would print for the
+			 * integer LUN value.
+			 */
+			printk("scsi: %s lun 0x", devname);
+			byte_pnt = (char*) fcp_cur_lun_pnt->scsi_lun;
+			for (i = 0; i < sizeof(ScsiLun); i++)
+				printk("%02x", byte_pnt[i]);
+			printk(" has a LUN larger than that supported by"
+				" the kernel\n");
+		} else if (lun == 0) {
+			/*
+			 * LUN 0 has already been scanned.
+			 */
+		} else if (lun > shpnt->max_lun) {
+			printk("scsi: %s lun %d has a LUN larger than allowed"
+				" by the host adapter\n", devname, lun);
+		} else {
+			/*
+			 * Don't use SDlun0_pnt after this call - it can be
+			 * overwritten via SDpnt2 if there was no real device
+			 * at LUN 0.
+			 */
+			if (scan_scsis_single(channel, dev, lun,
+			    scsi_level, SDpnt2, shpnt, scsi_result)
+				== SCSI_SCAN_NO_RESPONSE) {
+				/*
+				 * Got some results, but now none, abort.
+				 */
+				printk("scsi: no response from %s lun %d while"
+				       " scanning, scan aborted\n", devname, 
+				       lun);
+				break;
+			}
+		}
+	}
+
+	kfree((char *) lun_data_pnt);
+	return 0;
+
+#else
 	return 1;
+#endif	/* CONFIG_SCSI_REPORT_LUNS */
+
+}
+
+/*
+ * Function:    scan_scsis_target
+ *
+ * Purpose:     Scan the given scsi target dev, and as needed all LUNs
+ * 		on the target dev.
+ *
+ * Arguments:   channel    - the host's channel
+ * 		dev        - target dev (target id)
+ * 		SDpnt2     - pointer to pointer of a preallocated Scsi_Device
+ * 		shpnt      - host device to use
+ * 		scsi_result - preallocated buffer for the SCSI command result
+ *
+ * Lock status: None
+ *
+ * Returns:     void
+ *
+ * Notes:       This tries to be compatible with linux 2.4.x. This function
+ * 		relies on scan_scsis_single to setup SDlun0_pnt. 
+ *
+ * 		It would be better if the Scsi_Device allocation and freeing
+ * 		was done here, rather than oddly embedded in scan_scsis_single
+ * 		and scan_scsis.
+ */
+static void scan_scsis_target(unsigned int channel, unsigned int dev,
+		Scsi_Device **SDpnt2, struct Scsi_Host *shpnt,
+		char *scsi_result)
+{
+	int bflags, scsi_level;
+	Scsi_Device *SDlun0_pnt;
+	unsigned int sparse_lun = 0;
+	unsigned int max_dev_lun, lun;
+	unsigned int sdlun0_res;
+
+	/*
+	 * Scan lun 0, use the results to determine whether to scan further.
+	 * Ideally, we would not configure LUN 0 until we scan.
+	 */
+	SDlun0_pnt = *SDpnt2;
+	sdlun0_res = scan_scsis_single(channel, dev, 0 /* LUN 0 */, SCSI_2,
+		SDpnt2, shpnt, scsi_result);
+	if (sdlun0_res == SCSI_SCAN_NO_RESPONSE)
+		return;
+
+	/*
+	 * If no new SDpnt was allocated (SCSI_SCAN_DEVICE_PRESENT), SDlun0_pnt
+	 * can later be modified. It is unlikely the lun level would change,
+	 * but save it just in case.
+	 */
+	scsi_level = SDlun0_pnt->scsi_level;
+
+	/*
+	 * We could probably use and save the bflags from lun 0 for all luns
+	 * on a target, but be safe and match current behaviour. (LUN 0
+	 * bflags controls the target settings checked within this function.)
+	 */
+	bflags = get_device_flags (SDlun0_pnt->vendor,  SDlun0_pnt->model);
+
+	/*
+	 * Some scsi devices cannot be polled for lun != 0 due to firmware bugs
+	 */
+	if (bflags & BLIST_NOLUN)
+		return;
+
+	/*
+	 * Ending the scan here if max_scsi_luns == 1 breaks scanning of
+	 * SPARSE, FORCE, MAX5 LUN devices, and the report lun scans.
+	 */
+
+	if (scsi_report_lun_scan(SDlun0_pnt, channel, dev, SDpnt2, shpnt,
+	    scsi_result) == 0)
+		return;
+
+	SCSI_LOG_SCAN_BUS(3,
+		printk("scsi: Sequential scan of host %d channel %d id %d\n",
+		 SDlun0_pnt->host->host_no, SDlun0_pnt->channel,
+		 SDlun0_pnt->id));
+
+	max_dev_lun = (max_scsi_luns < shpnt->max_lun ?
+			max_scsi_luns : shpnt->max_lun);
+	/*
+	 * If this device is known to support sparse multiple units,
+	 * override the other settings, and scan all of them.
+	 */
+	if (bflags & BLIST_SPARSELUN) {
+		max_dev_lun = shpnt->max_lun;
+		sparse_lun = 1;
+	} else if (sdlun0_res == SCSI_SCAN_DEVICE_PRESENT) {
+		/*
+		 * LUN 0 responded, but no LUN 0 was added, don't scan any
+		 * further. This matches linux 2.4.x behaviour.
+		 */
+		return;
+	}
+	/*
+	 * If less than SCSI_1_CSS, and not a forced lun scan, stop
+	 * scanning, this matches 2.4 behaviour, but it could be a bug
+	 * to scan SCSI_1_CSS devices past LUN 0.
+	 */
+	if ((scsi_level < SCSI_1_CCS) && ((bflags &
+	     (BLIST_FORCELUN | BLIST_SPARSELUN | BLIST_MAX5LUN)) == 0))
+		return;
+	/*
+	 * If this device is known to support multiple units, override
+	 * the other settings, and scan all of them.
+	 */
+	if (bflags & BLIST_FORCELUN)
+		max_dev_lun = shpnt->max_lun;
+	/*
+	 * REGAL CDC-4X: avoid hang after LUN 4
+	 */
+	if (bflags & BLIST_MAX5LUN)
+		max_dev_lun = min(5U, max_dev_lun);
+	/*
+	 * Do not scan past LUN 7.
+	 */
+	if (scsi_level < SCSI_3)
+		max_dev_lun = min(8U, max_dev_lun);
+
+	/*
+	 * We have already scanned lun 0.
+	 */
+	for (lun = 1; lun < max_dev_lun; ++lun) {
+		int res;
+		/*
+		 * Scan until scan_scsis_single says stop,
+		 * unless sparse_lun is set.
+		 */
+		res = scan_scsis_single(channel, dev, lun,
+		     scsi_level, SDpnt2, shpnt, scsi_result);
+		if (res == SCSI_SCAN_NO_RESPONSE) {
+			/*
+			 * Got a response on LUN 0, but now no response.
+			 */
+			printk("scsi: no response from device"
+				" host%d/bus%d/target%d/lun%d"
+				" while scanning, scan aborted\n",
+				shpnt->host_no, channel, dev, lun);
+			return;
+		} else if ((res == SCSI_SCAN_DEVICE_PRESENT)
+			    && !sparse_lun)
+			return;
+	}
 }
 
 /*
- * The worker for scan_scsis.
  * Returns the scsi_level of lun0 on this host, channel and dev (if already
  * known), otherwise returns SCSI_2.
  */
diff -Nru a/drivers/scsi/sg.c b/drivers/scsi/sg.c
--- a/drivers/scsi/sg.c	Sun Apr 28 20:10:36 2002
+++ b/drivers/scsi/sg.c	Sun Apr 28 20:10:36 2002
@@ -7,7 +7,7 @@
  * Original driver (sg.c):
  *        Copyright (C) 1992 Lawrence Foard
  * Version 2 and 3 extensions to driver:
- *        Copyright (C) 1998 - 2001 Douglas Gilbert
+ *        Copyright (C) 1998 - 2002 Douglas Gilbert
  *
  *  Modified  19-JAN-1998  Richard Gooch <rgooch@atnf.csiro.au>  Devfs support
  *
@@ -19,9 +19,9 @@
  */
 #include <linux/config.h>
 #ifdef CONFIG_PROC_FS
- static char sg_version_str[] = "Version: 3.5.23 (20020103)";
+ static char * sg_version_str = "Version: 3.5.25 (20020425)";
 #endif
- static int sg_version_num = 30523; /* 2 digits for each component */
+ static int sg_version_num = 30525; /* 2 digits for each component */
 /*
  *  D. P. Gilbert (dgilbert@interlog.com, dougg@triode.net.au), notes:
  *      - scsi logging is available via SCSI_LOG_TIMEOUT macros. First
@@ -285,7 +285,7 @@
 
     if (flags & O_EXCL) {
         if (O_RDONLY == (flags & O_ACCMODE))  {
-            retval = -EACCES;   /* Can't lock it with read only access */
+            retval = -EPERM;   /* Can't lock it with read only access */
 	    goto error_out;
 	}
 	if (sdp->headfp && (flags & O_NONBLOCK))
@@ -574,7 +574,7 @@
     hp->iovec_count = 0;
     hp->mx_sb_len = 0;
     if (input_size > 0)
-	hp->dxfer_direction = ((old_hdr.reply_len - SZ_SG_HEADER) > 0) ?
+	hp->dxfer_direction = (old_hdr.reply_len > SZ_SG_HEADER) ?
 			      SG_DXFER_TO_FROM_DEV : SG_DXFER_TO_DEV;
     else
 	hp->dxfer_direction = (mxsize > 0) ? SG_DXFER_FROM_DEV :
@@ -643,7 +643,7 @@
     if (read_only &&
 	(! sg_allow_access(cmnd[0], sfp->parentdp->device->type))) {
 	sg_remove_request(sfp, srp);
-	return -EACCES;
+	return -EPERM;
     }
     k = sg_common_write(sfp, srp, cmnd, timeout, blocking);
     if (k < 0) return k;
@@ -718,6 +718,7 @@
     default:
 	SRpnt->sr_data_direction = SCSI_DATA_NONE; break;
     }
+    SRpnt->upper_private_data = srp;
     srp->data.k_use_sg = 0;
     srp->data.sglist_len = 0;
     srp->data.bufflen = 0;
@@ -972,7 +973,7 @@
 
 	    copy_from_user(&opcode, siocp->data, 1);
 	    if (! sg_allow_access(opcode, sdp->device->type))
-		return -EACCES;
+		return -EPERM;
 	}
         return scsi_ioctl_send_command(sdp->device, (void *)arg);
     case SG_SET_DEBUG:
@@ -989,7 +990,7 @@
         return scsi_ioctl(sdp->device, cmd_in, (void *)arg);
     default:
 	if (read_only)
-            return -EACCES; /* don't know so take safe approach */
+            return -EPERM; /* don't know so take safe approach */
         return scsi_ioctl(sdp->device, cmd_in, (void *)arg);
     }
 }
@@ -1188,43 +1189,28 @@
  * mid level when a command is completed (or has failed). */
 static void sg_cmd_done_bh(Scsi_Cmnd * SCpnt)
 {
-    Scsi_Request * SRpnt = SCpnt->sc_request;
-    int dev = minor(SRpnt->sr_request.rq_dev);
+    Scsi_Request * SRpnt = NULL;
     Sg_device * sdp = NULL;
     Sg_fd * sfp;
     Sg_request * srp = NULL;
 
-    read_lock(&sg_dev_arr_lock);
-    if (sg_dev_arr && (dev >= 0)) {
-	if (dev < sg_template.dev_max)
-	    sdp = sg_dev_arr[dev];
+    if (SCpnt && (SRpnt = SCpnt->sc_request))
+	srp = (Sg_request *)SRpnt->upper_private_data;
+    if (NULL == srp) {
+    	printk(KERN_ERR "sg_cmd_done_bh: NULL request\n");
+	if (SRpnt)
+	    scsi_release_request(SRpnt);
+	return;
     }
+    sfp = srp->parentfp;
+    if (sfp)
+    	sdp = sfp->parentdp;
     if ((NULL == sdp) || sdp->detached) {
-	read_unlock(&sg_dev_arr_lock);
-	SCSI_LOG_TIMEOUT(1, printk("sg...bh: dev=%d gone\n", dev));
-        scsi_release_request(SRpnt);
-        SRpnt = NULL;
-        return;
-    }
-    sfp = sdp->headfp;
-    while (sfp) {
-	read_lock(&sfp->rq_list_lock);
-	for (srp = sfp->headrp; srp; srp = srp->nextrp) {
-            if (SRpnt == srp->my_cmdp)
-                break;
-        }
-	read_unlock(&sfp->rq_list_lock);
-        if (srp)
-            break;
-        sfp = sfp->nextfp;
-    }
-    if (! srp) {
-	read_unlock(&sg_dev_arr_lock);
-	SCSI_LOG_TIMEOUT(1, printk("sg...bh: req missing, dev=%d\n", dev));
+    	printk(KERN_INFO "sg_cmd_done_bh: device detached\n");
         scsi_release_request(SRpnt);
-        SRpnt = NULL;
         return;
     }
+
     /* First transfer ownership of data buffers to sg_device object. */
     srp->data.k_use_sg = SRpnt->sr_use_sg;
     srp->data.sglist_len = SRpnt->sr_sglist_len;
@@ -1240,10 +1226,10 @@
 
     srp->my_cmdp = NULL;
     srp->done = 1;
-    read_unlock(&sg_dev_arr_lock);
 
     SCSI_LOG_TIMEOUT(4, printk("sg...bh: dev=%d, pack_id=%d, res=0x%x\n",
-		     dev, srp->header.pack_id, (int)SRpnt->sr_result));
+		     minor(sdp->i_rdev), srp->header.pack_id, 
+		     (int)SRpnt->sr_result));
     srp->header.resid = SCpnt->resid;
     /* sg_unmap_and(&srp->data, 0); */     /* unmap locked pages a.s.a.p. */
     /* N.B. unit of duration changes here from jiffies to millisecs */
@@ -2443,7 +2429,11 @@
         return resp;
     if (SG_HEAP_KMAL == mem_src) {
         resp = kmalloc(rqSz, page_mask);
-        if (resp && retSzp) *retSzp = rqSz;
+        if (resp) {
+	    if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
+	    	memset(resp, 0, rqSz);
+	    if (retSzp) *retSzp = rqSz;
+	}
         return resp;
     }
     else if (SG_HEAP_PAGE == mem_src) {
@@ -2460,7 +2450,11 @@
             resp = (char *)__get_free_pages(page_mask, order); /* try half */
             resSz = a_size;
         }
-        if (retSzp) *retSzp = resSz;
+	if (resp) {
+	    if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
+	    	memset(resp, 0, resSz);
+	    if (retSzp) *retSzp = resSz;
+	}
     }
     else
         printk(KERN_ERR "sg_low_malloc: bad mem_src=%d, rqSz=%df\n", 
@@ -2581,9 +2575,9 @@
     }
 }
 
-static unsigned char allow_ops[] = {TEST_UNIT_READY, INQUIRY,
-READ_CAPACITY, READ_BUFFER, READ_6, READ_10, READ_12,
-MODE_SENSE, MODE_SENSE_10};
+static unsigned char allow_ops[] = {TEST_UNIT_READY, REQUEST_SENSE,
+INQUIRY, READ_CAPACITY, READ_BUFFER, READ_6, READ_10, READ_12,
+MODE_SENSE, MODE_SENSE_10, LOG_SENSE};
 
 static int sg_allow_access(unsigned char opcode, char dev_type)
 {
@@ -2983,17 +2977,28 @@
 				 int size, int * eof, void * data)
 { SG_PROC_READ_FN(sg_proc_hoststrs_info); }
 
+#define SG_MAX_HOST_STR_LEN 256
+
 static int sg_proc_hoststrs_info(char * buffer, int * len, off_t * begin,
 				 off_t offset, int size)
 {
     struct Scsi_Host * shp;
     int k;
+    char buff[SG_MAX_HOST_STR_LEN];
+    char * cp;
 
     for (k = 0, shp = scsi_hostlist; shp; shp = shp->next, ++k) {
-    	for ( ; k < shp->host_no; ++k)
-	    PRINT_PROC("<no active host>\n");
-	PRINT_PROC("%s\n", shp->hostt->info ? shp->hostt->info(shp) :
-		    (shp->hostt->name ? shp->hostt->name : "<no name>"));
+        for ( ; k < shp->host_no; ++k)
+            PRINT_PROC("<no active host>\n");
+        strncpy(buff, shp->hostt->info ? shp->hostt->info(shp) :
+                    (shp->hostt->name ? shp->hostt->name : "<no name>"),
+                SG_MAX_HOST_STR_LEN);
+        buff[SG_MAX_HOST_STR_LEN - 1] = '\0';
+        for (cp = buff; *cp; ++cp) {
+            if ('\n' == *cp)
+                *cp = ' '; /* suppress imbedded newlines */
+        }
+        PRINT_PROC("%s\n", buff);
     }
     return 1;
 }
diff -Nru a/drivers/scsi/sr.c b/drivers/scsi/sr.c
--- a/drivers/scsi/sr.c	Sun Apr 28 20:10:32 2002
+++ b/drivers/scsi/sr.c	Sun Apr 28 20:10:32 2002
@@ -49,6 +49,7 @@
 #include <asm/uaccess.h>
 
 #define MAJOR_NR SCSI_CDROM_MAJOR
+#define LOCAL_END_REQUEST
 #include <linux/blk.h>
 #include "scsi.h"
 #include "hosts.h"
diff -Nru a/drivers/scsi/sun3_scsi.c b/drivers/scsi/sun3_scsi.c
--- a/drivers/scsi/sun3_scsi.c	Sun Apr 28 20:10:31 2002
+++ b/drivers/scsi/sun3_scsi.c	Sun Apr 28 20:10:31 2002
@@ -357,7 +357,7 @@
 	NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE );
 	NCR5380_read( RESET_PARITY_INTERRUPT_REG );
 
-	for( end = jiffies + AFTER_RESET_DELAY; jiffies < end; )
+	for( end = jiffies + AFTER_RESET_DELAY; time_before(jiffies, end); )
 		barrier();
 
 	/* switch on SCSI IRQ again */
diff -Nru a/drivers/usb/Config.help b/drivers/usb/Config.help
--- a/drivers/usb/Config.help	Sun Apr 28 20:10:35 2002
+++ b/drivers/usb/Config.help	Sun Apr 28 20:10:35 2002
@@ -69,3 +69,11 @@
   If you say N here, these conditions will cause warning messages
   about USB bandwidth usage to be logged and some devices or
   drivers may not work correctly.
+
+CONFIG_USB_DYNAMIC_MINORS
+  If you say Y here, the USB subsystem will use dynamic minor
+  allocation for any device that uses the USB major number.
+  This means that you can have more than 16 of a single type
+  of device (like USB printers).
+
+  If you are unsure about this, say N here.
diff -Nru a/drivers/usb/class/printer.c b/drivers/usb/class/printer.c
--- a/drivers/usb/class/printer.c	Sun Apr 28 20:10:32 2002
+++ b/drivers/usb/class/printer.c	Sun Apr 28 20:10:32 2002
@@ -107,7 +107,11 @@
 #define USBLP_REQ_RESET				0x02
 #define USBLP_REQ_HP_CHANNEL_CHANGE_REQUEST	0x00	/* HP Vendor-specific */
 
+#ifdef CONFIG_USB_DYNAMIC_MINORS
+#define USBLP_MINORS		256
+#else
 #define USBLP_MINORS		16
+#endif
 #define USBLP_MINOR_BASE	0
 
 #define USBLP_WRITE_TIMEOUT	(5*HZ)			/* 5 seconds */
@@ -208,6 +212,8 @@
 static int usblp_set_protocol(struct usblp *usblp, int protocol);
 static int usblp_cache_device_id_string(struct usblp *usblp);
 
+/* forward reference to make our lives easier */
+extern struct usb_driver usblp_driver;
 
 /*
  * Functions for usblp control messages.
@@ -366,6 +372,7 @@
 {
 	devfs_unregister (usblp->devfs);
 	usblp_table [usblp->minor] = NULL;
+	usb_deregister_dev (&usblp_driver, 1, usblp->minor);
 	info("usblp%d: removed", usblp->minor);
 
 	kfree (usblp->writeurb->transfer_buffer);
@@ -801,12 +808,14 @@
 	init_waitqueue_head(&usblp->wait);
 	usblp->ifnum = ifnum;
 
-	/* Look for a free usblp_table entry. */
-	while (usblp_table[usblp->minor]) {
-		usblp->minor++;
-		if (usblp->minor >= USBLP_MINORS) {
-			err("no more free usblp devices");
-			goto abort;
+	if (usb_register_dev(&usblp_driver, 1, &usblp->minor)) {
+		/* Look for a free usblp_table entry on our own. */
+		while (usblp_table[usblp->minor]) {
+			usblp->minor++;
+			if (usblp->minor >= USBLP_MINORS) {
+				err("no more free usblp devices");
+				goto abort;
+			}
 		}
 	}
 
diff -Nru a/drivers/usb/core/Config.in b/drivers/usb/core/Config.in
--- a/drivers/usb/core/Config.in	Sun Apr 28 20:10:34 2002
+++ b/drivers/usb/core/Config.in	Sun Apr 28 20:10:34 2002
@@ -4,11 +4,8 @@
 bool '  USB verbose debug messages' CONFIG_USB_DEBUG
 
 comment 'Miscellaneous USB options'
-bool '  USB device filesystem' CONFIG_USB_DEVICEFS
-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-   bool '  Enforce USB bandwidth allocation (EXPERIMENTAL)' CONFIG_USB_BANDWIDTH
-else
-   define_bool CONFIG_USB_BANDWIDTH n
-fi
-bool '  Long timeout for slow-responding devices (some MGE Ellipse UPSes)' CONFIG_USB_LONG_TIMEOUT
+    bool '  USB device filesystem' CONFIG_USB_DEVICEFS
+    bool '  Long timeout for slow-responding devices (some MGE Ellipse UPSes)' CONFIG_USB_LONG_TIMEOUT
+dep_bool '  Enforce USB bandwidth allocation (EXPERIMENTAL)' CONFIG_USB_BANDWIDTH $CONFIG_EXPERIMENTAL
+dep_bool '  Dynamic USB minor allocation (EXPERIMENTAL)' CONFIG_USB_DYNAMIC_MINORS $CONFIG_EXPERIMENTAL
 
diff -Nru a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
--- a/drivers/usb/core/usb.c	Sun Apr 28 20:10:37 2002
+++ b/drivers/usb/core/usb.c	Sun Apr 28 20:10:37 2002
@@ -11,6 +11,7 @@
  	more docs, etc)
  * (C) Copyright Yggdrasil Computing, Inc. 2000
  *     (usb_device_id matching changes by Adam J. Richter)
+ * (C) Copyright Greg Kroah-Hartman 2002
  *
  * NOTE! This is not actually a driver at all, rather this is
  * just a collection of helper routines that implement the
@@ -59,7 +60,47 @@
 
 devfs_handle_t usb_devfs_handle;	/* /dev/usb dir. */
 
-static struct usb_driver *usb_minors[256];
+#define MAX_USB_MINORS	256
+static struct usb_driver *usb_minors[MAX_USB_MINORS];
+static spinlock_t minor_lock = SPIN_LOCK_UNLOCKED;
+
+static int usb_register_minors (struct usb_driver *driver, int num_minors, int start_minor)
+{
+	int i;
+
+	dbg("registering %d minors, starting at %d", num_minors, start_minor);
+
+	if (start_minor + num_minors >= MAX_USB_MINORS)
+		return -EINVAL;
+
+	spin_lock (&minor_lock);
+	for (i = start_minor; i < (start_minor + num_minors); ++i)
+		if (usb_minors[i]) {
+			spin_unlock (&minor_lock);
+			err("minor %d is already in use, error registering %s driver",
+			    i, driver->name);
+			return -EINVAL;
+		}
+		
+	for (i = start_minor; i < (start_minor + num_minors); ++i)
+		usb_minors[i] = driver;
+
+	spin_unlock (&minor_lock);
+	return 0;
+}
+
+static void usb_deregister_minors (struct usb_driver *driver, int num_minors, int start_minor)
+{
+	int i;
+
+	dbg ("%s is removing %d minors starting at %d", driver->name,
+	     num_minors, start_minor);
+
+	spin_lock (&minor_lock);
+	for (i = start_minor; i < (start_minor + num_minors); ++i)
+		usb_minors[i] = NULL;
+	spin_unlock (&minor_lock);
+}
 
 /**
  *	usb_register - register a USB driver
@@ -72,18 +113,20 @@
  */
 int usb_register(struct usb_driver *new_driver)
 {
-	int i;
+	int retval = 0;
+	
+	if ((new_driver->fops) && (new_driver->num_minors == 0)) {
+		err ("%s driver must specify num_minors", new_driver->name);
+		return -EINVAL;
+	}
 
+#ifndef CONFIG_USB_DYNAMIC_MINORS
 	if (new_driver->fops != NULL) {
-		for (i = new_driver->minor; i < new_driver->minor + new_driver->num_minors; ++i) {
-			if (usb_minors[i]) {
-				err("error registering %s driver", new_driver->name);
-				return -EINVAL;
-			}
-		}
-		for (i = new_driver->minor; i < new_driver->minor + new_driver->num_minors; ++i)
-			usb_minors[i] = new_driver;
+		retval = usb_register_minors (new_driver, new_driver->num_minors, new_driver->minor);
+		if (retval)
+			return retval;
 	}
+#endif
 
 	info("registered new driver %s", new_driver->name);
 
@@ -96,10 +139,97 @@
 
 	usbfs_update_special();
 
-	return 0;
+	return retval;
+}
+
+
+/**
+ * usb_register_dev - register a USB device, and ask for a minor number
+ * @new_driver: USB operations for the driver
+ * @num_minors: number of minor numbers requested for this device
+ * @start_minor: place to put the new starting minor number
+ *
+ * Used to ask the USB core for a new minor number for a device that has
+ * just showed up.  This is used to dynamically allocate minor numbers
+ * from the pool of USB reserved minor numbers.
+ *
+ * This should be called by all drivers that use the USB major number.
+ * This only returns a good value of CONFIG_USB_DYNAMIC_MINORS is
+ * selected by the user.
+ *
+ * usb_deregister_dev() should be called when the driver is done with
+ * the minor numbers given out by this function.
+ *
+ * Returns -ENODEV if CONFIG_USB_DYNAMIC_MINORS is not enabled in this
+ * kernel, -EINVAL if something bad happens with trying to register a
+ * device, and 0 on success, alone with a value that the driver should
+ * use in start_minor.
+ */
+#ifdef CONFIG_USB_DYNAMIC_MINORS
+int usb_register_dev (struct usb_driver *new_driver, int num_minors, int *start_minor)
+{
+	int i;
+	int j;
+	int good_spot;
+	int retval = -EINVAL;
+
+	dbg ("%s is asking for %d minors", new_driver->name, num_minors);
+
+	if (new_driver->fops == NULL)
+		goto exit;
+
+	*start_minor = 0; 
+	spin_lock (&minor_lock);
+	for (i = 0; i < MAX_USB_MINORS; ++i) {
+		if (usb_minors[i])
+			continue;
+
+		good_spot = 1;
+		for (j = 1; j <= num_minors-1; ++j)
+			if (usb_minors[i+j]) {
+				good_spot = 0;
+				break;
+			}
+		if (good_spot == 0)
+			continue;
+
+		*start_minor = i;
+		spin_unlock (&minor_lock);
+		retval = usb_register_minors (new_driver, num_minors, *start_minor);
+		if (retval) {
+			/* someone snuck in here, so let's start looking all over again */
+			spin_lock (&minor_lock);
+			i = 0;
+			continue;
+		}
+		goto exit;
+	}
+	spin_unlock (&minor_lock);
+exit:
+	return retval;
 }
 
 /**
+ * usb_deregister_dev - deregister a USB device's dynamic minor.
+ * @driver: USB operations for the driver
+ * @num_minors: number of minor numbers to put back.
+ * @start_minor: the starting minor number
+ *
+ * Used in conjunction with usb_register_dev().  This function is called
+ * when the USB driver is finished with the minor numbers gotten from a
+ * call to usb_register_dev() (usually when the device is disconnected
+ * from the system.)
+ * 
+ * This should be called by all drivers that use the USB major number.
+ */
+void usb_deregister_dev (struct usb_driver *driver, int num_minors, int start_minor)
+{
+	usb_deregister_minors (driver, num_minors, start_minor);
+}
+#endif	/* CONFIG_USB_DYNAMIC_MINORS */
+
+
+/**
  *	usb_scan_devices - scans all unclaimed USB interfaces
  *	Context: !in_interrupt ()
  *
@@ -177,12 +307,13 @@
 void usb_deregister(struct usb_driver *driver)
 {
 	struct list_head *tmp;
-	int i;
 
 	info("deregistering driver %s", driver->name);
+
+#ifndef CONFIG_USB_DYNAMIC_MINORS
 	if (driver->fops != NULL)
-		for (i = driver->minor; i < driver->minor + driver->num_minors; ++i)
-			usb_minors[i] = NULL;
+		usb_deregister_minors (driver, driver->num_minors, driver->minor);
+#endif
 
 	/*
 	 * first we remove the driver, to be sure it doesn't get used by
@@ -2524,14 +2655,14 @@
 static int usb_open(struct inode * inode, struct file * file)
 {
 	int minor = minor(inode->i_rdev);
-	struct usb_driver *c = usb_minors[minor];
+	struct usb_driver *c;
 	int err = -ENODEV;
 	struct file_operations *old_fops, *new_fops = NULL;
 
-	/*
-	 * No load-on-demand? Randy, could you ACK that it's really not
-	 * supposed to be done?					-- AV
-	 */
+	spin_lock (&minor_lock);
+	c = usb_minors[minor];
+	spin_unlock (&minor_lock);
+
 	if (!c || !(new_fops = fops_get(c->fops)))
 		return err;
 	old_fops = file->f_op;
@@ -2623,9 +2754,13 @@
 EXPORT_SYMBOL(usb_deregister);
 EXPORT_SYMBOL(usb_scan_devices);
 
+#ifdef CONFIG_USB_DYNAMIC_MINORS
+EXPORT_SYMBOL(usb_register_dev);
+EXPORT_SYMBOL(usb_deregister_dev);
+#endif
+
 EXPORT_SYMBOL(usb_alloc_dev);
 EXPORT_SYMBOL(usb_free_dev);
-EXPORT_SYMBOL(usb_inc_dev_use);
 
 EXPORT_SYMBOL(usb_find_interface_driver_for_ifnum);
 EXPORT_SYMBOL(usb_driver_claim_interface);
diff -Nru a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
--- a/drivers/usb/host/ehci-hub.c	Sun Apr 28 20:10:36 2002
+++ b/drivers/usb/host/ehci-hub.c	Sun Apr 28 20:10:36 2002
@@ -91,7 +91,10 @@
 		if (!(temp & PORT_CONNECT))
 			ehci->reset_done [i] = 0;
 		if ((temp & (PORT_CSC | PORT_PEC | PORT_OCC)) != 0) {
-			set_bit (i, buf);
+			if (i < 7)
+			    buf [0] |= 1 << (i + 1);
+			else
+			    buf [1] |= 1 << (i - 7);
 			status = STS_PCD;
 		}
 	}
@@ -141,7 +144,7 @@
 ) {
 	struct ehci_hcd	*ehci = hcd_to_ehci (hcd);
 	int		ports = HCS_N_PORTS (ehci->hcs_params);
-	u32		temp;
+	u32		temp, status;
 	unsigned long	flags;
 	int		retval = 0;
 
@@ -219,22 +222,22 @@
 		if (!wIndex || wIndex > ports)
 			goto error;
 		wIndex--;
-		memset (buf, 0, 4);
+		status = 0;
 		temp = readl (&ehci->regs->port_status [wIndex]);
 
 		// wPortChange bits
 		if (temp & PORT_CSC)
-			set_bit (USB_PORT_FEAT_C_CONNECTION, buf);
+			status |= 1 << USB_PORT_FEAT_C_CONNECTION;
 		if (temp & PORT_PEC)
-			set_bit (USB_PORT_FEAT_C_ENABLE, buf);
+			status |= 1 << USB_PORT_FEAT_C_ENABLE;
 		// USB_PORT_FEAT_C_SUSPEND
 		if (temp & PORT_OCC)
-			set_bit (USB_PORT_FEAT_C_OVER_CURRENT, buf);
+			status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT;
 
 		/* whoever resets must GetPortStatus to complete it!! */
 		if ((temp & PORT_RESET)
 				&& jiffies > ehci->reset_done [wIndex]) {
-			set_bit (USB_PORT_FEAT_C_RESET, buf);
+			status |= 1 << USB_PORT_FEAT_C_RESET;
 
 			/* force reset to complete */
 			writel (temp & ~PORT_RESET,
@@ -252,26 +255,27 @@
 		// don't show wPortStatus if it's owned by a companion hc
 		if (!(temp & PORT_OWNER)) {
 			if (temp & PORT_CONNECT) {
-				set_bit (USB_PORT_FEAT_CONNECTION, buf);
-				set_bit (USB_PORT_FEAT_HIGHSPEED, buf);
+				status |= 1 << USB_PORT_FEAT_CONNECTION;
+				status |= 1 << USB_PORT_FEAT_HIGHSPEED;
 			}
 			if (temp & PORT_PE)
-				set_bit (USB_PORT_FEAT_ENABLE, buf);
+				status |= 1 << USB_PORT_FEAT_ENABLE;
 			if (temp & PORT_SUSPEND)
-				set_bit (USB_PORT_FEAT_SUSPEND, buf);
+				status |= 1 << USB_PORT_FEAT_SUSPEND;
 			if (temp & PORT_OC)
-				set_bit (USB_PORT_FEAT_OVER_CURRENT, buf);
+				status |= 1 << USB_PORT_FEAT_OVER_CURRENT;
 			if (temp & PORT_RESET)
-				set_bit (USB_PORT_FEAT_RESET, buf);
+				status |= 1 << USB_PORT_FEAT_RESET;
 			if (temp & PORT_POWER)
-				set_bit (USB_PORT_FEAT_POWER, buf);
+				status |= 1 << USB_PORT_FEAT_POWER;
 		}
 
 #ifndef	EHCI_VERBOSE_DEBUG
-	if (*(u16*)(buf+2))	/* only if wPortChange is interesting */
+	if (status & ~0xffff)	/* only if wPortChange is interesting */
 #endif
 		dbg_port (hcd, "GetStatus", wIndex + 1, temp);
-		cpu_to_le32s ((u32 *) buf);
+		// we "know" this alignment is good, caller used kmalloc()...
+		*((u32 *) buf) = cpu_to_le32 (status);
 		break;
 	case SetHubFeature:
 		switch (wValue) {
diff -Nru a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
--- a/drivers/usb/host/ohci-hub.c	Sun Apr 28 20:10:31 2002
+++ b/drivers/usb/host/ohci-hub.c	Sun Apr 28 20:10:31 2002
@@ -96,7 +96,10 @@
 				| RH_PS_OCIC | RH_PS_PRSC;
 		if (status) {
 			changed = 1;
-			set_bit (i + 1, buf);
+			if (i < 7)
+			    buf [0] |= 1 << (i + 1);
+			else
+			    buf [1] |= 1 << (i - 7);
 		}
 	}
 	return changed ? length : 0;
diff -Nru a/drivers/usb/image/mdc800.c b/drivers/usb/image/mdc800.c
--- a/drivers/usb/image/mdc800.c	Sun Apr 28 20:10:30 2002
+++ b/drivers/usb/image/mdc800.c	Sun Apr 28 20:10:30 2002
@@ -119,8 +119,12 @@
 #define TO_READ_FROM_IRQ 		TO_DEFAULT_COMMAND
 #define TO_GET_READY			TO_DEFAULT_COMMAND
 
+#ifdef CONFIG_USB_DYNAMIC_MINORS
+#define MDC800_DEVICE_MINOR_BASE 0
+#else
 /* Minor Number of the device (create with mknod /dev/mustek c 180 32) */
 #define MDC800_DEVICE_MINOR_BASE 32
+#endif
 
 
 /**************************************************************************
@@ -176,6 +180,7 @@
 
 	int			pic_index;	// Cache for the Imagesize (-1 for nothing cached )
 	int			pic_len;
+	int			minor;
 };
 
 
@@ -470,6 +475,8 @@
 
 	down (&mdc800->io_lock);
 
+	usb_register_dev (&mdc800_usb_driver, 1, &mdc800->minor);
+
 	mdc800->dev=dev;
 	mdc800->open=0;
 
@@ -525,6 +532,8 @@
 	if (mdc800->state == NOT_CONNECTED)
 		return;
 	
+	usb_deregister_dev (&mdc800_usb_driver, 1, mdc800->minor);
+
 	mdc800->state=NOT_CONNECTED;
 
 	usb_unlink_urb (mdc800->irq_urb);
diff -Nru a/drivers/usb/image/scanner.c b/drivers/usb/image/scanner.c
--- a/drivers/usb/image/scanner.c	Sun Apr 28 20:10:33 2002
+++ b/drivers/usb/image/scanner.c	Sun Apr 28 20:10:33 2002
@@ -953,9 +953,11 @@
 	
 	down(&scn_mutex);
 
-	for (scn_minor = 0; scn_minor < SCN_MAX_MNR; scn_minor++) {
-		if (!p_scn_table[scn_minor])
-			break;
+	if (usb_register_dev(&scanner_driver, 1, &scn_minor)) {
+		for (scn_minor = 0; scn_minor < SCN_MAX_MNR; scn_minor++) {
+			if (!p_scn_table[scn_minor])
+				break;
+		}
 	}
 
 /* Check to make sure that the last slot isn't already taken */
@@ -1086,6 +1088,7 @@
 
 	dbg("disconnect_scanner: De-allocating minor:%d", scn->scn_minor);
 	devfs_unregister(scn->devfs);
+	usb_deregister_dev(&scanner_driver, 1, scn->scn_minor);
 	p_scn_table[scn->scn_minor] = NULL;
 	usb_free_urb(scn->scn_irq);
 	up (&(scn->sem));
diff -Nru a/drivers/usb/image/scanner.h b/drivers/usb/image/scanner.h
--- a/drivers/usb/image/scanner.h	Sun Apr 28 20:10:35 2002
+++ b/drivers/usb/image/scanner.h	Sun Apr 28 20:10:35 2002
@@ -233,8 +233,13 @@
 #define SCANNER_IOCTL_CTRLMSG _IOWR('U', 0x22, struct usb_ctrlrequest)
 
 
+#ifdef CONFIG_USB_DYNAMIC_MINORS
+#define SCN_MAX_MNR 256
+#define SCN_BASE_MNR 0
+#else
 #define SCN_MAX_MNR 16		/* We're allocated 16 minors */
 #define SCN_BASE_MNR 48		/* USB Scanners start at minor 48 */
+#endif
 
 static DECLARE_MUTEX (scn_mutex); /* Initializes to unlocked */
 
diff -Nru a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c
--- a/drivers/usb/input/hid-core.c	Sun Apr 28 20:10:37 2002
+++ b/drivers/usb/input/hid-core.c	Sun Apr 28 20:10:37 2002
@@ -1271,6 +1271,7 @@
 #define USB_DEVICE_ID_ATEN_UC100KM     0x2004
 #define USB_DEVICE_ID_ATEN_CS124U      0x2202
 #define USB_DEVICE_ID_ATEN_2PORTKVM    0x2204
+#define USB_DEVICE_ID_ATEN_4PORTKVM    0x2205
 
 struct hid_blacklist {
 	__u16 idVendor;
@@ -1288,6 +1289,7 @@
 	{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET },
 	{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET },
 	{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET },
+	{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET },
 	{ 0, 0 }
 };
 
diff -Nru a/drivers/usb/input/hiddev.c b/drivers/usb/input/hiddev.c
--- a/drivers/usb/input/hiddev.c	Sun Apr 28 20:10:35 2002
+++ b/drivers/usb/input/hiddev.c	Sun Apr 28 20:10:35 2002
@@ -25,10 +25,7 @@
  * e-mail - mail your message to Paul Stewart <stewart@wetlogic.net>
  */
 
-#define HIDDEV_MINOR_BASE	96
-#define HIDDEV_MINORS		16
-#define HIDDEV_BUFFER_SIZE	64
-
+#include <linux/config.h>
 #include <linux/poll.h>
 #include <linux/slab.h>
 #include <linux/module.h>
@@ -39,6 +36,15 @@
 #include "hid.h"
 #include <linux/hiddev.h>
 
+#ifdef CONFIG_USB_DYNAMIC_MINORS
+#define HIDDEV_MINOR_BASE	0
+#define HIDDEV_MINORS		256
+#else
+#define HIDDEV_MINOR_BASE	96
+#define HIDDEV_MINORS		16
+#endif
+#define HIDDEV_BUFFER_SIZE	64
+
 struct hiddev {
 	int exist;
 	int open;
@@ -62,6 +68,9 @@
 static struct hiddev *hiddev_table[HIDDEV_MINORS];
 static devfs_handle_t hiddev_devfs_handle;
 
+/* forward reference to make our lives easier */
+extern struct usb_driver hiddev_driver;
+
 /*
  * Find a report, given the report's type and ID.  The ID can be specified
  * indirectly by REPORT_ID_FIRST (which returns the first report of the given
@@ -184,6 +193,7 @@
 static void hiddev_cleanup(struct hiddev *hiddev)
 {
 	devfs_unregister(hiddev->devfs);
+	usb_deregister_dev(&hiddev_driver, 1, hiddev->minor);
 	hiddev_table[hiddev->minor] = NULL;
 	kfree(hiddev);
 }
@@ -605,10 +615,12 @@
 	if (i == hid->maxapplication)
 		return -1;
 
-	for (minor = 0; minor < HIDDEV_MINORS && hiddev_table[minor]; minor++);
-	if (minor == HIDDEV_MINORS) {
-		printk(KERN_ERR "hiddev: no more free hiddev devices\n");
-		return -1;
+	if (usb_register_dev (&hiddev_driver, 1, &minor)) {
+		for (minor = 0; minor < HIDDEV_MINORS && hiddev_table[minor]; minor++);
+		if (minor == HIDDEV_MINORS) {
+			printk(KERN_ERR "hiddev: no more free hiddev devices\n");
+			return -1;
+		}
 	}
 
 	if (!(hiddev = kmalloc(sizeof(struct hiddev), GFP_KERNEL)))
diff -Nru a/drivers/usb/media/Makefile b/drivers/usb/media/Makefile
--- a/drivers/usb/media/Makefile	Sun Apr 28 20:10:30 2002
+++ b/drivers/usb/media/Makefile	Sun Apr 28 20:10:30 2002
@@ -4,7 +4,7 @@
 
 O_TARGET	:= media.o
 
-export-objs	:= ov511.o pwc-uncompress.o
+export-objs	:= ov511.o pwc-uncompress.o usbvideo.o
 
 pwc-objs	:= pwc-if.o pwc-misc.o pwc-ctrl.o pwc-uncompress.o
 
diff -Nru a/drivers/usb/media/dabusb.c b/drivers/usb/media/dabusb.c
--- a/drivers/usb/media/dabusb.c	Sun Apr 28 20:10:37 2002
+++ b/drivers/usb/media/dabusb.c	Sun Apr 28 20:10:37 2002
@@ -52,12 +52,17 @@
 
 /* --------------------------------------------------------------------- */
 
+#ifdef CONFIG_USB_DYNAMIC_MINORS
+#define NRDABUSB 256
+#else
 #define NRDABUSB 4
+#endif
 
 /*-------------------------------------------------------------------*/
 
 static dabusb_t dabusb[NRDABUSB];
 static int buffers = 256;
+extern struct usb_driver dabusb_driver;
 
 /*-------------------------------------------------------------------*/
 
@@ -734,15 +739,18 @@
 	if (ifnum != _DABUSB_IF && usbdev->descriptor.idProduct == 0x9999)
 		return NULL;
 
-	devnum = dabusb_find_struct ();
-	if (devnum == -1)
-		return NULL;
+	if (usb_register_dev (&dabusb_driver, 1, &devnum)) {
+		devnum = dabusb_find_struct ();
+		if (devnum == -1)
+			return NULL;
+	}
 
 	s = &dabusb[devnum];
 
 	down (&s->mutex);
 	s->remove_pending = 0;
 	s->usbdev = usbdev;
+	s->devnum = devnum;
 
 	if (usb_set_configuration (usbdev, usbdev->config[0].bConfigurationValue) < 0) {
 		err("set_configuration failed");
@@ -777,6 +785,7 @@
 
 	dbg("dabusb_disconnect");
 
+	usb_deregister_dev (&dabusb_driver, 1, s->devnum);
 	s->remove_pending = 1;
 	wake_up (&s->wait);
 	if (s->state == _started)
diff -Nru a/drivers/usb/media/dabusb.h b/drivers/usb/media/dabusb.h
--- a/drivers/usb/media/dabusb.h	Sun Apr 28 20:10:30 2002
+++ b/drivers/usb/media/dabusb.h	Sun Apr 28 20:10:30 2002
@@ -6,7 +6,11 @@
 	unsigned int pipe;
 }bulk_transfer_t,*pbulk_transfer_t;
 
+#ifdef CONFIG_USB_DYNAMIC_MINORS
+#define DABUSB_MINOR 0
+#else
 #define DABUSB_MINOR 240		/* some unassigned USB minor */
+#endif
 #define DABUSB_VERSION 0x1000
 #define IOCTL_DAB_BULK              _IOWR('d', 0x30, bulk_transfer_t)
 #define IOCTL_DAB_OVERRUNS	    _IOR('d',  0x15, int)
@@ -31,6 +35,7 @@
 	unsigned int overruns;
 	int readptr;
 	int opened;
+	int devnum;
 	struct list_head free_buff_list;
 	struct list_head rec_buff_list;
 } dabusb_t,*pdabusb_t;
diff -Nru a/drivers/usb/media/usbvideo.c b/drivers/usb/media/usbvideo.c
--- a/drivers/usb/media/usbvideo.c	Sun Apr 28 20:10:36 2002
+++ b/drivers/usb/media/usbvideo.c	Sun Apr 28 20:10:36 2002
@@ -18,7 +18,6 @@
 #include <linux/sched.h>
 #include <linux/list.h>
 #include <linux/slab.h>
-#define	__NO_VERSION__		/* Temporary: usbvideo is not a module yet */
 #include <linux/module.h>
 #include <linux/mm.h>
 #include <linux/smp_lock.h>
@@ -55,6 +54,24 @@
 	unsigned long count, void *data);
 #endif
 
+static void usbvideo_Disconnect(struct usb_device *dev, void *ptr);
+static void usbvideo_CameraRelease(uvd_t *uvd);
+
+static int usbvideo_v4l_ioctl(struct inode *inode, struct file *file,
+			      unsigned int cmd, unsigned long arg);
+static int usbvideo_v4l_mmap(struct file *file, struct vm_area_struct *vma);
+static int usbvideo_v4l_open(struct inode *inode, struct file *file);
+static int usbvideo_v4l_read(struct file *file, char *buf,
+			     size_t count, loff_t *ppos);
+static int usbvideo_v4l_close(struct inode *inode, struct file *file);
+
+static int usbvideo_StartDataPump(uvd_t *uvd);
+static void usbvideo_StopDataPump(uvd_t *uvd);
+static int usbvideo_GetFrame(uvd_t *uvd, int frameNum);
+static int usbvideo_NewFrame(uvd_t *uvd, int framenum);
+static void usbvideo_SoftwareContrastAdjustment(uvd_t *uvd,
+						usbvideo_frame_t *frame);
+
 /*******************************/
 /* Memory management functions */
 /*******************************/
@@ -73,7 +90,7 @@
 	return ret;
 }
 
-void *usbvideo_rvmalloc(unsigned long size)
+static void *usbvideo_rvmalloc(unsigned long size)
 {
 	void *mem;
 	unsigned long adr;
@@ -94,7 +111,7 @@
 	return mem;
 }
 
-void usbvideo_rvfree(void *mem, unsigned long size)
+static void usbvideo_rvfree(void *mem, unsigned long size)
 {
 	unsigned long adr;
 
@@ -110,13 +127,13 @@
 	vfree(mem);
 }
 
-void RingQueue_Initialize(RingQueue_t *rq)
+static void RingQueue_Initialize(RingQueue_t *rq)
 {
 	assert(rq != NULL);
 	init_waitqueue_head(&rq->wqh);
 }
 
-void RingQueue_Allocate(RingQueue_t *rq, int rqLen)
+static void RingQueue_Allocate(RingQueue_t *rq, int rqLen)
 {
 	assert(rq != NULL);
 	assert(rqLen > 0);
@@ -125,14 +142,14 @@
 	assert(rq->queue != NULL);
 }
 
-int RingQueue_IsAllocated(const RingQueue_t *rq)
+static int RingQueue_IsAllocated(const RingQueue_t *rq)
 {
 	if (rq == NULL)
 		return 0;
 	return (rq->queue != NULL) && (rq->length > 0);
 }
 
-void RingQueue_Free(RingQueue_t *rq)
+static void RingQueue_Free(RingQueue_t *rq)
 {
 	assert(rq != NULL);
 	if (RingQueue_IsAllocated(rq)) {
@@ -154,6 +171,8 @@
 	return len;
 }
 
+EXPORT_SYMBOL(RingQueue_Dequeue);
+
 int RingQueue_Enqueue(RingQueue_t *rq, const unsigned char *cdata, int n)
 {
 	int enqueued = 0;
@@ -184,6 +203,8 @@
 	return enqueued;
 }
 
+EXPORT_SYMBOL(RingQueue_Enqueue);
+
 int RingQueue_GetLength(const RingQueue_t *rq)
 {
 	int ri, wi;
@@ -200,7 +221,9 @@
 		return wi + (rq->length - ri);
 }
 
-void RingQueue_InterruptibleSleepOn(RingQueue_t *rq)
+EXPORT_SYMBOL(RingQueue_GetLength);
+
+static void RingQueue_InterruptibleSleepOn(RingQueue_t *rq)
 {
 	assert(rq != NULL);
 	interruptible_sleep_on(&rq->wqh);
@@ -213,6 +236,8 @@
 		wake_up_interruptible(&rq->wqh);
 }
 
+EXPORT_SYMBOL(RingQueue_WakeUpInterruptible);
+
 /*
  * usbvideo_VideosizeToString()
  *
@@ -222,7 +247,7 @@
  * 07-Aug-2000 Created.
  * 19-Oct-2000 Reworked for usbvideo module.
  */
-void usbvideo_VideosizeToString(char *buf, int bufLen, videosize_t vs)
+static void usbvideo_VideosizeToString(char *buf, int bufLen, videosize_t vs)
 {
 	char tmp[40];
 	int n;
@@ -241,8 +266,8 @@
  * History:
  * 01-Feb-2000 Created.
  */
-void usbvideo_OverlayChar(uvd_t *uvd, usbvideo_frame_t *frame,
-			  int x, int y, int ch)
+static void usbvideo_OverlayChar(uvd_t *uvd, usbvideo_frame_t *frame,
+				 int x, int y, int ch)
 {
 	static const unsigned short digits[16] = {
 		0xF6DE, /* 0 */
@@ -296,8 +321,8 @@
  * History:
  * 01-Feb-2000 Created.
  */
-void usbvideo_OverlayString(uvd_t *uvd, usbvideo_frame_t *frame,
-			    int x, int y, const char *str)
+static void usbvideo_OverlayString(uvd_t *uvd, usbvideo_frame_t *frame,
+				   int x, int y, const char *str)
 {
 	while (*str) {
 		usbvideo_OverlayChar(uvd, frame, x, y, *str);
@@ -314,7 +339,7 @@
  * History:
  * 01-Feb-2000 Created.
  */
-void usbvideo_OverlayStats(uvd_t *uvd, usbvideo_frame_t *frame)
+static void usbvideo_OverlayStats(uvd_t *uvd, usbvideo_frame_t *frame)
 {
 	const int y_diff = 8;
 	char tmp[16];
@@ -437,7 +462,7 @@
  * History:
  * 14-Jan-2000 Corrected default multiplier.
  */
-void usbvideo_ReportStatistics(const uvd_t *uvd)
+static void usbvideo_ReportStatistics(const uvd_t *uvd)
 {
 	if ((uvd != NULL) && (uvd->stats.urb_count > 0)) {
 		unsigned long allPackets, badPackets, goodPackets, percent;
@@ -549,6 +574,8 @@
 	}
 }
 
+EXPORT_SYMBOL(usbvideo_DrawLine);
+
 /*
  * usbvideo_TestPattern()
  *
@@ -636,6 +663,8 @@
 	usbvideo_OverlayStats(uvd, frame);
 }
 
+EXPORT_SYMBOL(usbvideo_TestPattern);
+
 /*
  * usbvideo_HexDump()
  *
@@ -663,6 +692,8 @@
 		printk("%s\n", tmp);
 }
 
+EXPORT_SYMBOL(usbvideo_HexDump);
+
 /* Debugging aid */
 void usbvideo_SayAndWait(const char *what)
 {
@@ -672,6 +703,8 @@
 	interruptible_sleep_on_timeout (&wq, HZ*3); /* Timeout */
 }
 
+EXPORT_SYMBOL(usbvideo_SayAndWait);
+
 /* ******************************************************************** */
 
 static void usbvideo_ClientIncModCount(uvd_t *uvd)
@@ -825,6 +858,8 @@
 	return 0;
 }
 
+EXPORT_SYMBOL(usbvideo_register);
+
 /*
  * usbvideo_Deregister()
  *
@@ -886,6 +921,8 @@
 	*pCams = NULL;
 }
 
+EXPORT_SYMBOL(usbvideo_Deregister);
+
 /*
  * usbvideo_Disconnect()
  *
@@ -908,7 +945,7 @@
  * 24-May-2000 Corrected to prevent race condition (MOD_xxx_USE_COUNT).
  * 19-Oct-2000 Moved to usbvideo module.
  */
-void usbvideo_Disconnect(struct usb_device *dev, void *ptr)
+static void usbvideo_Disconnect(struct usb_device *dev, void *ptr)
 {
 	static const char proc[] = "usbvideo_Disconnect";
 	uvd_t *uvd = (uvd_t *) ptr;
@@ -958,7 +995,7 @@
  * History:
  * 27-Jan-2000 Created.
  */
-void usbvideo_CameraRelease(uvd_t *uvd)
+static void usbvideo_CameraRelease(uvd_t *uvd)
 {
 	static const char proc[] = "usbvideo_CameraRelease";
 	if (uvd == NULL) {
@@ -1079,6 +1116,8 @@
 	return uvd;
 }
 
+EXPORT_SYMBOL(usbvideo_AllocateDevice);
+
 int usbvideo_RegisterVideoDevice(uvd_t *uvd)
 {
 	static const char proc[] = "usbvideo_RegisterVideoDevice";
@@ -1139,9 +1178,11 @@
 	return 0;
 }
 
+EXPORT_SYMBOL(usbvideo_RegisterVideoDevice);
+
 /* ******************************************************************** */
 
-int usbvideo_v4l_mmap(struct file *file, struct vm_area_struct *vma)
+static int usbvideo_v4l_mmap(struct file *file, struct vm_area_struct *vma)
 {
 	uvd_t *uvd = file->private_data;
 	unsigned long start = vma->vm_start;
@@ -1185,7 +1226,7 @@
  * 27-Jan-2000 Used USBVIDEO_NUMSBUF as number of URB buffers.
  * 24-May-2000 Corrected to prevent race condition (MOD_xxx_USE_COUNT).
  */
-int usbvideo_v4l_open(struct inode *inode, struct file *file)
+static int usbvideo_v4l_open(struct inode *inode, struct file *file)
 {
 	static const char proc[] = "usbvideo_v4l_open";
 	struct video_device *dev = video_devdata(file);
@@ -1300,7 +1341,7 @@
  * 27-Jan-2000 Used USBVIDEO_NUMSBUF as number of URB buffers.
  * 24-May-2000 Moved MOD_DEC_USE_COUNT outside of code that can sleep.
  */
-int usbvideo_v4l_close(struct inode *inode, struct file *file)
+static int usbvideo_v4l_close(struct inode *inode, struct file *file)
 {
 	static const char proc[] = "usbvideo_v4l_close";
 	struct video_device *dev = file->private_data;
@@ -1554,7 +1595,7 @@
 	return 0;
 }
 
-int usbvideo_v4l_ioctl(struct inode *inode, struct file *file,
+static int usbvideo_v4l_ioctl(struct inode *inode, struct file *file,
 		       unsigned int cmd, unsigned long arg)
 {
 	return video_usercopy(inode, file, cmd, arg, usbvideo_v4l_do_ioctl);
@@ -1571,7 +1612,7 @@
  * 20-Oct-2000 Created.
  * 01-Nov-2000 Added mutex (uvd->lock).
  */
-int usbvideo_v4l_read(struct file *file, char *buf,
+static int usbvideo_v4l_read(struct file *file, char *buf,
 		      size_t count, loff_t *ppos)
 {
 	static const char proc[] = "usbvideo_v4l_read";
@@ -1809,7 +1850,7 @@
  *             of hardcoded values. Simplified by using for loop,
  *             allowed any number of URBs.
  */
-int usbvideo_StartDataPump(uvd_t *uvd)
+static int usbvideo_StartDataPump(uvd_t *uvd)
 {
 	static const char proc[] = "usbvideo_StartDataPump";
 	struct usb_device *dev = uvd->dev;
@@ -1885,7 +1926,7 @@
  * 22-Jan-2000 Corrected order of actions to work after surprise removal.
  * 27-Jan-2000 Used uvd->iface, uvd->ifaceAltInactive instead of hardcoded values.
  */
-void usbvideo_StopDataPump(uvd_t *uvd)
+static void usbvideo_StopDataPump(uvd_t *uvd)
 {
 	static const char proc[] = "usbvideo_StopDataPump";
 	int i, j;
@@ -1929,7 +1970,7 @@
  * 29-Mar-00 Added copying of previous frame into the current one.
  * 6-Aug-00  Added model 3 video sizes, removed redundant width, height.
  */
-int usbvideo_NewFrame(uvd_t *uvd, int framenum)
+static int usbvideo_NewFrame(uvd_t *uvd, int framenum)
 {
 	usbvideo_frame_t *frame;
 	int n;
@@ -2004,7 +2045,7 @@
  * FLAGS_NO_DECODING set. Therefore, any regular build of any driver
  * based on usbvideo can use this feature at any time.
  */
-void usbvideo_CollectRawData(uvd_t *uvd, usbvideo_frame_t *frame)
+static void usbvideo_CollectRawData(uvd_t *uvd, usbvideo_frame_t *frame)
 {
 	int n;
 
@@ -2034,7 +2075,7 @@
 	}
 }
 
-int usbvideo_GetFrame(uvd_t *uvd, int frameNum)
+static int usbvideo_GetFrame(uvd_t *uvd, int frameNum)
 {
 	static const char proc[] = "usbvideo_GetFrame";
 	usbvideo_frame_t *frame = &uvd->frame[frameNum];
@@ -2225,6 +2266,8 @@
 		usbvideo_OverlayStats(uvd, frame);
 }
 
+EXPORT_SYMBOL(usbvideo_DeinterlaceFrame);
+
 /*
  * usbvideo_SoftwareContrastAdjustment()
  *
@@ -2235,7 +2278,8 @@
  * History:
  * 09-Feb-2001  Created.
  */
-void usbvideo_SoftwareContrastAdjustment(uvd_t *uvd, usbvideo_frame_t *frame)
+static void usbvideo_SoftwareContrastAdjustment(uvd_t *uvd, 
+						usbvideo_frame_t *frame)
 {
 	static const char proc[] = "usbvideo_SoftwareContrastAdjustment";
 	int i, j, v4l_linesize;
diff -Nru a/drivers/usb/media/usbvideo.h b/drivers/usb/media/usbvideo.h
--- a/drivers/usb/media/usbvideo.h	Sun Apr 28 20:10:35 2002
+++ b/drivers/usb/media/usbvideo.h	Sun Apr 28 20:10:35 2002
@@ -304,35 +304,22 @@
 #define	VALID_CALLBACK(uvd,cbName) ((((uvd) != NULL) && \
 		((uvd)->handle != NULL)) ? GET_CALLBACK(uvd,cbName) : NULL)
 
-void RingQueue_Initialize(RingQueue_t *rq);
-void RingQueue_Allocate(RingQueue_t *rq, int rqLen);
-int  RingQueue_IsAllocated(const RingQueue_t *rq);
-void RingQueue_Free(RingQueue_t *rq);
 int  RingQueue_Dequeue(RingQueue_t *rq, unsigned char *dst, int len);
 int  RingQueue_Enqueue(RingQueue_t *rq, const unsigned char *cdata, int n);
 int  RingQueue_GetLength(const RingQueue_t *rq);
-void RingQueue_InterruptibleSleepOn(RingQueue_t *rq);
 void RingQueue_WakeUpInterruptible(RingQueue_t *rq);
 
-void usbvideo_CollectRawData(uvd_t *uvd, usbvideo_frame_t *frame);
 void usbvideo_DrawLine(
 	usbvideo_frame_t *frame,
 	int x1, int y1,
 	int x2, int y2,
 	unsigned char cr, unsigned char cg, unsigned char cb);
 void usbvideo_HexDump(const unsigned char *data, int len);
-void usbvideo_OverlayChar(uvd_t *uvd, usbvideo_frame_t *frame, int x, int y, int ch);
-void usbvideo_OverlayString(uvd_t *uvd, usbvideo_frame_t *frame, int x, int y, const char *str);
-void usbvideo_OverlayStats(uvd_t *uvd, usbvideo_frame_t *frame);
-void usbvideo_ReportStatistics(const uvd_t *uvd);
 void usbvideo_SayAndWait(const char *what);
 void usbvideo_TestPattern(uvd_t *uvd, int fullframe, int pmode);
-void usbvideo_VideosizeToString(char *buf, int bufLen, videosize_t vs);
 
 /* Memory allocation routines */
 unsigned long usbvideo_kvirt_to_pa(unsigned long adr);
-void *usbvideo_rvmalloc(unsigned long size);
-void usbvideo_rvfree(void *mem, unsigned long size);
 
 int usbvideo_register(
 	usbvideo_t **pCams,
@@ -344,24 +331,10 @@
 uvd_t *usbvideo_AllocateDevice(usbvideo_t *cams);
 int usbvideo_RegisterVideoDevice(uvd_t *uvd);
 void usbvideo_Deregister(usbvideo_t **uvt);
-void usbvideo_Disconnect(struct usb_device *dev, void *ptr);
-void usbvideo_CameraRelease(uvd_t *uvd);
 
-int usbvideo_v4l_close(struct inode *inode, struct file *file);
 int usbvideo_v4l_initialize(struct video_device *dev);
-int usbvideo_v4l_ioctl(struct inode *inode, struct file *file,
-		       unsigned int cmd, unsigned long arg);
-int usbvideo_v4l_mmap(struct file *file, struct vm_area_struct *vma);
-int usbvideo_v4l_open(struct inode *inode, struct file *file);
-int usbvideo_v4l_read(struct file *file, char *buf,
-		      size_t count, loff_t *ppos);
 
-int usbvideo_GetFrame(uvd_t *uvd, int frameNum);
-int usbvideo_NewFrame(uvd_t *uvd, int framenum);
-int usbvideo_StartDataPump(uvd_t *uvd);
-void usbvideo_StopDataPump(uvd_t *uvd);
 void usbvideo_DeinterlaceFrame(uvd_t *uvd, usbvideo_frame_t *frame);
-void usbvideo_SoftwareContrastAdjustment(uvd_t *uvd, usbvideo_frame_t *frame);
 
 /*
  * This code performs bounds checking - use it when working with
diff -Nru a/drivers/usb/misc/auerswald.c b/drivers/usb/misc/auerswald.c
--- a/drivers/usb/misc/auerswald.c	Sun Apr 28 20:10:35 2002
+++ b/drivers/usb/misc/auerswald.c	Sun Apr 28 20:10:35 2002
@@ -60,12 +60,17 @@
 /* Auerswald Vendor ID */
 #define ID_AUERSWALD  	0x09BF
 
-#ifndef AUER_MINOR_BASE		/* allow external override */
+#ifdef CONFIG_USB_DYNAMIC_MINORS
+/* we can have up to 256 devices at once */
+#define AUER_MINOR_BASE	0
+#define AUER_MAX_DEVICES 256
+#else
 #define AUER_MINOR_BASE	112	/* auerswald driver minor number */
-#endif
 
 /* we can have up to this number of device plugged in at once */
 #define AUER_MAX_DEVICES 16
+#endif
+
 
 /* prefix for the device descriptors in /dev/usb */
 #define AU_PREFIX	"auer"
@@ -284,6 +289,7 @@
 /* Forwards */
 static void auerswald_ctrlread_complete (struct urb * urb);
 static void auerswald_removeservice (pauerswald_t cp, pauerscon_t scp);
+extern struct usb_driver auerswald_driver;
 
 
 /*-------------------------------------------------------------------*/
@@ -1941,16 +1947,18 @@
         auerbuf_init (&cp->bufctl);
 	init_waitqueue_head (&cp->bufferwait);
 
-	/* find a free slot in the device table */
 	down (&dev_table_mutex);
-	for (dtindex = 0; dtindex < AUER_MAX_DEVICES; ++dtindex) {
-		if (dev_table[dtindex] == NULL)
-			break;
-	}
-	if ( dtindex >= AUER_MAX_DEVICES) {
-		err ("more than %d devices plugged in, can not handle this device", AUER_MAX_DEVICES);
-		up (&dev_table_mutex);
-		goto pfail;
+	if (usb_register_dev (&auerswald_driver, 1, &dtindex)) {
+		/* find a free slot in the device table */
+		for (dtindex = 0; dtindex < AUER_MAX_DEVICES; ++dtindex) {
+			if (dev_table[dtindex] == NULL)
+				break;
+		}
+		if ( dtindex >= AUER_MAX_DEVICES) {
+			err ("more than %d devices plugged in, can not handle this device", AUER_MAX_DEVICES);
+			up (&dev_table_mutex);
+			goto pfail;
+		}
 	}
 
 	/* Give the device a name */
@@ -2081,6 +2089,9 @@
 	/* Nobody can see this device any more */
 	devfs_unregister (cp->devfs);
 
+	/* give back our USB minor number */
+	usb_deregister_dev (&auerswald_driver, 1, cp->dtindex);
+
 	/* Stop the interrupt endpoint */
 	auerswald_int_release (cp);
 
@@ -2181,6 +2192,7 @@
 
 MODULE_AUTHOR (DRIVER_AUTHOR);
 MODULE_DESCRIPTION (DRIVER_DESC);
+MODULE_LICENSE ("GPL");
 
 module_init (auerswald_init);
 module_exit (auerswald_cleanup);
diff -Nru a/drivers/usb/misc/brlvger.c b/drivers/usb/misc/brlvger.c
--- a/drivers/usb/misc/brlvger.c	Sun Apr 28 20:10:37 2002
+++ b/drivers/usb/misc/brlvger.c	Sun Apr 28 20:10:37 2002
@@ -315,14 +315,16 @@
 
 	down(&reserve_sem);
 
-	for( i = 0; i < MAX_NR_BRLVGER_DEVS; i++ )
-		if( display_table[i] == NULL )
-			break;
-
-	if( i == MAX_NR_BRLVGER_DEVS ) {
-		err( "This driver cannot handle more than %d "
-				"braille displays", MAX_NR_BRLVGER_DEVS);
-		goto error;
+	if (usb_register_dev(&brlvger_driver, 1, &i)) {
+		for( i = 0; i < MAX_NR_BRLVGER_DEVS; i++ )
+			if( display_table[i] == NULL )
+				break;
+
+		if( i == MAX_NR_BRLVGER_DEVS ) {
+			err( "This driver cannot handle more than %d "
+					"braille displays", MAX_NR_BRLVGER_DEVS);
+			goto error;
+		}
 	}
 
 	if( !(priv = kmalloc (sizeof *priv, GFP_KERNEL)) ){
@@ -423,7 +425,8 @@
 		info("Display %d disconnecting", priv->subminor);
 
 		devfs_unregister(priv->devfs);
-		
+		usb_deregister_dev(&brlvger_driver, 1, priv->subminor);
+
 		down(&disconnect_sem);
 		display_table[priv->subminor] = NULL;
 		up(&disconnect_sem);
diff -Nru a/drivers/usb/net/pegasus.h b/drivers/usb/net/pegasus.h
--- a/drivers/usb/net/pegasus.h	Sun Apr 28 20:10:36 2002
+++ b/drivers/usb/net/pegasus.h	Sun Apr 28 20:10:36 2002
@@ -255,6 +255,8 @@
 		DEFAULT_GPIO_RESET | PEGASUS_II)
 PEGASUS_DEV( "SOHOware NUB100 Ethernet", VENDOR_SOHOWARE, 0x9100,
 		DEFAULT_GPIO_RESET )
+PEGASUS_DEV( "SOHOware NUB110 Ethernet", VENDOR_SOHOWARE, 0x9110,
+		DEFAULT_GPIO_RESET | PEGASUS_II )
 PEGASUS_DEV( "SpeedStream USB 10/100 Ethernet", VENDOR_SIEMENS, 0x1001,
 		DEFAULT_GPIO_RESET )
 
diff -Nru a/drivers/usb/net/usbnet.c b/drivers/usb/net/usbnet.c
--- a/drivers/usb/net/usbnet.c	Sun Apr 28 20:10:30 2002
+++ b/drivers/usb/net/usbnet.c	Sun Apr 28 20:10:30 2002
@@ -121,7 +121,7 @@
 #define	CONFIG_USB_PL2301
 
 
-#define DRIVER_VERSION		"06-Apr-2002"
+#define DRIVER_VERSION		"26-Apr-2002"
 
 /*-------------------------------------------------------------------------*/
 
@@ -484,14 +484,6 @@
 	return 0;
 }
 
-#else
-
-static int genelink_check_connect (struct usbnet *dev)
-{
-	dbg ("%s: assuming peer is connected", dev->net.name);
-	return 0;
-}
-
 #endif
 
 // reset the device status
@@ -623,12 +615,15 @@
 	description:	"Genesys GeneLink",
 	flags:		FLAG_FRAMING_GL | FLAG_NO_SETINT,
 	reset:		genelink_reset,
-	check_connect:	genelink_check_connect,
 	rx_fixup:	genelink_rx_fixup,
 	tx_fixup:	genelink_tx_fixup,
 
 	in: 1, out: 2,
 	epsize:	64,
+
+#ifdef	GENELINK_ACK
+	check_connect:	genelink_check_connect,
+#endif
 };
 
 #endif /* CONFIG_USB_GENESYS */
@@ -652,11 +647,15 @@
  *
  *-------------------------------------------------------------------------*/
 
+static int linuxdev_check_connect (struct usbnet *dev)
+{
+	return 0;		// by definition, always connected
+}
 
 static const struct driver_info	linuxdev_info = {
 	description:	"Linux Device",
 	// no reset defined (yet?)
-	// no check_connect needed!
+	check_connect:	linuxdev_check_connect,
 	in: 2, out: 1,
 	epsize:	64,
 };
@@ -1169,21 +1168,11 @@
 		PL_S_EN|PL_RESET_OUT|PL_RESET_IN|PL_PEER_E);
 }
 
-static int pl_check_connect (struct usbnet *dev)
-{
-	// FIXME test interrupt data PL_PEER_E bit
-	// plus, there's some handshake done by
-	// the prolific win32 driver... 
-	dbg ("%s: assuming peer is connected", dev->net.name);
-	return 0;
-}
-
 static const struct driver_info	prolific_info = {
 	description:	"Prolific PL-2301/PL-2302",
 	flags:		FLAG_NO_SETINT,
 		/* some PL-2302 versions seem to fail usb_set_interface() */
 	reset:		pl_reset,
-	check_connect:	pl_check_connect,
 
 	in: 3, out: 2,
 	epsize:	64,
@@ -1567,7 +1556,7 @@
 		if (dev->driver_info->check_connect) {
 			struct ethtool_value	edata = { ETHTOOL_GLINK };
 
-			edata.data = dev->driver_info->check_connect (dev);
+			edata.data = dev->driver_info->check_connect (dev) == 0;
 			if (copy_to_user (useraddr, &edata, sizeof (edata)))
 				return -EFAULT;
 			return 0;
diff -Nru a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c
--- a/drivers/usb/serial/digi_acceleport.c	Sun Apr 28 20:10:36 2002
+++ b/drivers/usb/serial/digi_acceleport.c	Sun Apr 28 20:10:36 2002
@@ -732,7 +732,7 @@
 	while( count > 0 && ret == 0 ) {
 
 		while( (port->write_urb->status == -EINPROGRESS
-		|| priv->dp_write_urb_in_use) && jiffies < timeout ) {
+		|| priv->dp_write_urb_in_use) && time_before(jiffies, timeout)) {
 			cond_wait_interruptible_timeout_irqrestore(
 				&port->write_wait, DIGI_RETRY_TIMEOUT,
 				&priv->dp_port_lock, flags );
@@ -897,7 +897,7 @@
 
 	spin_lock_irqsave( &priv->dp_port_lock, flags );
 
-	while( jiffies < timeout && !priv->dp_transmit_idle ) {
+	while( time_before(jiffies, timeout) && !priv->dp_transmit_idle ) {
 		cond_wait_interruptible_timeout_irqrestore(
 			&priv->dp_transmit_idle_wait, DIGI_RETRY_TIMEOUT,
 			&priv->dp_port_lock, flags );
diff -Nru a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c
--- a/drivers/usb/serial/keyspan.c	Sun Apr 28 20:10:31 2002
+++ b/drivers/usb/serial/keyspan.c	Sun Apr 28 20:10:31 2002
@@ -28,6 +28,14 @@
 
   Change History
 
+    Wed Apr 25 12:00:00 PST 2002 (Keyspan)
+      Started with Hugh Blemings' code dated Jan 17, 2002.  All adapters
+      now supported (including QI and QW).  Modified port open, port
+      close, and send setup() logic to fix various data and endpoint
+      synchronization bugs and device LED status bugs.  Changed keyspan_
+      write_room() to accurately return transmit buffer availability.
+      Changed forwardingLength from 1 to 16 for all adapters.
+
     Fri Oct 12 16:45:00 EST 2001
       Preliminary USA-19QI and USA-28 support (both test OK for me, YMMV)
 
@@ -98,7 +106,7 @@
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v1.1.2"
+#define DRIVER_VERSION "v1.1.3"
 #define DRIVER_AUTHOR "Hugh Blemings <hugh@misc.nu"
 #define DRIVER_DESC "Keyspan USB to Serial Converter Driver"
 
@@ -423,9 +431,10 @@
 				
 		/* Resubmit urb so we continue receiving */
 	urb->dev = port->serial->dev;
-	if ((err = usb_submit_urb(urb, GFP_ATOMIC)) != 0) {
-		dbg("%s - resubmit read urb failed. (%d)", __FUNCTION__, err);
-	}
+	if (port->open_count)
+		if ((err = usb_submit_urb(urb, GFP_ATOMIC)) != 0) {
+			dbg("%s - resubmit read urb failed. (%d)", __FUNCTION__, err);
+		}
 	return;
 }
 
@@ -461,7 +470,7 @@
 
 	if (p_priv->resend_cont) {
 		dbg ("%s - sending setup", __FUNCTION__); 
-		keyspan_usa26_send_setup(port->serial, port, 0);
+		keyspan_usa26_send_setup(port->serial, port, p_priv->resend_cont - 1);
 	}
 }
 
@@ -519,12 +528,12 @@
 		/*	wake_up_interruptible(&p_priv->open_wait); */
 	}
 	
-exit:
 	/* Resubmit urb so we continue receiving */
 	urb->dev = serial->dev;
 	if ((err = usb_submit_urb(urb, GFP_ATOMIC)) != 0) {
 		dbg("%s - resubmit read urb failed. (%d)", __FUNCTION__, err);
 	}
+exit:
 }
 
 static void	usa26_glocont_callback(struct urb *urb)
@@ -572,9 +581,10 @@
 
 		/* Resubmit urb so we continue receiving */
 		urb->dev = port->serial->dev;
-		if ((err = usb_submit_urb(urb, GFP_ATOMIC)) != 0) {
-			dbg("%s - resubmit read urb failed. (%d)", __FUNCTION__, err);
-		}
+		if (port->open_count)
+			if ((err = usb_submit_urb(urb, GFP_ATOMIC)) != 0) {
+				dbg("%s - resubmit read urb failed. (%d)", __FUNCTION__, err);
+			}
 		p_priv->in_flip ^= 1;
 
 		urb = p_priv->in_urbs[p_priv->in_flip];
@@ -596,7 +606,7 @@
 
 	if (p_priv->resend_cont) {
 		dbg ("%s - sending setup", __FUNCTION__);
-		keyspan_usa28_send_setup(port->serial, port, 0);
+		keyspan_usa28_send_setup(port->serial, port, p_priv->resend_cont - 1);
 	}
 }
 
@@ -653,12 +663,12 @@
 		/*	wake_up_interruptible(&p_priv->open_wait); */
 	}
 
-exit:	
 		/* Resubmit urb so we continue receiving */
 	urb->dev = serial->dev;
 	if ((err = usb_submit_urb(urb, GFP_ATOMIC)) != 0) {
 		dbg("%s - resubmit read urb failed. (%d)", __FUNCTION__, err);
 	}
+exit:	
 }
 
 static void	usa28_glocont_callback(struct urb *urb)
@@ -683,7 +693,7 @@
 
 		if (p_priv->resend_cont) {
 			dbg ("%s - sending setup", __FUNCTION__); 
-			keyspan_usa49_send_setup(serial, port, 0);
+			keyspan_usa49_send_setup(serial, port, p_priv->resend_cont - 1);
 			break;
 		}
 	}
@@ -745,13 +755,13 @@
 		/*	wake_up_interruptible(&p_priv->open_wait); */
 	}
 
-exit:	
 		/* Resubmit urb so we continue receiving */
 	urb->dev = serial->dev;
 
 	if ((err = usb_submit_urb(urb, GFP_ATOMIC)) != 0) {
 		dbg("%s - resubmit read urb failed. (%d)", __FUNCTION__, err);
 	}
+exit:	
 }
 
 static void	usa49_inack_callback(struct urb *urb)
@@ -805,9 +815,10 @@
 				
 		/* Resubmit urb so we continue receiving */
 	urb->dev = port->serial->dev;
-	if ((err = usb_submit_urb(urb, GFP_ATOMIC)) != 0) {
-		dbg("%s - resubmit read urb failed. (%d)", __FUNCTION__, err);
-	}
+	if (port->open_count)
+		if ((err = usb_submit_urb(urb, GFP_ATOMIC)) != 0) {
+			dbg("%s - resubmit read urb failed. (%d)", __FUNCTION__, err);
+		}
 }
 
 /* not used, usa-49 doesn't have per-port control endpoints */
@@ -820,9 +831,27 @@
 
 static int keyspan_write_room (struct usb_serial_port *port)
 {
+	struct keyspan_port_private	*p_priv;
+	const struct keyspan_device_details	*d_details;
+	int				flip;
+	struct urb			*this_urb;
+
 	dbg("%s", __FUNCTION__);
-	return (32);
+	p_priv = (struct keyspan_port_private *)(port->private);
+	d_details = p_priv->device_details;
+
+	flip = p_priv->out_flip;
 
+	/* Check both endpoints to see if any are available. */
+	if ((this_urb = p_priv->out_urbs[flip]) != 0) {
+		if (this_urb->status != -EINPROGRESS)
+			return (63);
+		flip = (flip + 1) & d_details->outdat_endp_flip;        
+		if ((this_urb = p_priv->out_urbs[flip]) != 0) 
+			if (this_urb->status != -EINPROGRESS)
+				return (63);
+	}
+	return (0);
 }
 
 
@@ -873,7 +902,7 @@
 		if ((urb = p_priv->out_urbs[i]) == NULL)
 			continue;
 		urb->dev = serial->dev;
-		usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), 0);
+		/* usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), 0); */
 	}
 
 	keyspan_send_setup(port, 1);
@@ -909,8 +938,12 @@
 	p_priv->rts_state = 0;
 	p_priv->dtr_state = 0;
 	
-	if (serial->dev)
-		keyspan_send_setup(port, 1);
+	if (serial->dev) {
+		keyspan_send_setup(port, 2);
+		/* pilot-xfer seems to work best with this delay */
+		mdelay(100);
+		keyspan_set_termios(port, NULL);
+	}
 
 	/*while (p_priv->outcont_urb->status == -EINPROGRESS) {
 		dbg("%s - urb in progress", __FUNCTION__);
@@ -922,7 +955,7 @@
 	if (serial->dev) {
 		/* Stop reading/writing urbs */
 		stop_urb(p_priv->inack_urb);
-		stop_urb(p_priv->outcont_urb);
+		/* stop_urb(p_priv->outcont_urb); */
 		for (i = 0; i < 2; i++) {
 			stop_urb(p_priv->in_urbs[i]);
 			stop_urb(p_priv->out_urbs[i]);
@@ -1365,7 +1398,10 @@
 		return -1;
 	}
 
-	p_priv->resend_cont = 1;
+	/* Save reset port val for resend.
+	Don't overwrite resend for close condition. */
+	if (p_priv->resend_cont != 3)
+		p_priv->resend_cont = reset_port + 1;
 	if (this_urb->status == -EINPROGRESS) {
 		/*  dbg ("%s - already writing", __FUNCTION__); */
 		return(-1);
@@ -1414,12 +1450,26 @@
 	msg.ctsFlowControl = (p_priv->flow_control == flow_cts);
 	msg.xonFlowControl = 0;
 	msg.setFlowControl = 0xff;
-	
-	msg.forwardingLength = 1;
+	msg.forwardingLength = 16;
 	msg.xonChar = 17;
 	msg.xoffChar = 19;
 
-	if (reset_port) {
+	/* Opening port */
+	if (reset_port == 1) {
+		msg._txOn = 1;
+		msg._txOff = 0;
+		msg.txFlush = 0;
+		msg.txBreak = 0;
+		msg.rxOn = 1;
+		msg.rxOff = 0;
+		msg.rxFlush = 1;
+		msg.rxForward = 0;
+		msg.returnStatus = 0;
+		msg.resetDataToggle = 0xff;
+	}
+
+	/* Closing port */
+	else if (reset_port == 2) {
 		msg._txOn = 0;
 		msg._txOff = 1;
 		msg.txFlush = 0;
@@ -1429,14 +1479,16 @@
 		msg.rxFlush = 1;
 		msg.rxForward = 0;
 		msg.returnStatus = 0;
-		msg.resetDataToggle = 0xff;
+		msg.resetDataToggle = 0;
 	}
+
+	/* Sending intermediate configs */
 	else {
 		msg._txOn = (! p_priv->break_on);
 		msg._txOff = 0;
 		msg.txFlush = 0;
 		msg.txBreak = (p_priv->break_on);
-		msg.rxOn = 1;
+		msg.rxOn = 0;
 		msg.rxOff = 0;
 		msg.rxFlush = 0;
 		msg.rxForward = 0;
@@ -1496,7 +1548,10 @@
 		return -1;
 	}
 
-	p_priv->resend_cont = 1;
+	/* Save reset port val for resend.
+	   Don't overwrite resend for close condition. */
+	if (p_priv->resend_cont != 3)
+		p_priv->resend_cont = reset_port + 1;
 	if (this_urb->status == -EINPROGRESS) {
 		dbg ("%s already writing", __FUNCTION__);
 		return(-1);
@@ -1522,7 +1577,7 @@
 	msg.rts = p_priv->rts_state;
 	msg.dtr = p_priv->dtr_state;
 
-	msg.forwardingLength = 1;
+	msg.forwardingLength = 16;
 	msg.forwardMs = 10;
 	msg.breakThreshold = 45;
 	msg.xonChar = 17;
@@ -1530,8 +1585,22 @@
 
 	/*msg.returnStatus = 1;
 	msg.resetDataToggle = 0xff;*/
-
-	if (reset_port) {
+	/* Opening port */
+	if (reset_port == 1) {
+		msg._txOn = 1;
+		msg._txOff = 0;
+		msg.txFlush = 0;
+		msg.txForceXoff = 0;
+		msg.txBreak = 0;
+		msg.rxOn = 1;
+		msg.rxOff = 0;
+		msg.rxFlush = 1;
+		msg.rxForward = 0;
+		msg.returnStatus = 0;
+		msg.resetDataToggle = 0xff;
+	}
+	/* Closing port */
+	else if (reset_port == 2) {
 		msg._txOn = 0;
 		msg._txOff = 1;
 		msg.txFlush = 0;
@@ -1542,15 +1611,16 @@
 		msg.rxFlush = 1;
 		msg.rxForward = 0;
 		msg.returnStatus = 0;
-		msg.resetDataToggle = 0xff;
+		msg.resetDataToggle = 0;
 	}
+	/* Sending intermediate configs */
 	else {
 		msg._txOn = (! p_priv->break_on);
 		msg._txOff = 0;
 		msg.txFlush = 0;
 		msg.txForceXoff = 0;
 		msg.txBreak = (p_priv->break_on);
-		msg.rxOn = 1;
+		msg.rxOn = 0;
 		msg.rxOff = 0;
 		msg.rxFlush = 0;
 		msg.rxForward = 0;
@@ -1610,7 +1680,10 @@
 		return -1;
 	}
 
-	p_priv->resend_cont = 1;
+	/* Save reset port val for resend.
+	   Don't overwrite resend for close condition. */
+	if (p_priv->resend_cont != 3)
+		p_priv->resend_cont = reset_port + 1;
 	if (this_urb->status == -EINPROGRESS) {
 		/*  dbg ("%s - already writing", __FUNCTION__); */
 		return(-1);
@@ -1663,11 +1736,27 @@
 	msg.xonFlowControl = 0;
 	msg.setFlowControl = 0xff;
 	
-	msg.forwardingLength = 1;
+	msg.forwardingLength = 16;
 	msg.xonChar = 17;
 	msg.xoffChar = 19;
 
-	if (reset_port) {
+	/* Opening port */ 
+	if (reset_port == 1) {
+		msg._txOn = 1;
+		msg._txOff = 0;
+		msg.txFlush = 0;
+		msg.txBreak = 0;
+		msg.rxOn = 1;
+		msg.rxOff = 0;
+		msg.rxFlush = 1;
+		msg.rxForward = 0;
+		msg.returnStatus = 0;
+		msg.resetDataToggle = 0xff;
+		msg.enablePort = 1;
+		msg.disablePort = 0;
+	}
+	/* Closing port */
+	else if (reset_port == 2) {
 		msg._txOn = 0;
 		msg._txOff = 1;
 		msg.txFlush = 0;
@@ -1677,23 +1766,23 @@
 		msg.rxFlush = 1;
 		msg.rxForward = 0;
 		msg.returnStatus = 0;
-		msg.resetDataToggle = 0xff;
+		msg.resetDataToggle = 0;
 		msg.enablePort = 0;
-		msg.disablePort = 0xff;
-	
+		msg.disablePort = 1;
 	}
+	/* Sending intermediate configs */
 	else {
 		msg._txOn = (! p_priv->break_on);
 		msg._txOff = 0;
 		msg.txFlush = 0;
 		msg.txBreak = (p_priv->break_on);
-		msg.rxOn = 1;
+		msg.rxOn = 0;
 		msg.rxOff = 0;
 		msg.rxFlush = 0;
 		msg.rxForward = 0;
 		msg.returnStatus = 0;
 		msg.resetDataToggle = 0x0;
-		msg.enablePort = 0xff;
+		msg.enablePort = 0;
 		msg.disablePort = 0;
 	}
 
diff -Nru a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c
--- a/drivers/usb/usb-skeleton.c	Sun Apr 28 20:10:36 2002
+++ b/drivers/usb/usb-skeleton.c	Sun Apr 28 20:10:36 2002
@@ -85,12 +85,20 @@
 MODULE_DEVICE_TABLE (usb, skel_table);
 
 
-
+#ifdef CONFIG_USB_DYNAMIC_MINORS
+/* 
+ * if the user wants to use dynamic minor numbers, then we can have up to 256
+ * devices
+ */
+#define USB_SKEL_MINOR_BASE	0
+#define MAX_DEVICES		256
+#else
 /* Get a minor range for your devices from the usb maintainer */
-#define USB_SKEL_MINOR_BASE	200	
+#define USB_SKEL_MINOR_BASE	200
 
 /* we can have up to this number of device plugged in at once */
 #define MAX_DEVICES		16
+#endif
 
 /* Structure to hold all of our device specific stuff */
 struct usb_skel {
@@ -192,9 +200,6 @@
 };
 
 
-
-
-
 /**
  *	usb_skel_debug_data
  */
@@ -529,15 +534,17 @@
 		return NULL;
 	}
 
-	/* select a "subminor" number (part of a minor number) */
 	down (&minor_table_mutex);
-	for (minor = 0; minor < MAX_DEVICES; ++minor) {
-		if (minor_table[minor] == NULL)
-			break;
-	}
-	if (minor >= MAX_DEVICES) {
-		info ("Too many devices plugged in, can not handle this device.");
-		goto exit;
+	if (usb_register_dev (&skel_driver, 1, &minor)) {
+		/* we could not get a dynamic minor, so lets find one of our own */
+		for (minor = 0; minor < MAX_DEVICES; ++minor) {
+			if (minor_table[minor] == NULL)
+				break;
+		}
+		if (minor >= MAX_DEVICES) {
+			info ("Too many devices plugged in, can not handle this device.");
+			goto exit;
+		}
 	}
 
 	/* allocate memory for our device state and intialize it */
@@ -642,8 +649,11 @@
 	minor = dev->minor;
 
 	/* remove our devfs node */
-	devfs_unregister(dev->devfs);
+	devfs_unregister (dev->devfs);
 
+	/* give back our dynamic minor */
+	usb_deregister_dev (&skel_driver, 1, minor);
+	
 	/* if the device is not opened, then we clean up right now */
 	if (!dev->open_count) {
 		up (&dev->sem);
diff -Nru a/drivers/video/S3triofb.c b/drivers/video/S3triofb.c
--- a/drivers/video/S3triofb.c	Sun Apr 28 20:10:36 2002
+++ b/drivers/video/S3triofb.c	Sun Apr 28 20:10:36 2002
@@ -60,7 +60,6 @@
 
 #define IO_OUT16VAL(v, r)       (((v) << 8) | (r))
 
-static int currcon = 0;
 static struct display disp;
 static struct fb_info fb_info;
 static struct { u_char red, green, blue, pad; } palette[256];
@@ -84,11 +83,11 @@
 			  struct fb_info *info);
 static int s3trio_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			   struct fb_info *info);
-static int s3trio_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-			   struct fb_info *info);
+static int s3trio_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+                         u_int transp, struct fb_info *info);
 static int s3trio_pan_display(struct fb_var_screeninfo *var, int con,
 			      struct fb_info *info);
-
+static void s3triofb_blank(int blank, struct fb_info *info);
 
     /*
      *  Interface to the low level console driver
@@ -97,10 +96,6 @@
 int s3triofb_init(void);
 static int s3triofbcon_switch(int con, struct fb_info *info);
 static int s3triofbcon_updatevar(int con, struct fb_info *info);
-static void s3triofbcon_blank(int blank, struct fb_info *info);
-#if 0
-static int s3triofbcon_setcmap(struct fb_cmap *cmap, int con);
-#endif
 
     /*
      *  Text console acceleration
@@ -130,10 +125,6 @@
 
 static int s3trio_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
                          u_int *transp, struct fb_info *info);
-static int s3trio_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-                         u_int transp, struct fb_info *info);
-static void do_install_cmap(int con, struct fb_info *info);
-
 
 static struct fb_ops s3trio_ops = {
 	owner:		THIS_MODULE,
@@ -141,8 +132,10 @@
 	fb_get_var:	s3trio_get_var,
 	fb_set_var:	s3trio_set_var,
 	fb_get_cmap:	s3trio_get_cmap,
-	fb_set_cmap:	s3trio_set_cmap,
+	fb_set_cmap:	gen_set_cmap,
+	fb_setcolreg:	s3trio_setcolreg,
 	fb_pan_display:	s3trio_pan_display,
+	fb_blank:	s3triofb_blank,
 };
 
     /*
@@ -227,7 +220,7 @@
 static int s3trio_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			   struct fb_info *info)
 {
-    if (con == currcon) /* current console? */
+    if (con == info->currcon) /* current console? */
 	return fb_get_cmap(cmap, kspc, s3trio_getcolreg, info);
     else if (fb_display[con].cmap.len) /* non default colormap? */
 	fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
@@ -237,29 +230,6 @@
     return 0;
 }
 
-    /*
-     *  Set the Colormap
-     */
-
-static int s3trio_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-			   struct fb_info *info)
-{
-    int err;
-
-
-    if (!fb_display[con].cmap.len) {	/* no colormap allocated? */
-	if ((err = fb_alloc_cmap(&fb_display[con].cmap,
-				 1<<fb_display[con].var.bits_per_pixel, 0)))
-	    return err;
-    }
-    if (con == currcon)			/* current console? */
-	return fb_set_cmap(cmap, kspc, s3trio_setcolreg, info);
-    else
-	fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
-    return 0;
-}
-
-
 int __init s3triofb_init(void)
 {
 	struct device_node *dp;
@@ -533,7 +503,6 @@
     disp.cmap.start = 0;
     disp.cmap.len = 0;
     disp.cmap.red = disp.cmap.green = disp.cmap.blue = disp.cmap.transp = NULL;
-    disp.screen_base = s3trio_base;
     disp.visual = fb_fix.visual;
     disp.type = fb_fix.type;
     disp.type_aux = fb_fix.type_aux;
@@ -555,7 +524,9 @@
     strcpy(fb_info.modename, "Trio64 ");
     strncat(fb_info.modename, dp->full_name, sizeof(fb_info.modename));
     fb_info.node = NODEV;
+    fb_info.currcon = -1;
     fb_info.fbops = &s3trio_ops;
+    fb_info.screen_base = s3trio_base;	
 #if 0
     fb_info.fbvar_num = 1;
     fb_info.fbvar = &fb_var;
@@ -565,7 +536,6 @@
     fb_info.changevar = NULL;
     fb_info.switch_con = &s3triofbcon_switch;
     fb_info.updatevar = &s3triofbcon_updatevar;
-    fb_info.blank = &s3triofbcon_blank;
 #if 0
     fb_info.setcmap = &s3triofbcon_setcmap;
 #endif
@@ -598,10 +568,10 @@
 static int s3triofbcon_switch(int con, struct fb_info *info)
 {
     /* Do we have to save the colormap? */
-    if (fb_display[currcon].cmap.len)
-	fb_get_cmap(&fb_display[currcon].cmap, 1, s3trio_getcolreg, info);
+    if (fb_display[info->currcon].cmap.len)
+	fb_get_cmap(&fb_display[info->currcon].cmap, 1, s3trio_getcolreg, info);
 
-    currcon = con;
+    info->currcon = con;
     /* Install new colormap */
     do_install_cmap(con,info);
     return 0;
@@ -621,28 +591,17 @@
      *  Blank the display.
      */
 
-static void s3triofbcon_blank(int blank, struct fb_info *info)
+static int s3triofb_blank(int blank, struct fb_info *info)
 {
     unsigned char x;
 
     mem_out8(0x1, s3trio_base+0x1008000 + 0x03c4);
     x = mem_in8(s3trio_base+0x1008000 + 0x03c5);
     mem_out8((x & (~0x20)) | (blank << 5), s3trio_base+0x1008000 + 0x03c5);
+    return 0;	
 }
 
     /*
-     *  Set the colormap
-     */
-
-#if 0
-static int s3triofbcon_setcmap(struct fb_cmap *cmap, int con)
-{
-    return(s3trio_set_cmap(cmap, 1, con, &fb_info));
-}
-#endif
-
-
-    /*
      *  Read a single color register and split it into
      *  colors/transparent. Return != 0 for invalid regno.
      */
@@ -683,18 +642,6 @@
     mem_out8((blue & 0xff) >> 2,s3trio_base+0x1008000 + 0x3c9);
 
     return 0;
-}
-
-
-static void do_install_cmap(int con, struct fb_info *info)
-{
-    if (con != currcon)
-	return;
-    if (fb_display[con].cmap.len)
-	fb_set_cmap(&fb_display[con].cmap, 1, s3trio_setcolreg, &fb_info);
-    else
-	fb_set_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel), 1,
-		    s3trio_setcolreg, &fb_info);
 }
 
 static void Trio_WaitQueue(u_short fifo) {
diff -Nru a/drivers/video/acornfb.c b/drivers/video/acornfb.c
--- a/drivers/video/acornfb.c	Sun Apr 28 20:10:35 2002
+++ b/drivers/video/acornfb.c	Sun Apr 28 20:10:35 2002
@@ -737,7 +737,7 @@
 		  u_int trans, struct fb_info *info)
 {
 	union palette pal;
-	int bpp = fb_display[current_par.currcon].var.bits_per_pixel;
+	int bpp = fb_display[info->currcon].var.bits_per_pixel;
 
 	if (regno >= current_par.palette_size)
 		return 1;
@@ -780,7 +780,7 @@
 {
 	int err = 0;
 
-	if (con == current_par.currcon)
+	if (con == info->currcon)
 		err = fb_get_cmap(cmap, kspc, acornfb_getcolreg, info);
 	else if (fb_display[con].cmap.len)
 		fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
@@ -791,26 +791,6 @@
 }
 
 static int
-acornfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-		 struct fb_info *info)
-{
-	int err = 0;
-
-	if (!fb_display[con].cmap.len)
-		err = fb_alloc_cmap(&fb_display[con].cmap,
-				    current_par.palette_size, 0);
-	if (!err) {
-		if (con == current_par.currcon)
-			err = fb_set_cmap(cmap, kspc, acornfb_setcolreg,
-					  info);
-		else
-			fb_copy_cmap(cmap, &fb_display[con].cmap,
-				     kspc ? 0 : 1);
-	}
-	return err;
-}
-
-static int
 acornfb_decode_var(struct fb_var_screeninfo *var, int con)
 {
 	int err;
@@ -1037,7 +1017,7 @@
 		break;
 	}
 
-	display->screen_base	= (char *)current_par.screen_base;
+	info->screen_base	= (char *)current_par.screen_base;
 	display->type		= FB_TYPE_PACKED_PIXELS;
 	display->type_aux	= 0;
 	display->ypanstep	= 1;
@@ -1050,7 +1030,7 @@
 	if (chgvar && info && info->changevar)
 		info->changevar(con);
 
-	if (con == current_par.currcon) {
+	if (con == info->currcon) {
 		struct fb_cmap *cmap;
 		unsigned long start, size;
 		int control;
@@ -1087,7 +1067,7 @@
 		else
 			cmap = fb_default_cmap(current_par.palette_size);
 
-		fb_set_cmap(cmap, 1, acornfb_setcolreg, info);
+		fb_set_cmap(cmap, 1, info);
 	}
 	return 0;
 }
@@ -1165,15 +1145,17 @@
 	fb_get_var:	acornfb_get_var,
 	fb_set_var:	acornfb_set_var,
 	fb_get_cmap:	acornfb_get_cmap,
-	fb_set_cmap:	acornfb_set_cmap,
+	fb_set_cmap:	gen_set_cmap,
+	fb_set_colreg:	acornfb_setcolreg,
 	fb_pan_display:	acornfb_pan_display,
+	fb_blank:	acornfb_blank,
 	fb_mmap:	acornfb_mmap,
 };
 
 static int
 acornfb_updatevar(int con, struct fb_info *info)
 {
-	if (con == current_par.currcon)
+	if (con == info->currcon)
 		acornfb_update_dma(&fb_display[con].var);
 
 	return 0;
@@ -1184,14 +1166,14 @@
 {
 	struct fb_cmap *cmap;
 
-	if (current_par.currcon >= 0) {
-		cmap = &fb_display[current_par.currcon].cmap;
+	if (info->currcon >= 0) {
+		cmap = &fb_display[info->currcon].cmap;
 
 		if (cmap->len)
 			fb_get_cmap(cmap, 1, acornfb_getcolreg, info);
 	}
 
-	current_par.currcon = con;
+	info->currcon = con;
 
 	fb_display[con].var.activate = FB_ACTIVATE_NOW;
 
@@ -1200,11 +1182,11 @@
 	return 0;
 }
 
-static void
+static int 
 acornfb_blank(int blank, struct fb_info *info)
 {
 	union palette p;
-	int i, bpp = fb_display[current_par.currcon].var.bits_per_pixel;
+	int i, bpp = fb_display[info->currcon].var.bits_per_pixel;
 
 #ifdef FBCON_HAS_CFB16
 	if (bpp == 16) {
@@ -1232,6 +1214,7 @@
 			acornfb_palette_write(i, p);
 		}
 	}
+	return 0;
 }
 
 /*
@@ -1324,7 +1307,6 @@
 	fb_info.changevar	   = NULL;
 	fb_info.switch_con	   = acornfb_switch;
 	fb_info.updatevar	   = acornfb_updatevar;
-	fb_info.blank		   = acornfb_blank;
 	fb_info.flags		   = FBINFO_FLAG_DEFAULT;
 
 	global_disp.dispsw	   = &fbcon_dummy;
@@ -1635,7 +1617,7 @@
 		}
 	}
 
-	current_par.currcon	   = -1;
+	fb_info.currcon	   = -1;
 	current_par.screen_base	   = SCREEN_BASE;
 	current_par.screen_base_p  = SCREEN_START;
 	current_par.using_vram     = 0;
diff -Nru a/drivers/video/acornfb.h b/drivers/video/acornfb.h
--- a/drivers/video/acornfb.h	Sun Apr 28 20:10:37 2002
+++ b/drivers/video/acornfb.h	Sun Apr 28 20:10:37 2002
@@ -55,7 +55,6 @@
 	unsigned int	vram_half_sam;
 	unsigned int	palette_size;
 	  signed int	montype;
-	  signed int	currcon;
 	unsigned int	using_vram	: 1;
 	unsigned int	dpms		: 1;
 
diff -Nru a/drivers/video/amifb.c b/drivers/video/amifb.c
--- a/drivers/video/amifb.c	Sun Apr 28 20:10:34 2002
+++ b/drivers/video/amifb.c	Sun Apr 28 20:10:34 2002
@@ -739,8 +739,6 @@
 	u_short fmode;		/* vmode */
 } currentpar;
 
-static int currcon = 0;
-
 static struct display disp;
 
 static struct fb_info fb_info;
@@ -1096,12 +1094,13 @@
 			 struct fb_info *info);
 static int amifb_set_var(struct fb_var_screeninfo *var, int con,
 			 struct fb_info *info);
+static int amifb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+                           u_int transp, struct fb_info *info);
+static int amifb_blank(int blank, struct fb_info *info);
 static int amifb_pan_display(struct fb_var_screeninfo *var, int con,
 			     struct fb_info *info);
 static int amifb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			  struct fb_info *info);
-static int amifb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-			  struct fb_info *info);
 static int amifb_ioctl(struct inode *inode, struct file *file, u_int cmd,
 		       u_long arg, int con, struct fb_info *info);
 
@@ -1121,13 +1120,11 @@
 static void amifb_deinit(void);
 static int amifbcon_switch(int con, struct fb_info *info);
 static int amifbcon_updatevar(int con, struct fb_info *info);
-static void amifbcon_blank(int blank, struct fb_info *info);
 
 	/*
 	 * Internal routines
 	 */
 
-static void do_install_cmap(int con, struct fb_info *info);
 static int flash_cursor(void);
 static void amifb_interrupt(int irq, void *dev_id, struct pt_regs *fp);
 static u_long chipalloc(u_long size);
@@ -1152,8 +1149,6 @@
 static int ami_update_par(void);
 static int ami_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
                          u_int *transp, struct fb_info *info);
-static int ami_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-                         u_int transp, struct fb_info *info);
 static void ami_update_display(void);
 static void ami_init_display(void);
 static void ami_do_blank(void);
@@ -1175,8 +1170,10 @@
 	fb_get_var:	amifb_get_var,
 	fb_set_var:	amifb_set_var,
 	fb_get_cmap:	amifb_get_cmap,
-	fb_set_cmap:	amifb_set_cmap,
+	fb_set_cmap:	gen_set_cmap,
+	fb_setcolreg:	amifb_setcolreg,
 	fb_pan_display:	amifb_pan_display,
+	fb_blankL:	amifb_blank,
 	fb_ioctl:	amifb_ioctl,
 };
 
@@ -1336,7 +1333,6 @@
 			struct fb_fix_screeninfo fix;
 
 			ami_encode_fix(&fix, &par);
-			display->screen_base = (char *)videomemory;
 			display->visual = fix.visual;
 			display->type = fix.type;
 			display->type_aux = fix.type_aux;
@@ -1372,7 +1368,7 @@
 				return err;
 			do_install_cmap(con, info);
 		}
-		if (con == currcon)
+		if (con == info->currcon)
 			ami_set_var(&display->var);
 	}
 	return 0;
@@ -1399,7 +1395,7 @@
 		    var->yoffset+fb_display[con].var.yres > fb_display[con].var.yres_virtual)
 			return -EINVAL;
 	}
-	if (con == currcon)
+	if (con == info->currcon)
 		ami_pan_var(var);
 	fb_display[con].var.xoffset = var->xoffset;
 	fb_display[con].var.yoffset = var->yoffset;
@@ -1417,7 +1413,7 @@
 static int amifb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			  struct fb_info *info)
 {
-	if (con == currcon) /* current console? */
+	if (con == info->currcon) /* current console? */
 		return fb_get_cmap(cmap, kspc, ami_getcolreg, info);
 	else if (fb_display[con].cmap.len) /* non default colormap? */
 		fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
@@ -1426,29 +1422,7 @@
 			     cmap, kspc ? 0 : 2);
 	return 0;
 }
-
-	/*
-	 * Set the Colormap
-	 */
-
-static int amifb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-			  struct fb_info *info)
-{
-	int err;
-
-	if (!fb_display[con].cmap.len) {	/* no colormap allocated? */
-		if ((err = fb_alloc_cmap(&fb_display[con].cmap,
-					 1<<fb_display[con].var.bits_per_pixel,
-					 0)))
-			return err;
-	}
-	if (con == currcon)			/* current console? */
-		return fb_set_cmap(cmap, kspc, ami_setcolreg, info);
-	else
-		fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
-	return 0;
-}
-
+	
 	/*
 	 * Amiga Frame Buffer Specific ioctls
 	 */
@@ -1732,9 +1706,9 @@
 	fb_info.node = NODEV;
 	fb_info.fbops = &amifb_ops;
 	fb_info.disp = &disp;
+	fb_info.currcon = 1;
 	fb_info.switch_con = &amifbcon_switch;
 	fb_info.updatevar = &amifbcon_updatevar;
-	fb_info.blank = &amifbcon_blank;
 	fb_info.flags = FBINFO_FLAG_DEFAULT;
 	memset(&var, 0, sizeof(var));
 
@@ -1770,6 +1744,7 @@
 		videomemory = ZTWO_VADDR(videomemory_phys);
 	}
 
+	fb_info.screen_base = (char *)videomemory;
 	memset(dummysprite, 0, DUMMYSPRITEMEMSIZE);
 
 	/*
@@ -1819,10 +1794,10 @@
 static int amifbcon_switch(int con, struct fb_info *info)
 {
 	/* Do we have to save the colormap? */
-	if (fb_display[currcon].cmap.len)
-		fb_get_cmap(&fb_display[currcon].cmap, 1, ami_getcolreg, info);
+	if (fb_display[info->currcon].cmap.len)
+		fb_get_cmap(&fb_display[info->currcon].cmap, 1, ami_getcolreg, info);
 
-	currcon = con;
+	info->currcon = con;
 	ami_set_var(&fb_display[con].var);
 	/* Install new colormap */
 	do_install_cmap(con, info);
@@ -1843,24 +1818,10 @@
 	 * Blank the display.
 	 */
 
-static void amifbcon_blank(int blank, struct fb_info *info)
+static int amifb_blank(int blank, struct fb_info *info)
 {
 	do_blank = blank ? blank : -1;
-}
-
-	/*
-	 * Set the colormap
-	 */
-
-static void do_install_cmap(int con, struct fb_info *info)
-{
-	if (con != currcon)
-		return;
-	if (fb_display[con].cmap.len)
-		fb_set_cmap(&fb_display[con].cmap, 1, ami_setcolreg, info);
-	else
-		fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
-					    1, ami_setcolreg, info);
+	return 0;
 }
 
 static int flash_cursor(void)
@@ -2611,8 +2572,8 @@
 	 * entries in the var structure). Return != 0 for invalid regno.
 	 */
 
-static int ami_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-                         u_int transp, struct fb_info *info)
+static int amifb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+                           u_int transp, struct fb_info *info)
 {
 	if (IS_AGA) {
 		if (regno > 255)
diff -Nru a/drivers/video/anakinfb.c b/drivers/video/anakinfb.c
--- a/drivers/video/anakinfb.c	Sun Apr 28 20:10:37 2002
+++ b/drivers/video/anakinfb.c	Sun Apr 28 20:10:37 2002
@@ -24,7 +24,6 @@
 #include <video/fbcon-cfb16.h>
 
 static u16 colreg[16];
-static int currcon = 0;
 static struct fb_info fb_info;
 static struct display display;
 
@@ -118,7 +117,7 @@
 anakinfb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			struct fb_info *info)
 {
-	if (con == currcon)
+	if (con == info->currcon)
 		return fb_get_cmap(cmap, kspc, anakinfb_getcolreg, info);
 	else if (fb_display[con].cmap.len)
 		fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
@@ -128,26 +127,9 @@
 }
 
 static int
-anakinfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-			struct fb_info *info)
-{
-	int err;
-
-	if (!fb_display[con].cmap.len) {
-		if ((err = fb_alloc_cmap(&fb_display[con].cmap, 16, 0)))
-			return err;
-	}
-	if (con == currcon)
-		return fb_set_cmap(cmap, kspc, anakinfb_setcolreg, info);
-	else
-		fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
-	return 0;
-}
-
-static int
 anakinfb_switch_con(int con, struct fb_info *info)
 { 
-	currcon = con;
+	info->currcon = con;
 	return 0;
 
 }
@@ -172,7 +154,9 @@
 	fb_get_var:	anakinfb_get_var,
 	fb_set_var:	anakinfb_set_var,
 	fb_get_cmap:	anakinfb_get_cmap,
-	fb_set_cmap:	anakinfb_set_cmap,
+	fb_set_cmap:	gen_set_cmap,
+	fb_setcolreg:	anakinfb_setcolreg,
+	fb_blank:	anakinfb_blank,
 };
 
 int __init
@@ -183,18 +167,18 @@
 	fb_info.node = NODEV;
 	fb_info.flags = FBINFO_FLAG_DEFAULT;
 	fb_info.fbops = &anakinfb_ops;
+	fb_info.currcon = -1;
 	fb_info.disp = &display;
 	strcpy(fb_info.fontname, "VGA8x16");
 	fb_info.changevar = NULL;
 	fb_info.switch_con = &anakinfb_switch_con;
 	fb_info.updatevar = &anakinfb_updatevar;
-	fb_info.blank = &anakinfb_blank;
 
 	memset(&display, 0, sizeof(struct display));
 	anakinfb_get_var(&display.var, 0, &fb_info);
 	if (!(request_mem_region(VGA_START, VGA_SIZE, "vga")))
 		return -ENOMEM;
-	if (!(display.screen_base = ioremap(VGA_START, VGA_SIZE))) {
+	if (!(fb_info.screen_base = ioremap(VGA_START, VGA_SIZE))) {
 		release_mem_region(VGA_START, VGA_SIZE);
 		return -EIO;
 	}
@@ -215,7 +199,7 @@
 #endif
 
 	if (register_framebuffer(&fb_info) < 0) {
-		iounmap(display.screen_base);
+		iounmap(fb_info.screen_base);
 		release_mem_region(VGA_START, VGA_SIZE);
 		return -EINVAL;
 	}
diff -Nru a/drivers/video/atafb.c b/drivers/video/atafb.c
--- a/drivers/video/atafb.c	Sun Apr 28 20:10:31 2002
+++ b/drivers/video/atafb.c	Sun Apr 28 20:10:31 2002
@@ -173,8 +173,6 @@
 
 static int current_par_valid=0; 
 
-static int currcon=0;
-
 static int mono_moni=0;
 
 static struct display disp;
@@ -285,13 +283,6 @@
  * void (*set_par)( struct atafb_par *par )
  *   Set the hardware according to 'par'.
  *   
- * int (*setcolreg)( unsigned regno, unsigned red,
- *                   unsigned green, unsigned blue,
- *                   unsigned transp, struct fb_info *info )
- *   Set a single color register. The values supplied are already
- *   rounded down to the hardware's capabilities (according to the
- *   entries in the var structure). Return != 0 for invalid regno.
- *
  * int (*getcolreg)( unsigned regno, unsigned *red,
  *                   unsigned *green, unsigned *blue,
  *                   unsigned *transp, struct fb_info *info )
@@ -324,9 +315,6 @@
 	int  (*getcolreg)( unsigned regno, unsigned *red,
 					   unsigned *green, unsigned *blue,
 					   unsigned *transp, struct fb_info *info );
-	int  (*setcolreg)( unsigned regno, unsigned red,
-					   unsigned green, unsigned blue,
-					   unsigned transp, struct fb_info *info );
 	void (*set_screen_base)(void *s_base);
 	int  (*blank)( int blank_mode );
 	int  (*pan_display)( struct fb_var_screeninfo *var,
@@ -1597,7 +1585,7 @@
 							   struct atafb_par *par )
 {
 	int xoffset;
-	int bpp = fb_display[currcon].var.bits_per_pixel;
+	int bpp = fb_display[fb_info.currcon].var.bits_per_pixel;
 
 	if (bpp == 1)
 		var->xoffset = up(var->xoffset, 32);
@@ -1608,13 +1596,13 @@
 		var->xoffset = up(var->xoffset, 2);
 	}
 	par->hw.falcon.line_offset = bpp *
-	       	(fb_display[currcon].var.xres_virtual - fb_display[currcon].var.xres) / 16;
+	       	(fb_display[fb_info.currcon].var.xres_virtual - fb_display[currcon].var.xres) / 16;
 	if (par->hw.falcon.xoffset)
 		par->hw.falcon.line_offset -= bpp;
 	xoffset = var->xoffset - par->hw.falcon.xoffset;
 
 	par->screen_base = screen_base +
-	        (var->yoffset * fb_display[currcon].var.xres_virtual + xoffset) * bpp / 8;
+	        (var->yoffset * fb_display[fb_info.currcon].var.xres_virtual + xoffset) * bpp / 8;
 	if (fbhw->set_screen_base)
 		fbhw->set_screen_base (par->screen_base);
 	else
@@ -2313,8 +2301,8 @@
 		return -EINVAL;
 	var->xoffset = up(var->xoffset, 16);
 	par->screen_base = screen_base +
-	        (var->yoffset * fb_display[currcon].var.xres_virtual + var->xoffset)
-	        * fb_display[currcon].var.bits_per_pixel / 8;
+	        (var->yoffset * fb_display[fb_info.currcon].var.xres_virtual + var->xoffset)
+	        * fb_display[fb_info.currcon].var.bits_per_pixel / 8;
 	fbhw->set_screen_base (par->screen_base);
 	return 0;
 }
@@ -2326,7 +2314,7 @@
 #ifdef ATAFB_TT
 static struct fb_hwswitch tt_switch = {
 	tt_detect, tt_encode_fix, tt_decode_var, tt_encode_var,
-	tt_get_par, tt_set_par, tt_getcolreg, tt_setcolreg,
+	tt_get_par, tt_set_par, tt_getcolreg, 
 	set_screen_base, NULL, pan_display
 };
 #endif
@@ -2335,14 +2323,14 @@
 static struct fb_hwswitch falcon_switch = {
 	falcon_detect, falcon_encode_fix, falcon_decode_var, falcon_encode_var,
 	falcon_get_par, falcon_set_par, falcon_getcolreg,
-	falcon_setcolreg, set_screen_base, falcon_blank, falcon_pan_display
+	set_screen_base, falcon_blank, falcon_pan_display
 };
 #endif
 
 #ifdef ATAFB_STE
 static struct fb_hwswitch st_switch = {
 	stste_detect, stste_encode_fix, stste_decode_var, stste_encode_var,
-	stste_get_par, stste_set_par, stste_getcolreg, stste_setcolreg,
+	stste_get_par, stste_set_par, stste_getcolreg,
 	stste_set_screen_base, NULL, pan_display
 };
 #endif
@@ -2350,7 +2338,7 @@
 #ifdef ATAFB_EXT
 static struct fb_hwswitch ext_switch = {
 	ext_detect, ext_encode_fix, ext_decode_var, ext_encode_var,
-	ext_get_par, ext_set_par, ext_getcolreg, ext_setcolreg, NULL, NULL, NULL
+	ext_get_par, ext_set_par, ext_getcolreg, NULL, NULL, NULL
 };
 #endif
 
@@ -2410,20 +2398,6 @@
 	return 0;
 }
 
-/* Functions for handling colormap */
-
-static void
-do_install_cmap(int con, struct fb_info *info)
-{
-	if (con != currcon)
-		return;
-	if (fb_display[con].cmap.len)
-		fb_set_cmap(&fb_display[con].cmap, 1, fbhw->setcolreg, info);
-	else
-		fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
-					    1, fbhw->setcolreg, info);		
-}
-
 static int
 atafb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
 {
@@ -2468,7 +2442,7 @@
 	atafb_get_var(&var, con, info);
 	if (con == -1)
 		con=0;
-	display->screen_base = (void *)fix.smem_start;
+	info->screen_base = (void *)fix.smem_start;
 	display->visual = fix.visual;
 	display->type = fix.type;
 	display->type_aux = fix.type_aux;
@@ -2530,7 +2504,7 @@
 {
 	int err,oldxres,oldyres,oldbpp,oldxres_virtual,
 	    oldyres_virtual,oldyoffset;
-	if ((err=do_fb_set_var(var, con==currcon)))
+	if ((err=do_fb_set_var(var, con==info->currcon)))
 		return err;
 	if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
 		oldxres=fb_display[con].var.xres;
@@ -2560,7 +2534,7 @@
 static int
 atafb_get_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info)
 {
-	if (con == currcon) /* current console ? */
+	if (con == info->currcon) /* current console ? */
 		return fb_get_cmap(cmap, kspc, fbhw->getcolreg, info);
 	else
 		if (fb_display[con].cmap.len) /* non default colormap ? */
@@ -2572,23 +2546,6 @@
 }
 
 static int
-atafb_set_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info)
-{
-	int err;
-	if (! fb_display[con].cmap.len) { /* no colormap allocated ? */
-		if ((err = fb_alloc_cmap(&fb_display[con].cmap,
-					 1 << fb_display[con].var.bits_per_pixel,
-					 0)))
-		return err;
-	}
-	if (con == currcon) /* current console ? */
-		return fb_set_cmap(cmap, kspc, fbhw->setcolreg, info);
-	else
-		fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
-	return 0;
-}
-
-static int
 atafb_pan_display(struct fb_var_screeninfo *var, int con, struct fb_info *info)
 {
 	int xoffset = var->xoffset;
@@ -2599,7 +2556,7 @@
 	    || yoffset < 0 || yoffset + fb_display[con].var.yres > fb_display[con].var.yres_virtual)
 		return -EINVAL;
 
-	if (con == currcon) {
+	if (con == info->currcon) {
 		if (fbhw->pan_display) {
 			if ((err = fbhw->pan_display(var, &current_par)))
 				return err;
@@ -2642,8 +2599,9 @@
 	fb_get_var:	atafb_get_var,
 	fb_set_var:	atafb_set_var,
 	fb_get_cmap:	atafb_get_cmap,
-	fb_set_cmap:	atafb_set_cmap,
+	fb_set_cmap:	gen_set_cmap,
 	fb_pan_display:	atafb_pan_display,
+	fb_blank:	atafb_blank,
 	fb_ioctl:	atafb_ioctl,
 };
 
@@ -2692,11 +2650,11 @@
 atafb_switch(int con, struct fb_info *info)
 {
 	/* Do we have to save the colormap ? */
-	if (fb_display[currcon].cmap.len)
-		fb_get_cmap(&fb_display[currcon].cmap, 1, fbhw->getcolreg,
+	if (fb_display[info->currcon].cmap.len)
+		fb_get_cmap(&fb_display[info->currcon].cmap, 1, fbhw->getcolreg,
 			    info);
 	do_fb_set_var(&fb_display[con].var,1);
-	currcon=con;
+	info->currcon=con;
 	/* Install new colormap */
 	do_install_cmap(con, info);
 	return 0;
@@ -2709,13 +2667,13 @@
  * 3 = suspend hsync
  * 4 = off
  */
-static void
+static int 
 atafb_blank(int blank, struct fb_info *info)
 {
 	unsigned short black[16];
 	struct fb_cmap cmap;
 	if (fbhw->blank && !fbhw->blank(blank))
-		return;
+		return 1;
 	if (blank) {
 		memset(black, 0, 16*sizeof(unsigned short));
 		cmap.red=black;
@@ -2724,10 +2682,11 @@
 		cmap.transp=NULL;
 		cmap.start=0;
 		cmap.len=16;
-		fb_set_cmap(&cmap, 1, fbhw->setcolreg, info);
+		fb_set_cmap(&cmap, 1, info);
 	}
 	else
-		do_install_cmap(currcon, info);
+		do_install_cmap(info->currcon, info);
+	return 0;
 }
 
 int __init atafb_init(void)
@@ -2743,18 +2702,21 @@
 #ifdef ATAFB_EXT
 		if (external_addr) {
 			fbhw = &ext_switch;
+			fb_info.fb_setcolreg = &ext_setcolreg;
 			break;
 		}
 #endif
 #ifdef ATAFB_TT
 		if (ATARIHW_PRESENT(TT_SHIFTER)) {
 			fbhw = &tt_switch;
+			fb_info.fb_setcolreg = &tt_setcolreg;
 			break;
 		}
 #endif
 #ifdef ATAFB_FALCON
 		if (ATARIHW_PRESENT(VIDEL_SHIFTER)) {
 			fbhw = &falcon_switch;
+			fb_info.fb_setcolreg = &falcon_setcolreg;
 			request_irq(IRQ_AUTO_4, falcon_vbl_switcher, IRQ_TYPE_PRIO,
 			            "framebuffer/modeswitch", falcon_vbl_switcher);
 			break;
@@ -2764,9 +2726,11 @@
 		if (ATARIHW_PRESENT(STND_SHIFTER) ||
 		    ATARIHW_PRESENT(EXTD_SHIFTER)) {
 			fbhw = &st_switch;
+			fb_info.fb_setcolreg = &stste_setcolreg;
 			break;
 		}
 		fbhw = &st_switch;
+		fb_info.fb_setcolreg = &stste_setcolreg;
 		printk("Cannot determine video hardware; defaulting to ST(e)\n");
 #else /* ATAFB_STE */
 		/* no default driver included */
@@ -2831,9 +2795,9 @@
 	fb_info.node = NODEV;
 	fb_info.fbops = &atafb_ops;
 	fb_info.disp = &disp;
+	fb_info.currcon = -1;
 	fb_info.switch_con = &atafb_switch;
 	fb_info.updatevar = &fb_update_var;
-	fb_info.blank = &atafb_blank;
 	fb_info.flags = FBINFO_FLAG_DEFAULT;
 	do_fb_set_var(&atafb_predefined[default_par-1], 1);
 	strcat(fb_info.modename, fb_var_names[default_par-1][0]);
diff -Nru a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c
--- a/drivers/video/aty/atyfb_base.c	Sun Apr 28 20:10:31 2002
+++ b/drivers/video/aty/atyfb_base.c	Sun Apr 28 20:10:31 2002
@@ -1,4 +1,3 @@
-
 /*
  *  ATI Frame Buffer Device Driver Core
  *
@@ -147,8 +146,11 @@
 			 struct fb_info *fb);
 static int atyfb_set_var(struct fb_var_screeninfo *var, int con,
 			 struct fb_info *fb);
+static int atyfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+			 u_int transp, struct fb_info *fb);
 static int atyfb_pan_display(struct fb_var_screeninfo *var, int con,
 			     struct fb_info *fb);
+static int atyfb_blank(int blank, struct fb_info *fb);
 static int atyfb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			  struct fb_info *info);
 static int atyfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
@@ -168,8 +170,6 @@
 
 static int atyfbcon_switch(int con, struct fb_info *fb);
 static int atyfbcon_updatevar(int con, struct fb_info *fb);
-static void atyfbcon_blank(int blank, struct fb_info *fb);
-
 
     /*
      *  Internal routines
@@ -205,9 +205,6 @@
 			     int bpp, int accel);
 static int atyfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
 			 u_int *transp, struct fb_info *fb);
-static int atyfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-			 u_int transp, struct fb_info *fb);
-static void do_install_cmap(int con, struct fb_info *info);
 #ifdef CONFIG_PPC
 static int read_aty_sense(const struct fb_info_aty *info);
 #endif
@@ -222,8 +219,6 @@
 int atyfb_setup(char*);
 #endif
 
-static int currcon = 0;
-
 static struct fb_ops atyfb_ops = {
 	owner:		THIS_MODULE,
 	fb_open:	atyfb_open,
@@ -233,7 +228,9 @@
 	fb_set_var:	atyfb_set_var,
 	fb_get_cmap:	atyfb_get_cmap,
 	fb_set_cmap:	atyfb_set_cmap,
+	fb_setcolreg:	atyfb_setcolreg,
 	fb_pan_display:	atyfb_pan_display,
+	fb_blank:	atyfb_blank,
 	fb_ioctl:	atyfb_ioctl,
 #ifdef __sparc__
 	fb_mmap:	atyfb_mmap,
@@ -1116,7 +1113,7 @@
 	    struct fb_fix_screeninfo fix;
 
 	    encode_fix(&fix, &par, info);
-	    display->screen_base = (char *)info->frame_buffer;
+	    fb->screen_base = (char *)info->frame_buffer;
 	    display->visual = fix.visual;
 	    display->type = fix.type;
 	    display->type_aux = fix.type_aux;
@@ -1211,7 +1208,7 @@
 	    return err;
     }
     if (!info->display_fg || con == info->display_fg->vc_num)			/* current console? */
-	return fb_set_cmap(cmap, kspc, atyfb_setcolreg, info);
+	return fb_set_cmap(cmap, kspc, info);
     else
 	fb_copy_cmap(cmap, &disp->cmap, kspc ? 0 : 1);
     return 0;
@@ -1649,7 +1646,7 @@
 				       (void *)info->frame_buffer, nb);
 
 			/* Blank display and LCD */
-			atyfbcon_blank(VESA_POWERDOWN+1, (struct fb_info *)info);
+			atyfb_blank(VESA_POWERDOWN+1, (struct fb_info *)info);
 
 			/* Set chip to "suspend" mode */
 			result = aty_power_mgmt(1, info);
@@ -1667,7 +1664,7 @@
 			}
 			/* Restore display */
 			atyfb_set_par(&info->current_par, info);
-			atyfbcon_blank(0, (struct fb_info *)info);
+			atyfb_blank(0, (struct fb_info *)info);
 			break;
 		}
 	}
@@ -1988,11 +1985,11 @@
     info->fb_info.node = NODEV;
     info->fb_info.fbops = &atyfb_ops;
     info->fb_info.disp = disp;
+    info->fb_info.currcon = -1;  	
     strcpy(info->fb_info.fontname, fontname);
     info->fb_info.changevar = NULL;
     info->fb_info.switch_con = &atyfbcon_switch;
     info->fb_info.updatevar = &atyfbcon_updatevar;
-    info->fb_info.blank = &atyfbcon_blank;
     info->fb_info.flags = FBINFO_FLAG_DEFAULT;
 
 #ifdef CONFIG_PMAC_BACKLIGHT
@@ -2623,17 +2620,17 @@
     struct atyfb_par par;
 
     /* Do we have to save the colormap? */
-    if (fb_display[currcon].cmap.len)
-	fb_get_cmap(&fb_display[currcon].cmap, 1, atyfb_getcolreg, fb);
+    if (fb_display[fb->currcon].cmap.len)
+	fb_get_cmap(&fb_display[fb->currcon].cmap, 1, atyfb_getcolreg, fb);
 
 #ifdef CONFIG_FB_ATY_CT
     /* Erase HW Cursor */
     if (info->cursor)
-	atyfb_cursor(&fb_display[currcon], CM_ERASE,
+	atyfb_cursor(&fb_display[fb->currcon], CM_ERASE,
 		     info->cursor->pos.x, info->cursor->pos.y);
 #endif /* CONFIG_FB_ATY_CT */
 
-    currcon = con;
+    fb->currcon = con;
 
     atyfb_decode_var(&fb_display[con].var, &par, info);
     atyfb_set_par(&par, info);
@@ -2657,7 +2654,7 @@
      *  Blank the display.
      */
 
-static void atyfbcon_blank(int blank, struct fb_info *fb)
+static int atyfb_blank(int blank, struct fb_info *fb)
 {
     struct fb_info_aty *info = (struct fb_info_aty *)fb;
     u8 gen_cntl;
@@ -2691,6 +2688,7 @@
     if ((_machine == _MACH_Pmac) && !blank)
     	set_backlight_enable(1);
 #endif /* CONFIG_PMAC_BACKLIGHT */
+    return 0;	
 }
 
 
@@ -2767,20 +2765,6 @@
 	    }
     return 0;
 }
-
-
-static void do_install_cmap(int con, struct fb_info *info)
-{
-    if (con != currcon)
-	return;
-    if (fb_display[con].cmap.len)
-	fb_set_cmap(&fb_display[con].cmap, 1, atyfb_setcolreg, info);
-    else {
-	int size = fb_display[con].var.bits_per_pixel == 16 ? 32 : 256;
-	fb_set_cmap(fb_default_cmap(size), 1, atyfb_setcolreg, info);
-    }
-}
-
 
     /*
      *  Update the `var' structure (called by fbcon.c)
diff -Nru a/drivers/video/aty128fb.c b/drivers/video/aty128fb.c
--- a/drivers/video/aty128fb.c	Sun Apr 28 20:10:36 2002
+++ b/drivers/video/aty128fb.c	Sun Apr 28 20:10:36 2002
@@ -304,7 +304,6 @@
 #ifdef CONFIG_MTRR
     struct { int vram; int vram_valid; } mtrr;
 #endif
-    int currcon;
     int blitter_may_be_busy;
     int fifo_slots;                 /* free slots in FIFO (64 max) */
 };
@@ -327,20 +326,19 @@
 		       struct fb_info *info);
 static int aty128fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			struct fb_info *info);
-static int aty128fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-			struct fb_info *info);
+static int aty128fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+			      u_int transp, struct fb_info *info);
 static int aty128fb_pan_display(struct fb_var_screeninfo *var, int con,
 			   struct fb_info *fb);
+static int aty128fb_blank(int blank, struct fb_info *fb);
 static int aty128fb_rasterimg(struct fb_info *info, int start);
 
-
     /*
      *  Interface to the low level console driver
      */
 
 int aty128fb_init(void);
 static int aty128fbcon_switch(int con, struct fb_info *fb);
-static void aty128fbcon_blank(int blank, struct fb_info *fb);
 
     /*
      *  Internal routines
@@ -353,9 +351,6 @@
 			struct fb_info_aty128 *info, int bpp, int accel);
 static int aty128_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
 				u_int *transp, struct fb_info *info);
-static int aty128_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-				u_int transp, struct fb_info *info);
-static void do_install_cmap(int con, struct fb_info *info);
 static int aty128_encode_var(struct fb_var_screeninfo *var,
                              const struct aty128fb_par *par,
                              const struct fb_info_aty128 *info);
@@ -420,7 +415,8 @@
 	fb_get_var:	aty128fb_get_var,
 	fb_set_var:	aty128fb_set_var,
 	fb_get_cmap:	aty128fb_get_cmap,
-	fb_set_cmap:	aty128fb_set_cmap,
+	fb_set_cmap:	gen_set_cmap,
+	fb_setcolreg:	aty128fb_setcolreg,
 	fb_pan_display:	aty128fb_pan_display,
 	fb_rasterimg:	aty128fb_rasterimg,
 };
@@ -1387,7 +1383,7 @@
 	struct fb_fix_screeninfo fix;
 
 	aty128_encode_fix(&fix, &par, info);
-        display->screen_base = info->frame_buffer;
+        fb->screen_base = info->frame_buffer;
 	display->visual = fix.visual;
 	display->type = fix.type;
 	display->type_aux = fix.type_aux;
@@ -1555,7 +1551,7 @@
 #else
     struct fb_info_aty128 fb = (struct fb_info_aty128 *)info;
 
-    if (con == fb->currcon) /* current console? */
+    if (con == info->currcon) /* current console? */
 	return fb_get_cmap(cmap, kspc, aty128_getcolreg, info);
     else if (fb_display[con].cmap.len) /* non default colormap? */
 	fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
@@ -1568,38 +1564,6 @@
     return 0;
 }
 
-    /*
-     *  Set the Colormap
-     */
-
-static int
-aty128fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-			struct fb_info *info)
-{
-    int err;
-    struct fb_info_aty128 *fb = (struct fb_info_aty128 *)info;
-    struct display *disp;  
-
-    if (con >= 0)
-	disp = &fb_display[con];
-    else
-	disp = info->disp;
-
-    if (!disp->cmap.len) {      /* no colormap allocated? */
-        int size = (disp->var.bits_per_pixel <= 8) ? 256 : 32;
-	if ((err = fb_alloc_cmap(&disp->cmap, size, 0)))
-	    return err;
-    }
-
-    if (con == fb->currcon) /* current console? */
-	return fb_set_cmap(cmap, kspc, aty128_setcolreg, info);
-    else
-	fb_copy_cmap(cmap, &disp->cmap, kspc ? 0 : 1);
-
-    return 0;                
-}
-
-
 static int
 aty128fb_rasterimg(struct fb_info *info, int start)
 {
@@ -1711,7 +1675,6 @@
     info->fb_info.changevar  = NULL;
     info->fb_info.switch_con = &aty128fbcon_switch;
     info->fb_info.updatevar  = NULL;
-    info->fb_info.blank = &aty128fbcon_blank;
     info->fb_info.flags = FBINFO_FLAG_DEFAULT;
 
     var = default_var;
@@ -1871,7 +1834,7 @@
 	/* Copy PCI device info into info->pdev */
 	info->pdev = pdev;
 
-	info->currcon = -1;
+	info->fb_info.currcon = -1;
 
 	/* Virtualize mmio region */
 	info->regbase_phys = reg_addr;
@@ -2120,12 +2083,12 @@
     struct aty128fb_par par;
 
     /* Do we have to save the colormap? */
-    if (fb_display[info->currcon].cmap.len)
-    	fb_get_cmap(&fb_display[info->currcon].cmap, 1,
+    if (fb_display[fb->currcon].cmap.len)
+    	fb_get_cmap(&fb_display[fb->currcon].cmap, 1,
 			aty128_getcolreg, fb);
 
     /* set the current console */
-    info->currcon = con;
+    fb->currcon = con;
 
     aty128_decode_var(&fb_display[con].var, &par, info);
     aty128_set_par(&par, info);
@@ -2142,8 +2105,8 @@
     /*
      *  Blank the display.
      */
-static void
-aty128fbcon_blank(int blank, struct fb_info *fb)
+static int 
+aty128fb_blank(int blank, struct fb_info *fb)
 {
     struct fb_info_aty128 *info = (struct fb_info_aty128 *)fb;
     u8 state = 0;
@@ -2166,9 +2129,9 @@
     if ((_machine == _MACH_Pmac) && !blank)
     	set_backlight_enable(1);
 #endif /* CONFIG_PMAC_BACKLIGHT */
+    return 0;	
 }
 
-
     /*
      *  Read a single color register and split it into
      *  colors/transparent. Return != 0 for invalid regno.
@@ -2196,7 +2159,7 @@
      *  entries in the var structure). Return != 0 for invalid regno.
      */
 static int
-aty128_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+aty128fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
                          u_int transp, struct fb_info *fb)
 {
     struct fb_info_aty128 *info = (struct fb_info_aty128 *)fb;
@@ -2286,24 +2249,6 @@
 	}
     return 0;
 }
-
-
-static void
-do_install_cmap(int con, struct fb_info *info)
-{
-    struct fb_info_aty128 *fb = (struct fb_info_aty128 *)info;
-
-    if (con != fb->currcon)
-	return;
-
-    if (fb_display[con].cmap.len)
-	fb_set_cmap(&fb_display[con].cmap, 1, aty128_setcolreg, info);
-    else {
-	int size = (fb_display[con].var.bits_per_pixel <= 8) ? 256 : 16;
-	fb_set_cmap(fb_default_cmap(size), 1, aty128_setcolreg, info);
-    }
-}
-
 
 #ifdef CONFIG_PMAC_BACKLIGHT
 static int backlight_conv[] = {
diff -Nru a/drivers/video/bwtwofb.c b/drivers/video/bwtwofb.c
--- a/drivers/video/bwtwofb.c	Sun Apr 28 20:10:30 2002
+++ b/drivers/video/bwtwofb.c	Sun Apr 28 20:10:30 2002
@@ -83,7 +83,7 @@
 	{ 0,			0,			0		    }
 };
 
-static void bw2_blank (struct fb_info_sbusfb *fb)
+static int bw2_blank (struct fb_info_sbusfb *fb)
 {
 	unsigned long flags;
 	u8 tmp;
@@ -93,9 +93,10 @@
 	tmp &= ~BWTWO_CTL_ENABLE_VIDEO;
 	sbus_writeb(tmp, &fb->s.bw2.regs->control);
 	spin_unlock_irqrestore(&fb->lock, flags);
+	return 0;
 }
 
-static void bw2_unblank (struct fb_info_sbusfb *fb)
+static int bw2_unblank (struct fb_info_sbusfb *fb)
 {
 	unsigned long flags;
 	u8 tmp;
@@ -105,12 +106,13 @@
 	tmp |= BWTWO_CTL_ENABLE_VIDEO;
 	sbus_writeb(tmp, &fb->s.bw2.regs->control);
 	spin_unlock_irqrestore(&fb->lock, flags);
+ 	return 0;
 }
 
 static void bw2_margins (struct fb_info_sbusfb *fb, struct display *p,
 			 int x_margin, int y_margin)
 {
-	p->screen_base += (y_margin - fb->y_margin) *
+	fb->info.screen_base += (y_margin - fb->y_margin) *
 		p->line_length + ((x_margin - fb->x_margin) >> 3);
 }
 
@@ -235,11 +237,11 @@
 	
 	disp->scrollmode = SCROLL_YREDRAW;
 	disp->inverse = 1;
-	if (!disp->screen_base) {
-		disp->screen_base = (char *)
+	if (!fb->info.screen_base) {
+		fb->info.screen_base = (char *)
 			sbus_ioremap(resp, 0, type->fb_size, "bw2 ram");
 	}
-	disp->screen_base += fix->line_length * fb->y_margin + (fb->x_margin >> 3);
+	fb->info.screen_base += fix->line_length * fb->y_margin + (fb->x_margin >> 3);
 	fb->dispsw = fbcon_mfb;
 	fix->visual = FB_VISUAL_MONO01;
 
diff -Nru a/drivers/video/cgfourteenfb.c b/drivers/video/cgfourteenfb.c
--- a/drivers/video/cgfourteenfb.c	Sun Apr 28 20:10:34 2002
+++ b/drivers/video/cgfourteenfb.c	Sun Apr 28 20:10:34 2002
@@ -202,7 +202,7 @@
 static void cg14_margins (struct fb_info_sbusfb *fb, struct display *p,
 			  int x_margin, int y_margin)
 {
-	p->screen_base += (y_margin - fb->y_margin) *
+	fb->info.screen_base += (y_margin - fb->y_margin) *
 		p->line_length + (x_margin - fb->x_margin);
 }
 
@@ -387,7 +387,7 @@
 	fb->s.cg14.regs = (struct cg14_regs *)(unsigned long)bases[0];
 	fb->s.cg14.clut = (void *)((unsigned long)bases[0]+CG14_CLUT1);
 	fb->s.cg14.cursor = (void *)((unsigned long)bases[0]+CG14_CURSORREGS);
-	disp->screen_base = (char *)bases[1];
+	fb->info.screen_base = (char *)bases[1];
 	
 	/* CG14_VCA_8MB_MASK is not correctly set on the 501-2482
 	 * VSIMM, so we read the memory size from the PROM
@@ -418,7 +418,7 @@
 	fix->accel = FB_ACCEL_SUN_CG14;
 	
 	disp->scrollmode = SCROLL_YREDRAW;
-	disp->screen_base += fix->line_length * fb->y_margin + fb->x_margin;
+	fb->info.screen_base += fix->line_length * fb->y_margin + fb->x_margin;
 	fb->dispsw = fbcon_cfb8;
 	
 	type->fb_depth = 24;
diff -Nru a/drivers/video/cgsixfb.c b/drivers/video/cgsixfb.c
--- a/drivers/video/cgsixfb.c	Sun Apr 28 20:10:35 2002
+++ b/drivers/video/cgsixfb.c	Sun Apr 28 20:10:35 2002
@@ -582,7 +582,7 @@
 	spin_unlock_irqrestore(&fb->lock, flags);
 }
 
-static void cg6_blank (struct fb_info_sbusfb *fb)
+static int cg6_blank (struct fb_info_sbusfb *fb)
 {
 	unsigned long flags;
 	u32 tmp;
@@ -592,9 +592,10 @@
 	tmp &= ~CG6_THC_MISC_VIDEO;
 	sbus_writel(tmp, &fb->s.cg6.thc->thc_misc);
 	spin_unlock_irqrestore(&fb->lock, flags);
+	return 0;
 }
 
-static void cg6_unblank (struct fb_info_sbusfb *fb)
+static int cg6_unblank (struct fb_info_sbusfb *fb)
 {
 	unsigned long flags;
 	u32 tmp;
@@ -604,6 +605,7 @@
 	tmp |= CG6_THC_MISC_VIDEO;
 	sbus_writel(tmp, &fb->s.cg6.thc->thc_misc);
 	spin_unlock_irqrestore(&fb->lock, flags);
+	return 0;
 }
 
 static void cg6_reset (struct fb_info_sbusfb *fb)
@@ -670,7 +672,7 @@
 
 static void cg6_margins (struct fb_info_sbusfb *fb, struct display *p, int x_margin, int y_margin)
 {
-	p->screen_base += (y_margin - fb->y_margin) *
+	fb->info.screen_base += (y_margin - fb->y_margin) *
 		p->line_length + (x_margin - fb->x_margin);
 }
 
@@ -721,12 +723,12 @@
 	var->accel_flags = FB_ACCELF_TEXT;
 	
 	disp->scrollmode = SCROLL_YREDRAW;
-	if (!disp->screen_base) {
-		disp->screen_base = (char *)
+	if (!fb->info.screen_base) {
+		fb->info.screen_base = (char *)
 			sbus_ioremap(&sdev->resource[0], CG6_RAM_OFFSET,
 				     type->fb_size, "cgsix ram");
 	}
-	disp->screen_base += fix->line_length * fb->y_margin + fb->x_margin;
+	fb->info.screen_base += fix->line_length * fb->y_margin + fb->x_margin;
 	fb->s.cg6.fbc = (struct cg6_fbc *)
 		sbus_ioremap(&sdev->resource[0], CG6_FBC_OFFSET,
 			     4096, "cgsix fbc");
diff -Nru a/drivers/video/cgthreefb.c b/drivers/video/cgthreefb.c
--- a/drivers/video/cgthreefb.c	Sun Apr 28 20:10:34 2002
+++ b/drivers/video/cgthreefb.c	Sun Apr 28 20:10:34 2002
@@ -110,7 +110,7 @@
 	spin_unlock_irqrestore(&fb->lock, flags);
 }
 
-static void cg3_blank (struct fb_info_sbusfb *fb)
+static int cg3_blank (struct fb_info_sbusfb *fb)
 {
 	unsigned long flags;
 	u8 tmp;
@@ -120,9 +120,10 @@
 	tmp &= ~CG3_CR_ENABLE_VIDEO;
 	sbus_writeb(tmp, &fb->s.cg3.regs->control);
 	spin_unlock_irqrestore(&fb->lock, flags);
+	return 0;
 }
 
-static void cg3_unblank (struct fb_info_sbusfb *fb)
+static int cg3_unblank (struct fb_info_sbusfb *fb)
 {
 	unsigned long flags;
 	u8 tmp;
@@ -132,12 +133,13 @@
 	tmp |= CG3_CR_ENABLE_VIDEO;
 	sbus_writeb(tmp, &fb->s.cg3.regs->control);
 	spin_unlock_irqrestore(&fb->lock, flags);
+	return 0;
 }
 
 static void cg3_margins (struct fb_info_sbusfb *fb, struct display *p,
 			 int x_margin, int y_margin)
 {
-	p->screen_base += (y_margin - fb->y_margin) *
+	fb->info.screen_base += (y_margin - fb->y_margin) *
 		p->line_length + (x_margin - fb->x_margin);
 }
 
@@ -218,12 +220,12 @@
 	fix->accel = FB_ACCEL_SUN_CGTHREE;
 	
 	disp->scrollmode = SCROLL_YREDRAW;
-	if (!disp->screen_base) {
-		disp->screen_base = (char *)
+	if (!fb->info.screen_base) {
+		fb->info.screen_base = (char *)
 			sbus_ioremap(&sdev->resource[0], CG3_RAM_OFFSET,
 				     type->fb_size, "cg3 ram");
 	}
-	disp->screen_base += fix->line_length * fb->y_margin + fb->x_margin;
+	fb->info.screen_base += fix->line_length * fb->y_margin + fb->x_margin;
 	fb->dispsw = fbcon_cfb8;
 
 	fb->margins = cg3_margins;
diff -Nru a/drivers/video/chipsfb.c b/drivers/video/chipsfb.c
--- a/drivers/video/chipsfb.c	Sun Apr 28 20:10:36 2002
+++ b/drivers/video/chipsfb.c	Sun Apr 28 20:10:36 2002
@@ -48,8 +48,6 @@
 #include <video/fbcon-cfb16.h>
 #include <video/macmodes.h>
 
-static int currcon = 0;
-
 struct fb_info_chips {
 	struct fb_info info;
 	struct fb_fix_screeninfo fix;
@@ -127,8 +125,9 @@
 			 struct fb_info *info);
 static int chips_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			  struct fb_info *info);
-static int chips_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-			  struct fb_info *info);
+static int chipsfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+			     u_int transp, struct fb_info *info);
+static int chipsfb_blank(int blank, struct fb_info *info);
 
 static struct fb_ops chipsfb_ops = {
 	owner:		THIS_MODULE,
@@ -136,14 +135,13 @@
 	fb_get_var:	chips_get_var,
 	fb_set_var:	chips_set_var,
 	fb_get_cmap:	chips_get_cmap,
-	fb_set_cmap:	chips_set_cmap,
+	fb_set_cmap:	gen_set_cmap,
+	fb_setcolreg:	chipsfb_setcolreg,
+	fb_blank:	chipsfb_blank,
 };
 
 static int chipsfb_getcolreg(u_int regno, u_int *red, u_int *green,
 			     u_int *blue, u_int *transp, struct fb_info *info);
-static int chipsfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-			     u_int transp, struct fb_info *info);
-static void do_install_cmap(int con, struct fb_info *info);
 static void chips_set_bitdepth(struct fb_info_chips *p, struct display* disp, int con, int bpp);
 
 static int chips_get_fix(struct fb_fix_screeninfo *fix, int con,
@@ -188,7 +186,7 @@
 static int chips_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			  struct fb_info *info)
 {
-	if (con == currcon)		/* current console? */
+	if (con == info->currcon)		/* current console? */
 		return fb_get_cmap(cmap, kspc, chipsfb_getcolreg, info);
 	if (fb_display[con].cmap.len)	/* non default colormap? */
 		fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
@@ -199,35 +197,18 @@
 	return 0;
 }
 
-static int chips_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-			 struct fb_info *info)
-{
-	int err;
-
-	if (!fb_display[con].cmap.len) {	/* no colormap allocated? */
-		int size = fb_display[con].var.bits_per_pixel == 16 ? 32 : 256;
-		if ((err = fb_alloc_cmap(&fb_display[con].cmap, size, 0)))
-			return err;
-	}
-	if (con == currcon)			/* current console? */
-		return fb_set_cmap(cmap, kspc, chipsfb_setcolreg, info);
-	else
-		fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
-	return 0;
-}
-
 static int chipsfbcon_switch(int con, struct fb_info *info)
 {
 	struct fb_info_chips *p = (struct fb_info_chips *) info;
 	int new_bpp, old_bpp;
 
 	/* Do we have to save the colormap? */
-	if (fb_display[currcon].cmap.len)
-		fb_get_cmap(&fb_display[currcon].cmap, 1, chipsfb_getcolreg, info);
+	if (fb_display[info->currcon].cmap.len)
+		fb_get_cmap(&fb_display[info->currcon].cmap, 1, chipsfb_getcolreg, info);
 
 	new_bpp = fb_display[con].var.bits_per_pixel;
-	old_bpp = fb_display[currcon].var.bits_per_pixel;
-	currcon = con;
+	old_bpp = fb_display[info->currcon].var.bits_per_pixel;
+	info->currcon = con;
 
 	if (new_bpp != old_bpp)
 		chips_set_bitdepth(p, &fb_display[con], con, new_bpp);
@@ -241,7 +222,7 @@
 	return 0;
 }
 
-static void chipsfb_blank(int blank, struct fb_info *info)
+static int chipsfb_blank(int blank, struct fb_info *info)
 {
 	struct fb_info_chips *p = (struct fb_info_chips *) info;
 	int i;
@@ -279,6 +260,7 @@
 			outb(p->palette[i].blue, 0x3c9);
 		}
 	}
+	return 0;
 }
 
 static int chipsfb_getcolreg(u_int regno, u_int *red, u_int *green,
@@ -323,18 +305,6 @@
 	return 0;
 }
 
-static void do_install_cmap(int con, struct fb_info *info)
-{
-	if (con != currcon)
-		return;
-	if (fb_display[con].cmap.len)
-		fb_set_cmap(&fb_display[con].cmap, 1, chipsfb_setcolreg, info);
-	else {
-		int size = fb_display[con].var.bits_per_pixel == 16 ? 32 : 256;
-		fb_set_cmap(fb_default_cmap(size), 1, chipsfb_setcolreg, info);
-	}
-}
-
 static void chips_set_bitdepth(struct fb_info_chips *p, struct display* disp, int con, int bpp)
 {
 	int err;
@@ -342,7 +312,7 @@
 	struct fb_var_screeninfo* var = &p->var;
 	
 	if (bpp == 16) {
-		if (con == currcon) {
+		if (con == info->currcon) {
 			write_cr(0x13, 200);		// Set line length (doublewords)
 			write_xr(0x81, 0x14);		// 15 bit (555) color mode
 			write_xr(0x82, 0x00);		// Disable palettes
@@ -364,7 +334,7 @@
 		disp->dispsw = &fbcon_dummy;
 #endif
 	} else if (bpp == 8) {
-		if (con == currcon) {
+		if (con == info->currcon) {
 			write_cr(0x13, 100);		// Set line length (doublewords)
 			write_xr(0x81, 0x12);		// 8 bit color mode
 			write_xr(0x82, 0x08);		// Graphics gamma enable
@@ -568,7 +538,6 @@
 	p->disp.cmap.green = NULL;
 	p->disp.cmap.blue = NULL;
 	p->disp.cmap.transp = NULL;
-	p->disp.screen_base = p->frame_buffer;
 	p->disp.visual = p->fix.visual;
 	p->disp.type = p->fix.type;
 	p->disp.type_aux = p->fix.type_aux;
@@ -580,12 +549,13 @@
 	strcpy(p->info.modename, p->fix.id);
 	p->info.node = NODEV;
 	p->info.fbops = &chipsfb_ops;
+	p->info.screen_base = p->frame_buffer;
 	p->info.disp = &p->disp;
+	p->info.currcon = -1;
 	p->info.fontname[0] = 0;
 	p->info.changevar = NULL;
 	p->info.switch_con = &chipsfbcon_switch;
 	p->info.updatevar = &chipsfb_updatevar;
-	p->info.blank = &chipsfb_blank;
 	p->info.flags = FBINFO_FLAG_DEFAULT;
 
 	for (i = 0; i < 16; ++i) {
diff -Nru a/drivers/video/clgenfb.c b/drivers/video/clgenfb.c
--- a/drivers/video/clgenfb.c	Sun Apr 28 20:10:34 2002
+++ b/drivers/video/clgenfb.c	Sun Apr 28 20:10:34 2002
@@ -492,6 +492,10 @@
 static int clgenfb_open (struct fb_info *info, int user);
 static int clgenfb_release (struct fb_info *info, int user);
 
+static int clgenfb_setcolreg (unsigned regno, unsigned red, unsigned green,
+			      unsigned blue, unsigned transp,
+			      struct fb_info *info);
+
 /* function table of the above functions */
 static struct fb_ops clgenfb_ops = {
 	owner:		THIS_MODULE,
@@ -501,8 +505,10 @@
 	fb_get_var:	fbgen_get_var,
 	fb_set_var:	fbgen_set_var,
 	fb_get_cmap:	fbgen_get_cmap,
-	fb_set_cmap:	fbgen_set_cmap,
+	fb_set_cmap:	gen_set_cmap,
+	fb_setcolreg:	clgenfb_setcolreg,
 	fb_pan_display:	fbgen_pan_display,
+	fb_blank:	fbgen_blank,
 };
 
 /*--- Hardware Specific Routines -------------------------------------------*/
@@ -518,9 +524,6 @@
 static int clgen_getcolreg (unsigned regno, unsigned *red, unsigned *green,
 			    unsigned *blue, unsigned *transp,
 			    struct fb_info *info);
-static int clgen_setcolreg (unsigned regno, unsigned red, unsigned green,
-			    unsigned blue, unsigned transp,
-			    struct fb_info *info);
 static int clgen_pan_display (const struct fb_var_screeninfo *var,
 			      struct fb_info_gen *info);
 static int clgen_blank (int blank_mode, struct fb_info_gen *info);
@@ -538,7 +541,6 @@
 	clgen_get_par,
 	clgen_set_par,
 	clgen_getcolreg,
-	clgen_setcolreg,
 	clgen_pan_display,
 	clgen_blank,
 	clgen_set_disp
@@ -1674,9 +1676,9 @@
 }
 
 
-static int clgen_setcolreg (unsigned regno, unsigned red, unsigned green,
-			    unsigned blue, unsigned transp,
-			    struct fb_info *info)
+static int clgenfb_setcolreg (unsigned regno, unsigned red, unsigned green,
+			      unsigned blue, unsigned transp,
+			      struct fb_info *info)
 {
 	struct clgenfb_info *fb_info = (struct clgenfb_info *) info;
 
@@ -2179,13 +2181,13 @@
 	accel_text = _par->var.accel_flags & FB_ACCELF_TEXT;
 
 	printk ("Cirrus Logic video mode: ");
-	disp->screen_base = (char *) fb_info->fbmem;
+	info->info.screen_base = (char *) fb_info->fbmem;
 	switch (_par->var.bits_per_pixel) {
 #ifdef FBCON_HAS_MFB
 	case 1:
 		printk ("monochrome\n");
 		if (fb_info->btype == BT_GD5480)
-			disp->screen_base = (char *) fb_info->fbmem;
+			info->info.screen_base = (char *) fb_info->fbmem;
 		disp->dispsw = &fbcon_mfb;
 		break;
 #endif
@@ -2193,7 +2195,7 @@
 	case 8:
 		printk ("8 bit color depth\n");
 		if (fb_info->btype == BT_GD5480)
-			disp->screen_base = (char *) fb_info->fbmem;
+			info->info.screen_base = (char *) fb_info->fbmem;
 		if (accel_text)
 			disp->dispsw = &fbcon_clgen_8;
 		else
@@ -2208,7 +2210,7 @@
 		else
 			disp->dispsw = &fbcon_cfb16;
 		if (fb_info->btype == BT_GD5480)
-			disp->screen_base = (char *) fb_info->fbmem + 1 * MB_;
+			info->info.screen_base = (char *) fb_info->fbmem + 1 * MB_;
 		disp->dispsw_data = fb_info->fbcon_cmap.cfb16;
 		break;
 #endif
@@ -2217,7 +2219,7 @@
 		printk ("24 bit color depth\n");
 		disp->dispsw = &fbcon_cfb24;
 		if (fb_info->btype == BT_GD5480)
-			disp->screen_base = (char *) fb_info->fbmem + 2 * MB_;
+			info->info.screen_base = (char *) fb_info->fbmem + 2 * MB_;
 		disp->dispsw_data = fb_info->fbcon_cmap.cfb24;
 		break;
 #endif
@@ -2229,7 +2231,7 @@
 		else
 			disp->dispsw = &fbcon_cfb32;
 		if (fb_info->btype == BT_GD5480)
-			disp->screen_base = (char *) fb_info->fbmem + 2 * MB_;
+			info->info.screen_base = (char *) fb_info->fbmem + 2 * MB_;
 		disp->dispsw_data = fb_info->fbcon_cmap.cfb32;
 		break;
 #endif
@@ -2781,10 +2783,10 @@
 	fb_info->gen.info.node = NODEV;
 	fb_info->gen.info.fbops = &clgenfb_ops;
 	fb_info->gen.info.disp = &disp;
+	fb_info->gen.info.currcon = -1;
 	fb_info->gen.info.changevar = NULL;
 	fb_info->gen.info.switch_con = &fbgen_switch;
 	fb_info->gen.info.updatevar = &fbgen_update_var;
-	fb_info->gen.info.blank = &fbgen_blank;
 	fb_info->gen.info.flags = FBINFO_FLAG_DEFAULT;
 
 	for (j = 0; j < 256; j++) {
@@ -2814,7 +2816,7 @@
 
 	disp.var = clgenfb_default;
 	fbgen_set_disp (-1, &fb_info->gen);
-	fbgen_install_cmap (0, &fb_info->gen);
+	do_install_cmap (0, &fb_info->gen.info);
 
 	err = register_framebuffer (&fb_info->gen.info);
 	if (err) {
@@ -2860,8 +2862,7 @@
 	if (!options || !*options)
 		return 0;
 
-	for (this_opt = strtok (options, ","); this_opt != NULL;
-	     this_opt = strtok (NULL, ",")) {
+	while ((this_opt = strsep (&options, ",")) != NULL) {	
 		if (!*this_opt) continue;
 
 		DPRINTK("clgenfb_setup: option '%s'\n", this_opt);
diff -Nru a/drivers/video/clps711xfb.c b/drivers/video/clps711xfb.c
--- a/drivers/video/clps711xfb.c	Sun Apr 28 20:10:32 2002
+++ b/drivers/video/clps711xfb.c	Sun Apr 28 20:10:32 2002
@@ -36,10 +36,7 @@
 #include <asm/hardware/clps7111.h>
 #include <asm/arch/syspld.h>
 
-static struct clps7111fb_info {
-	struct fb_info		fb;
-	int			currcon;
-} *cfb;
+struct fb_info	*cfb;
 
 #define CMAP_SIZE	16
 
@@ -92,32 +89,6 @@
 }
 		    
 /*
- * Set the colormap
- */
-static int
-clps7111fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-		    struct fb_info *info)
-{
-	struct clps7111fb_info *cfb = (struct clps7111fb_info *)info;
-	struct fb_cmap *dcmap = &fb_display[con].cmap;
-	int err = 0;
-
-	/* no colormap allocated? */
-	if (!dcmap->len)
-		err = fb_alloc_cmap(dcmap, CMAP_SIZE, 0);
-
-	if (!err && con == cfb->currcon) {
-		err = fb_set_cmap(cmap, kspc, clps7111fb_setcolreg, &cfb->fb);
-		dcmap = &cfb->fb.cmap;
-	}
-
-	if (!err)
-		fb_copy_cmap(cmap, dcmap, kspc ? 0 : 1);
-
-	return err;
-}
-
-/*
  *    Set the User Defined Part of the Display
  */
 static int
@@ -134,19 +105,19 @@
 	if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW)
 		return -EINVAL;
 
-	if (cfb->fb.var.xres != var->xres)
+	if (info->var.xres != var->xres)
 		chgvar = 1;
-	if (cfb->fb.var.yres != var->yres)
+	if (info->var.yres != var->yres)
 		chgvar = 1;
-	if (cfb->fb.var.xres_virtual != var->xres_virtual)
+	if (info->var.xres_virtual != var->xres_virtual)
 		chgvar = 1;
-	if (cfb->fb.var.yres_virtual != var->yres_virtual)
+	if (info->var.yres_virtual != var->yres_virtual)
 		chgvar = 1;
-	if (cfb->fb.var.bits_per_pixel != var->bits_per_pixel)
+	if (info->var.bits_per_pixel != var->bits_per_pixel)
 		chgvar = 1;
 
 	if (con < 0) {
-		display = cfb->fb.disp;
+		display = info->disp;
 		chgvar = 0;
 	} else {
 		display = fb_display + con;
@@ -164,21 +135,21 @@
 	switch (var->bits_per_pixel) {
 #ifdef FBCON_HAS_MFB
 	case 1:
-		cfb->fb.fix.visual	= FB_VISUAL_MONO01;
+		info->fix.visual	= FB_VISUAL_MONO01;
 		display->dispsw		= &fbcon_mfb;
 		display->dispsw_data	= NULL;
 		break;
 #endif
 #ifdef FBCON_HAS_CFB2
 	case 2:
-		cfb->fb.fix.visual	= FB_VISUAL_PSEUDOCOLOR;
+		info->fix.visual	= FB_VISUAL_PSEUDOCOLOR;
 		display->dispsw		= &fbcon_cfb2;
 		display->dispsw_data	= NULL;
 		break;
 #endif
 #ifdef FBCON_HAS_CFB4
 	case 4:
-		cfb->fb.fix.visual	= FB_VISUAL_PSEUDOCOLOR;
+		info->fix.visual	= FB_VISUAL_PSEUDOCOLOR;
 		display->dispsw		= &fbcon_cfb4;
 		display->dispsw_data	= NULL;
 		break;
@@ -189,37 +160,36 @@
 
 	display->next_line	= var->xres_virtual * var->bits_per_pixel / 8;
 
-	cfb->fb.fix.line_length = display->next_line;
+	info->fix.line_length = display->next_line;
 
-	display->screen_base	= cfb->fb.screen_base;
-	display->line_length	= cfb->fb.fix.line_length;
-	display->visual		= cfb->fb.fix.visual;
-	display->type		= cfb->fb.fix.type;
-	display->type_aux	= cfb->fb.fix.type_aux;
-	display->ypanstep	= cfb->fb.fix.ypanstep;
-	display->ywrapstep	= cfb->fb.fix.ywrapstep;
+	display->line_length	= info->fix.line_length;
+	display->visual		= info->fix.visual;
+	display->type		= info->fix.type;
+	display->type_aux	= info->fix.type_aux;
+	display->ypanstep	= info->fix.ypanstep;
+	display->ywrapstep	= info->fix.ywrapstep;
 	display->can_soft_blank = 1;
 	display->inverse	= 0;
 
-	cfb->fb.var		= *var;
-	cfb->fb.var.activate	&= ~FB_ACTIVATE_ALL;
+	info->var		= *var;
+	info->var.activate	&= ~FB_ACTIVATE_ALL;
 
 	/*
 	 * Update the old var.  The fbcon drivers still use this.
-	 * Once they are using cfb->fb.var, this can be dropped.
+	 * Once they are using cfb->var, this can be dropped.
 	 *                                      --rmk
 	 */
-	display->var		= cfb->fb.var;
+	display->var		= info->var;
 
 	/*
 	 * If we are setting all the virtual consoles, also set the
 	 * defaults used to create new consoles.
 	 */
 	if (var->activate & FB_ACTIVATE_ALL)
-		cfb->fb.disp->var = cfb->fb.var;
+		info->disp->var = info->var;
 
-	if (chgvar && info && cfb->fb.changevar)
-		cfb->fb.changevar(con);
+	if (chgvar && info && info->changevar)
+		info->changevar(con);
 
 	/*
 	 * LCDCON must only be changed while the LCD is disabled
@@ -236,68 +206,39 @@
 	clps_writel(lcdcon, LCDCON);
 	clps_writel(syscon | SYSCON1_LCDEN, SYSCON1);
 
-	fb_set_cmap(&cfb->fb.cmap, 1, clps7111fb_setcolreg, &cfb->fb);
+	fb_set_cmap(&info->cmap, 1, info);
 
 	return 0;
 }
 
-/*
- * Get the currently displayed virtual consoles colormap.
- */
-static int
-gen_get_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info)
-{
-	fb_copy_cmap(&info->cmap, cmap, kspc ? 0 : 2);
-	return 0;
-}
-
-/*
- * Get the currently displayed virtual consoles fixed part of the display.
- */
-static int
-gen_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
-{
-	*fix = info->fix;
-	return 0;
-}
-
-/*
- * Get the current user defined part of the display.
- */
-static int
-gen_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
-{
-	*var = info->var;
-	return 0;
-}
-
 static struct fb_ops clps7111fb_ops = {
 	owner:		THIS_MODULE,
 	fb_set_var:	clps7111fb_set_var,
-	fb_set_cmap:	clps7111fb_set_cmap,
+	fb_set_cmap:	gen_set_cmap,
 	fb_get_fix:	gen_get_fix,
 	fb_get_var:	gen_get_var,
 	fb_get_cmap:	gen_get_cmap,
+	fb_setcolreg:	clps7111fb_setcolreg,
+	fb_blank:	clps7111fb_blank,
 };
 
 static int clps7111fb_switch(int con, struct fb_info *info)
 {
-	struct clps7111fb_info *cfb = (struct clps7111fb_info *)info;
 	struct display *disp;
 	struct fb_cmap *cmap;
 
-	if (cfb->currcon >= 0) {
-		disp = fb_display + cfb->currcon;
+	if (info->currcon >= 0) {
+		disp = fb_display + info->currcon;
 
 		/*
 		 * Save the old colormap and video mode.
 		 */
-		disp->var = cfb->fb.var;
+		disp->var = info->var;
 		if (disp->cmap.len)
-			fb_copy_cmap(&cfb->fb.cmap, &disp->cmap, 0);
+			fb_copy_cmap(&info->cmap, &disp->cmap, 0);
 	}
 
-	cfb->currcon = con;
+	info->currcon = con;
 	disp = fb_display + con;
 
 	/*
@@ -310,12 +251,12 @@
 	else
 		cmap = fb_default_cmap(CMAP_SIZE);
 
-	fb_copy_cmap(cmap, &cfb->fb.cmap, 0);
+	fb_copy_cmap(cmap, &info->cmap, 0);
 
-	cfb->fb.var = disp->var;
-	cfb->fb.var.activate = FB_ACTIVATE_NOW;
+	info->var = disp->var;
+	info->var.activate = FB_ACTIVATE_NOW;
 
-	clps7111fb_set_var(&cfb->fb.var, con, &cfb->fb);
+	clps7111fb_set_var(&info->var, con, info);
 
 	return 0;
 }
@@ -325,7 +266,7 @@
 	return -EINVAL;
 }
 
-static void clps7111fb_blank(int blank, struct fb_info *info)
+static int clps7111fb_blank(int blank, struct fb_info *info)
 {
     	if (blank) {
 		if (machine_is_edb7211()) {
@@ -369,6 +310,7 @@
 				clps_writeb(clps_readb(PDDR) | EDB_PD3_LCDBL, PDDR);
 		}
 	}
+	return 0;
 }
 
 static int 
@@ -435,31 +377,30 @@
 
 	cfb->currcon		= -1;
 
-	strcpy(cfb->fb.fix.id, "clps7111");
-	cfb->fb.screen_base	= (void *)PAGE_OFFSET;
-	cfb->fb.fix.smem_start	= PAGE_OFFSET;
-	cfb->fb.fix.smem_len	= 0x14000;
-	cfb->fb.fix.type	= FB_TYPE_PACKED_PIXELS;
-
-	cfb->fb.var.xres	 = 640;
-	cfb->fb.var.xres_virtual = 640;
-	cfb->fb.var.yres	 = 240;
-	cfb->fb.var.yres_virtual = 240;
-	cfb->fb.var.bits_per_pixel = 4;
-	cfb->fb.var.grayscale   = 1;
-	cfb->fb.var.activate	= FB_ACTIVATE_NOW;
-	cfb->fb.var.height	= -1;
-	cfb->fb.var.width	= -1;
-
-	cfb->fb.fbops		= &clps7111fb_ops;
-	cfb->fb.changevar	= NULL;
-	cfb->fb.switch_con	= clps7111fb_switch;
-	cfb->fb.updatevar	= clps7111fb_updatevar;
-	cfb->fb.blank		= clps7111fb_blank;
-	cfb->fb.flags		= FBINFO_FLAG_DEFAULT;
-	cfb->fb.disp		= (struct display *)(cfb + 1);
+	strcpy(cfb->fix.id, "clps7111");
+	cfb->screen_base	= (void *)PAGE_OFFSET;
+	cfb->fix.smem_start	= PAGE_OFFSET;
+	cfb->fix.smem_len	= 0x14000;
+	cfb->fix.type	= FB_TYPE_PACKED_PIXELS;
+
+	cfb->var.xres	 = 640;
+	cfb->var.xres_virtual = 640;
+	cfb->var.yres	 = 240;
+	cfb->var.yres_virtual = 240;
+	cfb->var.bits_per_pixel = 4;
+	cfb->var.grayscale   = 1;
+	cfb->var.activate	= FB_ACTIVATE_NOW;
+	cfb->var.height	= -1;
+	cfb->var.width	= -1;
+
+	cfb->fbops		= &clps7111fb_ops;
+	cfb->changevar	= NULL;
+	cfb->switch_con	= clps7111fb_switch;
+	cfb->updatevar	= clps7111fb_updatevar;
+	cfb->flags		= FBINFO_FLAG_DEFAULT;
+	cfb->disp		= (struct display *)(cfb + 1);
 
-	fb_alloc_cmap(&cfb->fb.cmap, CMAP_SIZE, 0);
+	fb_alloc_cmap(&cfb->cmap, CMAP_SIZE, 0);
 
 	/* Register the /proc entries. */
 	clps7111fb_backlight_proc_entry = create_proc_entry("backlight", 0444,
@@ -498,15 +439,15 @@
 		clps_writeb(clps_readb(PDDR) | EDB_PD3_LCDBL, PDDR);
 	}
 
-	clps7111fb_set_var(&cfb->fb.var, -1, &cfb->fb);
-	err = register_framebuffer(&cfb->fb);
+	clps7111fb_set_var(&cfb->var, -1, cfb);
+	err = register_framebuffer(cfb);
 
 out:	return err;
 }
 
 static void __exit clps711xfb_exit(void)
 {
-	unregister_framebuffer(&cfb->fb);
+	unregister_framebuffer(cfb);
 	kfree(cfb);
 
 	/*
diff -Nru a/drivers/video/controlfb.c b/drivers/video/controlfb.c
--- a/drivers/video/controlfb.c	Sun Apr 28 20:10:32 2002
+++ b/drivers/video/controlfb.c	Sun Apr 28 20:10:32 2002
@@ -149,8 +149,9 @@
 	struct fb_info *info);
 static int control_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 	struct fb_info *info);
-static int control_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-	struct fb_info *info);
+static int controlfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+	u_int transp, struct fb_info *info);
+static int controlfb_blank(int blank_mode, struct fb_info *info);
 static int control_mmap(struct fb_info *info, struct file *file,
 	struct vm_area_struct *vma);
 
@@ -159,15 +160,12 @@
  */
 static int controlfb_switch(int con, struct fb_info *info);
 static int controlfb_updatevar(int con, struct fb_info *info);
-static void controlfb_blank(int blank_mode, struct fb_info *info);
 
 /*
  * low level cmap set/get ops
  */
 static int controlfb_getcolreg(u_int regno, u_int *red, u_int *green,
 	u_int *blue, u_int *transp, struct fb_info *info);
-static int controlfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-	u_int transp, struct fb_info *info);
 
 /*
  * inititialization
@@ -184,7 +182,6 @@
 
 /******************** Prototypes for internal functions **********************/
 
-static void do_install_cmap(struct display *disp, struct fb_info *info);
 static void set_control_clock(unsigned char *params);
 static int init_control(struct fb_info_control *p);
 static void control_set_hardware(struct fb_info_control *p,
@@ -214,7 +211,6 @@
 
 /************************** Internal variables *******************************/
 
-static int currcon;
 static struct fb_info_control *control_fb;
 
 static char fontname[40] __initdata = { 0 };
@@ -228,8 +224,10 @@
 	fb_get_var:	control_get_var,
 	fb_set_var:	control_set_var,
 	fb_get_cmap:	control_get_cmap,
-	fb_set_cmap:	control_set_cmap,
+	fb_set_cmap:	gen_set_cmap,
+	fb_setcolreg:	control_setcolreg,
 	fb_pan_display:	control_pan_display,
+	fb_blank:	controlfb_blank,
 	fb_mmap:	control_mmap,
 };
 
@@ -324,12 +322,12 @@
 	} else
 		disp->var = *var;
 
-	if(con == currcon) {
+	if (con == info->currcon) {
 		control_set_hardware(p, &par);
 		if(depthchange) {
 			if((err = fb_alloc_cmap(&disp->cmap, 0, 0)))
 				return err;
-			do_install_cmap(disp, info);
+			do_install_cmap(con, info);
 		}
 	}
 
@@ -377,7 +375,7 @@
 static int control_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			  struct fb_info *info)
 {
-	if (con == currcon)		/* current console? */
+	if (con == info->currcon)		/* current console? */
 		return fb_get_cmap(cmap, kspc, controlfb_getcolreg, info);
 	if (fb_display[con].cmap.len)	/* non default colormap? */
 		fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0: 2);
@@ -388,24 +386,6 @@
 	return 0;
 }
 
-static int control_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-			 struct fb_info *info)
-{
-	struct display *disp = (con < 0)? info->disp: &fb_display[con];
-	int err, size = disp->var.bits_per_pixel == 16 ? 32 : 256;
-
-	if (disp->cmap.len != size) {
-		err = fb_alloc_cmap(&disp->cmap, size, 0);
-		if (err)
-			return err;
-	}
-	if (con == currcon)
-		return fb_set_cmap(cmap, kspc, controlfb_setcolreg, info);
-	fb_copy_cmap(cmap, &disp->cmap, kspc ? 0 : 1);
-	return 0;
-}
-
-
 /*
  * Private mmap since we want to have a different caching on the framebuffer
  * for controlfb.
@@ -463,16 +443,16 @@
 	struct fb_info_control	*p = (struct fb_info_control *)info;
 	struct fb_par_control	par;
 
-	if (currcon >= 0 && fb_display[currcon].cmap.len)
-		fb_get_cmap(&fb_display[currcon].cmap, 1, controlfb_getcolreg,
+	if (info->currcon >= 0 && fb_display[info->currcon].cmap.len)
+		fb_get_cmap(&fb_display[info->currcon].cmap, 1, controlfb_getcolreg,
 			    info);
-	currcon = con;
+	info->currcon = con;
 
 	fb_display[con].var.activate = FB_ACTIVATE_NOW;
 	control_var_to_par(&fb_display[con].var, &par, info);
 	control_set_hardware(p, &par);
 	control_set_dispsw(&fb_display[con], par.cmode, p);
-	do_install_cmap(&fb_display[con], info);
+	do_install_cmap(con, info);
 
 	return 1;
 }
@@ -489,7 +469,7 @@
 }
 
 
-static void controlfb_blank(int blank_mode, struct fb_info *info)
+static int controlfb_blank(int blank_mode, struct fb_info *info)
 {
 	struct fb_info_control *p = (struct fb_info_control *) info;
 	unsigned ctrl;
@@ -518,7 +498,7 @@
 	}
 	out_le32(CNTRL_REG(p,ctrl), ctrl);
 
-	return;
+	return 0;
 }
 
 
@@ -580,19 +560,6 @@
 	return 0;
 }
 
-static void do_install_cmap(struct display *disp, struct fb_info *info)
-{
-	if (disp->cmap.len)
-		fb_set_cmap(&disp->cmap, 1, controlfb_setcolreg,
-			    info);
-	else {
-		int size = disp->var.bits_per_pixel == 16 ? 32 : 256;
-		fb_set_cmap(fb_default_cmap(size), 1, controlfb_setcolreg,
-			    info);
-	}
-}
-
-
 static void set_control_clock(unsigned char *params)
 {
 #ifdef CONFIG_ADB_CUDA
@@ -668,7 +635,7 @@
 		var.yres_virtual = vyres;
 
 	control_init_info(&p->info, p);
-	currcon = -1;
+	p->info.currcon = -1;
 	var.activate = FB_ACTIVATE_NOW;
 
 	if (control_set_var(&var, -1, &p->info) < 0) {
@@ -1255,7 +1222,7 @@
 	disp->ywrapstep = fix->ywrapstep;
 
 	control_par_to_var(par, &disp->var);
-	disp->screen_base = (char *) p->frame_buffer + CTRLFB_OFF;
+	p->info.screen_base = (char *) p->frame_buffer + CTRLFB_OFF;
 	disp->visual = fix->visual;
 	disp->line_length = fix->line_length;
 	control_set_dispsw(disp, par->cmode, p);
@@ -1271,7 +1238,7 @@
     u8 *dest;
     int bytes = p->next_line, rows;
 
-    dest = p->screen_base + yy * fontheight(p) * bytes + xx * fontwidth(p)*2;
+    dest = p->info.screen_base + yy * fontheight(p) * bytes + xx * fontwidth(p)*2;
     for (rows = fontheight(p); rows--; dest += bytes) {
        switch (fontwidth(p)) {
        case 16:
@@ -1294,7 +1261,7 @@
     u8 *dest;
     int bytes = p->next_line, rows;
 
-    dest = p->screen_base + yy * fontheight(p) * bytes + xx * fontwidth(p) * 4;
+    dest = p->info.screen_base + yy * fontheight(p) * bytes + xx * fontwidth(p) * 4;
     for (rows = fontheight(p); rows--; dest += bytes) {
        switch (fontwidth(p)) {
        case 16:
@@ -1383,7 +1350,6 @@
 	info->changevar = NULL;
 	info->switch_con = &controlfb_switch;
 	info->updatevar = &controlfb_updatevar;
-	info->blank = &controlfb_blank;
 }
 
 
diff -Nru a/drivers/video/creatorfb.c b/drivers/video/creatorfb.c
--- a/drivers/video/creatorfb.c	Sun Apr 28 20:10:31 2002
+++ b/drivers/video/creatorfb.c	Sun Apr 28 20:10:31 2002
@@ -589,7 +589,7 @@
 }
 
 #if 0
-static void ffb_blank(struct fb_info_sbusfb *fb)
+static int ffb_blank(struct fb_info_sbusfb *fb)
 {
 	struct ffb_dac *dac = fb->s.ffb.dac;
 	unsigned long flags;
@@ -601,10 +601,11 @@
 	upa_writel(0x6000, &dac->type);
 	upa_writel(tmp, &dac->value);
 	spin_unlock_irqrestore(&fb->lock, flags);
+	return 0;
 }
 #endif
 
-static void ffb_unblank(struct fb_info_sbusfb *fb)
+static int ffb_unblank(struct fb_info_sbusfb *fb)
 {
 	struct ffb_dac *dac = fb->s.ffb.dac;
 	unsigned long flags;
@@ -616,6 +617,7 @@
 	upa_writel(0x6000, &dac->type);
 	upa_writel(tmp, &dac->value);
 	spin_unlock_irqrestore(&fb->lock, flags);
+	return 0;
 }
 
 static void ffb_loadcmap (struct fb_info_sbusfb *fb, struct display *p, int index, int count)
@@ -663,7 +665,7 @@
 	spin_lock_irqsave(&fb->lock, flags);
 	fb->s.ffb.xy_margin = (y_margin << 16) + x_margin;
 	fb->s.ffb.yx_margin = (((u64)y_margin) << 32) + x_margin;
-	p->screen_base += 8192 * (y_margin - fb->y_margin) + 4 * (x_margin - fb->x_margin);
+	fb->info.screen_base += 8192 * (y_margin - fb->y_margin) + 4 * (x_margin - fb->x_margin);
 	FFBWait(fbc);
 	spin_unlock_irqrestore(&fb->lock, flags);
 }
@@ -859,7 +861,7 @@
 	var->accel_flags = FB_ACCELF_TEXT;
 	
 	disp->scrollmode = SCROLL_YREDRAW;
-	disp->screen_base = (char *)__va(regs[0].phys_addr) + FFB_DFB24_POFF + 8192 * fb->y_margin + 4 * fb->x_margin;
+	fb->info.screen_base = (char *)__va(regs[0].phys_addr) + FFB_DFB24_POFF + 8192 * fb->y_margin + 4 * fb->x_margin;
 	fb->s.ffb.xy_margin = (fb->y_margin << 16) + fb->x_margin;
 	fb->s.ffb.yx_margin = (((u64)fb->y_margin) << 32) + fb->x_margin;
 	fb->s.ffb.fbc = (struct ffb_fbc *)(regs[0].phys_addr + FFB_FBC_REGS_POFF);
diff -Nru a/drivers/video/cyber2000fb.c b/drivers/video/cyber2000fb.c
--- a/drivers/video/cyber2000fb.c	Sun Apr 28 20:10:32 2002
+++ b/drivers/video/cyber2000fb.c	Sun Apr 28 20:10:32 2002
@@ -306,8 +306,8 @@
  *    Set a single color register. Return != 0 for invalid regno.
  */
 static int
-cyber2000_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-		    u_int transp, struct fb_info *info)
+cyber2000fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+		      u_int transp, struct fb_info *info)
 {
 	struct cfb_info *cfb = (struct cfb_info *)info;
 	struct fb_var_screeninfo *var = &cfb->display->var;
@@ -540,37 +540,6 @@
 	return 0;
 }
 
-/*
- * Set the Colormap
- */
-static int
-cyber2000fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-		     struct fb_info *info)
-{
-	struct cfb_info *cfb = (struct cfb_info *)info;
-	struct display *display = fb_display + con;
-	struct fb_cmap *dcmap = &display->cmap;
-	int err = 0;
-
-	/* no colormap allocated? */
-	if (!dcmap->len)
-		err = fb_alloc_cmap(dcmap, 256, 0);
-
-	/*
-	 * we should be able to remove this test once fbcon has been
-	 * "improved" --rmk
-	 */
-	if (!err && display == cfb->display) {
-		err = fb_set_cmap(cmap, kspc, cyber2000_setcolreg, &cfb->fb);
-		dcmap = &cfb->fb.cmap;
-	}
-
-	if (!err)
-		fb_copy_cmap(cmap, dcmap, kspc ? 0 : 1);
-
-	return err;
-}
-
 static int
 cyber2000fb_decode_crtc(struct par_info *hw, struct cfb_info *cfb,
 			struct fb_var_screeninfo *var)
@@ -956,7 +925,6 @@
 
 	cfb->fb.fix.line_length	= display->next_line;
 
-	display->screen_base	= cfb->fb.screen_base;
 	display->line_length	= cfb->fb.fix.line_length;
 	display->visual		= cfb->fb.fix.visual;
 	display->type		= cfb->fb.fix.type;
@@ -982,7 +950,7 @@
 
 	cyber2000fb_update_start(cfb, var);
 	cyber2000fb_set_timing(cfb, &hw);
-	fb_set_cmap(&cfb->fb.cmap, 1, cyber2000_setcolreg, &cfb->fb);
+	fb_set_cmap(&cfb->fb.cmap, 1, &cfb->fb);
 
 	return 0;
 }
@@ -1077,7 +1045,7 @@
 /*
  *    (Un)Blank the display.
  */
-static void cyber2000fb_blank(int blank, struct fb_info *info)
+static int cyber2000fb_blank(int blank, struct fb_info *info)
 {
 	struct cfb_info *cfb = (struct cfb_info *)info;
 	unsigned int sync = 0;
@@ -1137,43 +1105,16 @@
 		}
 		break;
 	}
-}
-
-/*
- * Get the currently displayed virtual consoles colormap.
- */
-static int
-gen_get_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info)
-{
-	fb_copy_cmap(&info->cmap, cmap, kspc ? 0 : 2);
-	return 0;
-}
-
-/*
- * Get the currently displayed virtual consoles fixed part of the display.
- */
-static int
-gen_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
-{
-	*fix = info->fix;
-	return 0;
-}
-
-/*
- * Get the current user defined part of the display.
- */
-static int
-gen_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
-{
-	*var = info->var;
 	return 0;
 }
 
 static struct fb_ops cyber2000fb_ops = {
 	owner:		THIS_MODULE,
 	fb_set_var:	cyber2000fb_set_var,
-	fb_set_cmap:	cyber2000fb_set_cmap,
+	fb_set_cmap:	gen_set_cmap,
+	fb_setcolreg:	cyber2000fb_setcolreg,
 	fb_pan_display:	cyber2000fb_pan_display,
+	fb_blank:	cyber2000fb_blank,
 	fb_get_fix:	gen_get_fix,
 	fb_get_var:	gen_get_var,
 	fb_get_cmap:	gen_get_cmap,
@@ -1400,7 +1341,6 @@
 	cfb->fb.changevar	= NULL;
 	cfb->fb.switch_con	= cyber2000fb_switch;
 	cfb->fb.updatevar	= cyber2000fb_updatevar;
-	cfb->fb.blank		= cyber2000fb_blank;
 	cfb->fb.flags		= FBINFO_FLAG_DEFAULT;
 	cfb->fb.node		= NODEV;
 	cfb->fb.disp		= (struct display *)(cfb + 1);
diff -Nru a/drivers/video/cyberfb.c b/drivers/video/cyberfb.c
--- a/drivers/video/cyberfb.c	Sun Apr 28 20:10:30 2002
+++ b/drivers/video/cyberfb.c	Sun Apr 28 20:10:30 2002
@@ -123,7 +123,6 @@
 static struct cyberfb_par current_par;
 
 static int current_par_valid = 0;
-static int currcon = 0;
 
 static struct display disp;
 static struct fb_info fb_info;
@@ -247,8 +246,9 @@
 			   struct fb_info *info);
 static int cyberfb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			    struct fb_info *info);
-static int cyberfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-			    struct fb_info *info);
+static int cyberfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+			     u_int transp, struct fb_info *info);
+static int cyberfb_blank(int blank, struct fb_info *info);
 
 /*
  *    Interface to the low level console driver
@@ -257,7 +257,6 @@
 int cyberfb_init(void);
 static int Cyberfb_switch(int con, struct fb_info *info);
 static int Cyberfb_updatevar(int con, struct fb_info *info);
-static void Cyberfb_blank(int blank, struct fb_info *info);
 
 /*
  *    Text console acceleration
@@ -295,8 +294,6 @@
 			    struct cyberfb_par *par);
 static int Cyber_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
 			   u_int *transp, struct fb_info *info);
-static int Cyber_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-			   u_int transp, struct fb_info *info);
 
 /*
  *    Internal routines
@@ -305,7 +302,6 @@
 static void cyberfb_get_par(struct cyberfb_par *par);
 static void cyberfb_set_par(struct cyberfb_par *par);
 static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive);
-static void do_install_cmap(int con, struct fb_info *info);
 static void cyberfb_set_disp(int con, struct fb_info *info);
 static int get_video_mode(const char *name);
 
@@ -379,8 +375,8 @@
 		*(CursorBase+3+(i*4)) = 0xffff0000;
 	}
 
-	Cyber_setcolreg (255, 56<<8, 100<<8, 160<<8, 0, NULL /* unused */);
-	Cyber_setcolreg (254, 0, 0, 0, 0, NULL /* unused */);
+	cyberfb_setcolreg (255, 56<<8, 100<<8, 160<<8, 0, NULL /* unused */);
+	cyberfb_setcolreg (254, 0, 0, 0, 0, NULL /* unused */);
 
 	DPRINTK("EXIT\n");
 	return 0;
@@ -517,7 +513,7 @@
  *    Set a single color register. Return != 0 for invalid regno.
  */
 
-static int Cyber_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+static int cyberfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
 			   u_int transp, struct fb_info *info)
 {
 	volatile unsigned char *regs = CyberRegs;
@@ -580,8 +576,7 @@
 *    blank: 1 = zero fb cmap
 *           0 = restore fb cmap from local cmap
 */
-
-void Cyberfb_blank(int blank, struct fb_info *info)
+static int cyberfb_blank(int blank, struct fb_info *info)
 {
 	volatile unsigned char *regs = CyberRegs;
 	int i;
@@ -800,25 +795,6 @@
 	return 0;
 }
 
-
-static void do_install_cmap(int con, struct fb_info *info)
-{
-	DPRINTK("ENTER\n");
-	if (con != currcon) {
-		DPRINTK("EXIT - Not current console\n");
-		return;
-	}
-	if (fb_display[con].cmap.len) {
-		DPRINTK("Use console cmap\n");
-		fb_set_cmap(&fb_display[con].cmap, 1, Cyber_setcolreg, info);
-	} else {
-		DPRINTK("Use default cmap\n");
-		fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
-			    1, Cyber_setcolreg, info);
-	}
-	DPRINTK("EXIT\n");
-}
-
 /*
  *    Get the Fixed Part of the Display
  */
@@ -878,7 +854,6 @@
 	cyberfb_get_fix(&fix, con, info);
 	if (con == -1)
 		con = 0;
-	display->screen_base = (unsigned char *)CyberMem;
 	display->visual = fix.visual;
 	display->type = fix.type;
 	display->type_aux = fix.type_aux;
@@ -919,7 +894,7 @@
 	int err, oldxres, oldyres, oldvxres, oldvyres, oldbpp, oldaccel;
 
 	DPRINTK("ENTER\n");
-	if ((err = do_fb_set_var(var, con == currcon))) {
+	if ((err = do_fb_set_var(var, con == info->currcon))) {
 		DPRINTK("EXIT - do_fb_set_var failed\n");
 		return(err);
 	}
@@ -956,7 +931,7 @@
 			    struct fb_info *info)
 {
 	DPRINTK("ENTER\n");
-	if (con == currcon) { /* current console? */
+	if (con == info->currcon) { /* current console? */
 		DPRINTK("EXIT - console is current console\n");
 		return(fb_get_cmap(cmap, kspc, Cyber_getcolreg, info));
 	} else if (fb_display[con].cmap.len) { /* non default colormap? */
@@ -971,43 +946,15 @@
 	return(0);
 }
 
-
-/*
- *    Set the Colormap
- */
-
-static int cyberfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-			    struct fb_info *info)
-{
-	int err;
-
-	DPRINTK("ENTER\n");
-	if (!fb_display[con].cmap.len) {       /* no colormap allocated? */
-		if ((err = fb_alloc_cmap(&fb_display[con].cmap,
-					 1<<fb_display[con].var.bits_per_pixel,
-					 0))) {
-			DPRINTK("EXIT - fb_alloc_cmap failed\n");
-			return(err);
-		}
-	}
-	if (con == currcon) {		 /* current console? */
-		DPRINTK("EXIT - Current console\n");
-		return(fb_set_cmap(cmap, kspc, Cyber_setcolreg, info));
-	} else {
-		fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
-	}
-	DPRINTK("EXIT\n");
-	return(0);
-}
-
-
 static struct fb_ops cyberfb_ops = {
 	owner:		THIS_MODULE,
 	fb_get_fix:	cyberfb_get_fix,
 	fb_get_var:	cyberfb_get_var,
 	fb_set_var:	cyberfb_set_var,
 	fb_get_cmap:	cyberfb_get_cmap,
-	fb_set_cmap:	cyberfb_set_cmap,
+	fb_set_cmap:	gen_set_cmap,
+	fb_setcolreg:	cyberfb_setcolreg,
+	fb_blank:	cyberfb_blank,
 };
 
 int __init cyberfb_setup(char *options)
@@ -1087,10 +1034,11 @@
 	    fb_info.changevar = NULL;
 	    fb_info.node = NODEV;
 	    fb_info.fbops = &cyberfb_ops;
+	    fb_info.screen_base = (unsigned char *)CyberMem;
 	    fb_info.disp = &disp;
+	    fb_info.currcon = -1;
 	    fb_info.switch_con = &Cyberfb_switch;
 	    fb_info.updatevar = &Cyberfb_updatevar;
-	    fb_info.blank = &Cyberfb_blank;
 
 	    Cyber_init();
 	    /* ++Andre: set cyberfb default mode */
@@ -1129,13 +1077,13 @@
 {
         DPRINTK("ENTER\n");
 	/* Do we have to save the colormap? */
-	if (fb_display[currcon].cmap.len) {
-		fb_get_cmap(&fb_display[currcon].cmap, 1, Cyber_getcolreg,
+	if (fb_display[info->currcon].cmap.len) {
+		fb_get_cmap(&fb_display[info->currcon].cmap, 1, Cyber_getcolreg,
 			    info);
 	}
 
 	do_fb_set_var(&fb_display[con].var, 1);
-	currcon = con;
+	info->currcon = con;
 	/* Install new colormap */
 	do_install_cmap(con, info);
 	DPRINTK("EXIT\n");
diff -Nru a/drivers/video/dn_cfb4.c b/drivers/video/dn_cfb4.c
--- a/drivers/video/dn_cfb4.c	Sun Apr 28 20:10:36 2002
+++ b/drivers/video/dn_cfb4.c	Sun Apr 28 20:10:36 2002
@@ -116,12 +116,10 @@
 			 struct fb_info *info);
 static int dn_fb_get_cmap(struct fb_cmap *cmap,int kspc,int con,
 			  struct fb_info *info);
-static int dn_fb_set_cmap(struct fb_cmap *cmap,int kspc,int con,
-			  struct fb_info *info);
+static int dn_fb_blank(int blank,struct fb_info *info);
 
 static int dnfbcon_switch(int con,struct fb_info *info);
 static int dnfbcon_updatevar(int con,struct fb_info *info);
-static void dnfbcon_blank(int blank,struct fb_info *info);
 
 static void dn_fb_set_disp(int con,struct fb_info *info);
 
@@ -133,11 +131,10 @@
 	fb_get_var:	dn_fb_get_var,
 	fb_set_var:	dn_fb_set_var,
 	fb_get_cmap:	dn_fb_get_cmap,
-	fb_set_cmap:	dn_fb_set_cmap,
+	fb_set_cmap:	gen_set_cmap,
+	fb_blank:	dn_fb_blank,
 };
 
-static int currcon=0;
-
 #define NUM_TOTAL_MODES 1
 struct fb_var_screeninfo dn_fb_predefined[] = {
 
@@ -248,15 +245,6 @@
 	return -EINVAL;
 }
 
-static int dn_fb_set_cmap(struct fb_cmap *cmap,int kspc,int con,
-			  struct fb_info *info) {
-
-	printk("set cmap not supported\n");
-
-	return -EINVAL;
-
-}
-
 static void dn_fb_set_disp(int con, struct fb_info *info) {
 
   struct fb_fix_screeninfo fix;
@@ -273,8 +261,6 @@
   if(con==-1) 
     con=0;
 
-   display->screen_base = (u_char *)fix.smem_start;
-printk("screenbase: %lx\n",fix.smem_start);
    display->visual = fix.visual;
    display->type = fix.type;
    display->type_aux = fix.type_aux;
@@ -304,9 +290,11 @@
 	fb_info.disp=disp;
 	fb_info.switch_con=&dnfbcon_switch;
 	fb_info.updatevar=&dnfbcon_updatevar;
-	fb_info.blank=&dnfbcon_blank;	
 	fb_info.node = NODEV;
 	fb_info.fbops = &dn_fb_ops;
+   	fb_info.screen_base = (u_char *)fix.smem_start;
+	printk("screenbase: %lx\n",fix.smem_start);
+	fb_info.currcon = -1;
 	fb_info.flags = FBINFO_FLAG_DEFAULT;	
 
         dn_fb_get_var(&disp[0].var,0, &fb_info);
@@ -336,7 +324,7 @@
 	
 static int dnfbcon_switch(int con,  struct fb_info *info) { 
 
-	currcon=con;
+	info->currcon = con;
 	
 	return 0;
 
@@ -348,17 +336,13 @@
 
 }
 
-static void dnfbcon_blank(int blank,  struct fb_info *info) {
-
-	if(blank)  {
+static int dn_fb_blank(int blank,  struct fb_info *info)
+{
+	if (blank)
         	outb(0x0,  AP_CONTROL_3A);
-	}
-	else {
+	else 
 	        outb(0x1,  AP_CONTROL_3A);
-	}
-
-	return ;
-
+	return 0;
 }
 
 void dn_bitblt(struct display *p,int x_src,int y_src, int x_dest, int y_dest,
@@ -371,7 +355,7 @@
 
 	incr=(y_dest<=y_src) ? 1 : -1 ;
 
-	src=(ushort *)(p->screen_base+ y_src*p->next_line+(x_src >> 4));
+	src=(ushort *)(p->fb_info.screen_base+ y_src*p->next_line+(x_src >> 4));
 	dest=y_dest*(p->next_line >> 1)+(x_dest >> 4);
 	
 	if(incr>0) {
@@ -449,20 +433,20 @@
     u_int rows;
 
     if (sx == 0 && dx == 0 && width == p->next_line) {
-	src = p->screen_base+sy*fontheight*width;
-	dest = p->screen_base+dy*fontheight*width;
+	src = p->fb_info.screen_base+sy*fontheight*width;
+	dest = p->fb_info.screen_base+dy*fontheight*width;
 	mymemmove(dest, src, height*fontheight*width);
     } else if (dy <= sy) {
-	src = p->screen_base+sy*fontheight*p->next_line+sx;
-	dest = p->screen_base+dy*fontheight*p->next_line+dx;
+	src = p->fb_info.screen_base+sy*fontheight*p->next_line+sx;
+	dest = p->fb_info.screen_base+dy*fontheight*p->next_line+dx;
 	for (rows = height*fontheight; rows--;) {
 	    mymemmove(dest, src, width);
 	    src += p->next_line;
 	    dest += p->next_line;
 	}
     } else {
-	src = p->screen_base+((sy+height)*fontheight-1)*p->next_line+sx;
-	dest = p->screen_base+((dy+height)*fontheight-1)*p->next_line+dx;
+	src = p->fb_info.screen_base+((sy+height)*fontheight-1)*p->next_line+sx;
+	dest = p->fb_info.screen_base+((dy+height)*fontheight-1)*p->next_line+dx;
 	for (rows = height*fontheight; rows--;) {
 	    mymemmove(dest, src, width);
 	    src -= p->next_line;
diff -Nru a/drivers/video/dn_cfb8.c b/drivers/video/dn_cfb8.c
--- a/drivers/video/dn_cfb8.c	Sun Apr 28 20:10:30 2002
+++ b/drivers/video/dn_cfb8.c	Sun Apr 28 20:10:30 2002
@@ -117,12 +117,11 @@
 			 struct fb_info *info);
 static int dn_fb_get_cmap(struct fb_cmap *cmap,int kspc,int con,
 			  struct fb_info *info);
-static int dn_fb_set_cmap(struct fb_cmap *cmap,int kspc,int con,
-			  struct fb_info *info);
+static int dn_fb_blank(int blank,struct fb_info *info);
+
 
 static int dnfbcon_switch(int con,struct fb_info *info);
 static int dnfbcon_updatevar(int con,struct fb_info *info);
-static void dnfbcon_blank(int blank,struct fb_info *info);
 
 static void dn_fb_set_disp(int con,struct fb_info *info);
 
@@ -134,11 +133,10 @@
 	fb_get_var:	dn_fb_get_var,
 	fb_set_var:	dn_fb_set_var,
 	fb_get_cmap:	dn_fb_get_cmap,
-	fb_set_cmap:	dn_fb_set_cmap,
+	fb_set_cmap:	gen_set_cmap,
+	fb_blank:	dn_fb_blank,
 };
 
-static int currcon=0;
-
 #define NUM_TOTAL_MODES 1
 struct fb_var_screeninfo dn_fb_predefined[] = {
 
@@ -249,15 +247,6 @@
 	return -EINVAL;
 }
 
-static int dn_fb_set_cmap(struct fb_cmap *cmap,int kspc,int con,
-			  struct fb_info *info) {
-
-	printk("set cmap not supported\n");
-
-	return -EINVAL;
-
-}
-
 static void dn_fb_set_disp(int con, struct fb_info *info) {
 
   struct fb_fix_screeninfo fix;
@@ -266,8 +255,6 @@
   if(con==-1) 
     con=0;
 
-   disp[con].screen_base = (u_char *)fix.smem_start;
-printk("screenbase: %p\n",fix.smem_start);
    disp[con].visual = fix.visual;
    disp[con].type = fix.type;
    disp[con].type_aux = fix.type_aux;
@@ -283,7 +270,7 @@
 
 	int err;
        
-printk("dn_fb_init\n");
+	printk("dn_fb_init\n");
 
 	fb_info.changevar=NULL;
 	strcpy(&fb_info.modename[0],dn_fb_name);
@@ -291,9 +278,10 @@
 	fb_info.disp=disp;
 	fb_info.switch_con=&dnfbcon_switch;
 	fb_info.updatevar=&dnfbcon_updatevar;
-	fb_info.blank=&dnfbcon_blank;	
 	fb_info.node = NODEV;
+	fb_info.currcon = -1;
 	fb_info.fbops = &dn_fb_ops;
+	printk("screenbase: %p\n",fix.smem_start);
 	
 printk("dn_fb_init: register\n");
 	err=register_framebuffer(&fb_info);
@@ -317,15 +305,14 @@
         dn_fb_get_var(&disp[0].var,0, &fb_info);
 
 	dn_fb_set_disp(-1, &fb_info);
-
+   	fb_info.screen_base = (u_char *)fix.smem_start;
 	return mem_start;
-
 }	
 
 	
 static int dnfbcon_switch(int con,  struct fb_info *info) { 
 
-	currcon=con;
+	info->currcon = con;
 	
 	return 0;
 
@@ -337,16 +324,13 @@
 
 }
 
-static void dnfbcon_blank(int blank,  struct fb_info *info) {
-
-	if(blank)  {
+static int dn_fb_blank(int blank, struct fb_info *info)
+{
+	if (blank)
         	outb(0x0,  AP_CONTROL_3A);
-	}
-	else {
+	else
 	        outb(0x1,  AP_CONTROL_3A);
-	}
-
-	return ;
+	return 0;
 
 }
 
@@ -360,7 +344,7 @@
 
 	incr=(y_dest<=y_src) ? 1 : -1 ;
 
-	src=(ushort *)(p->screen_base+ y_src*p->next_line+(x_src >> 4));
+	src=(ushort *)(p->fb_info.screen_base+ y_src*p->next_line+(x_src >> 4));
 	dest=y_dest*(p->next_line >> 1)+(x_dest >> 4);
 	
 	if(incr>0) {
@@ -433,20 +417,20 @@
     u_int rows;
 
     if (sx == 0 && dx == 0 && width == p->next_line) {
-	src = p->screen_base+sy*p->fontheight*width;
-	dest = p->screen_base+dy*p->fontheight*width;
+	src = p->fb_info.screen_base+sy*p->fontheight*width;
+	dest = p->fb_info.screen_base+dy*p->fontheight*width;
 	mymemmove(dest, src, height*p->fontheight*width);
     } else if (dy <= sy) {
-	src = p->screen_base+sy*p->fontheight*p->next_line+sx;
-	dest = p->screen_base+dy*p->fontheight*p->next_line+dx;
+	src = p->fb_info.screen_base+sy*p->fontheight*p->next_line+sx;
+	dest = p->fb_info.screen_base+dy*p->fontheight*p->next_line+dx;
 	for (rows = height*p->fontheight; rows--;) {
 	    mymemmove(dest, src, width);
 	    src += p->next_line;
 	    dest += p->next_line;
 	}
     } else {
-	src = p->screen_base+((sy+height)*p->fontheight-1)*p->next_line+sx;
-	dest = p->screen_base+((dy+height)*p->fontheight-1)*p->next_line+dx;
+	src = p->fb_info.screen_base+((sy+height)*p->fontheight-1)*p->next_line+sx;
+	dest = p->fb_info.screen_base+((dy+height)*p->fontheight-1)*p->next_line+dx;
 	for (rows = height*p->fontheight; rows--;) {
 	    mymemmove(dest, src, width);
 	    src -= p->next_line;
@@ -462,7 +446,7 @@
     u_char *dest;
     u_int rows;
 
-    dest = p->screen_base+sy*p->fontheight*p->next_line+sx;
+    dest = p->fb_info.screen_base+sy*p->fontheight*p->next_line+sx;
 
     if (sx == 0 && width == p->next_line)
 	if (attr_reverse(p,conp))
@@ -486,7 +470,7 @@
 
     c &= 0xff;
 
-    dest = p->screen_base+yy*p->fontheight*p->next_line+xx;
+    dest = p->fb_info.screen_base+yy*p->fontheight*p->next_line+xx;
     cdat = p->fontdata+c*p->fontheight;
     bold = attr_bold(p,conp);
     revs = attr_reverse(p,conp);
@@ -511,7 +495,7 @@
     u_int rows, bold, revs, underl;
     u_char c, d;
 
-    dest0 = p->screen_base+yy*p->fontheight*p->next_line+xx;
+    dest0 = p->fb_info.screen_base+yy*p->fontheight*p->next_line+xx;
     bold = attr_bold(p,conp);
     revs = attr_reverse(p,conp);
     underl = attr_underline(p,conp);
@@ -538,7 +522,7 @@
     u_char *dest;
     u_int rows;
 
-    dest = p->screen_base+yy*p->fontheight*p->next_line+xx;
+    dest = p->fb_info.screen_base+yy*p->fontheight*p->next_line+xx;
     for (rows = p->fontheight; rows--; dest += p->next_line)
 	*dest = ~*dest;
 }
diff -Nru a/drivers/video/dnfb.c b/drivers/video/dnfb.c
--- a/drivers/video/dnfb.c	Sun Apr 28 20:10:34 2002
+++ b/drivers/video/dnfb.c	Sun Apr 28 20:10:34 2002
@@ -122,12 +122,10 @@
 			 struct fb_info *info);
 static int dn_fb_get_cmap(struct fb_cmap *cmap,int kspc,int con,
 			  struct fb_info *info);
-static int dn_fb_set_cmap(struct fb_cmap *cmap,int kspc,int con,
-			  struct fb_info *info);
+static int dn_fb_blank(int blank,struct fb_info *info);
 
 static int dnfbcon_switch(int con,struct fb_info *info);
 static int dnfbcon_updatevar(int con,struct fb_info *info);
-static void dnfbcon_blank(int blank,struct fb_info *info);
 
 static void dn_fb_set_disp(int con,struct fb_info *info);
 
@@ -139,11 +137,10 @@
 	fb_get_var:	dn_fb_get_var,
 	fb_set_var:	dn_fb_set_var,
 	fb_get_cmap:	dn_fb_get_cmap,
-	fb_set_cmap:	dn_fb_set_cmap,
+	fb_set_cmap:	gen_set_cmap,
+	fb_blank:	dn_fb_blank,
 };
 
-static int currcon=0;
-
 #define NUM_TOTAL_MODES 1
 struct fb_var_screeninfo dn_fb_predefined[] = {
 
@@ -254,15 +251,6 @@
 	return -EINVAL;
 }
 
-static int dn_fb_set_cmap(struct fb_cmap *cmap,int kspc,int con,
-			  struct fb_info *info) {
-
-	printk("set cmap not supported\n");
-
-	return -EINVAL;
-
-}
-
 static void dn_fb_set_disp(int con, struct fb_info *info) {
 
   struct fb_fix_screeninfo fix;
@@ -278,7 +266,6 @@
   if(con==-1) 
     con=0;
 
-   display->screen_base = (u_char *)fix.smem_start;
    display->visual = fix.visual;
    display->type = fix.type;
    display->type_aux = fix.type_aux;
@@ -306,13 +293,15 @@
 	fb_info.disp=disp;
 	fb_info.switch_con=&dnfbcon_switch;
 	fb_info.updatevar=&dnfbcon_updatevar;
-	fb_info.blank=&dnfbcon_blank;	
 	fb_info.node = NODEV;
 	fb_info.fbops = &dn_fb_ops;
+	fb_info.currcon = -1;
 	
         dn_fb_get_var(&disp[0].var,0, &fb_info);
 
 	dn_fb_set_disp(-1, &fb_info);
+
+   	fb_info.screen_base = (u_char *)fix.smem_start;
 	
 	err=register_framebuffer(&fb_info);
 	if(err < 0) {
@@ -338,10 +327,8 @@
 	
 static int dnfbcon_switch(int con,  struct fb_info *info) { 
 
-	currcon=con;
-	
+	info->currcon = con;
 	return 0;
-
 }
 
 static int dnfbcon_updatevar(int con,  struct fb_info *info) {
@@ -350,17 +337,13 @@
 
 }
 
-static void dnfbcon_blank(int blank,  struct fb_info *info) {
-
-	if(blank)  {
+static int dn_fb_blank(int blank, struct fb_info *info)
+{
+	if (blank)
         	outb(0x0,  AP_CONTROL_3A);
-	}
-	else {
+	else
 	        outb(0x1,  AP_CONTROL_3A);
-	}
-
-	return ;
-
+	return 0;
 }
 
 void dn_bitblt(struct display *p,int x_src,int y_src, int x_dest, int y_dest,
@@ -373,7 +356,7 @@
 
 	incr=(y_dest<=y_src) ? 1 : -1 ;
 
-	src=(ushort *)(p->screen_base+ y_src*p->next_line+(x_src >> 4));
+	src=(ushort *)(p->fb_info.screen_base+ y_src*p->next_line+(x_src >> 4));
 	dest=y_dest*(p->next_line >> 1)+(x_dest >> 4);
 	
 	if(incr>0) {
@@ -453,20 +436,20 @@
     u_int rows;
 
     if (sx == 0 && dx == 0 && width == p->next_line) {
-	src = p->screen_base+sy*fontheight*width;
-	dest = p->screen_base+dy*fontheight*width;
+	src = p->fb_info.screen_base+sy*fontheight*width;
+	dest = p->fb_info.screen_base+dy*fontheight*width;
 	mymemmove(dest, src, height*fontheight*width);
     } else if (dy <= sy) {
-	src = p->screen_base+sy*fontheight*next_line+sx;
-	dest = p->screen_base+dy*fontheight*next_line+dx;
+	src = p->fb_info.screen_base+sy*fontheight*next_line+sx;
+	dest = p->fb_info.screen_base+dy*fontheight*next_line+dx;
 	for (rows = height*fontheight; rows--;) {
 	    mymemmove(dest, src, width);
 	    src += p->next_line;
 	    dest += p->next_line;
 	}
     } else {
-	src = p->screen_base+((sy+height)*fontheight-1)*p->next_line+sx;
-	dest = p->screen_base+((dy+height)*fontheight-1)*p->next_line+dx;
+	src = p->fb_info.screen_base+((sy+height)*fontheight-1)*p->next_line+sx;
+	dest = p->fb_info.screen_base+((dy+height)*fontheight-1)*p->next_line+dx;
 	for (rows = height*fontheight; rows--;) {
 	    mymemmove(dest, src, width);
 	    src -= p->next_line;
diff -Nru a/drivers/video/epson1355fb.c b/drivers/video/epson1355fb.c
--- a/drivers/video/epson1355fb.c	Sun Apr 28 20:10:31 2002
+++ b/drivers/video/epson1355fb.c	Sun Apr 28 20:10:31 2002
@@ -361,9 +361,9 @@
 	return 0;
 }
 
-static int e1355_setcolreg(unsigned regno, unsigned red, unsigned green,
-			   unsigned blue, unsigned transp,
-			   struct fb_info *info)
+static int e1355fb_setcolreg(unsigned regno, unsigned red, unsigned green,
+			     unsigned blue, unsigned transp,
+			     struct fb_info *info)
 {
 	u8 r = (red >> 8) & 0xf0;
 	u8 g = (green>>8) & 0xf0;
@@ -432,7 +432,6 @@
 {
 	struct display_switch *d;
 
-	disp->screen_base = (void *)E1355_FB_BASE;
 	disp->dispsw = &e1355_dispsw;
 	
 	switch(disp->var.bits_per_pixel) {
@@ -467,7 +466,6 @@
 	get_par:	e1355_get_par,
 	set_par:	e1355_set_par,
 	getcolreg:	e1355_getcolreg,
-	setcolreg:	e1355_setcolreg,
 	pan_display:	e1355_pan_display,
 	blank:		e1355_blank,
 	set_disp:	e1355_set_disp,
@@ -483,8 +481,10 @@
 	fb_get_var:	fbgen_get_var,
 	fb_set_var:	fbgen_set_var,
 	fb_get_cmap:	fbgen_get_cmap,
-	fb_set_cmap:	fbgen_set_cmap,
+	fb_set_cmap:	gen_set_cmap,
+	fb_setcolreg:	e1355fb_setcolreg,
 	fb_pan_display:	fbgen_pan_display,
+	fb_blank:	fbgen_blank,
 };
 
 static struct e1355fb_info fb_info;
@@ -502,18 +502,19 @@
 	fb_info.gen.info.changevar = NULL;
 	fb_info.gen.info.node = NODEV;
 	fb_info.gen.info.fbops = &e1355fb_ops;
+	fb_info.gen.info.screen_base = (void *)E1355_FB_BASE;
+	fb_info.gen.currcon = -1;
 	fb_info.gen.info.disp = &disp;
 	fb_info.gen.parsize = sizeof(struct e1355_par);
 	fb_info.gen.info.switch_con = &fbgen_switch;
 	fb_info.gen.info.updatevar = &fbgen_update_var;
-	fb_info.gen.info.blank = &fbgen_blank;
 	fb_info.gen.info.flags = FBINFO_FLAG_DEFAULT;
 	/* This should give a reasonable default video mode */
 	fbgen_get_var(&disp.var, -1, &fb_info.gen.info);
 	fbgen_do_set_var(&disp.var, 1, &fb_info.gen);
 	fbgen_set_disp(-1, &fb_info.gen);
 	if (disp.var.bits_per_pixel > 1) 
-		fbgen_install_cmap(0, &fb_info.gen);
+		do_install_cmap(0, &fb_info.gen);
 	if (register_framebuffer(&fb_info.gen.info) < 0)
 		return -EINVAL;
 	printk(KERN_INFO "fb%d: %s frame buffer device\n", GET_FB_IDX(fb_info.gen.info.node),
diff -Nru a/drivers/video/fbcmap.c b/drivers/video/fbcmap.c
--- a/drivers/video/fbcmap.c	Sun Apr 28 20:10:34 2002
+++ b/drivers/video/fbcmap.c	Sun Apr 28 20:10:34 2002
@@ -249,10 +249,7 @@
  *
  */
 
-int fb_set_cmap(struct fb_cmap *cmap, int kspc,
-    	    	int (*setcolreg)(u_int, u_int, u_int, u_int, u_int,
-				 struct fb_info *),
-		struct fb_info *info)
+int fb_set_cmap(struct fb_cmap *cmap, int kspc, struct fb_info *info)
 {
     int i, start;
     u16 *red, *green, *blue, *transp;
@@ -264,7 +261,7 @@
     transp = cmap->transp;
     start = cmap->start;
 
-    if (start < 0)
+    if (start < 0 || !info->fbops->fb_setcolreg)
 	return -EINVAL;
     for (i = 0; i < cmap->len; i++) {
 	if (kspc) {
@@ -286,7 +283,7 @@
 	blue++;
 	if (transp)
 	    transp++;
-	if (setcolreg(start++, hred, hgreen, hblue, htransp, info))
+	if (info->fbops->fb_setcolreg(start++, hred, hgreen, hblue, htransp, info))
 	    return 0;
     }
     return 0;
diff -Nru a/drivers/video/fbcon-accel.c b/drivers/video/fbcon-accel.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/video/fbcon-accel.c	Sun Apr 28 20:10:39 2002
@@ -0,0 +1,188 @@
+/*
+ *  linux/drivers/video/fbcon-accel.c -- Framebuffer accel console wrapper
+ *
+ *      Created 20 Feb 2001 by James Simmons <jsimmons@users.sf.net>
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License.  See the file COPYING in the main directory of this archive for
+ *  more details.
+ */
+
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/string.h>
+#include <linux/fb.h>
+
+#include <video/fbcon.h>
+#include "fbcon-accel.h"
+
+void fbcon_accel_setup(struct display *p)
+{
+	p->next_line = p->fb_info->fix.line_length;
+	p->next_plane = 0;
+}
+
+void fbcon_accel_bmove(struct display *p, int sy, int sx, int dy, int dx,
+		       int height, int width)
+{
+	struct fb_info *info = p->fb_info;
+	struct fb_copyarea area;
+
+	area.sx = sx * fontwidth(p);
+	area.sy = sy * fontheight(p);
+	area.dx = dx * fontwidth(p);
+	area.dy = dy * fontheight(p);
+	area.height = height * fontheight(p);
+	area.width  = width * fontwidth(p);
+
+	info->fbops->fb_copyarea(info, &area);
+}
+
+void fbcon_accel_clear(struct vc_data *vc, struct display *p, int sy, int sx,
+		       int height, int width)
+{
+	struct fb_info *info = p->fb_info;
+	struct fb_fillrect region;
+
+	region.color = attr_bgcol_ec(p,vc);
+	region.dx = sx * fontwidth(p);
+	region.dy = sy * fontheight(p);
+	region.width = width * fontwidth(p);
+	region.height = height * fontheight(p);
+	region.rop = ROP_COPY;
+
+	info->fbops->fb_fillrect(info, &region);
+}
+
+void fbcon_accel_putc(struct vc_data *vc, struct display *p, int c, int yy,
+		      int xx)
+{
+	struct fb_info *info = p->fb_info;
+	unsigned short charmask = p->charmask;
+	unsigned int width = ((fontwidth(p)+7)>>3);
+	struct fb_image image;
+
+	image.fg_color = attr_fgcol(p, c);
+	image.bg_color = attr_bgcol(p, c);
+	image.dx = xx * fontwidth(p);
+	image.dy = yy * fontheight(p);
+	image.width = fontwidth(p);
+	image.height = fontheight(p);
+	image.depth = 1;
+	image.data = p->fontdata + (c & charmask)*fontheight(p)*width;
+
+	info->fbops->fb_imageblit(info, &image);
+}
+
+void fbcon_accel_putcs(struct vc_data *vc, struct display *p,
+		       const unsigned short *s, int count, int yy, int xx)
+{
+	struct fb_info *info = p->fb_info;
+	unsigned short charmask = p->charmask;
+	unsigned int width = ((fontwidth(p)+7)>>3);
+	struct fb_image image;
+
+	image.fg_color = attr_fgcol(p, *s);
+	image.bg_color = attr_bgcol(p, *s);
+	image.dx = xx * fontwidth(p);
+	image.dy = yy * fontheight(p);
+	image.width = fontwidth(p);
+	image.height = fontheight(p);
+	image.depth = 1;
+
+	while (count--) {
+		image.data = p->fontdata +
+			(scr_readw(s++) & charmask) * fontheight(p) * width;
+		info->fbops->fb_imageblit(info, &image);
+		image.dx += fontwidth(p);
+	}
+}
+
+void fbcon_accel_revc(struct display *p, int xx, int yy)
+{
+	struct fb_info *info = p->fb_info;
+	struct fb_fillrect region;
+
+	region.color = attr_fgcol_ec(p, p->conp);
+	region.dx = xx * fontwidth(p);
+	region.dy = yy * fontheight(p);
+	region.width  = fontwidth(p);
+	region.height = fontheight(p); 
+	region.rop = ROP_XOR;
+	
+	info->fbops->fb_fillrect(info, &region);
+}
+
+void fbcon_accel_clear_margins(struct vc_data *vc, struct display *p,
+			       int bottom_only)
+{
+	struct fb_info *info = p->fb_info;
+	unsigned int cw = fontwidth(p);
+	unsigned int ch = fontheight(p);
+	unsigned int rw = info->var.xres % cw;
+	unsigned int bh = info->var.yres % ch;
+	unsigned int rs = info->var.xres - rw;
+	unsigned int bs = info->var.yres - bh;
+	struct fb_fillrect region;
+
+	region.color = attr_bgcol_ec(p,vc);
+	region.rop = ROP_COPY;
+
+	if (rw && !bottom_only) {
+		region.dx = info->var.xoffset + rs;
+		region.dy = 0;
+		region.width  = rw;
+		region.height = info->var.yres_virtual;
+		info->fbops->fb_fillrect(info, &region); 
+	}
+
+	if (bh) {
+		region.dx = info->var.xoffset;
+                region.dy = info->var.yoffset + bs;
+                region.width  = rs;
+                region.height = bh;
+		info->fbops->fb_fillrect(info, &region);
+	}
+}
+
+    /*
+     *  `switch' for the low level operations
+     */
+
+struct display_switch fbcon_accel = {
+	setup:		fbcon_accel_setup,
+	bmove:		fbcon_accel_bmove,
+	clear:		fbcon_accel_clear,
+	putc:		fbcon_accel_putc,
+	putcs:		fbcon_accel_putcs,
+	revc:		fbcon_accel_revc,
+	clear_margins:	fbcon_accel_clear_margins,
+	fontwidthmask:	FONTWIDTHRANGE(1, 32)	
+};
+
+#ifdef MODULE
+MODULE_LICENSE("GPL");
+
+int init_module(void)
+{
+    return 0;
+}
+
+void cleanup_module(void)
+{}
+#endif /* MODULE */
+
+
+    /*
+     *  Visible symbols for modules
+     */
+
+EXPORT_SYMBOL(fbcon_accel);
+EXPORT_SYMBOL(fbcon_accel_setup);
+EXPORT_SYMBOL(fbcon_accel_bmove);
+EXPORT_SYMBOL(fbcon_accel_clear);
+EXPORT_SYMBOL(fbcon_accel_putc);
+EXPORT_SYMBOL(fbcon_accel_putcs);
+EXPORT_SYMBOL(fbcon_accel_revc);
+EXPORT_SYMBOL(fbcon_accel_clear_margins);
diff -Nru a/drivers/video/fbcon-accel.h b/drivers/video/fbcon-accel.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/video/fbcon-accel.h	Sun Apr 28 20:10:39 2002
@@ -0,0 +1,34 @@
+/*
+ *  FBcon low-level driver for 32 bpp packed pixel (cfb32)
+ */
+
+#ifndef _VIDEO_FBCON_ACCEL_H
+#define _VIDEO_FBCON_ACCEL_H
+
+#include <linux/config.h>
+
+#ifdef MODULE
+#if defined(CONFIG_FBCON_ACCEL) || defined(CONFIG_FBCON_ACCEL_MODULE)
+#define FBCON_HAS_ACCEL
+#endif
+#else
+#if defined(CONFIG_FBCON_ACCEL)
+#define FBCON_HAS_ACCEL
+#endif
+#endif
+
+extern struct display_switch fbcon_accel;
+extern void fbcon_accel_setup(struct display *p);
+extern void fbcon_accel_bmove(struct display *p, int sy, int sx, int dy,
+			      int dx, int height, int width);
+extern void fbcon_accel_clear(struct vc_data *vc, struct display *p, int sy,
+			      int sx, int height, int width);
+extern void fbcon_accel_putc(struct vc_data *vc, struct display *p, int c,
+			     int yy, int xx);
+extern void fbcon_accel_putcs(struct vc_data *vc, struct display *p,
+			      const unsigned short *s, int count, int yy, int xx);
+extern void fbcon_accel_revc(struct display *p, int xx, int yy);
+extern void fbcon_accel_clear_margins(struct vc_data *vc, struct display *p,
+				      int bottom_only);
+
+#endif /* _VIDEO_FBCON_ACCEL_H */
diff -Nru a/drivers/video/fbcon-afb.c b/drivers/video/fbcon-afb.c
--- a/drivers/video/fbcon-afb.c	Sun Apr 28 20:10:30 2002
+++ b/drivers/video/fbcon-afb.c	Sun Apr 28 20:10:30 2002
@@ -174,8 +174,8 @@
     u_short i, j;
 
     if (sx == 0 && dx == 0 && width == p->next_line) {
-	src = p->screen_base+sy*fontheight(p)*width;
-	dest = p->screen_base+dy*fontheight(p)*width;
+	src = p->fb_info->screen_base+sy*fontheight(p)*width;
+	dest = p->fb_info->screen_base+dy*fontheight(p)*width;
 	i = p->var.bits_per_pixel;
 	do {
 	    fb_memmove(dest, src, height*fontheight(p)*width);
@@ -183,8 +183,8 @@
 	    dest += p->next_plane;
 	} while (--i);
     } else if (dy <= sy) {
-	src0 = p->screen_base+sy*fontheight(p)*p->next_line+sx;
-	dest0 = p->screen_base+dy*fontheight(p)*p->next_line+dx;
+	src0 = p->fb_info->screen_base+sy*fontheight(p)*p->next_line+sx;
+	dest0 = p->fb_info->screen_base+dy*fontheight(p)*p->next_line+dx;
 	i = p->var.bits_per_pixel;
 	do {
 	    src = src0;
@@ -199,8 +199,8 @@
 	    dest0 += p->next_plane;
 	} while (--i);
     } else {
-	src0 = p->screen_base+(sy+height)*fontheight(p)*p->next_line+sx;
-	dest0 = p->screen_base+(dy+height)*fontheight(p)*p->next_line+dx;
+	src0 = p->fb_info->screen_base+(sy+height)*fontheight(p)*p->next_line+sx;
+	dest0 = p->fb_info->screen_base+(dy+height)*fontheight(p)*p->next_line+dx;
 	i = p->var.bits_per_pixel;
 	do {
 	    src = src0;
@@ -224,7 +224,7 @@
     u_short i, j;
     int bg;
 
-    dest0 = p->screen_base+sy*fontheight(p)*p->next_line+sx;
+    dest0 = p->fb_info->screen_base+sy*fontheight(p)*p->next_line+sx;
 
     bg = attr_bgcol_ec(p,conp);
     i = p->var.bits_per_pixel;
@@ -250,7 +250,7 @@
     u_short i, j;
     int fg, bg;
 
-    dest0 = p->screen_base+yy*fontheight(p)*p->next_line+xx;
+    dest0 = p->fb_info->screen_base+yy*fontheight(p)*p->next_line+xx;
     cdat0 = p->fontdata+(c&p->charmask)*fontheight(p);
     fg = attr_fgcol(p,c);
     bg = attr_bgcol(p,c);
@@ -289,7 +289,7 @@
     u16 c1, c2, c3, c4;
     int fg0, bg0, fg, bg;
 
-    dest0 = p->screen_base+yy*fontheight(p)*p->next_line+xx;
+    dest0 = p->fb_info->screen_base+yy*fontheight(p)*p->next_line+xx;
     c1 = scr_readw(s);
     fg0 = attr_fgcol(p, c1);
     bg0 = attr_bgcol(p, c1);
@@ -382,7 +382,7 @@
     u_short i, j;
     int mask;
 
-    dest0 = p->screen_base+yy*fontheight(p)*p->next_line+xx;
+    dest0 = p->fb_info->screen_base+yy*fontheight(p)*p->next_line+xx;
     mask = p->fgcol ^ p->bgcol;
 
     /*
diff -Nru a/drivers/video/fbcon.c b/drivers/video/fbcon.c
--- a/drivers/video/fbcon.c	Sun Apr 28 20:10:31 2002
+++ b/drivers/video/fbcon.c	Sun Apr 28 20:10:31 2002
@@ -1564,8 +1564,8 @@
     if (!p->can_soft_blank) {
 	if (blank) {
 	    if (p->visual == FB_VISUAL_MONO01) {
-		if (p->screen_base)
-		    fb_memset255(p->screen_base,
+		if (p->fb_info->screen_base)
+		    fb_memset255(p->fb_info->screen_base,
 				 p->var.xres_virtual*p->var.yres_virtual*
 				 p->var.bits_per_pixel>>3);
 	    } else {
@@ -1590,7 +1590,8 @@
 	    return 1;
 	}
     }
-    (*info->blank)(blank, info);
+    if (info->fbops->fb_blank)	
+    	(*info->fbops->fb_blank)(blank, info);
     return 0;
 }
 
@@ -2123,7 +2124,7 @@
     struct display *p = &fb_display[fg_console]; /* draw to vt in foreground */
     int depth = p->var.bits_per_pixel;
     int line = p->next_line;
-    unsigned char *fb = p->screen_base;
+    unsigned char *fb = p->fb_info->screen_base;
     unsigned char *logo;
     unsigned char *dst, *src;
     int i, j, n, x1, y1, x;
diff -Nru a/drivers/video/fbgen.c b/drivers/video/fbgen.c
--- a/drivers/video/fbgen.c	Sun Apr 28 20:10:37 2002
+++ b/drivers/video/fbgen.c	Sun Apr 28 20:10:37 2002
@@ -22,9 +22,6 @@
 
 #include <video/fbcon.h>
 
-static int currcon = 0;
-
-
 /* ---- `Generic' versions of the frame buffer device operations ----------- */
 
 
@@ -59,6 +56,11 @@
     return fbhw->encode_fix(fix, &par, info2);
 }
 
+int gen_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
+{
+	*fix = info->fix;
+	return 0;
+}
 
 /**
  *	fbgen_get_var - get user defined part of display
@@ -87,6 +89,11 @@
     return 0;
 }
 
+int gen_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
+{
+	*var = info->var;
+	return 0;
+}
 
 /**
  *	fbgen_set_var - set the user defined part of display
@@ -107,7 +114,7 @@
     int err;
     int oldxres, oldyres, oldbpp, oldxres_virtual, oldyres_virtual, oldyoffset;
 
-    if ((err = fbgen_do_set_var(var, con == currcon, info2)))
+    if ((err = fbgen_do_set_var(var, con == info->currcon, info2)))
 	return err;
     if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
 	oldxres = fb_display[con].var.xres;
@@ -127,7 +134,7 @@
 		(*info->changevar)(con);
 	    if ((err = fb_alloc_cmap(&fb_display[con].cmap, 0, 0)))
 		return err;
-	    fbgen_install_cmap(con, info2);
+	    do_install_cmap(con, info);
 	}
     }
     var->activate = 0;
@@ -155,7 +162,7 @@
     struct fb_info_gen *info2 = (struct fb_info_gen *)info;
     struct fbgen_hwswitch *fbhw = info2->fbhw;
 
-    if (con == currcon)			/* current console ? */
+    if (con == info->currcon)			/* current console ? */
 	return fb_get_cmap(cmap, kspc, fbhw->getcolreg, info);
     else
 	if (fb_display[con].cmap.len)	/* non default colormap ? */
@@ -167,9 +174,14 @@
     return 0;
 }
 
+int gen_get_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info)
+{
+	fb_copy_cmap (&info->cmap, cmap, kspc ? 0 : 2);
+	return 0;
+}
 
 /**
- *	fbgen_set_cmap - set the colormap
+ *	gen_set_cmap - set the colormap
  *	@cmap: frame buffer colormap structure
  *	@kspc: boolean, 0 copy local, 1 get_user() function
  *	@con: virtual console number
@@ -182,22 +194,20 @@
  *
  */
 
-int fbgen_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-		   struct fb_info *info)
+int gen_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+		 struct fb_info *info)
 {
-    struct fb_info_gen *info2 = (struct fb_info_gen *)info;
-    struct fbgen_hwswitch *fbhw = info2->fbhw;
-    int err;
+    struct display *disp = (con < 0) ? info->disp: &fb_display[con]; 	
+    int err, size = disp->var.bits_per_pixel == 16 ? 32 : 256;
 
-    if (!fb_display[con].cmap.len) {	/* no colormap allocated ? */
-	int size = fb_display[con].var.bits_per_pixel == 16 ? 64 : 256;
-	if ((err = fb_alloc_cmap(&fb_display[con].cmap, size, 0)))
+    if (!disp->cmap.len) {	/* no colormap allocated ? */
+	if ((err = fb_alloc_cmap(&disp->cmap, size, 0)))
 	    return err;
     }
-    if (con == currcon)			/* current console ? */
-	return fb_set_cmap(cmap, kspc, fbhw->setcolreg, info);
+    if (con == info->currcon)			/* current console ? */
+	return fb_set_cmap(cmap, kspc, info);
     else
-	fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
+	fb_copy_cmap(cmap, &disp->cmap, kspc ? 0 : 1);
     return 0;
 }
 
@@ -231,7 +241,7 @@
 	yoffset < 0 ||
 	yoffset+fb_display[con].var.yres > fb_display[con].var.yres_virtual)
 	return -EINVAL;
-    if (con == currcon) {
+    if (con == info->currcon) {
 	if (fbhw->pan_display) {
 	    if ((err = fbhw->pan_display(var, info2)))
 		return err;
@@ -332,7 +342,7 @@
 
 
 /**
- *	fbgen_install_cmap - install the current colormap
+ *	do_install_cmap - install the current colormap
  *	@con: virtual console number
  *	@info: generic frame buffer info structure
  *
@@ -341,16 +351,15 @@
  *
  */
 
-void fbgen_install_cmap(int con, struct fb_info_gen *info)
+void do_install_cmap(int con, struct fb_info *info)
 {
-    struct fbgen_hwswitch *fbhw = info->fbhw;
-    if (con != currcon)
+    if (con != info->currcon)
 	return;
     if (fb_display[con].cmap.len)
-	fb_set_cmap(&fb_display[con].cmap, 1, fbhw->setcolreg, &info->info);
+	fb_set_cmap(&fb_display[con].cmap, 1, info);
     else {
 	int size = fb_display[con].var.bits_per_pixel == 16 ? 64 : 256;
-	fb_set_cmap(fb_default_cmap(size), 1, fbhw->setcolreg, &info->info);
+	fb_set_cmap(fb_default_cmap(size), 1, info);
     }
 }
 
@@ -399,13 +408,13 @@
     struct fbgen_hwswitch *fbhw = info2->fbhw;
 
     /* Do we have to save the colormap ? */
-    if (fb_display[currcon].cmap.len)
-	fb_get_cmap(&fb_display[currcon].cmap, 1, fbhw->getcolreg,
+    if (fb_display[info->currcon].cmap.len)
+	fb_get_cmap(&fb_display[info->currcon].cmap, 1, fbhw->getcolreg,
 		    &info2->info);
     fbgen_do_set_var(&fb_display[con].var, 1, info2);
-    currcon = con;
+    info->currcon = con;
     /* Install new colormap */
-    fbgen_install_cmap(con, info2);
+    do_install_cmap(con, info);
     return 0;
 }
 
@@ -419,7 +428,7 @@
  *
  */
 
-void fbgen_blank(int blank, struct fb_info *info)
+int fbgen_blank(int blank, struct fb_info *info)
 {
     struct fb_info_gen *info2 = (struct fb_info_gen *)info;
     struct fbgen_hwswitch *fbhw = info2->fbhw;
@@ -427,7 +436,7 @@
     struct fb_cmap cmap;
 
     if (fbhw->blank && !fbhw->blank(blank, info2))
-	return;
+	return 1;
     if (blank) {
 	memset(black, 0, 16*sizeof(u16));
 	cmap.red = black;
@@ -436,8 +445,27 @@
 	cmap.transp = NULL;
 	cmap.start = 0;
 	cmap.len = 16;
-	fb_set_cmap(&cmap, 1, fbhw->setcolreg, info);
+	fb_set_cmap(&cmap, 1, info);
     } else
-	fbgen_install_cmap(currcon, info2);
+	do_install_cmap(info->currcon, info);
+    return 0;	
 }
+
+EXPORT_SYMBOL(fbgen_get_fix);
+EXPORT_SYMBOL(gen_get_fix);
+EXPORT_SYMBOL(fbgen_get_var);
+EXPORT_SYMBOL(gen_get_var);
+EXPORT_SYMBOL(fbgen_set_var);
+EXPORT_SYMBOL(fbgen_get_cmap);
+EXPORT_SYMBOL(gen_get_cmap);
+EXPORT_SYMBOL(gen_set_cmap);
+EXPORT_SYMBOL(fbgen_pan_display);
+/* helper functions */
+EXPORT_SYMBOL(fbgen_do_set_var);
+EXPORT_SYMBOL(fbgen_set_disp);
+EXPORT_SYMBOL(do_install_cmap);
+EXPORT_SYMBOL(fbgen_update_var);
+EXPORT_SYMBOL(fbgen_switch);
+EXPORT_SYMBOL(fbgen_blank);
+
 MODULE_LICENSE("GPL");
diff -Nru a/drivers/video/fbmem.c b/drivers/video/fbmem.c
--- a/drivers/video/fbmem.c	Sun Apr 28 20:10:30 2002
+++ b/drivers/video/fbmem.c	Sun Apr 28 20:10:30 2002
@@ -382,7 +382,7 @@
 	if (count) {
 	    char *base_addr;
 
-	    base_addr = info->disp->screen_base;
+	    base_addr = info->screen_base;
 	    count -= copy_to_user(buf, base_addr+p, count);
 	    if (!count)
 		return -EFAULT;
@@ -418,7 +418,7 @@
 	if (count) {
 	    char *base_addr;
 
-	    base_addr = info->disp->screen_base;
+	    base_addr = info->screen_base;
 	    count -= copy_from_user(base_addr+p, buf, count);
 	    *ppos += count;
 	    err = -EFAULT;
@@ -522,10 +522,9 @@
 			set_con2fb_map(i, con2fb.framebuffer);
 		return 0;
 	case FBIOBLANK:
-		if (info->blank == 0)
+		if (fb->fb_blank == 0)
 			return -EINVAL;
-		(*info->blank)(arg, info);
-		return 0;
+		return fb->fb_blank(arg, info);
 	default:
 		if (fb->fb_ioctl == NULL)
 			return -EINVAL;
diff -Nru a/drivers/video/fm2fb.c b/drivers/video/fm2fb.c
--- a/drivers/video/fm2fb.c	Sun Apr 28 20:10:35 2002
+++ b/drivers/video/fm2fb.c	Sun Apr 28 20:10:35 2002
@@ -144,7 +144,6 @@
 static unsigned long fm2fb_reg_phys;
 static volatile unsigned char *fm2fb_reg;
 
-static int currcon = 0;
 static struct display disp;
 static struct fb_info fb_info;
 static struct { u_char red, green, blue, pad; } palette[16];
@@ -189,9 +188,9 @@
 			 struct fb_info *info);
 static int fm2fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			  struct fb_info *info);
-static int fm2fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-			  struct fb_info *info);
-
+static int fm2fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+			   u_int transp, struct fb_info *info);
+static int fm2fb_blank(int blank, struct fb_info *info);
 
     /*
      *  Interface to the low level console driver
@@ -200,8 +199,6 @@
 int fm2fb_init(void);
 static int fm2fbcon_switch(int con, struct fb_info *info);
 static int fm2fbcon_updatevar(int con, struct fb_info *info);
-static void fm2fbcon_blank(int blank, struct fb_info *info);
-
 
     /*
      *  Internal routines
@@ -209,10 +206,6 @@
 
 static int fm2fb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
 			   u_int *transp, struct fb_info *info);
-static int fm2fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-			   u_int transp, struct fb_info *info);
-static void do_install_cmap(int con, struct fb_info *info);
-
 
 static struct fb_ops fm2fb_ops = {
 	owner:		THIS_MODULE,
@@ -220,7 +213,9 @@
 	fb_get_var:	fm2fb_get_var,
 	fb_set_var:	fm2fb_set_var,
 	fb_get_cmap:	fm2fb_get_cmap,
-	fb_set_cmap:	fm2fb_set_cmap,
+	fb_set_cmap:	gen_set_cmap,
+	fb_setcolreg:	fm2fb_setcolreg,
+	fb_blank:	fm2fb_blank,
 };
 
     /*
@@ -291,7 +286,7 @@
 static int fm2fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			  struct fb_info *info)
 {
-    if (con == currcon) /* current console? */
+    if (con == info->currcon) /* current console? */
 	return fb_get_cmap(cmap, kspc, fm2fb_getcolreg, info);
     else if (fb_display[con].cmap.len) /* non default colormap? */
 	fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
@@ -301,28 +296,6 @@
 }
 
     /*
-     *  Set the Colormap
-     */
-
-static int fm2fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-			  struct fb_info *info)
-{
-    int err;
-
-    if (!fb_display[con].cmap.len) {	/* no colormap allocated? */
-	if ((err = fb_alloc_cmap(&fb_display[con].cmap, 256, 0)))
-	    return err;
-    }
-    if (con == currcon) {		/* current console? */
-	err = fb_set_cmap(cmap, kspc, fm2fb_setcolreg, info);
-	return err;
-    } else
-	fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
-    return 0;
-}
-
-
-    /*
      *  Initialisation
      */
 
@@ -361,7 +334,7 @@
 	    for (x = 0; x < 96; x++) *ptr++ = 0x0000ff;	/* blue */
 	    for (x = 0; x < 96; x++) *ptr++ = 0x000000;	/* black */
 	}
-	fm2fbcon_blank(0, NULL);
+	fm2fb_blank(0, NULL);
 
 	if (fm2fb_mode == -1)
 	    fm2fb_mode = FM2FB_MODE_PAL;
@@ -403,12 +376,12 @@
 	strcpy(fb_info.modename, fb_fix.id);
 	fb_info.node = NODEV;
 	fb_info.fbops = &fm2fb_ops;
+	fb_info.currcon = -1;
 	fb_info.disp = &disp;
 	fb_info.fontname[0] = '\0';
 	fb_info.changevar = NULL;
 	fb_info.switch_con = &fm2fbcon_switch;
 	fb_info.updatevar = &fm2fbcon_updatevar;
-	fb_info.blank = &fm2fbcon_blank;
 	fb_info.flags = FBINFO_FLAG_DEFAULT;
 
 	fm2fb_set_var(&fb_var, -1, &fb_info);
@@ -443,10 +416,10 @@
 static int fm2fbcon_switch(int con, struct fb_info *info)
 {
     /* Do we have to save the colormap? */
-    if (fb_display[currcon].cmap.len)
-	fb_get_cmap(&fb_display[currcon].cmap, 1, fm2fb_getcolreg, info);
+    if (fb_display[info->currcon].cmap.len)
+	fb_get_cmap(&fb_display[info->currcon].cmap, 1, fm2fb_getcolreg, info);
 
-    currcon = con;
+    info->currcon = con;
     /* Install new colormap */
     do_install_cmap(con, info);
     return 0;
@@ -466,13 +439,14 @@
      *  Blank the display.
      */
 
-static void fm2fbcon_blank(int blank, struct fb_info *info)
+static int fm2fb_blank(int blank, struct fb_info *info)
 {
     unsigned char t = FRAMEMASTER_ROM;
 
     if (!blank)
 	t |= FRAMEMASTER_ENABLE | FRAMEMASTER_NOLACE;
     fm2fb_reg[0] = t;
+    return 0;	
 }
 
     /*
@@ -515,17 +489,6 @@
     fbcon_cfb32_cmap[regno] = (red << 16) | (green << 8) | blue;
 #endif
     return 0;
-}
-
-
-static void do_install_cmap(int con, struct fb_info *info)
-{
-    if (con != currcon)
-	return;
-    if (fb_display[con].cmap.len)
-	fb_set_cmap(&fb_display[con].cmap, 1, fm2fb_setcolreg, info);
-    else
-	fb_set_cmap(fb_default_cmap(256), 1, fm2fb_setcolreg, info);
 }
 
 MODULE_LICENSE("GPL");
diff -Nru a/drivers/video/g364fb.c b/drivers/video/g364fb.c
--- a/drivers/video/g364fb.c	Sun Apr 28 20:10:31 2002
+++ b/drivers/video/g364fb.c	Sun Apr 28 20:10:31 2002
@@ -77,7 +77,6 @@
 #define MON_ID_REG 	0xe4100000 	/* unused */
 #define RESET_REG 	0xe4180000  	/* Write only */
 
-static int currcon = 0;
 static struct display disp;
 static struct fb_info fb_info;
 static struct { u_char red, green, blue, pad; } palette[256];
@@ -99,8 +98,9 @@
 			      struct fb_info *info);
 static int g364fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			   struct fb_info *info);
-static int g364fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-			   struct fb_info *info);
+static int g364fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+			    u_int transp, struct fb_info *info);
+static int g364fb_blank(int blank, struct fb_info *info);
 
 
 /*
@@ -109,18 +109,12 @@
 int g364fb_init(void);
 static int g364fbcon_switch(int con, struct fb_info *info);
 static int g364fbcon_updatevar(int con, struct fb_info *info);
-static void g364fbcon_blank(int blank, struct fb_info *info);
-
 
 /*
  *  Internal routines
  */
 static int g364fb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
 			    u_int *transp, struct fb_info *info);
-static int g364fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-			    u_int transp, struct fb_info *info);
-static void do_install_cmap(int con, struct fb_info *info);
-
 
 static struct fb_ops g364fb_ops = {
 	owner:		THIS_MODULE,
@@ -128,8 +122,10 @@
 	fb_get_var:	g364fb_get_var,
 	fb_set_var:	g364fb_set_var,
 	fb_get_cmap:	g364fb_get_cmap,
-	fb_set_cmap:	g364fb_set_cmap,
+	fb_set_cmap:	gen_set_cmap,
+	fb_setcolreg:	g364fb_setcolreg,
 	fb_pan_display:	g364fb_pan_display,
+	fb_blank:	g364fb_blank,
 };
 
 
@@ -241,7 +237,7 @@
 static int g364fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			   struct fb_info *info)
 {
-    if (con == currcon) /* current console? */
+    if (con == info->currcon) /* current console? */
 	return fb_get_cmap(cmap, kspc, g364fb_getcolreg, info);
     else if (fb_display[con].cmap.len) /* non default colormap? */
 	fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
@@ -252,27 +248,6 @@
 }
 
 /*
- *  Set the Colormap
- */
-static int g364fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-			   struct fb_info *info)
-{
-    int err;
-
-    if (!fb_display[con].cmap.len) {	/* no colormap allocated? */
-	if ((err = fb_alloc_cmap(&fb_display[con].cmap,
-				 1<<fb_display[con].var.bits_per_pixel, 0)))
-	    return err;
-    }
-    if (con == currcon) {		/* current console? */
-	return fb_set_cmap(cmap, kspc, g364fb_setcolreg, info);
-    } else
-	fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
-    return 0;
-}
-
-
-/*
  *  Initialisation
  */
 int __init g364fb_init(void)
@@ -366,7 +341,6 @@
     disp.cmap.start = 0;
     disp.cmap.len = 0;
     disp.cmap.red = disp.cmap.green = disp.cmap.blue = disp.cmap.transp = NULL;
-    disp.screen_base = (char *)G364_MEM_BASE; /* virtual kernel address */
     disp.visual = fb_fix.visual;
     disp.type = fb_fix.type;
     disp.type_aux = fb_fix.type_aux;
@@ -380,12 +354,13 @@
     strcpy(fb_info.modename, fb_fix.id);
     fb_info.node = NODEV;
     fb_info.fbops = &g364fb_ops;
+    fb_info.screen_base = (char *)G364_MEM_BASE; /* virtual kernel address */
     fb_info.disp = &disp;
+    fb_info.currcon = -1;	
     fb_info.fontname[0] = '\0';
     fb_info.changevar = NULL;
     fb_info.switch_con = &g364fbcon_switch;
     fb_info.updatevar = &g364fbcon_updatevar;
-    fb_info.blank = &g364fbcon_blank;
     fb_info.flags = FBINFO_FLAG_DEFAULT;
 
     g364fb_set_var(&fb_var, -1, &fb_info);
@@ -402,10 +377,10 @@
 static int g364fbcon_switch(int con, struct fb_info *info)
 {
     /* Do we have to save the colormap? */
-    if (fb_display[currcon].cmap.len)
-	fb_get_cmap(&fb_display[currcon].cmap, 1, g364fb_getcolreg, info);
+    if (fb_display[info->currcon].cmap.len)
+	fb_get_cmap(&fb_display[info->currcon].cmap, 1, g364fb_getcolreg, info);
 
-    currcon = con;
+    info->currcon = con;
     /* Install new colormap */
     do_install_cmap(con, info);
     g364fbcon_updatevar(con, info);    
@@ -417,8 +392,8 @@
  */
 static int g364fbcon_updatevar(int con, struct fb_info *info)
 {
-    if (con == currcon) {
-	struct fb_var_screeninfo *var = &fb_display[currcon].var;
+    if (con == info->currcon) {
+	struct fb_var_screeninfo *var = &fb_display[info->currcon].var;
 
 	/* hardware scrolling */
 	*(unsigned int *)TOP_REG = var->yoffset * var->xres;	
@@ -429,12 +404,13 @@
 /*
  *  Blank the display.
  */
-static void g364fbcon_blank(int blank, struct fb_info *info)
+static int g364fb_blank(int blank, struct fb_info *info)
 {
     if (blank)
 	*(unsigned int *) CTLA_REG |= FORCE_BLANK;	
     else
 	*(unsigned int *) CTLA_REG &= ~FORCE_BLANK;	
+    return 0;	
 }
 
 /*
@@ -474,18 +450,6 @@
     ptr[regno << 1] = (red << 16) | (green << 8) | blue;
 
     return 0;
-}
-
-
-static void do_install_cmap(int con, struct fb_info *info)
-{
-    if (con != currcon)
-	return;
-    if (fb_display[con].cmap.len)
-	fb_set_cmap(&fb_display[con].cmap, 1, g364fb_setcolreg, info);
-    else
-	fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), 1,
-		    g364fb_setcolreg, info);
 }
 
 MODULE_LICENSE("GPL");
diff -Nru a/drivers/video/hgafb.c b/drivers/video/hgafb.c
--- a/drivers/video/hgafb.c	Sun Apr 28 20:10:33 2002
+++ b/drivers/video/hgafb.c	Sun Apr 28 20:10:33 2002
@@ -156,7 +156,6 @@
 static struct display disp;
 
 /* Don't assume that tty1 will be the initial current console. */
-static int currcon = -1; 
 static int release_io_port = 0;
 static int release_io_ports = 0;
 
@@ -511,7 +510,7 @@
 }
 	
 /**
- *	hga_setcolreg - set color registers
+ *	hgafb_setcolreg - set color registers
  *	@regno:register index to set
  *	@red:red value, unused
  *	@green:green value, unused
@@ -524,8 +523,8 @@
  *	A zero is returned on success and 1 for failure.
  */
 
-static int hga_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-			 u_int transp, struct fb_info *info)
+static int hgafb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+			   u_int transp, struct fb_info *info)
 {
 	if (regno > 1)
 		return 1;
@@ -533,25 +532,6 @@
 }
 
 /**
- *	hga_set_cmap - set the colormap
- *	@cmap:struct fb_cmap to set
- *	@kspc:called from kernel space?
- *	@con:unused
- *	@info:pointer to fb_info object containing info for current hga board
- *
- *	This wrapper function passes it's input parameters to fb_set_cmap().
- *	Callback function hga_setcolreg() is used to set the color registers.
- */
-
-int hga_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-                 struct fb_info *info)
-{
-	CHKINFO(-EINVAL);
-	DPRINTK("hga_set_cmap: con:%d\n", con);
-	return fb_set_cmap(cmap, kspc, hga_setcolreg, info);
-}
-
-/**
  *	hga_pan_display - pan or wrap the display
  *	@var:contains new xoffset, yoffset and vmode values
  *	@con:unused
@@ -592,15 +572,38 @@
 	return 0;
 }
 
-    
+/**
+ *	hgafb_blank - (un)blank the screen
+ *	@blank_mode:blanking method to use
+ *	@info:unused
+ *	
+ *	Blank the screen if blank_mode != 0, else unblank. 
+ *	Implements VESA suspend and powerdown modes on hardware that supports 
+ *	disabling hsync/vsync:
+ *		@blank_mode == 2 means suspend vsync,
+ *		@blank_mode == 3 means suspend hsync,
+ *		@blank_mode == 4 means powerdown.
+ */
+
+static int hgafb_blank(int blank_mode, struct fb_info *info)
+{
+	CHKINFO( );
+	DPRINTK("hgafb_blank: blank_mode:%d, info:%x, fb_info:%x\n", blank_mode, (unsigned)info, (unsigned)&fb_info);
+
+	hga_blank(blank_mode);
+	return 0;
+}
+
 static struct fb_ops hgafb_ops = {
 	owner:		THIS_MODULE,
 	fb_get_fix:	hga_get_fix,
 	fb_get_var:	hga_get_var,
 	fb_set_var:	hga_set_var,
 	fb_get_cmap:	hga_get_cmap,
-	fb_set_cmap:	hga_set_cmap,
+	fb_set_cmap:	gen_set_cmap,
+	fb_setcolreg:	hgafb_setcolreg,
 	fb_pan_display:	hga_pan_display,
+	fb_blank:	hgafb_blank,
 };
 		
 
@@ -625,15 +628,15 @@
 static int hgafbcon_switch(int con, struct fb_info *info)
 {
 	CHKINFO(-EINVAL);
-	DPRINTK("hgafbcon_switch: currcon:%d, con:%d, info:%x, fb_info:%x\n", currcon, con, (unsigned)info, (unsigned)&fb_info);
+	DPRINTK("hgafbcon_switch: currcon:%d, con:%d, info:%x, fb_info:%x\n", info->currcon, con, (unsigned)info, (unsigned)&fb_info);
 
 	/* Save the colormap and video mode */
 #if 0	/* Not necessary in hgafb, we use fixed colormap */
-	fb_copy_cmap(&info->cmap, &fb_display[currcon].cmap, 0);
+	fb_copy_cmap(&info->cmap, &fb_display[info->currcon].cmap, 0);
 #endif
 
-	if (currcon != -1) /* this check is absolute necessary! */
-		memcpy(&fb_display[currcon].var, &info->var,
+	if (info->currcon != -1) /* this check is absolute necessary! */
+		memcpy(&fb_display[info->currcon].var, &info->var,
 				sizeof(struct fb_var_screeninfo));
 
 	/* Install a new colormap and change the video mode. By default fbcon
@@ -642,13 +645,13 @@
 	 */
 #if 0
 	fb_copy_cmap(&fb_display[con].cmap, &info->cmap, 0);
-	fb_set_cmap(&info->cmap, 1, hga_setcolreg, info);
+	fb_set_cmap(&info->cmap, 1, info);
 #endif
 
 	memcpy(&info->var, &fb_display[con].var,
 			sizeof(struct fb_var_screeninfo));
 	/* hga_set_var(&info->var, con, &fb_info); is it necessary? */
-	currcon = con;
+	info->currcon = con;
 
 	/* Hack to work correctly with XF86_Mono */
 	hga_gfx_mode();
@@ -673,28 +676,6 @@
 	return (con < 0) ? -EINVAL : hga_pan_display(&fb_display[con].var, con, info);
 }
 
-/**
- *	hgafbcon_blank - (un)blank the screen
- *	@blank_mode:blanking method to use
- *	@info:unused
- *	
- *	Blank the screen if blank_mode != 0, else unblank. 
- *	Implements VESA suspend and powerdown modes on hardware that supports 
- *	disabling hsync/vsync:
- *		@blank_mode == 2 means suspend vsync,
- *		@blank_mode == 3 means suspend hsync,
- *		@blank_mode == 4 means powerdown.
- */
-
-static void hgafbcon_blank(int blank_mode, struct fb_info *info)
-{
-	CHKINFO( );
-	DPRINTK("hga_blank: blank_mode:%d, info:%x, fb_info:%x\n", blank_mode, (unsigned)info, (unsigned)&fb_info);
-
-	hga_blank(blank_mode);
-}
-
-
 /* ------------------------------------------------------------------------- */
     
 	/*
@@ -721,8 +702,6 @@
 	hga_fix.smem_len = hga_vram_len;
 
 	disp.var = hga_default_var;
-/*	disp.cmap = ???; */
-	disp.screen_base = (char*)hga_fix.smem_start;
 	disp.visual = hga_fix.visual;
 	disp.type = hga_fix.type;
 	disp.type_aux = hga_fix.type_aux;
@@ -744,7 +723,6 @@
 	strcpy (fb_info.modename, hga_fix.id);
 	fb_info.node = NODEV;
 	fb_info.flags = FBINFO_FLAG_DEFAULT;
-/*	fb_info.open = ??? */
 	fb_info.var = hga_default_var;
 	fb_info.fix = hga_fix;
 	fb_info.monspecs.hfmin = 0;
@@ -755,12 +733,10 @@
 	fb_info.fbops = &hgafb_ops;
 	fb_info.screen_base = (char *)hga_fix.smem_start;
 	fb_info.disp = &disp;
-/*	fb_info.display_fg = ??? */
-/*	fb_info.fontname initialized later */
+	fb_info.currcon = 1;
 	fb_info.changevar = NULL;
 	fb_info.switch_con = hgafbcon_switch;
 	fb_info.updatevar = hgafbcon_updatevar;
-	fb_info.blank = hgafbcon_blank;
 	fb_info.pseudo_palette = NULL; /* ??? */
 	fb_info.par = NULL;
 
diff -Nru a/drivers/video/hitfb.c b/drivers/video/hitfb.c
--- a/drivers/video/hitfb.c	Sun Apr 28 20:10:31 2002
+++ b/drivers/video/hitfb.c	Sun Apr 28 20:10:31 2002
@@ -294,7 +294,6 @@
 {
     const struct hitfb_par *par = fb_par;
 
-    disp->screen_base = (void *)fb_info.hit_videobase;
     disp->scrollmode = SCROLL_YREDRAW;
 
     switch(((struct hitfb_par *)par)->bpp) {
@@ -323,7 +322,6 @@
     hitfb_get_par,
     hitfb_set_par,
     hitfb_getcolreg,
-    hitfb_setcolreg,
     hitfb_pan_display,
     hitfb_blank,
     hitfb_set_disp
@@ -337,7 +335,9 @@
     fb_set_var:		fbgen_set_var,
     fb_get_cmap:	fbgen_get_cmap,
     fb_set_cmap:	fbgen_set_cmap,
+    fb_setcolreg:	hitfb_setcolreg,	
     fb_pan_display:	fbgen_pan_display,
+    fb_blank:		fbgen_blank,	
 };
 
 
@@ -348,19 +348,20 @@
     fb_info.gen.info.flags = FBINFO_FLAG_DEFAULT;
     fb_info.gen.info.fbops = &hitfb_ops;
     fb_info.gen.info.disp = &fb_info.disp;
+    fb_info.gen.info.currcon = 1;	
     fb_info.gen.info.changevar = NULL;
     fb_info.gen.info.switch_con = &fbgen_switch;
     fb_info.gen.info.updatevar = &fbgen_update_var;
-    fb_info.gen.info.blank = &fbgen_blank;
     fb_info.gen.parsize = sizeof(struct hitfb_par);
     fb_info.gen.fbhw = &hitfb_switch;
     fb_info.gen.fbhw->detect();
+    fb_info.screen_base = (void *)fb_info.hit_videobase;
     
     fbgen_get_var(&fb_info.disp.var, -1, &fb_info.gen.info);
     fb_info.disp.var.activate = FB_ACTIVATE_NOW;
     fbgen_do_set_var(&fb_info.disp.var, 1, &fb_info.gen);
     fbgen_set_disp(-1, &fb_info.gen);
-    fbgen_install_cmap(0, &fb_info.gen);
+    do_install_cmap(0, &fb_info.gen);
     
     if(register_framebuffer(&fb_info.gen.info)<0) return -EINVAL;
     
diff -Nru a/drivers/video/hpfb.c b/drivers/video/hpfb.c
--- a/drivers/video/hpfb.c	Sun Apr 28 20:10:37 2002
+++ b/drivers/video/hpfb.c	Sun Apr 28 20:10:37 2002
@@ -74,7 +74,6 @@
 {
 };
 
-static int currcon = 0;
 struct hpfb_par current_par;
 
 static void hpfb_encode_var(struct fb_var_screeninfo *var, 
@@ -226,17 +225,10 @@
 static int hpfb_switch(int con, struct fb_info *info)
 {
 	do_fb_set_var(&fb_display[con].var,1);
-	currcon=con;
+	info->currcon = con;
 	return 0;
 }
 
-/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */
-
-static void hpfb_blank(int blank, struct fb_info *info)
-{
-	/* Not supported */
-}
-
 static void hpfb_set_disp(int con)
 {
 	struct fb_fix_screeninfo fix;
@@ -249,7 +241,6 @@
 
 	hpfb_get_fix(&fix, con, 0);
 
-	display->screen_base = fix.smem_start;
 	display->visual = fix.visual;
 	display->type = fix.type;
 	display->type_aux = fix.type_aux;
@@ -330,10 +321,11 @@
 	fb_info.changevar = NULL;
 	fb_info.node = NODEV;
 	fb_info.fbops = &hpfb_ops;
+	fb_info.screen_base = fb_start;
 	fb_info.disp = &disp;
+	fb_info.currcon = -1;
 	fb_info.switch_con = &hpfb_switch;
 	fb_info.updatevar = &fb_update_var;
-	fb_info.blank = &hpfb_blank;
 	fb_info.flags = FBINFO_FLAG_DEFAULT;
 	do_fb_set_var(&hpfb_defined, 1);
 
diff -Nru a/drivers/video/igafb.c b/drivers/video/igafb.c
--- a/drivers/video/igafb.c	Sun Apr 28 20:10:37 2002
+++ b/drivers/video/igafb.c	Sun Apr 28 20:10:37 2002
@@ -83,7 +83,6 @@
     struct pci_mmap_map *mmap_map;
     struct { u_short blue, green, red, pad; } palette[256];
     int video_cmap_len;
-    int currcon;
     struct display disp;
     struct display_switch dispsw; 
     union {
@@ -331,9 +330,9 @@
 	return 0;
 }
 
-static int iga_setcolreg(unsigned regno, unsigned red, unsigned green,
-                          unsigned blue, unsigned transp,
-                          struct fb_info *fb_info)
+static int igafb_setcolreg(unsigned regno, unsigned red, unsigned green,
+                           unsigned blue, unsigned transp,
+                           struct fb_info *fb_info)
 {
         /*
          *  Set a single color register. The values supplied are
@@ -383,26 +382,12 @@
 	return 0;
 }
 
-static void do_install_cmap(int con, struct fb_info *fb_info)
-{
-	struct fb_info_iga *info = (struct fb_info_iga*) fb_info;
-
-        if (con != info->currcon)
-                return;
-        if (fb_display[con].cmap.len)
-                fb_set_cmap(&fb_display[con].cmap, 1,
-                            iga_setcolreg, &info->fb_info);
-        else
-                fb_set_cmap(fb_default_cmap(info->video_cmap_len), 1, 
-			    iga_setcolreg, &info->fb_info);
-}
-
 static int igafb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
                            struct fb_info *fb_info)
 {
 	struct fb_info_iga *info = (struct fb_info_iga*) fb_info;
 	
-        if (con == info->currcon) /* current console? */
+        if (con == fb_info->currcon) /* current console? */
                 return fb_get_cmap(cmap, kspc, iga_getcolreg, &info->fb_info);
         else if (fb_display[con].cmap.len) /* non default colormap? */
                 fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
@@ -412,25 +397,6 @@
         return 0;
 }
 
-static int igafb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-	                  struct fb_info *info)
-{
-        int err;
-	struct fb_info_iga *fb = (struct fb_info_iga*) info;
-
-        if (!fb_display[con].cmap.len) {        /* no colormap allocated? */
-                err = fb_alloc_cmap(&fb_display[con].cmap,
-				    fb->video_cmap_len,0);
-                if (err)
-                        return err;
-        }
-        if (con == fb->currcon)                     /* current console? */
-                return fb_set_cmap(cmap, kspc, iga_setcolreg, info);
-        else
-                fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
-        return 0;
-}
-
 /*
  * Framebuffer option structure
  */
@@ -440,7 +406,8 @@
 	fb_get_var:	igafb_get_var,
 	fb_set_var:	igafb_set_var,
 	fb_get_cmap:	igafb_get_cmap,
-	fb_set_cmap:	igafb_set_cmap,
+	fb_set_cmap:	gen_set_cmap,
+	fb_setcolreg:	igafb_setcolreg,
 #ifdef __sparc__
 	fb_mmap:	igafb_mmap,
 #endif
@@ -460,7 +427,6 @@
         igafb_get_fix(&fix, con, &info->fb_info);
 
         memset(display, 0, sizeof(struct display));
-        display->screen_base = info->frame_buffer;
         display->visual = fix.visual;
         display->type = fix.type;
         display->type_aux = fix.type_aux;
@@ -514,27 +480,17 @@
 	struct fb_info_iga *info = (struct fb_info_iga*) fb_info;
 
         /* Do we have to save the colormap? */
-        if (fb_display[info->currcon].cmap.len)
-                fb_get_cmap(&fb_display[info->currcon].cmap, 1,
+        if (fb_display[fb_info->currcon].cmap.len)
+                fb_get_cmap(&fb_display[fb_info->currcon].cmap, 1,
                             iga_getcolreg, fb_info);
 
-	info->currcon = con;
+	fb_info->currcon = con;
 	/* Install new colormap */
 	do_install_cmap(con, fb_info);
 	igafb_update_var(con, fb_info);
         return 1;
 }
 
-
-
-/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */
-
-static void igafb_blank(int blank, struct fb_info *info)
-{
-        /* Not supported */
-}
-
-
 static int __init iga_init(struct fb_info_iga *info)
 {
         char vramsz = iga_inb(info, IGA_EXT_CNTRL, IGA_IDX_EXT_BUS_CNTL) 
@@ -571,11 +527,12 @@
 	info->fb_info.node = NODEV;
 	info->fb_info.fbops = &igafb_ops;
 	info->fb_info.disp = &info->disp;
+        info->fb_info.screen_base = info->frame_buffer;
+	info->fb_info.currcon = -1;
 	strcpy(info->fb_info.fontname, fontname);
 	info->fb_info.changevar = NULL;
 	info->fb_info.switch_con = &igafb_switch;
 	info->fb_info.updatevar = &igafb_update_var;
-	info->fb_info.blank = &igafb_blank;
 	info->fb_info.flags=FBINFO_FLAG_DEFAULT;
 
 	igafb_set_disp(-1, info);
diff -Nru a/drivers/video/imsttfb.c b/drivers/video/imsttfb.c
--- a/drivers/video/imsttfb.c	Sun Apr 28 20:10:36 2002
+++ b/drivers/video/imsttfb.c	Sun Apr 28 20:10:36 2002
@@ -378,7 +378,6 @@
 #define CURSOR_BLINK_RATE	20
 #define CURSOR_DRAW_DELAY	2
 
-static int currcon = 0;
 static int inverse = 0;
 static char fontname[40] __initdata = { 0 };
 static char curblink __initdata = 1;
@@ -1241,7 +1240,7 @@
 		   u_int transp, struct fb_info *info)
 {
 	struct fb_info_imstt *p = (struct fb_info_imstt *)info;
-	u_int bpp = fb_display[currcon].var.bits_per_pixel;
+	u_int bpp = fb_display[info->currcon].var.bits_per_pixel;
 
 	if (regno > 255)
 		return 1;
@@ -1269,7 +1268,7 @@
 		switch (bpp) {
 #ifdef FBCON_HAS_CFB16
 			case 16:
-				p->fbcon_cmap.cfb16[regno] = (regno << (fb_display[currcon].var.green.length == 5 ? 10 : 11)) | (regno << 5) | regno;
+				p->fbcon_cmap.cfb16[regno] = (regno << (fb_display[info->currcon].var.green.length == 5 ? 10 : 11)) | (regno << 5) | regno;
 				break;
 #endif
 #ifdef FBCON_HAS_CFB24
@@ -1289,17 +1288,6 @@
 	return 0;
 }
 
-static void
-do_install_cmap (int con, struct fb_info *info)
-{
-	if (fb_display[con].cmap.len)
-		fb_set_cmap(&fb_display[con].cmap, 1, imsttfb_setcolreg, info);
-	else {
-		u_int size = fb_display[con].var.bits_per_pixel == 16 ? 32 : 256;
-		fb_set_cmap(fb_default_cmap(size), 1, imsttfb_setcolreg, info);
-	}
-}
-
 static int
 imsttfb_get_fix (struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
 {
@@ -1414,7 +1402,6 @@
 
 	disp->visual = disp->var.bits_per_pixel == 8 ? FB_VISUAL_PSEUDOCOLOR
 					 	     : FB_VISUAL_DIRECTCOLOR;
-	disp->screen_base = (__u8 *)p->frame_buffer;
 	disp->visual = p->fix.visual;
 	disp->type = p->fix.type;
 	disp->type_aux = p->fix.type_aux;
@@ -1493,7 +1480,7 @@
 	if (info->changevar)
 		(*info->changevar)(con);
 
-	if (con == currcon) {
+	if (con == info->currcon) {
 		if (oldgreenlen != disp->var.green.length) {
 			if (disp->var.green.length == 6)
 				set_565(p);
@@ -1529,7 +1516,7 @@
 
 	disp->var.xoffset = var->xoffset;
 	disp->var.yoffset = var->yoffset;
-	if (con == currcon)
+	if (con == info->currcon)
 		set_offset(disp, p);
 
 	return 0;
@@ -1538,7 +1525,7 @@
 static int
 imsttfb_get_cmap (struct fb_cmap *cmap, int kspc, int con, struct fb_info *info)
 {
-	if (con == currcon)	/* current console? */
+	if (con == info->currcon)	/* current console? */
 		return fb_get_cmap(cmap, kspc, imsttfb_getcolreg, info);
 	else if (fb_display[con].cmap.len)	/* non default colormap? */
 		fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
@@ -1550,21 +1537,57 @@
 	return 0;
 }
 
-static int
-imsttfb_set_cmap (struct fb_cmap *cmap, int kspc, int con, struct fb_info *info)
+static int 
+imsttfb_blank (int blank, struct fb_info *info)
 {
-	int err;
+	struct fb_info_imstt *p = (struct fb_info_imstt *)info;
+	__u32 ctrl;
 
-	if (!fb_display[con].cmap.len) {	/* no colormap allocated? */
-		int size = fb_display[con].var.bits_per_pixel == 16 ? 32 : 256;
-		if ((err = fb_alloc_cmap(&fb_display[con].cmap, size, 0)))
-			return err;
+	ctrl = in_le32(&p->dc_regs[STGCTL]);
+	if (blank > 0) {
+		switch (blank - 1) {
+			case VESA_NO_BLANKING:
+			case VESA_POWERDOWN:
+				ctrl &= ~0x00000380;
+				if (p->ramdac == IBM) {
+					p->cmap_regs[PIDXHI] = 0;	eieio();
+					p->cmap_regs[PIDXLO] = MISCTL2;	eieio();
+					p->cmap_regs[PIDXDATA] = 0x55;	eieio();
+					p->cmap_regs[PIDXLO] = MISCTL1;	eieio();
+					p->cmap_regs[PIDXDATA] = 0x11;	eieio();
+					p->cmap_regs[PIDXLO] = SYNCCTL;	eieio();
+					p->cmap_regs[PIDXDATA] = 0x0f;	eieio();
+					p->cmap_regs[PIDXLO] = PWRMNGMT;eieio();
+					p->cmap_regs[PIDXDATA] = 0x1f;	eieio();
+					p->cmap_regs[PIDXLO] = CLKCTL;	eieio();
+					p->cmap_regs[PIDXDATA] = 0xc0;
+				}
+				break;
+			case VESA_VSYNC_SUSPEND:
+				ctrl &= ~0x00000020;
+				break;
+			case VESA_HSYNC_SUSPEND:
+				ctrl &= ~0x00000010;
+				break;
+		}
+	} else {
+		if (p->ramdac == IBM) {
+			ctrl |= 0x000017b0;
+			p->cmap_regs[PIDXHI] = 0;	eieio();
+			p->cmap_regs[PIDXLO] = CLKCTL;	eieio();
+			p->cmap_regs[PIDXDATA] = 0x01;	eieio();
+			p->cmap_regs[PIDXLO] = PWRMNGMT;eieio();
+			p->cmap_regs[PIDXDATA] = 0x00;	eieio();
+			p->cmap_regs[PIDXLO] = SYNCCTL;	eieio();
+			p->cmap_regs[PIDXDATA] = 0x00;	eieio();
+			p->cmap_regs[PIDXLO] = MISCTL1;	eieio();
+			p->cmap_regs[PIDXDATA] = 0x01;	eieio();
+			p->cmap_regs[PIDXLO] = MISCTL2;	eieio();
+			p->cmap_regs[PIDXDATA] = 0x45;	eieio();
+		} else
+			ctrl |= 0x00001780;
 	}
-	if (con == currcon)			/* current console? */
-		return fb_set_cmap(cmap, kspc, imsttfb_setcolreg, info);
-	else
-		fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
-
+	out_le32(&p->dc_regs[STGCTL], ctrl);
 	return 0;
 }
 
@@ -1652,8 +1675,10 @@
 	fb_get_var:	imsttfb_get_var,
 	fb_set_var:	imsttfb_set_var,
 	fb_get_cmap:	imsttfb_get_cmap,
-	fb_set_cmap:	imsttfb_set_cmap,
+	fb_set_cmap:	gen_set_cmap,
+	fb_setcolreg:	imsttfb_setcolreg,
 	fb_pan_display:	imsttfb_pan_display,
+	fb_blank:	imsttfb_blank,
 	fb_ioctl:	imsttfb_ioctl,
 };
 
@@ -1661,7 +1686,7 @@
 imsttfbcon_switch (int con, struct fb_info *info)
 {
 	struct fb_info_imstt *p = (struct fb_info_imstt *)info;
-	struct display *old = &fb_display[currcon], *new = &fb_display[con];
+	struct display *old = &fb_display[info->currcon], *new = &fb_display[con];
 
 	if (old->cmap.len)
 		fb_get_cmap(&old->cmap, 1, imsttfb_getcolreg, info);
@@ -1669,7 +1694,7 @@
 	if (old->conp && old->conp->vc_sw && old->conp->vc_sw->con_cursor)
 		old->conp->vc_sw->con_cursor(old->conp, CM_ERASE);
 
-	currcon = con;
+	info->currcon = con;
 
 	if (old->var.xres != new->var.xres
 	    || old->var.yres != new->var.yres
@@ -1702,7 +1727,7 @@
 	struct fb_info_imstt *p = (struct fb_info_imstt *)info;
 	struct display *disp = &fb_display[con];
 
-	if (con != currcon)
+	if (con != info->currcon)
 		goto out;
 
 	if (p->ramdac == IBM)
@@ -1714,59 +1739,6 @@
 	return 0;
 }
 
-static void
-imsttfbcon_blank (int blank, struct fb_info *info)
-{
-	struct fb_info_imstt *p = (struct fb_info_imstt *)info;
-	__u32 ctrl;
-
-	ctrl = in_le32(&p->dc_regs[STGCTL]);
-	if (blank > 0) {
-		switch (blank - 1) {
-			case VESA_NO_BLANKING:
-			case VESA_POWERDOWN:
-				ctrl &= ~0x00000380;
-				if (p->ramdac == IBM) {
-					p->cmap_regs[PIDXHI] = 0;	eieio();
-					p->cmap_regs[PIDXLO] = MISCTL2;	eieio();
-					p->cmap_regs[PIDXDATA] = 0x55;	eieio();
-					p->cmap_regs[PIDXLO] = MISCTL1;	eieio();
-					p->cmap_regs[PIDXDATA] = 0x11;	eieio();
-					p->cmap_regs[PIDXLO] = SYNCCTL;	eieio();
-					p->cmap_regs[PIDXDATA] = 0x0f;	eieio();
-					p->cmap_regs[PIDXLO] = PWRMNGMT;eieio();
-					p->cmap_regs[PIDXDATA] = 0x1f;	eieio();
-					p->cmap_regs[PIDXLO] = CLKCTL;	eieio();
-					p->cmap_regs[PIDXDATA] = 0xc0;
-				}
-				break;
-			case VESA_VSYNC_SUSPEND:
-				ctrl &= ~0x00000020;
-				break;
-			case VESA_HSYNC_SUSPEND:
-				ctrl &= ~0x00000010;
-				break;
-		}
-	} else {
-		if (p->ramdac == IBM) {
-			ctrl |= 0x000017b0;
-			p->cmap_regs[PIDXHI] = 0;	eieio();
-			p->cmap_regs[PIDXLO] = CLKCTL;	eieio();
-			p->cmap_regs[PIDXDATA] = 0x01;	eieio();
-			p->cmap_regs[PIDXLO] = PWRMNGMT;eieio();
-			p->cmap_regs[PIDXDATA] = 0x00;	eieio();
-			p->cmap_regs[PIDXLO] = SYNCCTL;	eieio();
-			p->cmap_regs[PIDXDATA] = 0x00;	eieio();
-			p->cmap_regs[PIDXLO] = MISCTL1;	eieio();
-			p->cmap_regs[PIDXDATA] = 0x01;	eieio();
-			p->cmap_regs[PIDXLO] = MISCTL2;	eieio();
-			p->cmap_regs[PIDXDATA] = 0x45;	eieio();
-		} else
-			ctrl |= 0x00001780;
-	}
-	out_le32(&p->dc_regs[STGCTL], ctrl);
-}
-
 static void __init 
 init_imstt(struct fb_info_imstt *p)
 {
@@ -1869,10 +1841,11 @@
 	p->info.node = NODEV;
 	p->info.fbops = &imsttfb_ops;
 	p->info.disp = &p->disp;
+	p->info.screen_base = (__u8 *)p->frame_buffer;
+	p->info.currcon = -1;
 	p->info.changevar = 0;
 	p->info.switch_con = &imsttfbcon_switch;
 	p->info.updatevar = &imsttfbcon_updatevar;
-	p->info.blank = &imsttfbcon_blank;
 	p->info.flags = FBINFO_FLAG_DEFAULT;
 
 	for (i = 0; i < 16; i++) {
diff -Nru a/drivers/video/leofb.c b/drivers/video/leofb.c
--- a/drivers/video/leofb.c	Sun Apr 28 20:10:39 2002
+++ b/drivers/video/leofb.c	Sun Apr 28 20:10:39 2002
@@ -258,7 +258,7 @@
 	sbus_writel(attr_bgcol(p,c) << 24, &ss->bg);
 	sbus_writel(0xFFFFFFFF<<(32-fontwidth(p)),
 		    &us->fontmsk);
-	u = ((u32 *)p->screen_base) + y + x;
+	u = ((u32 *)p->fb_info.screen_base) + y + x;
 	if (fontwidth(p) <= 8) {
 		for (i = 0; i < fontheight(p); i++, u += 2048) {
 			u32 val = *fd++ << 24;
@@ -304,7 +304,7 @@
 		y = yy << (fontheightlog(p) + 11);
 	else
 		y = (yy * fontheight(p)) << 11;
-	u = ((u32 *)p->screen_base) + y + x;
+	u = ((u32 *)p->fb_info.screen_base) + y + x;
 	if (fontwidth(p) <= 8) {
 		sbus_writel(0xFFFFFFFF<<(32-4*fontwidth(p)), &us->fontmsk);
 		x = 4*fontwidth(p) - fontheight(p)*2048;
@@ -535,7 +535,7 @@
 	spin_unlock_irqrestore(&fb->lock, flags);
 }
 
-static void leo_blank (struct fb_info_sbusfb *fb)
+static int leo_blank (struct fb_info_sbusfb *fb)
 {
 	unsigned long flags;
 	u32 tmp;
@@ -547,9 +547,10 @@
 	tmp &= ~LEO_KRN_CSR_ENABLE;
 	sbus_writel(tmp, &fb->s.leo.lx_krn->krn_csr);
 	spin_unlock_irqrestore(&fb->lock, flags);
+	return 0;
 }
 
-static void leo_unblank (struct fb_info_sbusfb *fb)
+static int leo_unblank (struct fb_info_sbusfb *fb)
 {
 	unsigned long flags;
 	u32 tmp;
@@ -563,6 +564,7 @@
 		sbus_writel(tmp, &fb->s.leo.lx_krn->krn_csr);
 	}
 	spin_unlock_irqrestore(&fb->lock, flags);
+	return 0;
 }
 
 static int __init
@@ -592,7 +594,7 @@
 
 static void leo_margins (struct fb_info_sbusfb *fb, struct display *p, int x_margin, int y_margin)
 {
-	p->screen_base += 8192 * (y_margin - fb->y_margin) + 4 * (x_margin - fb->x_margin);
+	fb->fb_info.screen_base += 8192 * (y_margin - fb->y_margin) + 4 * (x_margin - fb->x_margin);
 }
 
 static void leo_switch_from_graph (struct fb_info_sbusfb *fb)
@@ -673,12 +675,12 @@
 	fb->info.fbops = fbops;
 	
 	disp->scrollmode = SCROLL_YREDRAW;
-	if (!disp->screen_base) {
-		disp->screen_base = (char *)
+	if (!fb->info.screen_base) {
+		fb->info.screen_base = (char *)
 			sbus_ioremap(&sdev->resource[0], LEO_OFF_SS0,
 				     0x800000, "leo ram");
 	}
-	disp->screen_base += 8192 * fb->y_margin + 4 * fb->x_margin;
+	fb->info.screen_base += 8192 * fb->y_margin + 4 * fb->x_margin;
 	us = fb->s.leo.lc_ss0_usr = (struct leo_lc_ss0_usr *)
 		sbus_ioremap(&sdev->resource[0], LEO_OFF_LC_SS0_USR,
 			     0x1000, "leolc ss0usr");
diff -Nru a/drivers/video/macfb.c b/drivers/video/macfb.c
--- a/drivers/video/macfb.c	Sun Apr 28 20:10:31 2002
+++ b/drivers/video/macfb.c	Sun Apr 28 20:10:31 2002
@@ -205,7 +205,6 @@
 
 static int             inverse   = 0;
 static int             vidtest   = 0;
-static int             currcon   = 0;
 
 static int macfb_update_var(int con, struct fb_info *info)
 {
@@ -251,7 +250,6 @@
 	macfb_get_fix(&fix, con, &fb_info);
 
 	memset(display, 0, sizeof(struct display));
-	display->screen_base = video_vbase;
 	display->visual = fix.visual;
 	display->type = fix.type;
 	display->type_aux = fix.type_aux;
@@ -789,21 +787,10 @@
     return 0;
 }
 
-static void do_install_cmap(int con, struct fb_info *info)
-{
-	if (con != currcon)
-		return;
-	if (fb_display[con].cmap.len)
-		fb_set_cmap(&fb_display[con].cmap, 1, macfb_setcolreg, info);
-	else
-		fb_set_cmap(fb_default_cmap(video_cmap_len), 1,
-			    macfb_setcolreg, info);
-}
-
 static int macfb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			  struct fb_info *info)
 {
-	if (con == currcon) /* current console? */
+	if (con == info->currcon) /* current console? */
 		return fb_get_cmap(cmap, kspc, macfb_getcolreg, info);
 	else if (fb_display[con].cmap.len) /* non default colormap? */
 		fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
@@ -813,30 +800,14 @@
 	return 0;
 }
 
-static int macfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-			  struct fb_info *info)
-{
-	int err;
-
-	if (!fb_display[con].cmap.len) {	/* no colormap allocated? */
-		err = fb_alloc_cmap(&fb_display[con].cmap,video_cmap_len,0);
-		if (err)
-			return err;
-	}
-	if (con == currcon)			/* current console? */
-		return fb_set_cmap(cmap, kspc, macfb_setcolreg, info);
-	else
-		fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
-	return 0;
-}
-
 static struct fb_ops macfb_ops = {
 	owner:		THIS_MODULE,
 	fb_get_fix:	macfb_get_fix,
 	fb_get_var:	macfb_get_var,
 	fb_set_var:	macfb_set_var,
 	fb_get_cmap:	macfb_get_cmap,
-	fb_set_cmap:	macfb_set_cmap,
+	fb_set_cmap:	gen_set_cmap,
+	fb_setcolreg:	macfb_setcolreg,
 };
 
 void __init macfb_setup(char *options, int *ints)
@@ -864,22 +835,17 @@
 static int macfb_switch(int con, struct fb_info *info)
 {
 	/* Do we have to save the colormap? */
-	if (fb_display[currcon].cmap.len)
-		fb_get_cmap(&fb_display[currcon].cmap, 1, macfb_getcolreg,
+	if (fb_display[info->currcon].cmap.len)
+		fb_get_cmap(&fb_display[info->currcon].cmap, 1, macfb_getcolreg,
 			    info);
 	
-	currcon = con;
+	info->currcon = con;
 	/* Install new colormap */
 	do_install_cmap(con, info);
 	macfb_update_var(con, info);
 	return 1;
 }
 
-static void macfb_blank(int blank, struct fb_info *info)
-{
-	/* Not supported */
-}
-
 void __init macfb_init(void)
 {
 	struct nubus_dev* ndev = NULL;
@@ -1220,14 +1186,14 @@
 			break;
 		}
 	
-	fb_info.changevar  = NULL;
-	fb_info.node       = NODEV;
-	fb_info.fbops      = &macfb_ops;
-	fb_info.disp       = &disp;
-	fb_info.switch_con = &macfb_switch;
-	fb_info.updatevar  = &macfb_update_var;
-	fb_info.blank      = &macfb_blank;
-	fb_info.flags      = FBINFO_FLAG_DEFAULT;
+	fb_info.changevar   = NULL;
+	fb_info.node        = NODEV;
+	fb_info.fbops       = &macfb_ops;
+	fb_info.screen_base = video_vbase;
+	fb_info.disp        = &disp;
+	fb_info.switch_con  = &macfb_switch;
+	fb_info.updatevar   = &macfb_update_var;
+	fb_info.flags       = FBINFO_FLAG_DEFAULT;
 	macfb_set_disp(-1);
 	do_install_cmap(0, &fb_info);
 	
diff -Nru a/drivers/video/matrox/matroxfb_base.c b/drivers/video/matrox/matroxfb_base.c
--- a/drivers/video/matrox/matroxfb_base.c	Sun Apr 28 20:10:37 2002
+++ b/drivers/video/matrox/matroxfb_base.c	Sun Apr 28 20:10:37 2002
@@ -257,7 +257,7 @@
 		    var->yoffset+fb_display[con].var.yres > fb_display[con].var.yres_virtual)
 			return -EINVAL;
 	}
-	if (con == ACCESS_FBINFO(currcon))
+	if (con == ACCESS_FBINFO(fbcon.currcon))
 		matrox_pan_var(PMINFO var);
 	fb_display[con].var.xoffset = var->xoffset;
 	fb_display[con].var.yoffset = var->yoffset;
@@ -572,16 +572,16 @@
 	return 0;
 }
 
-static int matrox_setcolreg(unsigned regno, unsigned red, unsigned green,
-			    unsigned blue, unsigned transp,
-			    struct fb_info *fb_info)
+static int matroxfb_setcolreg(unsigned regno, unsigned red, unsigned green,
+			      unsigned blue, unsigned transp,
+			      struct fb_info *fb_info)
 {
 	struct display* p;
 #ifdef CONFIG_FB_MATROX_MULTIHEAD
 	struct matrox_fb_info* minfo = (struct matrox_fb_info*)fb_info;
 #endif
 
-	DBG("matrox_setcolreg")
+	DBG("matroxfb_setcolreg")
 
 	/*
 	 *  Set a single color register. The values supplied are
@@ -656,17 +656,6 @@
 	return 0;
 }
 
-static void do_install_cmap(WPMINFO struct display* dsp)
-{
-	DBG("do_install_cmap")
-
-	if (dsp->cmap.len)
-		fb_set_cmap(&dsp->cmap, 1, matrox_setcolreg, &ACCESS_FBINFO(fbcon));
-	else
-		fb_set_cmap(fb_default_cmap(ACCESS_FBINFO(curr.cmap_len)),
-			    1, matrox_setcolreg, &ACCESS_FBINFO(fbcon));
-}
-
 static int matroxfb_get_fix(struct fb_fix_screeninfo *fix, int con,
 			 struct fb_info *info)
 {
@@ -760,7 +749,7 @@
 	}
 	display->var = *var;
 	/* cmap */
-	display->screen_base = vaddr_va(ACCESS_FBINFO(video.vbase)) + ydstorg;
+	ACCESS_FBINFO(fbcon.screen_base) = vaddr_va(ACCESS_FBINFO(video.vbase)) + ydstorg;
 	display->visual = visual;
 	display->ypanstep = 1;
 	display->ywrapstep = 0;
@@ -782,7 +771,7 @@
 	/* fgshift, bgshift, charmask */
 	if (chgvar && info && info->changevar)
 		info->changevar(con);
-	if (con == ACCESS_FBINFO(currcon)) {
+	if (con == ACCESS_FBINFO(fbcon.currcon)) {
 		unsigned int pos;
 
 		ACCESS_FBINFO(curr.cmap_len) = cmap_len;
@@ -880,7 +869,7 @@
 				up_read(&ACCESS_FBINFO(altout.lock));
 			}
 			matrox_cfbX_init(PMINFO display);
-			do_install_cmap(PMINFO display);
+			do_install_cmap(ACCESS_FBINFO(fbcon.currcon),&ACCESS_FBINFO(fbcon));
 #if defined(CONFIG_FB_COMPAT_XPMAC)
 			if (console_fb_info == &ACCESS_FBINFO(fbcon)) {
 				int vmode, cmode;
@@ -943,7 +932,7 @@
 		return -ENXIO;
 	}
 
-	if (con == ACCESS_FBINFO(currcon)) /* current console? */
+	if (con == ACCESS_FBINFO(fbcon.currcon)) /* current console? */
 		return fb_get_cmap(cmap, kspc, matrox_getcolreg, info);
 	else if (dsp->cmap.len) /* non default colormap? */
 		fb_copy_cmap(&dsp->cmap, cmap, kspc ? 0 : 2);
@@ -975,8 +964,8 @@
 		if (err)
 			return err;
 	}
-	if (con == ACCESS_FBINFO(currcon)) {			/* current console? */
-		return fb_set_cmap(cmap, kspc, matrox_setcolreg, info);
+	if (con == ACCESS_FBINFO(fbcon.currcon)) {			/* current console? */
+		return fb_set_cmap(cmap, kspc, info);
 	} else
 		fb_copy_cmap(cmap, &dsp->cmap, kspc ? 0 : 1);
 	return 0;
@@ -1054,7 +1043,7 @@
 						if (val != 1)
 							return val;
 						if (ACCESS_FBINFO(output.ph) & MATROXFB_OUTPUT_CONN_SECONDARY)
-							matroxfb_switch(ACCESS_FBINFO(currcon), info);
+							matroxfb_switch(ACCESS_FBINFO(fbcon.currcon), info);
 						if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_SECONDARY) {
 							struct matroxfb_dh_fb_info* crtc2;
 
@@ -1130,7 +1119,7 @@
 				if (tmp == ACCESS_FBINFO(output.ph))
 					return 0;
 				ACCESS_FBINFO(output.ph) = tmp;
-				matroxfb_switch(ACCESS_FBINFO(currcon), info);
+				matroxfb_switch(ACCESS_FBINFO(fbcon.currcon), info);
 				return 0;
 			}
 		case MATROXFB_GET_OUTPUT_CONNECTION:
@@ -1163,6 +1152,40 @@
 #undef minfo
 }
 
+/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */
+
+static int matroxfb_blank(int blank, struct fb_info *info)
+{
+#define minfo ((struct matrox_fb_info*)info)
+	int seq;
+	int crtc;
+	CRITFLAGS
+
+	DBG("matroxfb_blank")
+
+	if (ACCESS_FBINFO(dead))
+		return 1;
+
+	switch (blank) {
+		case 1:  seq = 0x20; crtc = 0x00; break; /* works ??? */
+		case 2:  seq = 0x20; crtc = 0x10; break;
+		case 3:  seq = 0x20; crtc = 0x20; break;
+		case 4:  seq = 0x20; crtc = 0x30; break;
+		default: seq = 0x00; crtc = 0x00; break;
+	}
+
+	CRITBEGIN
+
+	mga_outb(M_SEQ_INDEX, 1);
+	mga_outb(M_SEQ_DATA, (mga_inb(M_SEQ_DATA) & ~0x20) | seq);
+	mga_outb(M_EXTVGA_INDEX, 1);
+	mga_outb(M_EXTVGA_DATA, (mga_inb(M_EXTVGA_DATA) & ~0x30) | crtc);
+
+	CRITEND
+	return 0;
+#undef minfo
+}
+
 static struct fb_ops matroxfb_ops = {
 	owner:		THIS_MODULE,
 	fb_open:	matroxfb_open,
@@ -1172,7 +1195,9 @@
 	fb_set_var:	matroxfb_set_var,
 	fb_get_cmap:	matroxfb_get_cmap,
 	fb_set_cmap:	matroxfb_set_cmap,
+	fb_setcolreg:	matroxfb_setcolreg,
 	fb_pan_display:	matroxfb_pan_display,
+	fb_blank:	matroxfb_blank,
 	fb_ioctl:	matroxfb_ioctl,
 };
 
@@ -1184,10 +1209,10 @@
 
 	DBG("matroxfb_switch");
 
-	if (ACCESS_FBINFO(currcon) >= 0) {
+	if (ACCESS_FBINFO(fbcon.currcon) >= 0) {
 		/* Do we have to save the colormap? */
 		cmap = &(ACCESS_FBINFO(currcon_display)->cmap);
-		dprintk(KERN_DEBUG "switch1: con = %d, cmap.len = %d\n", ACCESS_FBINFO(currcon), cmap->len);
+		dprintk(KERN_DEBUG "switch1: con = %d, cmap.len = %d\n", ACCESS_FBINFO(fbcon.currcon), cmap->len);
 
 		if (cmap->len) {
 			dprintk(KERN_DEBUG "switch1a: %p %p %p %p\n", cmap->red, cmap->green, cmap->blue, cmap->transp);
@@ -1199,7 +1224,7 @@
 #endif
 		}
 	}
-	ACCESS_FBINFO(currcon) = con;
+	ACCESS_FBINFO(fbcon.currcon) = con;
 	if (con < 0)
 		p = ACCESS_FBINFO(fbcon.disp);
 	else
@@ -1226,40 +1251,6 @@
 #undef minfo
 }
 
-/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */
-
-static void matroxfb_blank(int blank, struct fb_info *info)
-{
-#define minfo ((struct matrox_fb_info*)info)
-	int seq;
-	int crtc;
-	CRITFLAGS
-
-	DBG("matroxfb_blank")
-
-	if (ACCESS_FBINFO(dead))
-		return;
-
-	switch (blank) {
-		case 1:  seq = 0x20; crtc = 0x00; break; /* works ??? */
-		case 2:  seq = 0x20; crtc = 0x10; break;
-		case 3:  seq = 0x20; crtc = 0x20; break;
-		case 4:  seq = 0x20; crtc = 0x30; break;
-		default: seq = 0x00; crtc = 0x00; break;
-	}
-
-	CRITBEGIN
-
-	mga_outb(M_SEQ_INDEX, 1);
-	mga_outb(M_SEQ_DATA, (mga_inb(M_SEQ_DATA) & ~0x20) | seq);
-	mga_outb(M_EXTVGA_INDEX, 1);
-	mga_outb(M_EXTVGA_DATA, (mga_inb(M_EXTVGA_DATA) & ~0x30) | crtc);
-
-	CRITEND
-
-#undef minfo
-}
-
 #define RSDepth(X)	(((X) >> 8) & 0x0F)
 #define RS8bpp		0x1
 #define RS15bpp		0x2
@@ -1731,7 +1722,7 @@
 	}
 	ACCESS_FBINFO(devflags.ydstorg) = 0;
 
-	ACCESS_FBINFO(currcon) = -1;
+	ACCESS_FBINFO(fbcon.currcon) = -1;
 	ACCESS_FBINFO(currcon_display) = d;
 	ACCESS_FBINFO(video.base) = video_base_phys;
 	ACCESS_FBINFO(video.len_usable) = ACCESS_FBINFO(video.len);
@@ -1775,7 +1766,6 @@
 	ACCESS_FBINFO(fbcon.disp) = d;
 	ACCESS_FBINFO(fbcon.switch_con) = &matroxfb_switch;
 	ACCESS_FBINFO(fbcon.updatevar) = &matroxfb_updatevar;
-	ACCESS_FBINFO(fbcon.blank) = &matroxfb_blank;
 	/* after __init time we are like module... no logo */
 	ACCESS_FBINFO(fbcon.flags) = hotplug ? FBINFO_FLAG_MODULE : FBINFO_FLAG_DEFAULT;
 	ACCESS_FBINFO(video.len_usable) &= PAGE_MASK;
@@ -1898,7 +1888,7 @@
 	}
 	printk("fb%d: %s frame buffer device\n",
 	       GET_FB_IDX(ACCESS_FBINFO(fbcon.node)), ACCESS_FBINFO(fbcon.modename));
-	if (ACCESS_FBINFO(currcon) < 0) {
+	if (ACCESS_FBINFO(fbcon.currcon) < 0) {
 		/* there is no console on this fb... but we have to initialize hardware
 		 * until someone tells me what is proper thing to do */
 		printk(KERN_INFO "fb%d: initializing hardware\n",
diff -Nru a/drivers/video/matrox/matroxfb_base.h b/drivers/video/matrox/matroxfb_base.h
--- a/drivers/video/matrox/matroxfb_base.h	Sun Apr 28 20:10:36 2002
+++ b/drivers/video/matrox/matroxfb_base.h	Sun Apr 28 20:10:36 2002
@@ -479,7 +479,6 @@
 	unsigned int	max_pixel_clock;
 
 	struct matrox_switch*	hw_switch;
-	int		currcon;
 	struct display*	currcon_display;
 
 	struct {
diff -Nru a/drivers/video/matrox/matroxfb_crtc2.c b/drivers/video/matrox/matroxfb_crtc2.c
--- a/drivers/video/matrox/matroxfb_crtc2.c	Sun Apr 28 20:10:34 2002
+++ b/drivers/video/matrox/matroxfb_crtc2.c	Sun Apr 28 20:10:34 2002
@@ -84,13 +84,6 @@
 #undef m2info
 }
 
-static void do_install_cmap(struct matroxfb_dh_fb_info* m2info, struct display* p) {
-	if (p->cmap.len)
-		fb_set_cmap(&p->cmap, 1, matroxfb_dh_setcolreg, &m2info->fbcon);
-	else
-		fb_set_cmap(fb_default_cmap(16), 1, matroxfb_dh_setcolreg, &m2info->fbcon);
-}
-
 static void matroxfb_dh_restore(struct matroxfb_dh_fb_info* m2info,
 		struct my_timming* mt,
 		struct display* p,
@@ -390,7 +383,7 @@
 		chgvar = 0;
 	p->var = *var;
 	/* cmap */
-	p->screen_base = vaddr_va(m2info->video.vbase);
+	ACCESS_FBINFO(fbcon.screen_base) = vaddr_va(m2info->video.vbase);
 	p->visual = visual;
 	p->ypanstep = 1;
 	p->ywrapstep = 0;
@@ -402,7 +395,7 @@
 	initMatroxDH(m2info, p);
 	if (chgvar && info && info->changevar)
 		info->changevar(con);
-	if (con == m2info->currcon) {
+	if (con == m2info->fbcon.currcon) {
 		struct my_timming mt;
 		unsigned int pos;
 
@@ -446,7 +439,7 @@
 			up_read(&ACCESS_FBINFO(altout.lock));
 		}
 		matroxfb_dh_cfbX_init(m2info, p);
-		do_install_cmap(m2info, p);
+		do_install_cmap(ACCESS_FBINFO(fbcon.currcon), &ACCESS_FBINFO(fbcon));
 	}
 	return 0;
 #undef m2info
@@ -461,7 +454,7 @@
 		dsp = m2info->fbcon.disp;
 	else
 		dsp = fb_display + con;
-	if (con == m2info->currcon)
+	if (con == m2info->fbcon.currcon)
 		return fb_get_cmap(cmap, kspc, matroxfb_dh_getcolreg, info);
 	else if (dsp->cmap.len)
 		fb_copy_cmap(&dsp->cmap, cmap, kspc ? 0 : 2);
@@ -487,8 +480,8 @@
 		if (err)
 			return err;
 	}
-	if (con == m2info->currcon)
-		return fb_set_cmap(cmap, kspc, matroxfb_dh_setcolreg, info);
+	if (con == m2info->fbcon.currcon)
+		return fb_set_cmap(cmap, kspc, info);
 	else
 		fb_copy_cmap(cmap, &dsp->cmap, kspc ? 0 : 1);
 	return 0;
@@ -501,7 +494,7 @@
 	if (var->xoffset + fb_display[con].var.xres > fb_display[con].var.xres_virtual ||
 	    var->yoffset + fb_display[con].var.yres > fb_display[con].var.yres_virtual)
 		return -EINVAL;
-	if (con == m2info->currcon)
+	if (con == m2info->fbcon.currcon)
 		matroxfb_dh_pan_var(m2info, var);
 	fb_display[con].var.xoffset = var->xoffset;
 	fb_display[con].var.yoffset = var->yoffset;
@@ -571,7 +564,7 @@
 				if (tmp == ACCESS_FBINFO(output.sh))
 					return 0;
 				ACCESS_FBINFO(output.sh) = tmp;
-				matroxfb_dh_switch(m2info->currcon, info);
+				matroxfb_dh_switch(m2info->fbcon.currcon, info);
 				return 0;
 			}
 		case MATROXFB_GET_OUTPUT_CONNECTION:
@@ -598,6 +591,20 @@
 #undef m2info
 }
 
+static int matroxfb_dh_blank(int blank, struct fb_info* info) {
+#define m2info ((struct matroxfb_dh_fb_info*)info)
+	switch (blank) {
+		case 1:
+		case 2:
+		case 3:
+		case 4:
+		default:;
+	}
+	/* do something... */
+	return 0;
+#undef m2info
+}
+
 static struct fb_ops matroxfb_dh_ops = {
 	owner:		THIS_MODULE,
 	fb_open:	matroxfb_dh_open,
@@ -607,7 +614,9 @@
 	fb_set_var:	matroxfb_dh_set_var,
 	fb_get_cmap:	matroxfb_dh_get_cmap,
 	fb_set_cmap:	matroxfb_dh_set_cmap,
+	fb_setcolreg:	matroxfb_dh_setcolreg,
 	fb_pan_display:	matroxfb_dh_pan_display,
+	fb_blank:	matroxfb_dh_blank,
 	fb_ioctl:	matroxfb_dh_ioctl,
 };
 
@@ -616,13 +625,13 @@
 	struct fb_cmap* cmap;
 	struct display* p;
 
-	if (m2info->currcon >= 0) {
+	if (m2info->fbcon.currcon >= 0) {
 		cmap = &m2info->currcon_display->cmap;
 		if (cmap->len) {
 			fb_get_cmap(cmap, 1, matroxfb_dh_getcolreg, info);
 		}
 	}
-	m2info->currcon = con;
+	m2info->fbcon.currcon = con;
 	if (con < 0)
 		p = m2info->fbcon.disp;
 	else
@@ -641,19 +650,6 @@
 #undef m2info
 }
 
-static void matroxfb_dh_blank(int blank, struct fb_info* info) {
-#define m2info ((struct matroxfb_dh_fb_info*)info)
-	switch (blank) {
-		case 1:
-		case 2:
-		case 3:
-		case 4:
-		default:;
-	}
-	/* do something... */
-#undef m2info
-}
-
 static struct fb_var_screeninfo matroxfb_dh_defined = {
 		640,480,640,480,/* W,H, virtual W,H */
 		0,0,		/* offset */
@@ -692,9 +688,8 @@
 	m2info->fbcon.disp = d;
 	m2info->fbcon.switch_con = &matroxfb_dh_switch;
 	m2info->fbcon.updatevar = &matroxfb_dh_updatevar;
-	m2info->fbcon.blank = &matroxfb_dh_blank;
 	m2info->fbcon.flags = FBINFO_FLAG_DEFAULT;
-	m2info->currcon = -1;
+	m2info->fbcon.currcon = -1;
 	m2info->currcon_display = d;
 
 	if (mem < 64)
@@ -734,7 +729,7 @@
 		kfree(d);
 		return -ENXIO;
 	}
-	if (m2info->currcon < 0) {
+	if (m2info->fbcon.currcon < 0) {
 		matroxfb_dh_set_var(&matroxfb_dh_defined, -1, &m2info->fbcon);
 	}
 	down_write(&ACCESS_FBINFO(crtc2.lock));
diff -Nru a/drivers/video/matrox/matroxfb_g450.c b/drivers/video/matrox/matroxfb_g450.c
--- a/drivers/video/matrox/matroxfb_g450.c	Sun Apr 28 20:10:34 2002
+++ b/drivers/video/matrox/matroxfb_g450.c	Sun Apr 28 20:10:34 2002
@@ -79,7 +79,7 @@
 	ACCESS_FBINFO(altout.output) = &matroxfb_g450_altout;
 	up_write(&ACCESS_FBINFO(altout.lock));
 	ACCESS_FBINFO(output.all) |= MATROXFB_OUTPUT_CONN_SECONDARY;
-	matroxfb_switch(ACCESS_FBINFO(currcon), (struct fb_info*)MINFO);	
+	matroxfb_switch(ACCESS_FBINFO(fbcon.currcon), (struct fb_info*)MINFO);	
 	return 0;
 }
 
diff -Nru a/drivers/video/maxinefb.c b/drivers/video/maxinefb.c
--- a/drivers/video/maxinefb.c	Sun Apr 28 20:10:33 2002
+++ b/drivers/video/maxinefb.c	Sun Apr 28 20:10:33 2002
@@ -78,7 +78,6 @@
 struct maxinefb_par {
 };
 
-static int currcon = 0;
 struct maxinefb_par current_par;
 
 /* Reference to machine type set in arch/mips/dec/prom/identify.c, KM */
@@ -275,7 +274,7 @@
 static int maxinefb_switch(int con, struct fb_info *info)
 {
 	maxinefb_do_fb_set_var(&fb_display[con].var, 1);
-	currcon = con;
+	info->currcon = con;
 	return 0;
 }
 
@@ -291,7 +290,6 @@
 
 	maxinefb_get_fix(&fix, con, 0);
 
-	display->screen_base = fix.smem_start;
 	display->visual = fix.visual;
 	display->type = fix.type;
 	display->type_aux = fix.type_aux;
@@ -360,10 +358,11 @@
 	fb_info.changevar = NULL;
 	fb_info.node = NODEV;
 	fb_info.fbops = &maxinefb_ops;
+	fb_info.screen_base = (char *) fb_start;
 	fb_info.disp = &disp;
+	fb_info.currcon = -1;
 	fb_info.switch_con = &maxinefb_switch;
 	fb_info.updatevar = &maxinefb_fb_update_var;
-	fb_info.blank = NULL;
 	fb_info.flags = FBINFO_FLAG_DEFAULT;
 	maxinefb_do_fb_set_var(&maxinefb_defined, 1);
 
diff -Nru a/drivers/video/neofb.c b/drivers/video/neofb.c
--- a/drivers/video/neofb.c	Sun Apr 28 20:10:31 2002
+++ b/drivers/video/neofb.c	Sun Apr 28 20:10:31 2002
@@ -858,7 +858,7 @@
    * we should be able to remove this test once fbcon has been
    * "improved" --rmk
    */
-  if (!err && con == info->currcon)
+  if (!err && con == info->fb.currcon)
     {
       err = fb_set_cmap (cmap, kspc, neo_setcolreg, fb);
       dcmap = &fb->cmap;
@@ -1565,7 +1565,7 @@
   if (chgvar && fb && fb->changevar)
     fb->changevar (con);
 
-  if (con == info->currcon)
+  if (con == info->fb.currcon)
     {
       if (chgvar || con < 0)
         neofb_set_par (info, &par);
@@ -1634,9 +1634,9 @@
   struct display *disp;
   struct fb_cmap *cmap;
 
-  if (info->currcon >= 0)
+  if (info->fb.currcon >= 0)
     {
-      disp = fb_display + info->currcon;
+      disp = fb_display + info->fb.currcon;
 
       /*
        * Save the old colormap and video mode.
@@ -1646,7 +1646,7 @@
 	fb_copy_cmap(&fb->cmap, &disp->cmap, 0);
     }
 
-  info->currcon = con;
+  info->fb.currcon = con;
   disp = fb_display + con;
 
   /*
@@ -2099,7 +2099,7 @@
 
   memset (info, 0, sizeof(struct neofb_info) + sizeof(struct display));
 
-  info->currcon = -1;
+  info->fb.currcon = -1;
   info->pcidev  = dev;
   info->accel   = id->driver_data;
 
diff -Nru a/drivers/video/neofb.h b/drivers/video/neofb.h
--- a/drivers/video/neofb.h	Sun Apr 28 20:10:37 2002
+++ b/drivers/video/neofb.h	Sun Apr 28 20:10:37 2002
@@ -121,9 +121,6 @@
   struct display_switch	*dispsw;
 
   struct pci_dev *pcidev;
-
-  int   currcon;
-
   int   accel;
   char *name;
 
diff -Nru a/drivers/video/offb.c b/drivers/video/offb.c
--- a/drivers/video/offb.c	Sun Apr 28 20:10:36 2002
+++ b/drivers/video/offb.c	Sun Apr 28 20:10:36 2002
@@ -42,9 +42,6 @@
 #include <video/fbcon-cfb32.h>
 #include <video/macmodes.h>
 
-
-static int currcon = 0;
-
 /* Supported palette hacks */
 enum {
 	cmap_unknown,
@@ -97,6 +94,10 @@
 			struct fb_info *info);
 static int offb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
 			struct fb_info *info);
+static int offb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+			 u_int transp, struct fb_info *info);
+static int offb_blank(int blank, struct fb_info *info);
+
 #ifdef CONFIG_BOOTX_TEXT
 extern boot_infos_t *boot_infos;
 #endif
@@ -112,8 +113,6 @@
 
 static int offbcon_switch(int con, struct fb_info *info);
 static int offbcon_updatevar(int con, struct fb_info *info);
-static void offbcon_blank(int blank, struct fb_info *info);
-
 
     /*
      *  Internal routines
@@ -121,10 +120,6 @@
 
 static int offb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
 			 u_int *transp, struct fb_info *info);
-static int offb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-			 u_int transp, struct fb_info *info);
-static void do_install_cmap(int con, struct fb_info *info);
-
 
 static struct fb_ops offb_ops = {
 	owner:		THIS_MODULE,
@@ -133,6 +128,8 @@
 	fb_set_var:	offb_set_var,
 	fb_get_cmap:	offb_get_cmap,
 	fb_set_cmap:	offb_set_cmap,
+	fb_setcolreg:	offb_setcolreg,
+	fb_blank:	offb_blank,
 };
 
     /*
@@ -210,7 +207,7 @@
 static int offb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			 struct fb_info *info)
 {
-    if (con == currcon) /* current console? */
+    if (con == info->currcon) /* current console? */
 	return fb_get_cmap(cmap, kspc, offb_getcolreg, info);
     else if (fb_display[con].cmap.len) /* non default colormap? */
 	fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
@@ -240,13 +237,63 @@
 	if ((err = fb_alloc_cmap(&fb_display[con].cmap, size, 0)))
 	    return err;
     }
-    if (con == currcon)			/* current console? */
-	return fb_set_cmap(cmap, kspc, offb_setcolreg, info);
+    if (con == info->currcon)			/* current console? */
+	return fb_set_cmap(cmap, kspc, info);
     else
 	fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
     return 0;
 }
 
+    /*
+     *  Blank the display.
+     */
+
+static int offb_blank(int blank, struct fb_info *info)
+{
+    struct fb_info_offb *info2 = (struct fb_info_offb *)info;
+    int i, j;
+
+    if (!info2->cmap_adr)
+	return;
+
+    if (blank)
+	for (i = 0; i < 256; i++) {
+	    switch(info2->cmap_type) {
+	    case cmap_m64:
+	        *info2->cmap_adr = i;
+	  	mach_eieio();
+	  	for (j = 0; j < 3; j++) {
+		    *info2->cmap_data = 0;
+		    mach_eieio();
+	    	}
+	    	break;
+	    case cmap_M3A:
+	        /* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */
+	    	out_le32((unsigned *)(info2->cmap_adr + 0x58),
+	    		in_le32((unsigned *)(info2->cmap_adr + 0x58)) & ~0x20);
+	    case cmap_r128:
+	    	/* Set palette index & data */
+    	        out_8(info2->cmap_adr + 0xb0, i);
+	    	out_le32((unsigned *)(info2->cmap_adr + 0xb4), 0);
+	    	break;
+	    case cmap_M3B:
+	        /* Set PALETTE_ACCESS_CNTL in DAC_CNTL */
+	    	out_le32((unsigned *)(info2->cmap_adr + 0x58),
+	    		in_le32((unsigned *)(info2->cmap_adr + 0x58)) | 0x20);
+	    	/* Set palette index & data */
+	    	out_8(info2->cmap_adr + 0xb0, i);
+	    	out_le32((unsigned *)(info2->cmap_adr + 0xb4), 0);
+	    	break;
+	    case cmap_radeon:
+    	        out_8(info2->cmap_adr + 0xb0, i);
+	    	out_le32((unsigned *)(info2->cmap_adr + 0xb4), 0);
+	    	break;
+	    }
+	}
+    else
+	do_install_cmap(info->currcon, info);
+    return 0;	
+}
 
     /*
      *  Initialisation
@@ -513,7 +560,6 @@
     disp->cmap.green = NULL;
     disp->cmap.blue = NULL;
     disp->cmap.transp = NULL;
-    disp->screen_base = ioremap(address, fix->smem_len);
     disp->visual = fix->visual;
     disp->type = fix->type;
     disp->type_aux = fix->type_aux;
@@ -568,12 +614,13 @@
     strncat(info->info.modename, full_name, sizeof(info->info.modename));
     info->info.node = NODEV;
     info->info.fbops = &offb_ops;
+    info->info.screen_base = ioremap(address, fix->smem_len);
     info->info.disp = disp;
+    info->info.currcon = -1;	
     info->info.fontname[0] = '\0';
     info->info.changevar = NULL;
     info->info.switch_con = &offbcon_switch;
     info->info.updatevar = &offbcon_updatevar;
-    info->info.blank = &offbcon_blank;
     info->info.flags = FBINFO_FLAG_DEFAULT;
 
     for (i = 0; i < 16; i++) {
@@ -621,10 +668,10 @@
 static int offbcon_switch(int con, struct fb_info *info)
 {
     /* Do we have to save the colormap? */
-    if (fb_display[currcon].cmap.len)
-	fb_get_cmap(&fb_display[currcon].cmap, 1, offb_getcolreg, info);
+    if (fb_display[info->currcon].cmap.len)
+	fb_get_cmap(&fb_display[info->currcon].cmap, 1, offb_getcolreg, info);
 
-    currcon = con;
+    info->currcon = con;
     /* Install new colormap */
     do_install_cmap(con, info);
     return 0;
@@ -641,56 +688,6 @@
 }
 
     /*
-     *  Blank the display.
-     */
-
-static void offbcon_blank(int blank, struct fb_info *info)
-{
-    struct fb_info_offb *info2 = (struct fb_info_offb *)info;
-    int i, j;
-
-    if (!info2->cmap_adr)
-	return;
-
-    if (blank)
-	for (i = 0; i < 256; i++) {
-	    switch(info2->cmap_type) {
-	    case cmap_m64:
-	        *info2->cmap_adr = i;
-	  	mach_eieio();
-	  	for (j = 0; j < 3; j++) {
-		    *info2->cmap_data = 0;
-		    mach_eieio();
-	    	}
-	    	break;
-	    case cmap_M3A:
-	        /* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */
-	    	out_le32((unsigned *)(info2->cmap_adr + 0x58),
-	    		in_le32((unsigned *)(info2->cmap_adr + 0x58)) & ~0x20);
-	    case cmap_r128:
-	    	/* Set palette index & data */
-    	        out_8(info2->cmap_adr + 0xb0, i);
-	    	out_le32((unsigned *)(info2->cmap_adr + 0xb4), 0);
-	    	break;
-	    case cmap_M3B:
-	        /* Set PALETTE_ACCESS_CNTL in DAC_CNTL */
-	    	out_le32((unsigned *)(info2->cmap_adr + 0x58),
-	    		in_le32((unsigned *)(info2->cmap_adr + 0x58)) | 0x20);
-	    	/* Set palette index & data */
-	    	out_8(info2->cmap_adr + 0xb0, i);
-	    	out_le32((unsigned *)(info2->cmap_adr + 0xb4), 0);
-	    	break;
-	    case cmap_radeon:
-    	        out_8(info2->cmap_adr + 0xb0, i);
-	    	out_le32((unsigned *)(info2->cmap_adr + 0xb4), 0);
-	    	break;
-	    }
-	}
-    else
-	do_install_cmap(currcon, info);
-}
-
-    /*
      *  Read a single color register and split it into
      *  colors/transparent. Return != 0 for invalid regno.
      */
@@ -789,20 +786,6 @@
        }
 
     return 0;
-}
-
-
-static void do_install_cmap(int con, struct fb_info *info)
-{
-    if (con != currcon)
-	return;
-    if (fb_display[con].cmap.len)
-	fb_set_cmap(&fb_display[con].cmap, 1, offb_setcolreg, info);
-    else
-    {
-	int size = fb_display[con].var.bits_per_pixel == 16 ? 32 : 256;
-	fb_set_cmap(fb_default_cmap(size), 1, offb_setcolreg, info);
-    }
 }
 
 MODULE_LICENSE("GPL");
diff -Nru a/drivers/video/p9100fb.c b/drivers/video/p9100fb.c
--- a/drivers/video/p9100fb.c	Sun Apr 28 20:10:39 2002
+++ b/drivers/video/p9100fb.c	Sun Apr 28 20:10:39 2002
@@ -87,7 +87,7 @@
 	spin_unlock_irqrestore(&fb->lock, flags);
 }
 
-static void p9100_blank (struct fb_info_sbusfb *fb)
+static int p9100_blank (struct fb_info_sbusfb *fb)
 {
 	unsigned long flags;
 	u32 val;
@@ -97,9 +97,10 @@
 	val &= ~ SCREENPAINT_TIMECTL1_ENABLE_VIDEO;
 	WRITECTL(vid_screenpaint_timectl1, val);
 	spin_unlock_irqrestore(&fb->lock, flags);
+	return 0;
 }
 
-static void p9100_unblank (struct fb_info_sbusfb *fb)
+static int p9100_unblank (struct fb_info_sbusfb *fb)
 {
 	unsigned long flags;
 	u32 val;
@@ -109,11 +110,12 @@
 	val |= SCREENPAINT_TIMECTL1_ENABLE_VIDEO;
 	WRITECTL(vid_screenpaint_timectl1, val);
 	spin_unlock_irqrestore(&fb->lock, flags);
+	return 0;
 }
 
 static void p9100_margins (struct fb_info_sbusfb *fb, struct display *p, int x_margin, int y_margin)
 {
-  p->screen_base += (y_margin - fb->y_margin) * p->line_length + 
+  fb->info.screen_base += (y_margin - fb->y_margin) * p->line_length + 
     (x_margin - fb->x_margin);
 }
 
@@ -149,12 +151,12 @@
 	fix->line_length = fb->var.xres_virtual;
 
 	disp->scrollmode = SCROLL_YREDRAW;
-	if (!disp->screen_base)
-		disp->screen_base = (char *)
+	if (!fb->info.screen_base)
+		fb->info.screen_base = (char *)
 			sbus_ioremap(&sdev->resource[2], 0,
 				     type->fb_size, "p9100 ram");
-	fb->s.p9100.fbmem = (volatile u32 *)disp->screen_base;
-	disp->screen_base += fix->line_length * fb->y_margin + fb->x_margin;
+	fb->s.p9100.fbmem = (volatile u32 *)fb->info.screen_base;
+	fb->info.screen_base += fix->line_length * fb->y_margin + fb->x_margin;
 
 	READCTL(sys_config, tmp);
         switch ((tmp >> SYS_CONFIG_PIXELSIZE_SHIFT) & 7) {
@@ -186,7 +188,7 @@
 	fb->mmap_map = p9100_mmap_map;
 	
 	sprintf(idstring, "%s at 0x%x", "p9100", 
-		(unsigned int)disp->screen_base);
+		(unsigned int)fb->info.screen_base);
 
 	return idstring;
 }
diff -Nru a/drivers/video/platinumfb.c b/drivers/video/platinumfb.c
--- a/drivers/video/platinumfb.c	Sun Apr 28 20:10:35 2002
+++ b/drivers/video/platinumfb.c	Sun Apr 28 20:10:35 2002
@@ -50,8 +50,6 @@
 
 static char fontname[40] __initdata = { 0 };
 
-static int currcon = 0;
-
 static int default_vmode = VMODE_NVRAM;
 static int default_cmode = CMODE_NVRAM;
 
@@ -110,6 +108,9 @@
 			     struct fb_info *info);
 static int platinum_set_cmap(struct fb_cmap *cmap, int kspc, int con,
 			     struct fb_info *info);
+static int platinum_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+			      u_int transp, struct fb_info *fb);
+static int platinum_blank(int blank, struct fb_info *fb);
 
 
 /*
@@ -118,8 +119,6 @@
 
 static int platinum_switch(int con, struct fb_info *fb);
 static int platinum_updatevar(int con, struct fb_info *fb);
-static void platinum_blank(int blank, struct fb_info *fb);
-
 
 /*
  * internal functions
@@ -144,10 +143,6 @@
 				int accel);
 static int platinum_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
 			      u_int *transp, struct fb_info *fb);
-static int platinum_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-			      u_int transp, struct fb_info *fb);
-static void do_install_cmap(int con, struct fb_info *info);
-
 
 /*
  * Interface used by the world
@@ -163,6 +158,8 @@
 	fb_set_var:	platinum_set_var,
 	fb_get_cmap:	platinum_get_cmap,
 	fb_set_cmap:	platinum_set_cmap,
+	fb_setcolreg:	platinum_setcolreg,
+	fb_blank:	platinum_blank,
 };
 
 static int platinum_get_fix(struct fb_fix_screeninfo *fix, int con,
@@ -260,7 +257,7 @@
 	    struct fb_fix_screeninfo fix;
 
 	    platinum_encode_fix(&fix, &par, info);
-	    display->screen_base = (char *) info->frame_buffer + init->fb_offset + 0x20;
+	    info->fb_info.screen_base = (char *) info->frame_buffer + init->fb_offset + 0x20;
 	    display->visual = fix.visual;
 	    display->type = fix.type;
 	    display->type_aux = fix.type_aux;
@@ -321,7 +318,7 @@
 
 	if (!info->display_fg ||
 	    info->display_fg->vc_num == con)	/* current console? */
-		return fb_set_cmap(cmap, kspc, platinum_setcolreg, info);
+		return fb_set_cmap(cmap, kspc, info);
 	else
 		fb_copy_cmap(cmap, &disp->cmap, kspc ? 0 : 1);
 	return 0;
@@ -332,10 +329,10 @@
 	struct fb_info_platinum *info = (struct fb_info_platinum *) fb;
 	struct fb_par_platinum par;
 
-	if (fb_display[currcon].cmap.len)
-		fb_get_cmap(&fb_display[currcon].cmap, 1, platinum_getcolreg,
+	if (fb_display[info->currcon].cmap.len)
+		fb_get_cmap(&fb_display[info->currcon].cmap, 1, platinum_getcolreg,
 			    fb);
-	currcon = con;
+	info->currcon = con;
 
 	platinum_var_to_par(&fb_display[con].var, &par, info);
 	platinum_set_par(&par, info);
@@ -351,7 +348,7 @@
 	return 0;
 }
 
-static void platinum_blank(int blank,  struct fb_info *fb)
+static int platinum_blank(int blank,  struct fb_info *fb)
 {
 /*
  *  Blank the screen if blank_mode != 0, else unblank. If blank == NULL
@@ -378,7 +375,7 @@
 	out_le32(&info->platinum_regs->ctrl.r, ctrl);
 */
 /* TODO: Figure out how the heck to powerdown this thing! */
-    return;
+    return 0;
 }
 
 static int platinum_getcolreg(u_int regno, u_int *red, u_int *green,
@@ -429,20 +426,6 @@
 	return 0;
 }
 
-static void do_install_cmap(int con, struct fb_info *info)
-{
-	if (con != currcon)
-		return;
-	if (fb_display[con].cmap.len)
-		fb_set_cmap(&fb_display[con].cmap, 1, platinum_setcolreg,
-			    info);
-	else {
-		int size = fb_display[con].var.bits_per_pixel == 16 ? 32 : 256;
-		fb_set_cmap(fb_default_cmap(size), 1, platinum_setcolreg,
-			    info);
-	}
-}
-
 static inline int platinum_vram_reqd(int video_mode, int color_mode)
 {
 	return vmode_attrs[video_mode-1].vres *
@@ -594,11 +577,11 @@
 	info->fb_info.node = NODEV;
 	info->fb_info.fbops = &platinumfb_ops;
 	info->fb_info.disp = disp;
+	info->fb_info.currcon = -1;
 	strcpy(info->fb_info.fontname, fontname);
 	info->fb_info.changevar = NULL;
 	info->fb_info.switch_con = &platinum_switch;
 	info->fb_info.updatevar = &platinum_updatevar;
-	info->fb_info.blank = &platinum_blank;
 	info->fb_info.flags = FBINFO_FLAG_DEFAULT;
 
 	for (j = 0; j < 16; j++) {
diff -Nru a/drivers/video/pm2fb.c b/drivers/video/pm2fb.c
--- a/drivers/video/pm2fb.c	Sun Apr 28 20:10:39 2002
+++ b/drivers/video/pm2fb.c	Sun Apr 28 20:10:39 2002
@@ -375,7 +375,7 @@
 static struct fbgen_hwswitch pm2fb_hwswitch={
 	pm2fb_detect, pm2fb_encode_fix, pm2fb_decode_var,
 	pm2fb_encode_var, pm2fb_get_par, pm2fb_set_par,
-	pm2fb_getcolreg, pm2fb_setcolreg, pm2fb_pan_display,
+	pm2fb_getcolreg, pm2fb_pan_display,
 	pm2fb_blank, pm2fb_set_disp
 };
 
@@ -385,8 +385,10 @@
 	fb_get_var:	fbgen_get_var,
 	fb_set_var:	fbgen_set_var,
 	fb_get_cmap:	fbgen_get_cmap,
-	fb_set_cmap:	fbgen_set_cmap,
+	fb_set_cmap:	gen_set_cmap,
+	fb_setcolreg:	pm2fb_setcolreg,
 	fb_pan_display:	fbgen_pan_display,
+	fb_blank:	fbgen_blank,
 };
 
 /***************************************************************************
@@ -1809,7 +1811,7 @@
 
 	save_flags(flags);
 	cli();
-	disp->screen_base = i->regions.v_fb;
+	i->gen.info.screen_base = i->regions.v_fb;
 	switch (depth=((struct pm2fb_par* )par)->depth) {
 #ifdef FBCON_HAS_CFB8
 		case 8:
@@ -2087,11 +2089,10 @@
 	strcpy(fb_info.gen.info.fontname, pm2fb_options.font);
 	fb_info.gen.info.switch_con=&fbgen_switch;
 	fb_info.gen.info.updatevar=&fbgen_update_var;
-	fb_info.gen.info.blank=&fbgen_blank;
 	fbgen_get_var(&fb_info.disp.var, -1, &fb_info.gen.info);
 	fbgen_do_set_var(&fb_info.disp.var, 1, &fb_info.gen);
 	fbgen_set_disp(-1, &fb_info.gen);
-	fbgen_install_cmap(0, &fb_info.gen);
+	do_install_cmap(0, &fb_info.gen.info);
 	if (register_framebuffer(&fb_info.gen.info)<0) {
 		printk(KERN_ERR "pm2fb: unable to register.\n");
 		MOD_DEC_USE_COUNT;
diff -Nru a/drivers/video/pmag-ba-fb.c b/drivers/video/pmag-ba-fb.c
--- a/drivers/video/pmag-ba-fb.c	Sun Apr 28 20:10:39 2002
+++ b/drivers/video/pmag-ba-fb.c	Sun Apr 28 20:10:39 2002
@@ -90,7 +90,6 @@
 struct pmagbafb_par {
 };
 
-static int currcon = 0;
 struct pmagbafb_par current_par;
 
 static void pmagbafb_encode_var(struct fb_var_screeninfo *var,
@@ -270,38 +269,13 @@
 	return 0;
 }
 
-
-static int pmagbafb_ioctl(struct inode *inode, struct file *file,
-			  unsigned int cmd, unsigned long arg, int con,
-			  struct fb_info *info)
-{
-	return -EINVAL;
-}
-
 static int pmagbafb_switch(int con, struct fb_info *info)
 {
 	pmagba_do_fb_set_var(&fb_display[con].var, 1);
-	currcon = con;
-
+	info->currcon = con;
 	return 0;
 }
 
-/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */
-
-static void pmagbafb_blank(int blank, struct fb_info *info)
-{
-	/* Not supported */
-}
-
-static int pmagbafb_open(struct fb_info *info, int user)
-{
-	/*
-	 * Nothing, only a usage count for the moment
-	 */
-	MOD_INC_USE_COUNT;
-	return (0);
-}
-
 static void pmagbafb_set_disp(int con, struct pmag_ba_my_fb_info *info)
 {
 	struct fb_fix_screeninfo fix;
@@ -314,7 +288,6 @@
 
 	pmagbafb_get_fix(&fix, con, (struct fb_info *) info);
 
-	display->screen_base = (char *) fix.smem_start;
 	display->visual = fix.visual;
 	display->type = fix.type;
 	display->type_aux = fix.type_aux;
@@ -328,24 +301,13 @@
 	display->dispsw = &fbcon_cfb8;
 }
 
-static int pmagbafb_release(struct fb_info *info, int user)
-{
-	MOD_DEC_USE_COUNT;
-	return (0);
-}
-
 static struct fb_ops pmagbafb_ops = {
-	owner:THIS_MODULE,
-	fb_open:pmagbafb_open,
-	fb_release:pmagbafb_release,
-	fb_get_fix:pmagbafb_get_fix,
-	fb_get_var:pmagbafb_get_var,
-	fb_set_var:pmagbafb_set_var,
-	fb_get_cmap:pmagbafb_get_cmap,
-	fb_set_cmap:pmagbafb_set_cmap,
-	fb_ioctl:pmagbafb_ioctl,
-	fb_mmap:0,
-	fb_rasterimg:0
+	owner:		THIS_MODULE,
+	fb_get_fix:	pmagbafb_get_fix,
+	fb_get_var:	pmagbafb_get_var,
+	fb_set_var:	pmagbafb_set_var,
+	fb_get_cmap:	pmagbafb_get_cmap,
+	fb_set_cmap:	pmagbafb_set_cmap,
 };
 
 int __init pmagbafb_init_one(int slot)
@@ -388,16 +350,17 @@
 	ip->info.changevar = NULL;
 	ip->info.node = NODEV;
 	ip->info.fbops = &pmagbafb_ops;
+	ip->info.screen_base = ip->pmagba_fb_start;
 	ip->info.disp = &disp;
+	ip->info.currcon = -1;
 	ip->info.switch_con = &pmagbafb_switch;
 	ip->info.updatevar = &pmagba_fb_update_var;
-	ip->info.blank = &pmagbafb_blank;
 	ip->info.flags = FBINFO_FLAG_DEFAULT;
 
 	pmagba_do_fb_set_var(&pmagbafb_defined, 1);
 	pmagbafb_get_var(&disp.var, -1, (struct fb_info *) ip);
 	pmagbafb_set_disp(-1, ip);
-
+	
 	if (register_framebuffer((struct fb_info *) ip) < 0)
 		return 1;
 
diff -Nru a/drivers/video/pmagb-b-fb.c b/drivers/video/pmagb-b-fb.c
--- a/drivers/video/pmagb-b-fb.c	Sun Apr 28 20:10:30 2002
+++ b/drivers/video/pmagb-b-fb.c	Sun Apr 28 20:10:30 2002
@@ -93,7 +93,6 @@
 struct pmagbbfb_par {
 };
 
-static int currcon = 0;
 struct pmagbbfb_par current_par;
 
 static void pmagbbfb_encode_var(struct fb_var_screeninfo *var,
@@ -273,38 +272,13 @@
 	return 0;
 }
 
-
-static int pmagbbfb_ioctl(struct inode *inode, struct file *file,
-			  unsigned int cmd, unsigned long arg, int con,
-			  struct fb_info *info)
-{
-	return -EINVAL;
-}
-
 static int pmagbbfb_switch(int con, struct fb_info *info)
 {
 	pmagbb_do_fb_set_var(&fb_display[con].var, 1);
-	currcon = con;
-
+	info->currcon = con;
 	return 0;
 }
 
-/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */
-
-static void pmagbbfb_blank(int blank, struct fb_info *info)
-{
-	/* Not supported */
-}
-
-static int pmagbbfb_open(struct fb_info *info, int user)
-{
-	/*
-	 * Nothing, only a usage count for the moment
-	 */
-	MOD_INC_USE_COUNT;
-	return (0);
-}
-
 static void pmagbbfb_set_disp(int con, struct pmagb_b_my_fb_info *info)
 {
 	struct fb_fix_screeninfo fix;
@@ -317,7 +291,6 @@
 
 	pmagbbfb_get_fix(&fix, con, (struct fb_info *) info);
 
-	display->screen_base = (char *) fix.smem_start;
 	display->visual = fix.visual;
 	display->type = fix.type;
 	display->type_aux = fix.type_aux;
@@ -331,24 +304,13 @@
 	display->dispsw = &fbcon_cfb8;
 }
 
-static int pmagbbfb_release(struct fb_info *info, int user)
-{
-	MOD_DEC_USE_COUNT;
-	return (0);
-}
-
 static struct fb_ops pmagbbfb_ops = {
-	owner:THIS_MODULE,
-	fb_open:pmagbbfb_open,
-	fb_release:pmagbbfb_release,
-	fb_get_fix:pmagbbfb_get_fix,
-	fb_get_var:pmagbbfb_get_var,
-	fb_set_var:pmagbbfb_set_var,
-	fb_get_cmap:pmagbbfb_get_cmap,
-	fb_set_cmap:pmagbbfb_set_cmap,
-	fb_ioctl:pmagbbfb_ioctl,
-	fb_mmap:0,
-	fb_rasterimg:0
+	owner:		THIS_MODULE,
+	fb_get_fix:	pmagbbfb_get_fix,
+	fb_get_var:	pmagbbfb_get_var,
+	fb_set_var:	pmagbbfb_set_var,
+	fb_get_cmap:	pmagbbfb_get_cmap,
+	fb_set_cmap:	pmagbbfb_set_cmap,
 };
 
 int __init pmagbbfb_init_one(int slot)
@@ -391,10 +353,12 @@
 	ip->info.changevar = NULL;
 	ip->info.node = NODEV;
 	ip->info.fbops = &pmagbbfb_ops;
+	ip->info.screen_base = (char *) ip->pmagbb_fb_start;
+	fix->smem_len = info->pmagbb_fb_size;
 	ip->info.disp = &disp;
+	ip->info.currcon = -1;
 	ip->info.switch_con = &pmagbbfb_switch;
 	ip->info.updatevar = &pmagbb_fb_update_var;
-	ip->info.blank = &pmagbbfb_blank;
 	ip->info.flags = FBINFO_FLAG_DEFAULT;
 
 	pmagbb_do_fb_set_var(&pmagbbfb_defined, 1);
diff -Nru a/drivers/video/pvr2fb.c b/drivers/video/pvr2fb.c
--- a/drivers/video/pvr2fb.c	Sun Apr 28 20:10:31 2002
+++ b/drivers/video/pvr2fb.c	Sun Apr 28 20:10:31 2002
@@ -157,7 +157,6 @@
 	
 } currentpar;
 
-static int currcon = 0;
 static int currbpp;
 static struct display disp;
 static struct fb_info fb_info;
@@ -214,6 +213,9 @@
                              struct fb_info *info);
 static int pvr2fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
                              struct fb_info *info);
+static int pvr2fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+                            u_int transp, struct fb_info *info);
+static int pvr2fb_blank(int blank, struct fb_info *info);
 
 	/*
 	 * Interface to the low level console driver
@@ -221,19 +223,15 @@
 
 static int pvr2fbcon_switch(int con, struct fb_info *info);
 static int pvr2fbcon_updatevar(int con, struct fb_info *info);
-static void pvr2fbcon_blank(int blank, struct fb_info *info);
 
 	/*
 	 * Internal/hardware-specific routines
 	 */
 
-static void do_install_cmap(int con, struct fb_info *info);
 static u_long get_line_length(int xres_virtual, int bpp);
 static void set_color_bitfields(struct fb_var_screeninfo *var);
 static int pvr2_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
                             u_int *transp, struct fb_info *info);
-static int pvr2_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-                            u_int transp, struct fb_info *info);
 
 static int pvr2_encode_fix(struct fb_fix_screeninfo *fix,
                              struct pvr2fb_par *par);
@@ -260,7 +258,9 @@
 	fb_set_var:	pvr2fb_set_var,
 	fb_get_cmap:	pvr2fb_get_cmap,
 	fb_set_cmap:	pvr2fb_set_cmap,
+	fb_setcolreg:	pvr2fb_setcolreg,
 	fb_pan_display: pvr2fb_pan_display,
+	fb_blank:	pvr2fb_blank,
 };
 
 static struct fb_videomode pvr2_modedb[] __initdata = {
@@ -381,7 +381,6 @@
 			struct fb_fix_screeninfo fix;
 
 			pvr2_encode_fix(&fix, &par);
-			display->screen_base = (char *)fix.smem_start;
 			display->scrollmode = SCROLL_YREDRAW;
 			display->visual = fix.visual;
 			display->type = fix.type;
@@ -422,7 +421,7 @@
 				return err;
 			do_install_cmap(con, info);
 		}
-		if (con == currcon)
+		if (con == info->currcon)
 			pvr2_set_var(&display->var);
 	}
 
@@ -448,7 +447,7 @@
 		    fb_display[con].var.yres_virtual)
 		    return -EINVAL;
 	}
-	if (con == currcon)
+	if (con == info->currcon)
 		pvr2_pan_var(var);
 	fb_display[con].var.xoffset = var->xoffset;
 	fb_display[con].var.yoffset = var->yoffset;
@@ -465,7 +464,7 @@
 static int pvr2fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
                              struct fb_info *info)
 {
-	if (con == currcon) /* current console? */
+	if (con == info->currcon) /* current console? */
 		return fb_get_cmap(cmap, kspc, pvr2_getcolreg, info);
 	else if (fb_display[con].cmap.len) /* non default colormap? */
 		fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
@@ -488,8 +487,8 @@
 					 0)))
 			 return err;
 	}
-	if (con == currcon)                     /* current console? */
-		return fb_set_cmap(cmap, kspc, pvr2_setcolreg, info);
+	if (con == info->currcon)                     /* current console? */
+		return fb_set_cmap(cmap, kspc, info);
 	else
 		fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
 
@@ -499,10 +498,10 @@
 static int pvr2fbcon_switch(int con, struct fb_info *info)
 {
 	/* Do we have to save the colormap? */
-	if (fb_display[currcon].cmap.len)
-		fb_get_cmap(&fb_display[currcon].cmap, 1, pvr2_getcolreg, info);
+	if (fb_display[info->currcon].cmap.len)
+		fb_get_cmap(&fb_display[info->currcon].cmap, 1, pvr2_getcolreg, info);
 
-	currcon = con;
+	info->currcon = con;
 	pvr2_set_var(&fb_display[con].var);
 	/* Install new colormap */
 	do_install_cmap(con, info);
@@ -515,24 +514,11 @@
 	return 0;
 }
 
-static void pvr2fbcon_blank(int blank, struct fb_info *info)
+static int pvr2fb_blank(int blank, struct fb_info *info)
 {
 	do_blank = blank ? blank : -1;
 }
 
-/* Setup the colormap */
-
-static void do_install_cmap(int con, struct fb_info *info)
-{
-	if (con != currcon)
-		return;
-	if (fb_display[con].cmap.len)
-		fb_set_cmap(&fb_display[con].cmap, 1, pvr2_setcolreg, info);
-	else
-		fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
-                            1, pvr2_setcolreg, info);
-}
-
 static inline u_long get_line_length(int xres_virtual, int bpp)
 {
 	return (u_long)((((xres_virtual*bpp)+31)&~31) >> 3);
@@ -575,7 +561,7 @@
 	return 0;
 }
 	
-static int pvr2_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+static int pvr2fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
                             u_int transp, struct fb_info *info)
 {
 	if (regno > 255)
@@ -1036,10 +1022,11 @@
 	fb_info.changevar = NULL;
 	fb_info.node = NODEV;
 	fb_info.fbops = &pvr2fb_ops;
+	fb_info.screen_base = (char *) videomemory;
 	fb_info.disp = &disp;
+	fb_info.currcon = -1;
 	fb_info.switch_con = &pvr2fbcon_switch;
 	fb_info.updatevar = &pvr2fbcon_updatevar;
-	fb_info.blank = &pvr2fbcon_blank;
 	fb_info.flags = FBINFO_FLAG_DEFAULT;
 	memset(&var, 0, sizeof(var));
 
diff -Nru a/drivers/video/q40fb.c b/drivers/video/q40fb.c
--- a/drivers/video/q40fb.c	Sun Apr 28 20:10:34 2002
+++ b/drivers/video/q40fb.c	Sun Apr 28 20:10:34 2002
@@ -22,7 +22,6 @@
 #define FBIOSETSCROLLMODE   0x4611
 
 #define Q40_PHYS_SCREEN_ADDR 0xFE800000
-static unsigned long q40_screen_addr;
 
 static u16 fbcon_cmap_cfb16[16];
 
@@ -38,13 +37,12 @@
 			 struct fb_info *info);
 static int q40fb_set_cmap(struct fb_cmap *cmap,int kspc,int con,
 			 struct fb_info *info);
-static int q40fb_ioctl(struct inode *inode, struct file *file,
-		      unsigned int cmd, unsigned long arg, int con,
-		      struct fb_info *info);
+static int q40fb_setcolreg(unsigned regno, unsigned red, unsigned green,
+			   unsigned blue, unsigned transp,
+			   const struct fb_info *info);
 
 static int q40con_switch(int con, struct fb_info *info);
 static int q40con_updatevar(int con, struct fb_info *info);
-static void q40con_blank(int blank, struct fb_info *info);
 
 static void q40fb_set_disp(int con, struct fb_info *info);
 
@@ -57,11 +55,9 @@
 	fb_set_var:	q40fb_set_var,
 	fb_get_cmap:	q40fb_get_cmap,
 	fb_set_cmap:	q40fb_set_cmap,
-	fb_ioctl:	q40fb_ioctl,
+	fb_setcolreg:	q40fb_setcolreg,
 };
 
-static int currcon=0;
-
 static char q40fb_name[]="Q40";
 
 static int q40fb_get_fix(struct fb_fix_screeninfo *fix, int con,
@@ -70,7 +66,7 @@
 	memset(fix, 0, sizeof(struct fb_fix_screeninfo));
 
 	strcpy(fix->id,"Q40");
-	fix->smem_start=q40_screen_addr;
+	fix->smem_start = info->screen_base;
 	fix->smem_len=1024*1024;
 	fix->type=FB_TYPE_PACKED_PIXELS;
 	fix->type_aux=0;
@@ -185,9 +181,9 @@
     return 0;
 }
 
-static int q40_setcolreg(unsigned regno, unsigned red, unsigned green,
-			 unsigned blue, unsigned transp,
-			 const struct fb_info *info)
+static int q40fb_setcolreg(unsigned regno, unsigned red, unsigned green,
+			   unsigned blue, unsigned transp,
+			   const struct fb_info *info)
 {
     /*
      *  Set a single color register. The values supplied have a 16 bit
@@ -211,7 +207,7 @@
 			 struct fb_info *info)
 {
 #if 1
-	if (con == currcon) /* current console? */
+	if (con == info->currcon) /* current console? */
 		return fb_get_cmap(cmap, kspc, q40_getcolreg, info);
 	else if (fb_display[con].cmap.len) /* non default colormap? */
 		fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
@@ -238,8 +234,8 @@
 					 0)))
 			return err;
 	}
-	if (con == currcon)			/* current console? */
-		return fb_set_cmap(cmap, kspc, q40_setcolreg, info);
+	if (con == info->currcon)			/* current console? */
+		return fb_set_cmap(cmap, kspc, info);
 	else
 		fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
 	return 0;
@@ -250,34 +246,6 @@
 #endif
 }
 
-static int q40fb_ioctl(struct inode *inode, struct file *file,
-		      unsigned int cmd, unsigned long arg, int con,
-		      struct fb_info *info)
-{
-#if 0
-        unsigned long i;
-	struct display *display;
-
-	if (con>=0)
-	  display = &fb_display[con];
-	else
-	  display = &disp[0];
-
-        if (cmd == FBIOSETSCROLLMODE)
-	  {
-	    i = verify_area(VERIFY_READ, (void *)arg, sizeof(unsigned long));
-	    if (!i) 
-	      {
-		copy_from_user(&i, (void *)arg, sizeof(unsigned long));
-		display->scrollmode = i;
-	      }
-	    q40_updatescrollmode(display);
-	    return i;
-	  }
-#endif
-	return -EINVAL;
-}
-
 static void q40fb_set_disp(int con, struct fb_info *info)
 {
   struct fb_fix_screeninfo fix;
@@ -292,7 +260,6 @@
 
   if (con<0) con=0;
 
-   display->screen_base = fix.smem_start;
    display->visual = fix.visual;
    display->type = fix.type;
    display->type_aux = fix.type_aux;
@@ -318,19 +285,19 @@
         if ( !MACH_IS_Q40)
 	  return -ENXIO;
 #if 0
-        q40_screen_addr = kernel_map(Q40_PHYS_SCREEN_ADDR, 1024*1024,
+        fb_info.screen_base = kernel_map(Q40_PHYS_SCREEN_ADDR, 1024*1024,
 					   KERNELMAP_NO_COPYBACK, NULL);
 #else
-	q40_screen_addr = Q40_PHYS_SCREEN_ADDR; /* mapped in q40/config.c */
+	fb_info.screen_base = Q40_PHYS_SCREEN_ADDR; /* mapped in q40/config.c */
 #endif
 
 	fb_info.changevar=NULL;
 	strcpy(&fb_info.modename[0],q40fb_name);
 	fb_info.fontname[0]=0;
 	fb_info.disp=disp;
+	fb_info.currcon = -1;
 	fb_info.switch_con=&q40con_switch;
 	fb_info.updatevar=&q40con_updatevar;
-	fb_info.blank=&q40con_blank;	
 	fb_info.node = NODEV;
 	fb_info.fbops = &q40fb_ops;
 	fb_info.flags = FBINFO_FLAG_DEFAULT;  /* not as module for now */
@@ -353,19 +320,13 @@
 	
 static int q40con_switch(int con, struct fb_info *info)
 { 
-	currcon=con;
-	
+	info->currcon=con;
 	return 0;
-
 }
 
 static int q40con_updatevar(int con, struct fb_info *info)
 {
 	return 0;
-}
-
-static void q40con_blank(int blank, struct fb_info *info)
-{
 }
 
 MODULE_LICENSE("GPL");
diff -Nru a/drivers/video/radeonfb.c b/drivers/video/radeonfb.c
--- a/drivers/video/radeonfb.c	Sun Apr 28 20:10:39 2002
+++ b/drivers/video/radeonfb.c	Sun Apr 28 20:10:39 2002
@@ -239,7 +239,6 @@
 	struct pci_dev *pdev;
 
 	struct display disp;
-	int currcon;
 	struct display *currcon_display;
 
 	struct { u8 red, green, blue, pad; } palette[256];
@@ -565,21 +564,19 @@
                              struct fb_info *info);
 static int radeonfb_get_cmap (struct fb_cmap *cmap, int kspc, int con,
                               struct fb_info *info);
-static int radeonfb_set_cmap (struct fb_cmap *cmap, int kspc, int con,
-                              struct fb_info *info);
+static int radeonfb_setcolreg (unsigned regno, unsigned red, unsigned green,
+                               unsigned blue, unsigned transp, struct fb_info *info);
 static int radeonfb_pan_display (struct fb_var_screeninfo *var, int con,
                                  struct fb_info *info);
+static int radeonfb_blank (int blank, struct fb_info *info);
 static int radeonfb_ioctl (struct inode *inode, struct file *file, unsigned int cmd,
                            unsigned long arg, int con, struct fb_info *info);
 static int radeonfb_switch (int con, struct fb_info *info);
 static int radeonfb_updatevar (int con, struct fb_info *info);
-static void radeonfb_blank (int blank, struct fb_info *info);
 static int radeon_get_cmap_len (const struct fb_var_screeninfo *var);
 static int radeon_getcolreg (unsigned regno, unsigned *red, unsigned *green,
                              unsigned *blue, unsigned *transp,
                              struct fb_info *info);
-static int radeon_setcolreg (unsigned regno, unsigned red, unsigned green,
-                             unsigned blue, unsigned transp, struct fb_info *info);
 static void radeon_set_dispsw (struct radeonfb_info *rinfo, struct display *disp);
 static void radeon_save_state (struct radeonfb_info *rinfo,
                                struct radeon_regs *save);
@@ -609,8 +606,10 @@
 	fb_get_var:		radeonfb_get_var,
 	fb_set_var:		radeonfb_set_var,
 	fb_get_cmap:		radeonfb_get_cmap,
-	fb_set_cmap:		radeonfb_set_cmap,
+	fb_set_cmap:		gen_set_cmap,
+	fb_setcolreg:		radeonfb_setcolreg,
 	fb_pan_display:		radeonfb_pan_display,
+	fb_blank:		radeonfb_blank,
 	fb_ioctl:		radeonfb_ioctl,
 };
 
@@ -1308,13 +1307,14 @@
         info->node = NODEV;
         info->flags = FBINFO_FLAG_DEFAULT;
         info->fbops = &radeon_fb_ops;
-        info->display_fg = NULL;
+        info->screen_base = (char*)rinfo->fb_base;
+  	info->display_fg = NULL;
+	info->currcon = -1;
         strncpy (info->fontname, fontname, sizeof (info->fontname));
         info->fontname[sizeof (info->fontname) - 1] = 0;
         info->changevar = NULL;
         info->switch_con = radeonfb_switch;
         info->updatevar = radeonfb_updatevar;
-        info->blank = radeonfb_blank;
 
         if (radeon_init_disp (rinfo) < 0)
                 return -1;   
@@ -1382,7 +1382,6 @@
                 
         disp->dispsw_data = NULL;
         
-        disp->screen_base = (char*)rinfo->fb_base;
         disp->type = FB_TYPE_PACKED_PIXELS;
         disp->type_aux = 0;
         disp->ypanstep = 1;
@@ -1431,25 +1430,6 @@
         return;
 }
                         
-
-
-static void do_install_cmap(int con, struct fb_info *info)
-{
-        struct radeonfb_info *rinfo = (struct radeonfb_info *) info;
-                
-        if (con != rinfo->currcon)
-                return;
-                
-        if (fb_display[con].cmap.len)
-                fb_set_cmap(&fb_display[con].cmap, 1, radeon_setcolreg, info);
-        else {
-                int size = fb_display[con].var.bits_per_pixel == 8 ? 256 : 32;
-                fb_set_cmap(fb_default_cmap(size), 1, radeon_setcolreg, info);
-        }
-}
-
-
-
 static int radeonfb_do_maximize(struct radeonfb_info *rinfo,
                                 struct fb_var_screeninfo *var,
                                 struct fb_var_screeninfo *v,
@@ -1711,7 +1691,7 @@
                 
         disp = (con < 0) ? rinfo->info.disp : &fb_display[con];
         
-        if (con == rinfo->currcon) {
+        if (con == info->currcon) {
                 int rc = fb_get_cmap (cmap, kspc, radeon_getcolreg, info);
                 return rc;
         } else if (disp->cmap.len)
@@ -1741,8 +1721,8 @@
                         return err;
         }
  
-        if (con == rinfo->currcon) {
-                int rc = fb_set_cmap (cmap, kspc, radeon_setcolreg, info);
+        if (con == info->currcon) {
+                int rc = fb_set_cmap (cmap, kspc, info);
                 return rc;
         } else
                 fb_copy_cmap (cmap, &disp->cmap, kspc ? 0 : 1);
@@ -1789,7 +1769,7 @@
         
         disp = (con < 0) ? rinfo->info.disp : &fb_display[con];
                 
-        if (rinfo->currcon >= 0) {
+        if (info->currcon >= 0) {
                 cmap = &(rinfo->currcon_display->cmap);
                 if (cmap->len)
                         fb_get_cmap (cmap, 1, radeon_getcolreg, info);
@@ -1802,7 +1782,7 @@
                 switchcon = 1;
         
         if (switchcon) {
-                rinfo->currcon = con;
+                info->currcon = con;
                 rinfo->currcon_display = disp;
                 disp->var.activate = FB_ACTIVATE_NOW;
         
@@ -1833,7 +1813,7 @@
         return rc;
 }
 
-static void radeonfb_blank (int blank, struct fb_info *info)
+static int radeonfb_blank (int blank, struct fb_info *info)
 {
         struct radeonfb_info *rinfo = (struct radeonfb_info *) info;
         u32 val = INREG(CRTC_EXT_CNTL);
@@ -1858,10 +1838,9 @@
         }
         
         OUTREG(CRTC_EXT_CNTL, val);
+	return 0;
 }
 
-
-
 static int radeon_get_cmap_len (const struct fb_var_screeninfo *var)
 {
         int rc = 16;            /* reasonable default */
@@ -1899,8 +1878,8 @@
 
 
 
-static int radeon_setcolreg (unsigned regno, unsigned red, unsigned green,
-                             unsigned blue, unsigned transp, struct fb_info *info)
+static int radeonfb_setcolreg (unsigned regno, unsigned red, unsigned green,
+                               unsigned blue, unsigned transp, struct fb_info *info)
 {
         struct radeonfb_info *rinfo = (struct radeonfb_info *) info;
 	u32 pindex;
diff -Nru a/drivers/video/retz3fb.c b/drivers/video/retz3fb.c
--- a/drivers/video/retz3fb.c	Sun Apr 28 20:10:38 2002
+++ b/drivers/video/retz3fb.c	Sun Apr 28 20:10:38 2002
@@ -105,7 +105,6 @@
 	volatile unsigned char *regs;
 	unsigned long physfbmem;
 	unsigned long physregs;
-	int currcon;
 	int current_par_valid; /* set to 0 by memset */
 	int blitbusy;
 	struct display disp;
@@ -273,8 +272,10 @@
 			   struct fb_info *info);
 static int retz3fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			    struct fb_info *info);
-static int retz3fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-			    struct fb_info *info);
+static int retz3fb_setcolreg(unsigned int regno, unsigned int red,
+			     unsigned int green, unsigned int blue,
+			     unsigned int transp, struct fb_info *info);
+static int retz3fb_blank(int blank, struct fb_info *info);
 
 
 /*
@@ -284,7 +285,6 @@
 int retz3fb_init(void);
 static int z3fb_switch(int con, struct fb_info *info);
 static int z3fb_updatevar(int con, struct fb_info *info);
-static void z3fb_blank(int blank, struct fb_info *info);
 
 
 /*
@@ -320,9 +320,6 @@
 static int retz3_getcolreg(unsigned int regno, unsigned int *red,
 			   unsigned int *green, unsigned int *blue,
 			   unsigned int *transp, struct fb_info *info);
-static int retz3_setcolreg(unsigned int regno, unsigned int red,
-			   unsigned int green, unsigned int blue,
-			   unsigned int transp, struct fb_info *info);
 
 /*
  *    Internal routines
@@ -332,7 +329,6 @@
 static void retz3fb_set_par(struct fb_info *info, struct retz3fb_par *par);
 static int do_fb_set_var(struct fb_info *info,
 			 struct fb_var_screeninfo *var, int isactive);
-static void do_install_cmap(int con, struct fb_info *info);
 static void retz3fb_set_disp(int con, struct fb_info *info);
 static int get_video_mode(const char *name);
 
@@ -896,9 +892,9 @@
  *    Set a single color register. Return != 0 for invalid regno.
  */
 
-static int retz3_setcolreg(unsigned int regno, unsigned int red,
-			   unsigned int green, unsigned int blue,
-			   unsigned int transp, struct fb_info *info)
+static int retz3fb_setcolreg(unsigned int regno, unsigned int red,
+			     unsigned int green, unsigned int blue,
+			     unsigned int transp, struct fb_info *info)
 {
 	struct retz3_fb_info *zinfo = retz3info(info);
 	volatile unsigned char *regs = zinfo->regs;
@@ -1099,20 +1095,6 @@
 	return 0;
 }
 
-
-static void do_install_cmap(int con, struct fb_info *info)
-{
-	struct retz3_fb_info *zinfo = retz3info(info);
-
-	if (con != zinfo->currcon)
-		return;
-	if (fb_display[con].cmap.len)
-		fb_set_cmap(&fb_display[con].cmap, 1, retz3_setcolreg, info);
-	else
-		fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
-					    1, retz3_setcolreg, info);
-}
-
 /*
  *    Get the Fixed Part of the Display
  */
@@ -1166,7 +1148,6 @@
 	if (con == -1)
 		con = 0;
 
-	display->screen_base = zinfo->fbmem;
 	display->visual = fix.visual;
 	display->type = fix.type;
 	display->type_aux = fix.type_aux;
@@ -1218,7 +1199,7 @@
 	else
 		display = &zinfo->disp;	/* used during initialization */
 
-	if ((err = do_fb_set_var(info, var, con == zinfo->currcon)))
+	if ((err = do_fb_set_var(info, var, con == info->currcon)))
 		return err;
 	if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
 		oldxres = display->var.xres;
@@ -1296,7 +1277,7 @@
 {
 	struct retz3_fb_info *zinfo = retz3info(info);
 
-	if (con == zinfo->currcon) /* current console? */
+	if (con == info->currcon) /* current console? */
 		return(fb_get_cmap(cmap, kspc, retz3_getcolreg, info));
 	else if (fb_display[con].cmap.len) /* non default colormap? */
 		fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
@@ -1306,41 +1287,44 @@
 	return 0;
 }
 
-
 /*
- *    Set the Colormap
+ *    Blank the display.
  */
 
-static int retz3fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-			    struct fb_info *info)
+static int retz3fb_blank(int blank, struct fb_info *info)
 {
-	int err;
 	struct retz3_fb_info *zinfo = retz3info(info);
+	volatile unsigned char *regs = retz3info(info)->regs;
+	short i;
 
-	if (!fb_display[con].cmap.len) {       /* no colormap allocated? */
-		if ((err = fb_alloc_cmap(&fb_display[con].cmap,
-					 1<<fb_display[con].var.bits_per_pixel,
-					 0)))
-			return err;
-	}
-	if (con == zinfo->currcon)              /* current console? */
-		return(fb_set_cmap(cmap, kspc, retz3_setcolreg, info));
+	if (blank)
+		for (i = 0; i < 256; i++){
+			reg_w(regs, VDAC_ADDRESS_W, i);
+			reg_w(regs, VDAC_DATA, 0);
+			reg_w(regs, VDAC_DATA, 0);
+			reg_w(regs, VDAC_DATA, 0);
+		}
 	else
-		fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
+		for (i = 0; i < 256; i++){
+			reg_w(regs, VDAC_ADDRESS_W, i);
+			reg_w(regs, VDAC_DATA, zinfo->color_table[i][0]);
+			reg_w(regs, VDAC_DATA, zinfo->color_table[i][1]);
+			reg_w(regs, VDAC_DATA, zinfo->color_table[i][2]);
+		}
 	return 0;
 }
 
-
 static struct fb_ops retz3fb_ops = {
 	owner:		THIS_MODULE,
 	fb_get_fix:	retz3fb_get_fix,
 	fb_get_var:	retz3fb_get_var,
 	fb_set_var:	retz3fb_set_var,
 	fb_get_cmap:	retz3fb_get_cmap,
-	fb_set_cmap:	retz3fb_set_cmap,
+	fb_set_cmap:	gen_set_cmap,
+	fb_setcolreg:	retz3fb_setcolreg,
+	fb_blank:	retz3fb_blank,
 };
 
-
 int __init retz3fb_setup(char *options)
 {
 	char *this_opt;
@@ -1417,17 +1401,18 @@
 		/* Disable hardware cursor */
 		seq_w(regs, SEQ_CURSOR_Y_INDEX, 0x00);
 
-		retz3_setcolreg (255, 56<<8, 100<<8, 160<<8, 0, fb_info);
-		retz3_setcolreg (254, 0, 0, 0, 0, fb_info);
+		retz3fb_setcolreg (255, 56<<8, 100<<8, 160<<8, 0, fb_info);
+		retz3fb_setcolreg (254, 0, 0, 0, 0, fb_info);
 
 		strcpy(fb_info->modename, retz3fb_name);
 		fb_info->changevar = NULL;
 		fb_info->node = NODEV;
 		fb_info->fbops = &retz3fb_ops;
+		fb_info->screen_base = zinfo->fbmem;
 		fb_info->disp = &zinfo->disp;
+		fb_info->currcon = -1;
 		fb_info->switch_con = &z3fb_switch;
 		fb_info->updatevar = &z3fb_updatevar;
-		fb_info->blank = &z3fb_blank;
 		fb_info->flags = FBINFO_FLAG_DEFAULT;
 		strncpy(fb_info->fontname, fontname, 40);
 
@@ -1465,12 +1450,12 @@
 	struct retz3_fb_info *zinfo = retz3info(info);
 
 	/* Do we have to save the colormap? */
-	if (fb_display[zinfo->currcon].cmap.len)
-		fb_get_cmap(&fb_display[zinfo->currcon].cmap, 1,
+	if (fb_display[info->currcon].cmap.len)
+		fb_get_cmap(&fb_display[info->currcon].cmap, 1,
 			    retz3_getcolreg, info);
 
 	do_fb_set_var(info, &fb_display[con].var, 1);
-	zinfo->currcon = con;
+	info->currcon = con;
 	/* Install new colormap */
 	do_install_cmap(con, info);
 	return 0;
@@ -1488,34 +1473,6 @@
 {
 	return 0;
 }
-
-
-/*
- *    Blank the display.
- */
-
-static void z3fb_blank(int blank, struct fb_info *info)
-{
-	struct retz3_fb_info *zinfo = retz3info(info);
-	volatile unsigned char *regs = retz3info(info)->regs;
-	short i;
-
-	if (blank)
-		for (i = 0; i < 256; i++){
-			reg_w(regs, VDAC_ADDRESS_W, i);
-			reg_w(regs, VDAC_DATA, 0);
-			reg_w(regs, VDAC_DATA, 0);
-			reg_w(regs, VDAC_DATA, 0);
-		}
-	else
-		for (i = 0; i < 256; i++){
-			reg_w(regs, VDAC_ADDRESS_W, i);
-			reg_w(regs, VDAC_DATA, zinfo->color_table[i][0]);
-			reg_w(regs, VDAC_DATA, zinfo->color_table[i][1]);
-			reg_w(regs, VDAC_DATA, zinfo->color_table[i][2]);
-		}
-}
-
 
 /*
  *    Get a Video Mode
diff -Nru a/drivers/video/riva/fbdev.c b/drivers/video/riva/fbdev.c
--- a/drivers/video/riva/fbdev.c	Sun Apr 28 20:10:32 2002
+++ b/drivers/video/riva/fbdev.c	Sun Apr 28 20:10:32 2002
@@ -107,7 +107,7 @@
  *
  * ------------------------------------------------------------------------- */
 
-static void rivafb_blank(int blank, struct fb_info *info);
+static int rivafb_blank(int blank, struct fb_info *info);
 
 extern void riva_setup_accel(struct rivafb_info *rinfo);
 extern inline void wait_for_idle(struct rivafb_info *rinfo);
@@ -662,7 +662,6 @@
 
 	disp->dispsw_data = NULL;
 
-	disp->screen_base = rinfo->fb_base;
 	disp->type = FB_TYPE_PACKED_PIXELS;
 	disp->type_aux = 0;
 	disp->ypanstep = 1;
@@ -725,7 +724,7 @@
  * Sets color register @regnum.
  *
  * CALLED FROM:
- * riva_setcolreg()
+ * rivafb_setcolreg()
  */
 static void riva_wclut(RIVA_HW_INST *chip,
 		       unsigned char regnum, unsigned char red,
@@ -1092,7 +1091,7 @@
  *
  * CALLED FROM:
  * riva_getcolreg()
- * riva_setcolreg()
+ * rivafb_setcolreg()
  * rivafb_get_cmap()
  * rivafb_set_cmap()
  */
@@ -1170,7 +1169,7 @@
 }
 
 /**
- * riva_setcolreg
+ * rivafb_setcolreg
  * @regno: register index
  * @red: red component
  * @green: green component
@@ -1189,15 +1188,15 @@
  * rivafb_set_cmap()
  * fbcmap.c:fb_set_cmap()
  *	fbgen.c:fbgen_get_cmap()
- *	fbgen.c:fbgen_install_cmap()
+ *	fbgen.c:do_install_cmap()
  *		fbgen.c:fbgen_set_var()
  *		fbgen.c:fbgen_switch()
  *		fbgen.c:fbgen_blank()
  *	fbgen.c:fbgen_blank()
  */
-static int riva_setcolreg(unsigned regno, unsigned red, unsigned green,
-			  unsigned blue, unsigned transp,
-			  struct fb_info *info)
+static int rivafb_setcolreg(unsigned regno, unsigned red, unsigned green,
+			    unsigned blue, unsigned transp,
+			    struct fb_info *info)
 {
 	struct rivafb_info *rivainfo = (struct rivafb_info *)info;
 	RIVA_HW_INST *chip = &rivainfo->riva;
@@ -1518,7 +1517,7 @@
 
 	dsp = (con < 0) ? rivainfo->info.disp : &fb_display[con];
 
-	if (con == rivainfo->currcon) {	/* current console? */
+	if (con == info->currcon) {	/* current console? */
 		int rc = fb_get_cmap(cmap, kspc, riva_getcolreg, info);
 		DPRINTK("EXIT - returning %d\n", rc);
 		return rc;
@@ -1556,8 +1555,8 @@
 			return err;
 		}
 	}
-	if (con == rivainfo->currcon) {	/* current console? */
-		int rc = fb_set_cmap(cmap, kspc, riva_setcolreg, info);
+	if (con == info->currcon) {	/* current console? */
+		int rc = fb_set_cmap(cmap, kspc, info);
 		DPRINTK("EXIT - returning %d\n", rc);
 		return rc;
 	} else
@@ -1611,7 +1610,7 @@
 
 	base = var->yoffset * dsp->line_length + var->xoffset;
 
-	if (con == rivainfo->currcon) {
+	if (con == info->currcon) {
 		rivainfo->riva.SetStartAddress(&rivainfo->riva, base);
 	}
 
@@ -1665,11 +1664,11 @@
 
 	dsp = (con < 0) ? rivainfo->info.disp : &fb_display[con];
 
-	if (rivainfo->currcon >= 0) {
+	if (info->currcon >= 0) {
 		/* Do we have to save the colormap? */
 		cmap = &(rivainfo->currcon_display->cmap);
 		DPRINTK("switch1: con = %d, cmap.len = %d\n",
-			 rivainfo->currcon, cmap->len);
+			 info->currcon, cmap->len);
 
 		if (cmap->len) {
 			DPRINTK("switch1a: %p %p %p %p\n", cmap->red,
@@ -1677,7 +1676,7 @@
 			fb_get_cmap(cmap, 1, riva_getcolreg, info);
 		}
 	}
-	rivainfo->currcon = con;
+	info->currcon = con;
 	rivainfo->currcon_display = dsp;
 
 	rivafb_set_var(&dsp->var, con, info);
@@ -1699,7 +1698,7 @@
 	return rc;
 }
 
-static void rivafb_blank(int blank, struct fb_info *info)
+static int rivafb_blank(int blank, struct fb_info *info)
 {
 	unsigned char tmp, vesa;
 	struct rivafb_info *rinfo = (struct rivafb_info *)info;
@@ -1732,6 +1731,7 @@
 	CRTCout(rinfo, 0x1a, vesa);
 
 	DPRINTK("EXIT\n");
+	return 0;
 }
 
 
@@ -1750,7 +1750,9 @@
 	fb_set_var:	rivafb_set_var,
 	fb_get_cmap:	rivafb_get_cmap,
 	fb_set_cmap:	rivafb_set_cmap,
+	fb_setcolreg:	rivafb_setcolreg,
 	fb_pan_display:	rivafb_pan_display,
+	fb_blank:	rivafb_blank,
 	fb_ioctl:	rivafb_ioctl,
 	fb_rasterimg:	rivafb_rasterimg,
 };
@@ -1814,17 +1816,17 @@
 	info->node = NODEV;
 	info->flags = FBINFO_FLAG_DEFAULT;
 	info->fbops = &riva_fb_ops;
-
+	info->screen_base = rinfo->fb_base;
 	/* FIXME: set monspecs to what??? */
 
 	info->display_fg = NULL;
+	info->currcon = -1;
 	strncpy(info->fontname, fontname, sizeof(info->fontname));
 	info->fontname[sizeof(info->fontname) - 1] = 0;
 
 	info->changevar = NULL;
 	info->switch_con = rivafb_switch;
 	info->updatevar = rivafb_updatevar;
-	info->blank = rivafb_blank;
 
 	if (riva_init_disp(rinfo) < 0)	/* must be done last */
 		return -1;
diff -Nru a/drivers/video/sa1100fb.c b/drivers/video/sa1100fb.c
--- a/drivers/video/sa1100fb.c	Sun Apr 28 20:10:32 2002
+++ b/drivers/video/sa1100fb.c	Sun Apr 28 20:10:32 2002
@@ -806,8 +806,7 @@
  */
 static inline struct fb_var_screeninfo *get_con_var(struct fb_info *info, int con)
 {
-	struct sa1100fb_info *fbi = (struct sa1100fb_info *)info;
-	return (con == fbi->currcon || con == -1) ? &fbi->fb.var : &fb_display[con].var;
+	return (con == info->currcon || con == -1) ? &info->var : &fb_display[con].var;
 }
 
 /*
@@ -824,8 +823,7 @@
  */
 static inline struct fb_cmap *get_con_cmap(struct fb_info *info, int con)
 {
-	struct sa1100fb_info *fbi = (struct sa1100fb_info *)info;
-	return (con == fbi->currcon || con == -1) ? &fbi->fb.cmap : &fb_display[con].cmap;
+	return (con == info->currcon || con == -1) ? &info->cmap : &fb_display[con].cmap;
 }
 
 static inline u_int
@@ -883,7 +881,7 @@
 		   u_int trans, struct fb_info *info)
 {
 	struct sa1100fb_info *fbi = (struct sa1100fb_info *)info;
-	struct display *disp = get_con_display(info, fbi->currcon);
+	struct display *disp = get_con_display(info, info->currcon);
 	u_int val;
 	int ret = 1;
 
@@ -1042,7 +1040,7 @@
 	fbi->palette_cpu = (u16 *)(fbi->map_cpu + PAGE_SIZE - palette_mem_size);
 	fbi->palette_dma = fbi->map_dma + PAGE_SIZE - palette_mem_size;
 
-	fb_set_cmap(&fbi->fb.cmap, 1, sa1100fb_setcolreg, &fbi->fb);
+	fb_set_cmap(&fbi->fb.cmap, 1, &fbi->fb);
 
 	/* Set board control register to handle new color depth */
 	sa1100fb_set_truecolor(var->bits_per_pixel >= 16);
@@ -1139,7 +1137,6 @@
 		break;
 	}
 
-	display->screen_base	= fbi->screen_cpu;
 	display->next_line	= display->line_length;
 	display->type		= fbi->fb.fix.type;
 	display->type_aux	= fbi->fb.fix.type_aux;
@@ -1189,7 +1186,7 @@
 		fbi->fb.changevar(con);
 
 	/* If the current console is selected, activate the new var. */
-	if (con != fbi->currcon)
+	if (con != fbi->fb.currcon)
 		return 0;
 
 	sa1100fb_hw_set_var(dvar, fbi);
@@ -1206,14 +1203,14 @@
 	int err = 0;
 
 	if (con == -1)
-		con = fbi->currcon;
+		con = info->currcon;
 
 	/* no colormap allocated? (we always have "this" colour map allocated) */
 	if (con >= 0)
 		err = fb_alloc_cmap(&fb_display[con].cmap, fbi->palette_size, 0);
 
-	if (!err && con == fbi->currcon)
-		err = fb_set_cmap(cmap, kspc, sa1100fb_setcolreg, info);
+	if (!err && con == info->currcon)
+		err = fb_set_cmap(cmap, kspc, info);
 
 	if (!err)
 		fb_copy_cmap(cmap, dcmap, kspc ? 0 : 1);
@@ -1268,6 +1265,8 @@
 	fb_set_var:	sa1100fb_set_var,
 	fb_get_cmap:	sa1100fb_get_cmap,
 	fb_set_cmap:	sa1100fb_set_cmap,
+	fb_setcolreg:	sa1100fb_setcolreg,
+	fb_blank:	sa1100fb_blank,
 };
 
 /*
@@ -1285,11 +1284,11 @@
 
 	DPRINTK("con=%d info->modename=%s\n", con, fbi->fb.modename);
 
-	if (con == fbi->currcon)
+	if (con == info->currcon)
 		return 0;
 
-	if (fbi->currcon >= 0) {
-		disp = fb_display + fbi->currcon;
+	if (info->currcon >= 0) {
+		disp = fb_display + info->currcon;
 
 		/*
 		 * Save the old colormap and video mode.
@@ -1300,7 +1299,7 @@
 			fb_copy_cmap(&fbi->fb.cmap, &disp->cmap, 0);
 	}
 
-	fbi->currcon = con;
+	info->currcon = con;
 	disp = fb_display + con;
 
 	/*
@@ -1357,7 +1356,7 @@
  * 	12 and 16 bpp modes don't really use the palette, so this will not
  *      blank the display in all modes.  
  */
-static void sa1100fb_blank(int blank, struct fb_info *info)
+static int sa1100fb_blank(int blank, struct fb_info *info)
 {
 	struct sa1100fb_info *fbi = (struct sa1100fb_info *)info;
 	int i;
@@ -1383,9 +1382,10 @@
 			sa1100fb_blank_helper(blank);
 		if (fbi->fb.disp->visual == FB_VISUAL_PSEUDOCOLOR ||
 		    fbi->fb.disp->visual == FB_VISUAL_STATIC_PSEUDOCOLOR)
-			fb_set_cmap(&fbi->fb.cmap, 1, sa1100fb_setcolreg, info);
+			fb_set_cmap(&fbi->fb.cmap, 1, info);
 		sa1100fb_schedule_task(fbi, C_ENABLE);
 	}
+	return 0;
 }
 
 static int sa1100fb_updatevar(int con, struct fb_info *info)
@@ -1986,7 +1986,7 @@
 					&fbi->map_dma);
 
 	if (fbi->map_cpu) {
-		fbi->screen_cpu = fbi->map_cpu + PAGE_SIZE;
+		fbi->fb.screen_base = fbi->map_cpu + PAGE_SIZE;
 		fbi->screen_dma = fbi->map_dma + PAGE_SIZE;
 		fbi->fb.fix.smem_start = fbi->screen_dma;
 	}
@@ -2012,7 +2012,7 @@
 
 	memset(fbi, 0, sizeof(struct sa1100fb_info) + sizeof(struct display));
 
-	fbi->currcon		= -1;
+	fbi->fb.currcon		= -1;
 
 	strcpy(fbi->fb.fix.id, SA1100_NAME);
 
@@ -2037,10 +2037,10 @@
 	fbi->fb.changevar	= NULL;
 	fbi->fb.switch_con	= sa1100fb_switch;
 	fbi->fb.updatevar	= sa1100fb_updatevar;
-	fbi->fb.blank		= sa1100fb_blank;
 	fbi->fb.flags		= FBINFO_FLAG_DEFAULT;
 	fbi->fb.node		= NODEV;
 	fbi->fb.monspecs	= monspecs;
+	fbi->fb.currcon		= -1;
 	fbi->fb.disp		= (struct display *)(fbi + 1);
 	fbi->fb.pseudo_palette	= (void *)(fbi->fb.disp + 1);
 
diff -Nru a/drivers/video/sa1100fb.h b/drivers/video/sa1100fb.h
--- a/drivers/video/sa1100fb.h	Sun Apr 28 20:10:31 2002
+++ b/drivers/video/sa1100fb.h	Sun Apr 28 20:10:31 2002
@@ -63,8 +63,6 @@
 
 struct sa1100fb_info {
 	struct fb_info		fb;
-	signed int		currcon;
-
 	struct sa1100fb_rgb	*rgb[NR_RGB];
 
 	u_int			max_bpp;
diff -Nru a/drivers/video/sbusfb.c b/drivers/video/sbusfb.c
--- a/drivers/video/sbusfb.c	Sun Apr 28 20:10:32 2002
+++ b/drivers/video/sbusfb.c	Sun Apr 28 20:10:32 2002
@@ -56,7 +56,6 @@
 int sbusfb_init(void);
 int sbusfb_setup(char*);
 
-static int currcon;
 static int defx_margin = -1, defy_margin = -1;
 static char fontname[40] __initdata = { 0 };
 static int curblink __initdata = 1;
@@ -89,6 +88,9 @@
 			struct fb_info *info);
 static int sbusfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
 			struct fb_info *info);
+static int sbusfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+			    u_int transp, struct fb_info *info);
+static int sbusfb_blank(int blank, struct fb_info *info);
 static int sbusfb_ioctl(struct inode *inode, struct file *file, u_int cmd,
 			    u_long arg, int con, struct fb_info *info);
 static void sbusfb_cursor(struct display *p, int mode, int x, int y);
@@ -101,8 +103,6 @@
 
 static int sbusfbcon_switch(int con, struct fb_info *info);
 static int sbusfbcon_updatevar(int con, struct fb_info *info);
-static void sbusfbcon_blank(int blank, struct fb_info *info);
-
 
     /*
      *  Internal routines
@@ -110,9 +110,6 @@
 
 static int sbusfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
 			    u_int *transp, struct fb_info *info);
-static int sbusfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-			    u_int transp, struct fb_info *info);
-static void do_install_cmap(int con, struct fb_info *info);
 
 static struct fb_ops sbusfb_ops = {
 	owner:		THIS_MODULE,
@@ -123,6 +120,8 @@
 	fb_set_var:	sbusfb_set_var,
 	fb_get_cmap:	sbusfb_get_cmap,
 	fb_set_cmap:	sbusfb_set_cmap,
+	fb_setcolreg:	sbusfb_setcolreg,
+	fb_blank:	sbusfb_blank,
 	fb_ioctl:	sbusfb_ioctl,
 	fb_mmap:	sbusfb_mmap,
 };
@@ -265,7 +264,7 @@
 		rects [15] = fb->var.yres_virtual;
 		(*fb->fill)(fb, p, s, 4, rects);
 	} else {
-		unsigned char *fb_base = p->screen_base, *q;
+		unsigned char *fb_base = fb->info.screen_base, *q;
 		int skip_bytes = fb->y_margin * fb->var.xres_virtual;
 		int scr_size = fb->var.xres_virtual * fb->var.yres_virtual;
 		int h, he, incr, size;
@@ -523,8 +522,8 @@
 		if ((err = fb_alloc_cmap(&disp->cmap, 1<<disp->var.bits_per_pixel, 0)))
 			return err;
 	}
-	if (con == currcon) {			/* current console? */
-		err = fb_set_cmap(cmap, kspc, sbusfb_setcolreg, info);
+	if (con == info->currcon) {			/* current console? */
+		err = fb_set_cmap(cmap, kspc, info);
 		if (!err) {
 			struct fb_info_sbusfb *fb = sbusfbinfo(info);
 			
@@ -762,8 +761,8 @@
 	int lastconsole;
     
 	/* Do we have to save the colormap? */
-	if (fb_display[currcon].cmap.len)
-		fb_get_cmap(&fb_display[currcon].cmap, 1, sbusfb_getcolreg, info);
+	if (fb_display[info->currcon].cmap.len)
+		fb_get_cmap(&fb_display[info->currcon].cmap, 1, sbusfb_getcolreg, info);
 
 	if (info->display_fg) {
 		lastconsole = info->display_fg->vc_num;
@@ -780,7 +779,7 @@
 		fb->x_margin = x_margin; fb->y_margin = y_margin;
 		sbusfb_clear_margin(&fb_display[con], 0);
 	}
-	currcon = con;
+	info->currcon = con;
 	/* Install new colormap */
 	do_install_cmap(con, info);
 	return 0;
@@ -800,7 +799,7 @@
      *  Blank the display.
      */
 
-static void sbusfbcon_blank(int blank, struct fb_info *info)
+static int sbusfb_blank(int blank, struct fb_info *info)
 {
     struct fb_info_sbusfb *fb = sbusfbinfo(info);
     
@@ -852,22 +851,6 @@
 	return 0;
 }
 
-
-static void do_install_cmap(int con, struct fb_info *info)
-{
-	struct fb_info_sbusfb *fb = sbusfbinfo(info);
-	
-	if (con != currcon)
-		return;
-	if (fb_display[con].cmap.len)
-		fb_set_cmap(&fb_display[con].cmap, 1, sbusfb_setcolreg, info);
-	else
-		fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
-			    1, sbusfb_setcolreg, info);
-	if (fb->loadcmap)
-		(*fb->loadcmap)(fb, &fb_display[con], 0, 256);
-}
-
 static int sbusfb_set_font(struct display *p, int width, int height)
 {
 	int margin;
@@ -977,7 +960,7 @@
 	fb->emulations[0] = fbtype;
 	
 #ifndef __sparc_v9__
-	disp->screen_base = (unsigned char *)prom_getintdefault(node, "address", 0);
+	fb->info.screen_base = (unsigned char *)prom_getintdefault(node, "address", 0);
 #endif
 	
 	type->fb_height = h = prom_getintdefault(node, "height", 900);
@@ -1022,11 +1005,11 @@
 	fb->info.node = NODEV;
 	fb->info.fbops = &sbusfb_ops;
 	fb->info.disp = disp;
+	fb->info.currcon = -1;
 	strcpy(fb->info.fontname, fontname);
 	fb->info.changevar = NULL;
 	fb->info.switch_con = &sbusfbcon_switch;
 	fb->info.updatevar = &sbusfbcon_updatevar;
-	fb->info.blank = &sbusfbcon_blank;
 	fb->info.flags = FBINFO_FLAG_DEFAULT;
 	
 	fb->cursor.hwsize.fbx = 32;
diff -Nru a/drivers/video/sgivwfb.c b/drivers/video/sgivwfb.c
--- a/drivers/video/sgivwfb.c	Sun Apr 28 20:10:36 2002
+++ b/drivers/video/sgivwfb.c	Sun Apr 28 20:10:36 2002
@@ -52,7 +52,6 @@
 u_long                sgivwfb_mem_phys;
 u_long                sgivwfb_mem_size;
 
-static volatile char  *fbmem;
 static asregs         *regs;
 static struct fb_info fb_info;
 static struct { u_char red, green, blue, pad; } palette[256];
@@ -62,7 +61,6 @@
 static int            ywrap      = 0;
 
 /* console related variables */
-static int currcon = 0;
 static struct display disp;
 
 static union {
@@ -99,8 +97,8 @@
 			   struct fb_info *info);
 static int sgivwfb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			    struct fb_info *info);
-static int sgivwfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-			    struct fb_info *info);
+static int sgivwfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+			     u_int transp, struct fb_info *info);
 static int sgivwfb_mmap(struct fb_info *info, struct file *file,
                         struct vm_area_struct *vma);
 
@@ -110,7 +108,8 @@
 	fb_get_var:	sgivwfb_get_var,
 	fb_set_var:	sgivwfb_set_var,
 	fb_get_cmap:	sgivwfb_get_cmap,
-	fb_set_cmap:	sgivwfb_set_cmap,
+	fb_set_cmap:	gen_set_cmap,
+	fb_setcolreg:	sgivwfb_setcolreg,
 	fb_mmap:	sgivwfb_mmap,
 };
 
@@ -120,7 +119,6 @@
 int sgivwfb_init(void);
 static int sgivwfbcon_switch(int con, struct fb_info *info);
 static int sgivwfbcon_updatevar(int con, struct fb_info *info);
-static void sgivwfbcon_blank(int blank, struct fb_info *info);
 
 /*
  *  Internal routines
@@ -132,9 +130,6 @@
 			       struct fb_var_screeninfo *var);
 static int sgivwfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
 			     u_int *transp, struct fb_info *info);
-static int sgivwfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-			     u_int transp, struct fb_info *info);
-static void do_install_cmap(int con, struct fb_info *info);
 
 static unsigned long get_line_length(int xres_virtual, int bpp)
 {
@@ -556,17 +551,6 @@
   return 0;
 }
 
-static void do_install_cmap(int con, struct fb_info *info)
-{
-    if (con != currcon)
-	return;
-    if (fb_display[con].cmap.len)
-	fb_set_cmap(&fb_display[con].cmap, 1, sgivwfb_setcolreg, info);
-    else
-	fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), 1,
-		    sgivwfb_setcolreg, info);
-}
-
 /* ---------------------------------------------------- */
 
 /*
@@ -755,7 +739,6 @@
 	     var->xres_virtual, var->yres_virtual);
       activate_par(&par_current);
       sgivwfb_encode_fix(&fix, var);
-      display->screen_base = (char *)fbmem;
       display->visual = fix.visual;
       display->type = fix.type;
       display->type_aux = fix.type_aux;
@@ -805,7 +788,7 @@
 static int sgivwfb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			    struct fb_info *info)
 {
-  if (con == currcon) /* current console? */
+  if (con == info->currcon) /* current console? */
     return fb_get_cmap(cmap, kspc, sgivwfb_getcolreg, info);
   else if (fb_display[con].cmap.len) /* non default colormap? */
     fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
@@ -815,26 +798,6 @@
   return 0;
 }
 
-/*
- *  Set the Colormap
- */
-static int sgivwfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-			    struct fb_info *info)
-{
-  int err;
-
-  if (!fb_display[con].cmap.len) {	/* no colormap allocated? */
-    int size = fb_display[con].var.bits_per_pixel == 16 ? 32 : 256;
-    if ((err = fb_alloc_cmap(&fb_display[con].cmap, size, 0)))
-      return err;
-  }
-  if (con == currcon)			/* current console? */
-    return fb_set_cmap(cmap, kspc, sgivwfb_setcolreg, info);
-  else
-    fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
-  return 0;
-}
-
 static int sgivwfb_mmap(struct fb_info *info, struct file *file,
                         struct vm_area_struct *vma)
 {
@@ -893,14 +856,14 @@
   fb_info.node = NODEV;
   fb_info.fbops = &sgivwfb_ops;
   fb_info.disp = &disp;
+  fb_info.currcon = -1;	
   fb_info.switch_con = &sgivwfbcon_switch;
   fb_info.updatevar = &sgivwfbcon_updatevar;
-  fb_info.blank = &sgivwfbcon_blank;
   fb_info.flags = FBINFO_FLAG_DEFAULT;
 
-  fbmem = ioremap_nocache((unsigned long)sgivwfb_mem_phys, sgivwfb_mem_size);
-  if (!fbmem) {
-    printk(KERN_ERR "sgivwfb: couldn't ioremap fbmem\n");
+  fb_info.screen_base = ioremap_nocache((unsigned long)sgivwfb_mem_phys, sgivwfb_mem_size);
+  if (!fb_info.screen_base) {
+    printk(KERN_ERR "sgivwfb: couldn't ioremap screen_base\n");
     goto fail_ioremap_fbmem;
   }
 
@@ -918,7 +881,7 @@
   return 0;
 
  fail_register_framebuffer:
-  iounmap((char*)fbmem);
+  iounmap((char*)fb_info.screen_base);
  fail_ioremap_fbmem:
   iounmap(regs);
  fail_ioremap_regs:
@@ -928,10 +891,10 @@
 static int sgivwfbcon_switch(int con, struct fb_info *info)
 {
   /* Do we have to save the colormap? */
-  if (fb_display[currcon].cmap.len)
-    fb_get_cmap(&fb_display[currcon].cmap, 1, sgivwfb_getcolreg, info);
+  if (fb_display[info->currcon].cmap.len)
+    fb_get_cmap(&fb_display[info->currcon].cmap, 1, sgivwfb_getcolreg, info);
 
-  currcon = con;
+  info->currcon = con;
   /* Install new colormap */
   do_install_cmap(con, info);
   return 0;
@@ -946,14 +909,6 @@
     return 0;
 }
 
-/*
- *  Blank the display.
- */
-static void sgivwfbcon_blank(int blank, struct fb_info *info)
-{
-    /* Nothing */
-}
-
 #ifdef MODULE
 MODULE_LICENSE("GPL");
 
@@ -967,7 +922,7 @@
   unregister_framebuffer(&fb_info);
   dbe_TurnOffDma();
   iounmap(regs);
-  iounmap(fbmem);
+  iounmap(&fb_info.screen_base);
 }
 
 #endif /* MODULE */
diff -Nru a/drivers/video/sis/sis_main.c b/drivers/video/sis/sis_main.c
--- a/drivers/video/sis/sis_main.c	Sun Apr 28 20:10:37 2002
+++ b/drivers/video/sis/sis_main.c	Sun Apr 28 20:10:37 2002
@@ -204,9 +204,9 @@
 
 /* -------------------- Export functions ----------------------------- */
 
-static void sis_get_glyph (SIS_GLYINFO * gly)
+static void sis_get_glyph (struct fb_info *info, SIS_GLYINFO * gly)
 {
-	struct display *p = &fb_display[currcon];
+	struct display *p = &fb_display[info->currcon];
 	u16 c;
 	u8 *cdat;
 	int widthb;
@@ -399,8 +399,8 @@
 	return 0;
 }
 
-static int sis_setcolreg (unsigned regno, unsigned red, unsigned green, unsigned blue,
-	       unsigned transp, struct fb_info *fb_info)
+static int sisfb_setcolreg (unsigned regno, unsigned red, unsigned green, unsigned blue,
+	      		    unsigned transp, struct fb_info *fb_info)
 {
 
 	if (regno >= video_cmap_len)
@@ -631,14 +631,13 @@
 
 static void sisfb_do_install_cmap (int con, struct fb_info *info)
 {
-	if (con != currcon)
+	if (con != info->currcon)
 		return;
 
 	if (fb_display[con].cmap.len)
-		fb_set_cmap (&fb_display[con].cmap, 1, sis_setcolreg, info);
+		fb_set_cmap (&fb_display[con].cmap, 1, info);
 	else
-		fb_set_cmap (fb_default_cmap (video_cmap_len), 1,
-			     sis_setcolreg, info);
+		fb_set_cmap (fb_default_cmap (video_cmap_len), 1, info);
 }
 
 /* --------------- Chip-dependent Routines --------------------------- */
@@ -2034,7 +2033,7 @@
 
 	fb_display[con].var.activate = FB_ACTIVATE_NOW;
 
-	if (sisfb_do_set_var (var, con == currcon, info)) {
+	if (sisfb_do_set_var (var, con == info->currcon, info)) {
 		sisfb_crtc_to_var (var);
 		return -EINVAL;
 	}
@@ -2061,7 +2060,7 @@
 
 static int sisfb_get_cmap (struct fb_cmap *cmap, int kspc, int con, struct fb_info *info)
 {
-	if (con == currcon)
+	if (con == info->currcon)
 		return fb_get_cmap (cmap, kspc, sis_getcolreg, info);
 	else if (fb_display[con].cmap.len)
 		fb_copy_cmap (&fb_display[con].cmap, cmap, kspc ? 0 : 2);
@@ -2081,8 +2080,8 @@
 		if (err)
 			return err;
 	}
-	if (con == currcon)
-		return fb_set_cmap (cmap, kspc, sis_setcolreg, info);
+	if (con == info->currcon)
+		return fb_set_cmap (cmap, kspc, info);
 	else
 		fb_copy_cmap (cmap, &fb_display[con].cmap, kspc ? 0 : 1);
 	return 0;
@@ -2103,7 +2102,7 @@
 		sis_free (*(unsigned long *) arg);
 		break;
 	case FBIOGET_GLYPH:
-		sis_get_glyph ((SIS_GLYINFO *) arg);
+		sis_get_glyph (info, (SIS_GLYINFO *) arg);
 		break;
 	case FBIOGET_HWCINFO:
 		{
@@ -2158,7 +2157,7 @@
 
 	if (off >= len) {
 		off -= len;
-		sisfb_get_var (&var, currcon, info);
+		sisfb_get_var (&var, info->currcon, info);
 		if (var.accel_flags)
 			return -EINVAL;
 		start = (unsigned long) ivideo.mmio_base;
@@ -2182,14 +2181,16 @@
 }
 
 static struct fb_ops sisfb_ops = {
-	owner:THIS_MODULE,
-	fb_get_fix:sisfb_get_fix,
-	fb_get_var:sisfb_get_var,
-	fb_set_var:sisfb_set_var,
-	fb_get_cmap:sisfb_get_cmap,
-	fb_set_cmap:sisfb_set_cmap,
-	fb_ioctl:sisfb_ioctl,
-	fb_mmap:sisfb_mmap,
+	owner:		THIS_MODULE,
+	fb_get_fix:	sisfb_get_fix,
+	fb_get_var:	sisfb_get_var,
+	fb_set_var:	sisfb_set_var,
+	fb_get_cmap:	sisfb_get_cmap,
+	fb_set_cmap:	sisfb_set_cmap,
+	fb_setcolreg:	sisfb_setcolreg,
+	fb_blank:	sisfb_blank,
+	fb_ioctl:	sisfb_ioctl,
+	fb_mmap:	sisfb_mmap,
 };
 
 /* ------------ Interface to the low level console driver -------------*/
@@ -2203,17 +2204,17 @@
 {
 	int cols, rows;
 
-	if (fb_display[currcon].cmap.len)
-		fb_get_cmap (&fb_display[currcon].cmap, 1, sis_getcolreg, info);
+	if (fb_display[info->currcon].cmap.len)
+		fb_get_cmap (&fb_display[info->currcon].cmap, 1, sis_getcolreg, info);
 
 	fb_display[con].var.activate = FB_ACTIVATE_NOW;
 
-	if (!memcmp(&fb_display[con].var, &fb_display[currcon].var, sizeof (struct fb_var_screeninfo))) {
-		currcon = con;
+	if (!memcmp(&fb_display[con].var, &fb_display[info->currcon].var, sizeof (struct fb_var_screeninfo))) {
+		info->currcon = con;
 		return 1;
 	}
 
-	currcon = con;
+	info->currcon = con;
 
 	sisfb_do_set_var (&fb_display[con].var, 1, info);
 
@@ -2231,7 +2232,7 @@
 
 }
 
-static void sisfb_blank (int blank, struct fb_info *info)
+static int sisfb_blank(int blank, struct fb_info *info)
 {
 	u8 reg;
 
@@ -2245,6 +2246,7 @@
 
 	vgawb (CRTC_ADR, 0x17);
 	vgawb (CRTC_DATA, reg);
+	return 0;
 }
 
 int sisfb_setup (char *options)
@@ -2257,8 +2259,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;
 
@@ -2769,9 +2770,9 @@
 	fb_info.node = NODEV;
 	fb_info.fbops = &sisfb_ops;
 	fb_info.disp = &disp;
+	fb_info.currcon = -1;
 	fb_info.switch_con = &sisfb_switch;
 	fb_info.updatevar = &sisfb_update_var;
-	fb_info.blank = &sisfb_blank;
 	fb_info.flags = FBINFO_FLAG_DEFAULT;
 
 	sisfb_set_disp (-1, &default_var);
diff -Nru a/drivers/video/sis/sis_main.h b/drivers/video/sis/sis_main.h
--- a/drivers/video/sis/sis_main.h	Sun Apr 28 20:10:33 2002
+++ b/drivers/video/sis/sis_main.h	Sun Apr 28 20:10:33 2002
@@ -242,7 +242,6 @@
 static int sisfb_crt1off = 0;
 static int sisfb_inverse = 0;
 static int sisvga_enabled = 0;
-static int currcon = 0;
 /*karl*/
 static int sisfb_tvmode = 0;
 static int sisfb_mem = 0;
@@ -599,8 +598,9 @@
 			  struct fb_info *info);
 static int sisfb_get_cmap (struct fb_cmap *cmap, int kspc, int con,
 			   struct fb_info *info);
-static int sisfb_set_cmap (struct fb_cmap *cmap, int kspc, int con,
-			   struct fb_info *info);
+static int sisfb_setcolreg (unsigned regno, unsigned red, unsigned green,
+			    unsigned blue, unsigned transp,
+			    struct fb_info *fb_info);
 static int sisfb_ioctl (struct inode *inode, struct file *file,
 			unsigned int cmd, unsigned long arg, int con,
 			struct fb_info *info);
@@ -609,7 +609,7 @@
 int sisfb_init (void);
 static int sisfb_update_var (int con, struct fb_info *info);
 static int sisfb_switch (int con, struct fb_info *info);
-static void sisfb_blank (int blank, struct fb_info *info);
+static int sisfb_blank(int blank, struct fb_info *info);
 
 /* hardware access routines */
 void sisfb_set_reg1 (u16 port, u16 index, u16 data);
@@ -630,13 +630,9 @@
 static int sis_getcolreg (unsigned regno, unsigned *red, unsigned *green,
 			  unsigned *blue, unsigned *transp,
 			  struct fb_info *fb_info);
-static int sis_setcolreg (unsigned regno, unsigned red, unsigned green,
-			  unsigned blue, unsigned transp,
-			  struct fb_info *fb_info);
 static int sisfb_do_set_var (struct fb_var_screeninfo *var, int isactive,
 			     struct fb_info *info);
 static void sisfb_set_disp (int con, struct fb_var_screeninfo *var);
-static void sisfb_do_install_cmap (int con, struct fb_info *info);
 
 /* Chip-dependent Routines */
 #ifdef CONFIG_FB_SIS_300
@@ -668,7 +664,7 @@
 static void sisfb_crtc_to_var (struct fb_var_screeninfo *var);
 
 /* Export functions  */
-static void sis_get_glyph (SIS_GLYINFO * gly);
+static void sis_get_glyph (struct fb_info *info, SIS_GLYINFO * gly);
 void sis_dispinfo (struct ap_data *rec);
 void sis_malloc (struct sis_memreq *req);
 void sis_free (unsigned long base);
diff -Nru a/drivers/video/skeletonfb.c b/drivers/video/skeletonfb.c
--- a/drivers/video/skeletonfb.c	Sun Apr 28 20:10:32 2002
+++ b/drivers/video/skeletonfb.c	Sun Apr 28 20:10:32 2002
@@ -70,7 +70,6 @@
 
 static struct fb_var_screeninfo default_var;
 
-static int currcon = 0;
 static int inverse = 0;
 
 int xxxfb_init(void);
@@ -176,9 +175,9 @@
     return 0;
 }
 
-static int xxx_setcolreg(unsigned regno, unsigned red, unsigned green,
-			 unsigned blue, unsigned transp,
-			 const struct fb_info *info)
+static int xxxfb_setcolreg(unsigned regno, unsigned red, unsigned green,
+			   unsigned blue, unsigned transp,
+			   const struct fb_info *info)
 {
     /*
      *  Set a single color register. The values supplied have a 16 bit
@@ -254,7 +253,6 @@
      *  If you don't have any appropriate operations, you must fill in a
      *  pointer to dummy operations, and there will be no text output.
      */
-    disp->screen_base = virtual_frame_buffer_address;
 #ifdef FBCON_HAS_CFB8
     if (is_cfb8) {
 	disp->dispsw = fbcon_cfb8;
@@ -287,7 +285,7 @@
 
 struct fbgen_hwswitch xxx_switch = {
     xxx_detect, xxx_encode_fix, xxx_decode_var, xxx_encode_var, xxx_get_par,
-    xxx_set_par, xxx_getcolreg, xxx_setcolreg, xxx_pan_display, xxx_blank,
+    xxx_set_par, xxx_getcolreg, xxx_pan_display, xxx_blank,
     xxx_set_disp
 };
 
@@ -308,16 +306,17 @@
     fb_info.gen.info.changevar = NULL;
     fb_info.gen.info.node = NODEV;
     fb_info.gen.info.fbops = &xxxfb_ops;
+    fb_info.gen.info.screen_base = virtual_frame_buffer_address;
     fb_info.gen.info.disp = &disp;
+    fb_info.gen.info.currcon = -1;
     fb_info.gen.info.switch_con = &xxxfb_switch;
     fb_info.gen.info.updatevar = &xxxfb_update_var;
-    fb_info.gen.info.blank = &xxxfb_blank;
     fb_info.gen.info.flags = FBINFO_FLAG_DEFAULT;
     /* This should give a reasonable default video mode */
     fbgen_get_var(&disp.var, -1, &fb_info.gen.info);
     fbgen_do_set_var(&disp.var, 1, &fb_info.gen);
     fbgen_set_disp(-1, &fb_info.gen);
-    fbgen_install_cmap(0, &fb_info.gen);
+    do_install_cmap(0, &fb_info.gen);
     if (register_framebuffer(&fb_info.gen.info) < 0)
 	return -EINVAL;
     printk(KERN_INFO "fb%d: %s frame buffer device\n", GET_FB_IDX(fb_info.gen.info.node),
@@ -388,8 +387,10 @@
 	fb_get_var:	fbgen_get_var,
 	fb_set_var:	fbgen_set_var,
 	fb_get_cmap:	fbgen_get_cmap,
-	fb_set_cmap:	fbgen_set_cmap,
+	fb_set_cmap:	gen_set_cmap,
+	fb_setcolreg:	xxxfb_setcolreg,
 	fb_pan_display:	fbgen_pan_display,
+	fb_blank:	fbgen_blank,
 	fb_ioctl:	xxxfb_ioctl,   /* optional */
 };
 
diff -Nru a/drivers/video/sstfb.c b/drivers/video/sstfb.c
--- a/drivers/video/sstfb.c	Sun Apr 28 20:10:37 2002
+++ b/drivers/video/sstfb.c	Sun Apr 28 20:10:37 2002
@@ -176,7 +176,6 @@
 
 /********/
 
-int currcon; /* =0 */
 int num_sst; /* =0*/		/* number of initialized boards */
 
 /* initialized by setup */
@@ -224,8 +223,8 @@
                          int con, struct fb_info *info);
 static int sstfb_get_cmap(struct fb_cmap *cmap, int kspc,
                           int con, struct fb_info *info);
-static int sstfb_set_cmap(struct fb_cmap *cmap, int kspc,
-                          int con, struct fb_info *info);
+static int sstfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+                           u_int transp, struct fb_info *info);
 static int sstfb_pan_display(struct fb_var_screeninfo *var,
                              int con, struct fb_info *info);
 static int sstfb_ioctl(struct inode *inode, struct file *file,
@@ -235,14 +234,10 @@
 /* Interface to the low level console driver */
 static int sstfbcon_switch(int con, struct fb_info *info);
 static int sstfbcon_updatevar(int con, struct fb_info *info);
-static void sstfbcon_blank(int blank, struct fb_info *info);
 
 /* Internal routines */
-static void sstfb_install_cmap(int con, struct fb_info *info);
 static int sstfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
                            u_int *transp, struct fb_info *info);
-static int sstfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-                           u_int transp, struct fb_info *info);
 
 static int sstfb_set_par(const struct sstfb_par *par,
                           struct sstfb_info *sst_info);
@@ -284,7 +279,8 @@
 	fb_get_var:	sstfb_get_var,
 	fb_set_var:	sstfb_set_var,
 	fb_get_cmap:	sstfb_get_cmap,
-	fb_set_cmap:	sstfb_set_cmap,
+	fb_set_cmap:	gen_set_cmap,
+	fb_setcolreg:	sstfb_setcolreg,
 	fb_pan_display:	sstfb_pan_display,
 	fb_ioctl:	sstfb_ioctl,
 };
@@ -500,26 +496,6 @@
 	sst_dac_write(DACREG_DATA_I, val);
 }
 
-/*
- *
- *  Internal routines
- *
- */
-
-static void sstfb_install_cmap(int con, struct fb_info *info)
-{
-	f_dprintk("sstfb_install_cmap(con: %d)\n",con);
-	f_ddprintk("currcon: %d\n", currcon);
-	if (con != currcon)
-		return;
-	if (fb_display[con].cmap.len)
-		fb_set_cmap(&fb_display[con].cmap, 1, sstfb_setcolreg, info);
-	else
-		fb_set_cmap(
-			fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
-			1, sstfb_setcolreg, info);
-}
-
 static int sstfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
                            u_int *transp, struct fb_info *info)
 {
@@ -826,7 +802,7 @@
 	if ((old_xres != var->xres) || (old_yres != var->yres)
 	    || (old_bpp != var->bits_per_pixel)) {
 		/* 2-3  lignes redondantes avec get_fix */
-		display->screen_base = (char *) sst_info->video.vbase;
+		info->screen_base = (char *) sst_info->video.vbase;
 		display->visual = FB_VISUAL_TRUECOLOR;
 		display->type = FB_TYPE_PACKED_PIXELS;
 		display->type_aux = 0;
@@ -864,7 +840,7 @@
 		}
 	}
 
-	if ((con <0) || (con==currcon)) {
+	if ((con <0) || (con==info->currcon)) {
 		sstfb_set_par (&par, sst_info);
 	}
 	print_var(var, "var");
@@ -873,45 +849,22 @@
 	if (old_bpp != var->bits_per_pixel) {
 	    if ((err = fb_alloc_cmap(&display->cmap, 0, 0)))
 		return err;
-	    sstfb_install_cmap(con, info);
+	    do_install_cmap(con, info);
 	}
 
 	return 0;
 #undef sst_info
 }
 
-
-static int sstfb_set_cmap(struct fb_cmap *cmap, int kspc,
-                          int con, struct fb_info *info)
-{
-	struct display *d = (con<0) ? info->disp : fb_display + con;
-
-	f_dprintk("sstfb_set_cmap\n");
-	f_ddprintk("con: %d, currcon: %d, d->cmap.len %d\n",
-		 con, currcon, d->cmap.len);
-
-	if (d->cmap.len != 16 ) {	/* or test if cmap.len == 0 ? */
-		int err;
-		err = fb_alloc_cmap(&d->cmap, 16, 0); /* cmap size=16 */
-		if (err) return err;
-	}
-	if (con == currcon) {
-		return fb_set_cmap(cmap, kspc, sstfb_setcolreg, info);
-	} else {
-		fb_copy_cmap(cmap, &d->cmap, kspc ? 0 : 1);
-	}
-	return 0;
-}
-
 static int sstfb_get_cmap(struct fb_cmap *cmap, int kspc,
                           int con, struct fb_info *info)
 {
 	f_dprintk("sstfb_get_cmap\n");
 	f_ddprintk("con %d, curcon %d, cmap.len %d\n",
-		 con, currcon, fb_display[con].cmap.len);
+		 con, info->currcon, fb_display[con].cmap.len);
 
 	/* FIXME: check if con = -1 ? cf sstfb_set_cmap...  */
-	if (con == currcon)
+	if (con == info->currcon)
 		return fb_get_cmap(cmap, kspc, sstfb_getcolreg, info);
 	else if (fb_display[con].cmap.len)
 		fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
@@ -982,9 +935,9 @@
 	case _IO('F', 0xde):		/* 0x46de */
 		f_dprintk("test color display\n");
 		f_ddprintk("currcon: %d, bpp %d\n",
-			  currcon, fb_display[currcon].var.bits_per_pixel);
+			  info->currcon, fb_display[currcon].var.bits_per_pixel);
 		memset_io(sst_info->video.vbase, 0, sst_info->video.len);
-	switch (fb_display[currcon].var.bits_per_pixel) {
+	switch (fb_display[info->currcon].var.bits_per_pixel) {
 /* FIXME broken : if we call this ioctl from a tty not bound to the fb, we use its depth and not the current one ... */
 	       	case 16:
 			sstfb_test16(sst_info);
@@ -997,7 +950,7 @@
 #  endif
 		default:
 			dprintk("bug line %d: bad depth '%u'\n", __LINE__,
-			        fb_display[currcon].var.bits_per_pixel);
+			        fb_display[info->currcon].var.bits_per_pixel);
 			}
 		return 0;
 	}
@@ -1801,10 +1754,10 @@
 		fb_info.info.flags      = FBINFO_FLAG_DEFAULT;
 		fb_info.info.fbops      = &sstfb_ops;
 		fb_info.info.disp       = &disp;
+		fb_info.info.currcon	= -1;
 		fb_info.info.changevar  = NULL;
 		fb_info.info.switch_con = &sstfbcon_switch;
 		fb_info.info.updatevar  = &sstfbcon_updatevar;
-		fb_info.info.blank      = &sstfbcon_blank;
 		if ( !mode_option &&
 	             !fb_find_mode(&var, &fb_info.info, mode_option,
 		                   NULL, 0, NULL, 16)) {
@@ -1849,15 +1802,15 @@
 	struct sstfb_par par;
 
 	f_dprintk("sstfbcon_switch(con: %d)\n",con);
-	f_ddprintk("currcon: %d\n", currcon);
-	v_dprintk("currcon: %d\n", currcon);
+	f_ddprintk("currcon: %d\n", info->currcon);
+	v_dprintk("currcon: %d\n", info->currcon);
 
-	if (currcon >=  0) {
-		if (fb_display[currcon].cmap.len)
-			fb_get_cmap(&fb_display[currcon].cmap, 1,
+	if (info->currcon >=  0) {
+		if (fb_display[info->currcon].cmap.len)
+			fb_get_cmap(&fb_display[info->currcon].cmap, 1,
 			            sstfb_getcolreg, info);
 	}
-	currcon = con;
+	info->currcon = con;
 	fb_display[con].var.activate = FB_ACTIVATE_NOW;
 
 	print_var(&fb_display[con].var, "&fb_display[con: %d].var",con);
@@ -1865,7 +1818,7 @@
 	if (memcmp(&par,&(sst_info->current_par),sizeof(par))) {
 		sstfb_set_par(&par, sst_info);
 	}
-	sstfb_install_cmap(con, info);
+	do_install_cmap(con, info);
 	return 0;
 #undef sst_info
 }
@@ -1875,12 +1828,6 @@
 	f_dprintk("sstfbcon_updatevar\n");
 	return -EINVAL;
 }
-
-static void sstfbcon_blank(int blank, struct fb_info *info)
-{
-	f_dprintk("sstfbcon_blank(level %d)\n", blank);
-}
-
 
 /* print some squares on the fb (presuming 16bpp)  */
 static void sstfb_test16(struct sstfb_info *sst_info)
diff -Nru a/drivers/video/stifb.c b/drivers/video/stifb.c
--- a/drivers/video/stifb.c	Sun Apr 28 20:10:37 2002
+++ b/drivers/video/stifb.c	Sun Apr 28 20:10:37 2002
@@ -108,19 +108,10 @@
 	return 0;
 }
 
-static int
-sti_setcolreg(unsigned regno, unsigned red, unsigned green,
-	      unsigned blue, unsigned transp, struct fb_info *info)
-{
-	return 0;
-}
-
 static void
 sti_set_disp(const void *par, struct display *disp,
 	     struct fb_info_gen *info)
 {
-	disp->screen_base =
-		(void *) PTR_STI(fb_info.sti->glob_cfg)->region_ptrs[1];
 	disp->dispsw = &fbcon_sti;
 }
 
@@ -129,12 +120,6 @@
 {
 }
 
-static int
-sti_blank(int blank_mode, const struct fb_info *info)
-{
-	return 0;
-}
-
 /* ------------ Interfaces to hardware functions ------------ */
 
 struct fbgen_hwswitch sti_switch = {
@@ -145,9 +130,6 @@
 	get_par:	sti_get_par,
 	set_par:	sti_set_par,
 	getcolreg:	sti_getcolreg,
-	setcolreg:	sti_setcolreg,
-	pan_display:	NULL,
-	blank:		sti_blank,
 	set_disp:	sti_set_disp
 };
 
@@ -173,16 +155,16 @@
 	fb_info.gen.info.changevar = NULL;
 	fb_info.gen.info.switch_con = &fbgen_switch;
 	fb_info.gen.info.updatevar = &fbgen_update_var;
-	fb_info.gen.info.blank = &fbgen_blank;
 	strcpy(fb_info.gen.info.modename, "STI Generic");
 	fb_info.gen.fbhw = &sti_switch;
 	fb_info.gen.fbhw->detect();
-
+	fb_info.gen.info.screen_base = 
+		(void *) PTR_STI(fb_info.sti->glob_cfg)->region_ptrs[1];
 	/* This should give a reasonable default video mode */
 	fbgen_get_var(&disp.var, -1, &fb_info.gen.info);
 	fbgen_do_set_var(&disp.var, 1, &fb_info.gen);
 	fbgen_set_disp(-1, &fb_info.gen);
-	fbgen_install_cmap(0, &fb_info.gen);
+	do_install_cmap(0, &fb_info.gen);
 	pdc_console_die();
 	if (register_framebuffer(&fb_info.gen.info) < 0)
 		return -EINVAL;
@@ -218,13 +200,11 @@
 
 static struct fb_ops stifb_ops = {
 	owner:		THIS_MODULE,
-	fb_open:	NULL,
-	fb_release:	NULL,
 	fb_get_fix:	fbgen_get_fix,
 	fb_get_var:	fbgen_get_var,
 	fb_set_var:	fbgen_set_var,
 	fb_get_cmap:	fbgen_get_cmap,
-	fb_set_cmap:	fbgen_set_cmap,
+	fb_set_cmap:	gen_set_cmap,
 	fb_pan_display:	fbgen_pan_display,
-	fb_ioctl:	NULL
+	fb_blank:	fbgen_blank,
 };
diff -Nru a/drivers/video/sun3fb.c b/drivers/video/sun3fb.c
--- a/drivers/video/sun3fb.c	Sun Apr 28 20:10:32 2002
+++ b/drivers/video/sun3fb.c	Sun Apr 28 20:10:32 2002
@@ -66,7 +66,6 @@
 int sun3fb_init(void);
 int sun3fb_setup(char *options);
 
-static int currcon;
 static char fontname[40] __initdata = { 0 };
 static int curblink __initdata = 1;
 
@@ -80,9 +79,11 @@
 			struct fb_info *info);
 static int sun3fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
 			struct fb_info *info);
+static int sun3fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+			    u_int transp, struct fb_info *info);
+static int sun3fb_blank(int blank, struct fb_info *info);
 static void sun3fb_cursor(struct display *p, int mode, int x, int y);
 static void sun3fb_clear_margin(struct display *p, int s);
-			    
 
     /*
      *  Interface to the low level console driver
@@ -90,8 +91,6 @@
 
 static int sun3fbcon_switch(int con, struct fb_info *info);
 static int sun3fbcon_updatevar(int con, struct fb_info *info);
-static void sun3fbcon_blank(int blank, struct fb_info *info);
-
 
     /*
      *  Internal routines
@@ -99,9 +98,6 @@
 
 static int sun3fb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
 			    u_int *transp, struct fb_info *info);
-static int sun3fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-			    u_int transp, struct fb_info *info);
-static void do_install_cmap(int con, struct fb_info *info);
 
 static struct fb_ops sun3fb_ops = {
 	owner:		THIS_MODULE,
@@ -110,6 +106,8 @@
 	fb_set_var:	sun3fb_set_var,
 	fb_get_cmap:	sun3fb_get_cmap,
 	fb_set_cmap:	sun3fb_set_cmap,
+	fb_setcolreg:	sun3fb_setcolreg,
+	fb_blank:	sun3fb_blank,
 };
 
 static void sun3fb_clear_margin(struct display *p, int s)
@@ -139,7 +137,7 @@
 		rects [15] = fb->var.yres_virtual;
 		(*fb->fill)(fb, p, s, 4, rects);
 	} else {
-		unsigned char *fb_base = p->screen_base, *q;
+		unsigned char *fb_base = fb->info.screen_base, *q;
 		int skip_bytes = fb->y_margin * fb->var.xres_virtual;
 		int scr_size = fb->var.xres_virtual * fb->var.yres_virtual;
 		int h, he, incr, size;
@@ -294,7 +292,7 @@
 static int sun3fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			 struct fb_info *info)
 {
-	if (con == currcon) /* current console? */
+	if (con == info->currcon) /* current console? */
 		return fb_get_cmap(cmap, kspc, sun3fb_getcolreg, info);
 	else if (fb_display[con].cmap.len) /* non default colormap? */
 		fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
@@ -316,8 +314,8 @@
 		if ((err = fb_alloc_cmap(&fb_display[con].cmap, 1<<fb_display[con].var.bits_per_pixel, 0)))
 			return err;
 	}
-	if (con == currcon) {			/* current console? */
-		err = fb_set_cmap(cmap, kspc, sun3fb_setcolreg, info);
+	if (con == info->currcon) {			/* current console? */
+		err = fb_set_cmap(cmap, kspc, info);
 		if (!err) {
 			struct fb_info_sbusfb *fb = sbusfbinfo(info);
 			
@@ -364,8 +362,8 @@
 	int lastconsole;
     
 	/* Do we have to save the colormap? */
-	if (fb_display[currcon].cmap.len)
-		fb_get_cmap(&fb_display[currcon].cmap, 1, sun3fb_getcolreg, info);
+	if (fb_display[info->currcon].cmap.len)
+		fb_get_cmap(&fb_display[info->currcon].cmap, 1, sun3fb_getcolreg, info);
 
 	if (info->display_fg) {
 		lastconsole = info->display_fg->vc_num;
@@ -382,7 +380,7 @@
 		fb->x_margin = x_margin; fb->y_margin = y_margin;
 		sun3fb_clear_margin(&fb_display[con], 0);
 	}
-	currcon = con;
+	info->currcon = con;
 	/* Install new colormap */
 	do_install_cmap(con, info);
 	return 0;
@@ -402,7 +400,7 @@
      *  Blank the display.
      */
 
-static void sun3fbcon_blank(int blank, struct fb_info *info)
+static int sun3fb_blank(int blank, struct fb_info *info)
 {
     struct fb_info_sbusfb *fb = sbusfbinfo(info);
     
@@ -454,22 +452,6 @@
 	return 0;
 }
 
-
-static void do_install_cmap(int con, struct fb_info *info)
-{
-	struct fb_info_sbusfb *fb = sbusfbinfo(info);
-	
-	if (con != currcon)
-		return;
-	if (fb_display[con].cmap.len)
-		fb_set_cmap(&fb_display[con].cmap, 1, sun3fb_setcolreg, info);
-	else
-		fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
-			    1, sun3fb_setcolreg, info);
-	if (fb->loadcmap)
-		(*fb->loadcmap)(fb, &fb_display[con], 0, 256);
-}
-
 static int sun3fb_set_font(struct display *p, int width, int height)
 {
 	int w = p->var.xres_virtual, h = p->var.yres_virtual;
@@ -576,11 +558,11 @@
 	fb->info.node = NODEV;
 	fb->info.fbops = &sun3fb_ops;
 	fb->info.disp = disp;
+	fb->info.currcon = -1;
 	strcpy(fb->info.fontname, fontname);
 	fb->info.changevar = NULL;
 	fb->info.switch_con = &sun3fbcon_switch;
 	fb->info.updatevar = &sun3fbcon_updatevar;
-	fb->info.blank = &sun3fbcon_blank;
 	fb->info.flags = FBINFO_FLAG_DEFAULT;
 	
 	fb->cursor.hwsize.fbx = 32;
@@ -601,7 +583,7 @@
 		p = bwtwofb_init(fb); break;
 #endif
 	}
-	fix->smem_start = fb->disp.screen_base;
+	fix->smem_start = fb->info.screen_base;
 	
 	if (!p) {
 		kfree(fb);
diff -Nru a/drivers/video/tcxfb.c b/drivers/video/tcxfb.c
--- a/drivers/video/tcxfb.c	Sun Apr 28 20:10:31 2002
+++ b/drivers/video/tcxfb.c	Sun Apr 28 20:10:31 2002
@@ -198,7 +198,7 @@
 	spin_unlock_irqrestore(&fb->lock, flags);
 }
 
-static void tcx_blank (struct fb_info_sbusfb *fb)
+static int tcx_blank (struct fb_info_sbusfb *fb)
 {
 	unsigned long flags;
 	u32 tmp;
@@ -211,9 +211,10 @@
         tmp |= TCX_THC_MISC_HSYNC_DIS;
 	sbus_writel(tmp, &fb->s.tcx.thc->thc_misc);
 	spin_unlock_irqrestore(&fb->lock, flags);
+	return 0;
 }
 
-static void tcx_unblank (struct fb_info_sbusfb *fb)
+static int tcx_unblank (struct fb_info_sbusfb *fb)
 {
 	unsigned long flags;
 	u32 tmp;
@@ -225,6 +226,7 @@
 	tmp |= TCX_THC_MISC_VIDEO;
 	sbus_writel(tmp, &fb->s.tcx.thc->thc_misc);
 	spin_unlock_irqrestore(&fb->lock, flags);
+	return 0;
 }
 
 static void tcx_reset (struct fb_info_sbusfb *fb)
@@ -251,7 +253,7 @@
 
 static void tcx_margins (struct fb_info_sbusfb *fb, struct display *p, int x_margin, int y_margin)
 {
-	p->screen_base += (y_margin - fb->y_margin) * p->line_length + (x_margin - fb->x_margin);
+	fb->info.screen_base += (y_margin - fb->y_margin) * p->line_length + (x_margin - fb->x_margin);
 }
 
 static char idstring[60] __initdata = { 0 };
@@ -282,12 +284,12 @@
 	fix->accel = FB_ACCEL_SUN_TCX;
 
 	disp->scrollmode = SCROLL_YREDRAW;
-	if (!disp->screen_base) {
-		disp->screen_base = (char *)
+	if (!fb->info.screen_base) {
+		fb->info.screen_base = (char *)
 			sbus_ioremap(&sdev->resource[0], 0,
 				     type->fb_size, "tcx ram");
 	}
-	disp->screen_base += fix->line_length * fb->y_margin + fb->x_margin;
+	fb->info.screen_base += fix->line_length * fb->y_margin + fb->x_margin;
 	fb->s.tcx.tec = (struct tcx_tec *)
 		sbus_ioremap(&sdev->resource[7], 0,
 			     sizeof(struct tcx_tec), "tcx tec");
diff -Nru a/drivers/video/tdfxfb.c b/drivers/video/tdfxfb.c
--- a/drivers/video/tdfxfb.c	Sun Apr 28 20:10:33 2002
+++ b/drivers/video/tdfxfb.c	Sun Apr 28 20:10:33 2002
@@ -175,9 +175,9 @@
 #define BIT(x) (1UL << (x))
 
 /* COMMAND_2D reg. values */
-#define ROP_COPY	0xcc     // src
-#define ROP_INVERT      0x55     // NOT dst
-#define ROP_XOR         0x66     // src XOR dst
+#define TDFX_ROP_COPY	     0xcc     // src
+#define TDFX_ROP_INVERT      0x55     // NOT dst
+#define TDFX_ROP_XOR         0x66     // src XOR dst
 
 #define AUTOINC_DSTX                    BIT(10)
 #define AUTOINC_DSTY                    BIT(11)
@@ -376,6 +376,12 @@
 static int tdfxfb_set_var(struct fb_var_screeninfo* var,
 			  int con,
 			  struct fb_info* fb);
+static int  tdfxfb_setcolreg(u_int regno, 
+			     u_int red, 
+			     u_int green, 
+			     u_int blue,
+			     u_int transp, 
+			     struct fb_info* fb);
 static int tdfxfb_pan_display(struct fb_var_screeninfo* var, 
 			      int con,
 			      struct fb_info* fb);
@@ -395,7 +401,7 @@
 			      struct fb_info* fb);
 static int  tdfxfb_updatevar(int con, 
 			     struct fb_info* fb);
-static void tdfxfb_blank(int blank, 
+static int tdfxfb_blank(int blank, 
 			 struct fb_info* fb);
 
 /*
@@ -423,14 +429,6 @@
 			     u_int* blue,
 			     u_int* transp, 
 			     struct fb_info* fb);
-static int  tdfxfb_setcolreg(u_int regno, 
-			     u_int red, 
-			     u_int green, 
-			     u_int blue,
-			     u_int transp, 
-			     struct fb_info* fb);
-static void  tdfxfb_install_cmap(struct display *d, 
-				 struct fb_info *info);
 
 static void tdfxfb_hwcursor_init(void);
 static void tdfxfb_createcursorshape(struct display* p);
@@ -466,8 +464,6 @@
 static int tdfxfb_probe(struct pci_dev *pdev, const struct pci_device_id *id);
 static void tdfxfb_remove(struct pci_dev *pdev);
 
-static int currcon = 0;
-
 static struct fb_ops tdfxfb_ops = {
 	owner:		THIS_MODULE,
 	fb_get_fix:	tdfxfb_get_fix,
@@ -475,7 +471,9 @@
 	fb_set_var:	tdfxfb_set_var,
 	fb_get_cmap:	tdfxfb_get_cmap,
 	fb_set_cmap:	tdfxfb_set_cmap,
+	fb_setcolreg:	tdfxfb_setcolreg,
 	fb_pan_display:	tdfxfb_pan_display,
+	fb_blank:	tdfxfb_blank,
 };
 
 static struct pci_device_id tdfxfb_id_table[] __devinitdata = {
@@ -715,7 +713,7 @@
 			   u32 stride,
 			   u32 bpp) {
 
-   u32 blitcmd = COMMAND_2D_S2S_BITBLT | (ROP_COPY << 24);
+   u32 blitcmd = COMMAND_2D_S2S_BITBLT | (TDFX_ROP_COPY << 24);
    u32 fmt= stride | ((bpp+((bpp==8) ? 0 : 8)) << 13); 
    
    if (curx <= dstx) {
@@ -761,7 +759,7 @@
    tdfx_outl(COLORBACK, bgx);
    tdfx_outl(SRCXY,     0);
    tdfx_outl(DSTXY,     xx | (yy << 16));
-   tdfx_outl(COMMAND_2D, COMMAND_2D_H2S_BITBLT | (ROP_COPY << 24));
+   tdfx_outl(COMMAND_2D, COMMAND_2D_H2S_BITBLT | (TDFX_ROP_COPY << 24));
    tdfx_outl(SRCFORMAT, 0x400000);
    tdfx_outl(DSTFORMAT, fmt);
    tdfx_outl(DSTSIZE,   fontwidth(p) | (fontheight(p) << 16));
@@ -824,7 +822,7 @@
    tdfx_outl(DSTFORMAT, fmt);
    tdfx_outl(DSTSIZE, w | (h << 16));
    tdfx_outl(SRCXY,     0);
-   tdfx_outl(COMMAND_2D, COMMAND_2D_H2S_BITBLT | (ROP_COPY << 24));
+   tdfx_outl(COMMAND_2D, COMMAND_2D_H2S_BITBLT | (TDFX_ROP_COPY << 24));
    
    while (count--) {
       u8 *chardata=p->fontdata+(scr_readw(s++) & p->charmask)*h*fw;
@@ -1029,14 +1027,14 @@
       do_fillrect( p->var.xoffset+rs, 0, 
 		  rw, p->var.yres_virtual, 0, 
 		  fb_info.current_par.lpitch,
-		  fb_info.current_par.bpp, ROP_COPY);
+		  fb_info.current_par.bpp, TDFX_ROP_COPY);
    }
    
    if (bh) { 
       do_fillrect( p->var.xoffset, p->var.yoffset+bs, 
 		  rs, bh, 0, 
 		  fb_info.current_par.lpitch,
-		  fb_info.current_par.bpp, ROP_COPY);
+		  fb_info.current_par.bpp, TDFX_ROP_COPY);
    }
 }
 static void tdfx_cfbX_bmove(struct display* p, 
@@ -1127,7 +1125,7 @@
 		   fontheight(p)*height,
 		   bg, 
 		   fb_info.current_par.lpitch, 
-		   fb_info.current_par.bpp,ROP_COPY);
+		   fb_info.current_par.bpp, TDFX_ROP_COPY);
 }
 
 static void tdfx_cfb16_clear(struct vc_data* conp, 
@@ -1145,7 +1143,7 @@
 		   fontheight(p)*height,
 		   bg, 
 		   fb_info.current_par.lpitch, 
-		   fb_info.current_par.bpp,ROP_COPY);
+		   fb_info.current_par.bpp, TDFX_ROP_COPY);
 }
 
 static void tdfx_cfb32_clear(struct vc_data* conp, 
@@ -1163,7 +1161,7 @@
 		   fontheight(p)*height,
 		   bg, 
 		   fb_info.current_par.lpitch, 
-		   fb_info.current_par.bpp,ROP_COPY);
+		   fb_info.current_par.bpp, TDFX_ROP_COPY);
 }
 static void tdfx_cfbX_revc(struct display *p, int xx, int yy)
 {
@@ -1172,7 +1170,7 @@
    do_fillrect( xx * fontwidth(p), yy * fontheight(p), 
 	        fontwidth(p), fontheight(p), 
 	        (bpp==8) ? 0x0f : 0xffffffff, 
-	        fb_info.current_par.lpitch, bpp, ROP_XOR);
+	        fb_info.current_par.lpitch, bpp, TDFX_ROP_XOR);
    
 }
 static void tdfx_cfbX_cursor(struct display *p, int mode, int x, int y) 
@@ -1781,7 +1779,6 @@
 	 struct fb_fix_screeninfo fix;
 	 
 	 tdfxfb_encode_fix(&fix, &par, info);
-	 display->screen_base    = info->bufbase_virt;
 	 display->visual         = fix.visual;
 	 display->type           = fix.type;
 	 display->type_aux       = fix.type_aux;
@@ -1820,7 +1817,7 @@
       if(oldbpp != var->bits_per_pixel || con < 0) {
 	 if((err = fb_alloc_cmap(&display->cmap, 0, 0)))
 	   return err;
-	 tdfxfb_install_cmap(display, &(info->fb_info));
+	 do_install_cmap(con, &(info->fb_info));
       }
    }
   
@@ -1838,7 +1835,7 @@
   if(nowrap && 
      (var->yoffset + var->yres > var->yres_virtual)) return -EINVAL;
  
-  if (con==currcon)
+  if (con == fb->currcon)
     do_pan_var(var,i);
    
   fb_display[con].var.xoffset=var->xoffset;
@@ -1854,7 +1851,7 @@
    struct fb_info_tdfx* i = (struct fb_info_tdfx*)fb;
    struct display *d=(con<0) ? fb->disp : fb_display + con;
    
-   if(con == currcon) {
+   if(con == fb->currcon) {
       /* current console? */
       return fb_get_cmap(cmap, kspc, tdfxfb_getcolreg, fb);
    } else if(d->cmap.len) {
@@ -1879,9 +1876,9 @@
       if((err = fb_alloc_cmap(&d->cmap, cmap_len, 0)))
 	return err;
    }
-   if(con == currcon) {
+   if(con == fb->currcon) {
       /* current console? */
-      return fb_set_cmap(cmap, kspc, tdfxfb_setcolreg, fb);
+      return fb_set_cmap(cmap, kspc, fb);
    } else {
       fb_copy_cmap(cmap, &d->cmap, kspc ? 0 : 1);
    }
@@ -1961,7 +1958,7 @@
 
 	/* clear framebuffer memory */
 	memset_io(fb_info.bufbase_virt, 0, fb_info.bufbase_size);
-	currcon = -1;
+	fb_info.fb_info.currcon = -1;
 
 	if (!nohwcursor)
 		tdfxfb_hwcursor_init();
@@ -1978,10 +1975,10 @@
 	fb_info.fb_info.node       = NODEV;
 	fb_info.fb_info.fbops      = &tdfxfb_ops;
 	fb_info.fb_info.disp       = &fb_info.disp;
+	fb_info.fb_info.currcon	   = -1;
 	strcpy(fb_info.fb_info.fontname, fontname);
 	fb_info.fb_info.switch_con = &tdfxfb_switch_con;
 	fb_info.fb_info.updatevar  = &tdfxfb_updatevar;
-	fb_info.fb_info.blank      = &tdfxfb_blank;
 	fb_info.fb_info.flags      = FBINFO_FLAG_DEFAULT;
       
 	memset(&var, 0, sizeof(var));
@@ -2013,7 +2010,7 @@
 		}
 	}
 
-	fb_info.disp.screen_base = fb_info.bufbase_virt;
+	fb_info.fb_info.screen_base = fb_info.bufbase_virt;
 	fb_info.disp.var         = var;
       
 	if (tdfxfb_set_var(&var, -1, &fb_info.fb_info)) {
@@ -2117,16 +2114,16 @@
 			     struct fb_info *fb) {
    struct fb_info_tdfx *info = (struct fb_info_tdfx*)fb;
    struct tdfxfb_par par;
-   int old_con = currcon;
+   int old_con = fb->currcon;
    int set_par = 1;
 
    /* Do we have to save the colormap? */
-   if (currcon>=0)
-     if(fb_display[currcon].cmap.len)
-       fb_get_cmap(&fb_display[currcon].cmap, 1, tdfxfb_getcolreg, fb);
+   if (fb->currcon>=0)
+     if(fb_display[fb->currcon].cmap.len)
+       fb_get_cmap(&fb_display[fb->currcon].cmap, 1, tdfxfb_getcolreg, fb);
    
-   currcon = con;
-   fb_display[currcon].var.activate = FB_ACTIVATE_NOW; 
+   fb->currcon = con;
+   fb_display[fb->currcon].var.activate = FB_ACTIVATE_NOW; 
    tdfxfb_decode_var(&fb_display[con].var, &par, info);
    if (old_con>=0 && vt_cons[old_con]->vc_mode!=KD_GRAPHICS) {
      /* check if we have to change video registers */
@@ -2155,15 +2152,16 @@
 		     par.bpp,
 		     par.accel_flags & FB_ACCELF_TEXT);
    
-   tdfxfb_install_cmap(&fb_display[con], fb);
+   do_install_cmap(con, fb);
    tdfxfb_updatevar(con, fb);
    
    return 1;
 }
 
 /* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */
-static void tdfxfb_blank(int blank, 
-			 struct fb_info *fb) {
+static int tdfxfb_blank(int blank, 
+			 struct fb_info *fb)
+{
   u32 dacmode, state = 0, vgablank = 0;
 
   dacmode = tdfx_inl(DACMODE);
@@ -2199,15 +2197,14 @@
     vga_disable_video();
   else
     vga_enable_video();
-
-  return;
+  return 0;
 }
 
 static int  tdfxfb_updatevar(int con, 
 			     struct fb_info* fb) {
 
    struct fb_info_tdfx* i = (struct fb_info_tdfx*)fb;
-   if ((con==currcon) && (!nopan)) 
+   if ((con==fb->currcon) && (!nopan)) 
      do_pan_var(&fb_display[con].var,i);
    return 0;
 }
@@ -2284,18 +2281,6 @@
       break;
    }
    return 0;
-}
-
-static void tdfxfb_install_cmap(struct display *d,struct fb_info *info) 
-{
-   struct fb_info_tdfx* i = (struct fb_info_tdfx*)info;
-
-   if(d->cmap.len) {
-      fb_set_cmap(&(d->cmap), 1, tdfxfb_setcolreg, info);
-   } else {
-      fb_set_cmap(fb_default_cmap(i->current_par.cmap_len), 1, 
-		  tdfxfb_setcolreg, info);
-   }
 }
 
 static void tdfxfb_createcursorshape(struct display* p) 
diff -Nru a/drivers/video/tgafb.c b/drivers/video/tgafb.c
--- a/drivers/video/tgafb.c	Sun Apr 28 20:10:32 2002
+++ b/drivers/video/tgafb.c	Sun Apr 28 20:10:32 2002
@@ -64,8 +64,6 @@
 static struct fb_var_screeninfo default_var;
 static int default_var_valid = 0;
 
-static int currcon = 0;
-
 static struct { u_char red, green, blue, pad; } palette[256];
 #ifdef FBCON_HAS_CFB32
 static u32 fbcon_cfb32_cmap[16];
@@ -748,8 +746,8 @@
 	if ((err = fb_alloc_cmap(&fb_display[con].cmap, 256, 0)))
 	    return err;
     }
-    if (con == currcon) {		/* current console? */
-	err = fb_set_cmap(cmap, kspc, tgafb_setcolreg, info);
+    if (con == info->currcon) {		/* current console? */
+	err = fb_set_cmap(cmap, kspc, info);
 #if 1
 	if (fb_info.tga_type != TGA_TYPE_8PLANE)
 		tgafb_update_palette();
@@ -831,7 +829,6 @@
 static void tgafb_set_disp(const void *fb_par, struct display *disp,
 	struct fb_info_gen *info)
 {
-    disp->screen_base = (char *)fb_info.tga_fb_base;
     switch (fb_info.tga_type) {
 #ifdef FBCON_HAS_CFB8
 	case TGA_TYPE_8PLANE:
@@ -855,7 +852,7 @@
 
 struct fbgen_hwswitch tgafb_hwswitch = {
     tgafb_detect, tgafb_encode_fix, tgafb_decode_var, tgafb_encode_var, tgafb_get_par,
-    tgafb_set_par, tgafb_getcolreg, tgafb_setcolreg, NULL, tgafb_blank, 
+    tgafb_set_par, tgafb_getcolreg, NULL, tgafb_blank, 
     tgafb_set_disp
 };
 
@@ -876,6 +873,8 @@
 	fb_set_var:	fbgen_set_var,
 	fb_get_cmap:	fbgen_get_cmap,
 	fb_set_cmap:	tgafb_set_cmap,
+	fb_setcolreg:	tgafb_setcolreg,
+	fb_blank:	fbgen_blank,
 };
 
 
@@ -940,11 +939,12 @@
     fb_info.gen.info.node = NODEV;
     fb_info.gen.info.flags = FBINFO_FLAG_DEFAULT;
     fb_info.gen.info.fbops = &tgafb_ops;
+    fb_info.gen.info.screen_base = (char *)fb_info.tga_fb_base;
     fb_info.gen.info.disp = &disp;
+    fb_info.gen.info.currcon = -1;	
     fb_info.gen.info.changevar = NULL;
     fb_info.gen.info.switch_con = &fbgen_switch;
     fb_info.gen.info.updatevar = &fbgen_update_var;
-    fb_info.gen.info.blank = &fbgen_blank;
     strcpy(fb_info.gen.info.fontname, default_fontname);
     fb_info.gen.parsize = sizeof (struct tgafb_par);
     fb_info.gen.fbhw = &tgafb_hwswitch;
@@ -978,7 +978,7 @@
     disp.var.activate = FB_ACTIVATE_NOW;
     fbgen_do_set_var(&disp.var, 1, &fb_info.gen);
     fbgen_set_disp(-1, &fb_info.gen);
-    fbgen_install_cmap(0, &fb_info.gen);
+    do_install_cmap(0, &fb_info.gen);
     if (register_framebuffer(&fb_info.gen.info) < 0)
 	return -EINVAL;
     printk(KERN_INFO "fb%d: %s frame buffer device at 0x%lx\n", 
diff -Nru a/drivers/video/tx3912fb.c b/drivers/video/tx3912fb.c
--- a/drivers/video/tx3912fb.c	Sun Apr 28 20:10:37 2002
+++ b/drivers/video/tx3912fb.c	Sun Apr 28 20:10:37 2002
@@ -35,7 +35,6 @@
 static struct fb_info fb_info;
 static struct { u_char red, green, blue, pad; } palette[256];
 static struct display global_disp;
-static int currcon = 0;
 
 /*
  * Interface used by the world
@@ -48,10 +47,8 @@
 				struct fb_info *info);
 static int tx3912fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 				struct fb_info *info);
-static int tx3912fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-				struct fb_info *info);
-static int tx3912fb_ioctl(struct inode *inode, struct file *file, u_int cmd,
-				u_long arg, int con, struct fb_info *info);
+static int tx3912fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+			u_int transp, struct fb_info *info);
 
 /*
  * Interface used by console driver
@@ -59,7 +56,6 @@
 int tx3912fb_init(void);
 static int tx3912fbcon_switch(int con, struct fb_info *info);
 static int tx3912fbcon_updatevar(int con, struct fb_info *info);
-static void tx3912fbcon_blank(int blank, struct fb_info *info);
 
 /*
  * Macros
@@ -72,10 +68,6 @@
  */
 static int tx3912fb_getcolreg(u_int regno, u_int *red, u_int *green,
 			u_int *blue, u_int *transp, struct fb_info *info);
-static int tx3912fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-			u_int transp, struct fb_info *info);
-static void tx3912fb_install_cmap(int con, struct fb_info *info);
-
 
 /*
  * Frame buffer operations structure used by console driver
@@ -86,8 +78,8 @@
 	fb_get_var: tx3912fb_get_var,
 	fb_set_var: tx3912fb_set_var,
 	fb_get_cmap: tx3912fb_get_cmap,
-	fb_set_cmap: tx3912fb_set_cmap,
-	fb_ioctl: tx3912fb_ioctl,
+	fb_set_cmap: gen_set_cmap,
+	fb_setcolreg:	tx3912fb_setcolreg,
 };
 
 
@@ -226,7 +218,6 @@
 		    oldvyres != var->yres_virtual ||
 		    oldbpp != var->bits_per_pixel) {
 
-			display->screen_base = (u_char *) tx3912fb_vaddr;
 
 			switch (var->bits_per_pixel) {
 			case 1:
@@ -285,7 +276,7 @@
 		if (oldbpp != var->bits_per_pixel) {
 			if ((err = fb_alloc_cmap(&display->cmap, 0, 0)))
 				return err;
-			tx3912fb_install_cmap(con, info);
+			do_install_cmap(con, info);
 		}
 	}
 
@@ -298,7 +289,7 @@
 static int tx3912fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			struct fb_info *info)
 {
-	if (con == currcon)
+	if (con == info->currcon)
 		return fb_get_cmap(cmap, kspc, tx3912fb_getcolreg, info);
 	else if (fb_display[con].cmap.len) /* non default colormap? */
 		fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
@@ -309,36 +300,6 @@
 }
 
 /*
- *  Set the Colormap
- */
-static int tx3912fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-			struct fb_info *info)
-{
-	int err;
-
-	if (!fb_display[con].cmap.len)
-		if ((err = fb_alloc_cmap(&fb_display[con].cmap,
-				1<<fb_display[con].var.bits_per_pixel, 0)))
-			return err;
-
-	if (con == currcon)
-		return fb_set_cmap(cmap, kspc, tx3912fb_setcolreg, info);
-	else
-		fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
-
-	return 0;
-}
-
-/*
- *  Framebuffer ioctl
- */
-static int tx3912fb_ioctl(struct inode *inode, struct file *file, u_int cmd,
-				u_long arg, int con, struct fb_info *info)
-{
-	return -EINVAL;
-}
-
-/*
  * Initialization of the framebuffer
  */
 int __init tx3912fb_init(void)
@@ -399,10 +360,11 @@
 	fb_info.changevar = NULL;
 	fb_info.node = NODEV;
 	fb_info.fbops = &tx3912fb_ops;
+	fb_info.screen_base = (u_char *) tx3912fb_vaddr;
 	fb_info.disp = &global_disp;
+	fb_info.currcon = -1;
 	fb_info.switch_con = &tx3912fbcon_switch;
 	fb_info.updatevar = &tx3912fbcon_updatevar;
-	fb_info.blank = &tx3912fbcon_blank;
 	fb_info.flags = FBINFO_FLAG_DEFAULT;
 
 	tx3912fb_set_var(&tx3912fb_info, -1, &fb_info);
@@ -422,16 +384,15 @@
 static int tx3912fbcon_switch(int con, struct fb_info *info)
 {
 	/* Save off the color map if needed */
-	if (fb_display[currcon].cmap.len)
-		fb_get_cmap(&fb_display[currcon].cmap, 1,
+	if (fb_display[info->currcon].cmap.len)
+		fb_get_cmap(&fb_display[info->currcon].cmap, 1,
 			tx3912fb_getcolreg, info);
 
 	/* Make the switch */
-	currcon = con;
+	info->currcon = con;
 
 	/* Install new colormap */
-	tx3912fb_install_cmap(con, info);
-
+	do_install_cmap(con, info);
 	return 0;
 }
 
@@ -445,15 +406,6 @@
 }
 
 /*
- * Blank the display
- */
-static void tx3912fbcon_blank(int blank, struct fb_info *info)
-{
-	/* FIXME */
-	printk("tx3912fbcon_blank\n");
-}
-
-/*
  * Read a single color register
  */
 static int tx3912fb_getcolreg(u_int regno, u_int *red, u_int *green,
@@ -510,20 +462,6 @@
 	palette[regno].blue = blue;
 
 	return 0;
-}
-
-/*
- * Install the color map
- */
-static void tx3912fb_install_cmap(int con, struct fb_info *info)
-{
-	if (con != currcon)
-		return;
-
-	if (fb_display[con].cmap.len)
-		fb_set_cmap(&fb_display[con].cmap, 1, tx3912fb_setcolreg, info);
-	else
-		fb_set_cmap(fb_default_cmap(1 << fb_display[con].var.bits_per_pixel), 1, tx3912fb_setcolreg, info);
 }
 
 MODULE_LICENSE("GPL");
diff -Nru a/drivers/video/valkyriefb.c b/drivers/video/valkyriefb.c
--- a/drivers/video/valkyriefb.c	Sun Apr 28 20:10:34 2002
+++ b/drivers/video/valkyriefb.c	Sun Apr 28 20:10:34 2002
@@ -75,7 +75,6 @@
 static int default_cmode = CMODE_NVRAM;
 static char fontname[40] __initdata = { 0 };
 
-static int currcon = 0;
 static int switching = 0;
 
 struct fb_par_valkyrie {
@@ -126,8 +125,9 @@
 			 struct fb_info *info);
 static int valkyrie_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			  struct fb_info *info);
-static int valkyrie_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-			  struct fb_info *info);
+static int valkyriefb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+			     u_int transp, struct fb_info *info);
+static int valkyriefb_blank(int blank_mode, struct fb_info *info);
 
 static int read_valkyrie_sense(struct fb_info_valkyrie *p);
 static inline int valkyrie_vram_reqd(int video_mode, int color_mode);
@@ -151,14 +151,13 @@
 	fb_get_var:	valkyrie_get_var,
 	fb_set_var:	valkyrie_set_var,
 	fb_get_cmap:	valkyrie_get_cmap,
-	fb_set_cmap:	valkyrie_set_cmap,
+	fb_set_cmap:	gen_set_cmap,
+	fb_setcolreg:	valkyriefb_setcolreg,
+	fb_blank:	valkyriefb_blank,
 };
 
 static int valkyriefb_getcolreg(u_int regno, u_int *red, u_int *green,
 			     u_int *blue, u_int *transp, struct fb_info *info);
-static int valkyriefb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-			     u_int transp, struct fb_info *info);
-static void do_install_cmap(int con, struct fb_info *info);
 
 static int valkyrie_get_fix(struct fb_fix_screeninfo *fix, int con,
 			 struct fb_info *info)
@@ -224,7 +223,7 @@
 		/* Don't want to do this if just switching consoles. */
 		(*info->changevar)(con);
 	}
-	if (con == currcon)
+	if (con == info->currcon)
 		valkyrie_set_par(&par, p);
 	if (depthchange)
 		if ((err = fb_alloc_cmap(&disp->cmap, 0, 0)))
@@ -237,7 +236,7 @@
 static int valkyrie_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			  struct fb_info *info)
 {
-	if (con == currcon)	{
+	if (con == info->currcon)	{
 		/* current console? */
 		return fb_get_cmap(cmap, kspc, valkyriefb_getcolreg, info);
 	}
@@ -251,36 +250,15 @@
 	return 0;
 }
 
-static int valkyrie_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-			 struct fb_info *info)
-{
-	struct display *disp = &fb_display[con];
-	int err;
-
-	if (disp->cmap.len == 0) {
-		int size = fb_display[con].var.bits_per_pixel == 16 ? 32 : 256;
-		err = fb_alloc_cmap(&disp->cmap, size, 0);
-		if (err) {
-			return err;
-		}
-	}
-
-	if (con == currcon) {
-		return fb_set_cmap(cmap, kspc, valkyriefb_setcolreg, info);
-	}
-	fb_copy_cmap(cmap, &disp->cmap, kspc ? 0 : 1);
-	return 0;
-}
-
 static int valkyriefb_switch(int con, struct fb_info *fb)
 {
 	struct fb_info_valkyrie *info = (struct fb_info_valkyrie *) fb;
 	struct fb_par_valkyrie par;
 
-	if (fb_display[currcon].cmap.len)
-		fb_get_cmap(&fb_display[currcon].cmap, 1, valkyriefb_getcolreg,
+	if (fb_display[fb->currcon].cmap.len)
+		fb_get_cmap(&fb_display[fb->currcon].cmap, 1, valkyriefb_getcolreg,
 			    fb);
-	currcon = con;
+	fb->currcon = con;
 #if 1
 	valkyrie_var_to_par(&fb_display[currcon].var, &par, fb);
 	valkyrie_set_par(&par, info);
@@ -302,7 +280,7 @@
 	return 0;
 }
 
-static void valkyriefb_blank(int blank_mode, struct fb_info *info)
+static int valkyriefb_blank(int blank_mode, struct fb_info *info)
 {
 /*
  *  Blank the screen if blank_mode != 0, else unblank. If blank_mode == NULL
@@ -342,6 +320,7 @@
 			break;
 		}
 	}
+	return 0;
 }
 
 static int valkyriefb_getcolreg(u_int regno, u_int *red, u_int *green,
@@ -391,21 +370,6 @@
 	return 0;
 }
 
-static void do_install_cmap(int con, struct fb_info *info)
-{
-	if (con != currcon)
-		return;
-	if (fb_display[con].cmap.len) {
-		fb_set_cmap(&fb_display[con].cmap, 1, valkyriefb_setcolreg,
-			    info);
-	}
-	else {
-		int size = fb_display[con].var.bits_per_pixel == 16 ? 32 : 256;
-		fb_set_cmap(fb_default_cmap(size), 1, valkyriefb_setcolreg,
-			    info);
-	}
-}
-
 #ifdef CONFIG_FB_COMPAT_XPMAC
 extern struct vc_mode display_info;
 extern struct fb_info *console_fb_info;
@@ -750,7 +714,6 @@
   struct display *disp, struct fb_fix_screeninfo *fix, struct fb_info_valkyrie *p)
 {
 	disp->var = p->var;
-	disp->screen_base = (char *) p->frame_buffer + 0x1000;
 	disp->visual = fix->visual;
 	disp->line_length = fix->line_length;
 
@@ -781,12 +744,13 @@
 	strcpy(info->modename, p->fix.id);
 	info->node = NODEV;
 	info->fbops = &valkyriefb_ops;
+	info->screen_base = (char *) p->frame_buffer + 0x1000;
 	info->disp = &p->disp;
+	info->currcon = -1;
 	strcpy(info->fontname, fontname);
 	info->changevar = NULL;
 	info->switch_con = &valkyriefb_switch;
 	info->updatevar = &valkyriefb_updatevar;
-	info->blank = &valkyriefb_blank;
 	info->flags = FBINFO_FLAG_DEFAULT;
 }
 
diff -Nru a/drivers/video/vesafb.c b/drivers/video/vesafb.c
--- a/drivers/video/vesafb.c	Sun Apr 28 20:10:31 2002
+++ b/drivers/video/vesafb.c	Sun Apr 28 20:10:31 2002
@@ -94,7 +94,6 @@
 
 static int             inverse   = 0;
 static int             mtrr      = 0;
-static int             currcon   = 0;
 
 static int             pmi_setpal = 0;	/* pmi for palette changes ??? */
 static int             ypan       = 0;  /* 0..nothing, 1..ypan, 2..ywrap */
@@ -135,8 +134,8 @@
 
 static int vesafb_update_var(int con, struct fb_info *info)
 {
-	if (con == currcon && ypan) {
-		struct fb_var_screeninfo *var = &fb_display[currcon].var;
+	if (con == info->currcon && ypan) {
+		struct fb_var_screeninfo *var = &fb_display[info->currcon].var;
 		return vesafb_pan_display(var,con,info);
 	}
 	return 0;
@@ -183,7 +182,6 @@
 	vesafb_get_fix(&fix, con, 0);
 
 	memset(display, 0, sizeof(struct display));
-	display->screen_base = video_vbase;
 	display->visual = fix.visual;
 	display->type = fix.type;
 	display->type_aux = fix.type_aux;
@@ -329,9 +327,9 @@
 
 #endif
 
-static int vesa_setcolreg(unsigned regno, unsigned red, unsigned green,
-			  unsigned blue, unsigned transp,
-			  struct fb_info *fb_info)
+static int vesafb_setcolreg(unsigned regno, unsigned red, unsigned green,
+			    unsigned blue, unsigned transp,
+			    struct fb_info *fb_info)
 {
 	/*
 	 *  Set a single color register. The values supplied are
@@ -397,21 +395,10 @@
     return 0;
 }
 
-static void do_install_cmap(int con, struct fb_info *info)
-{
-	if (con != currcon)
-		return;
-	if (fb_display[con].cmap.len)
-		fb_set_cmap(&fb_display[con].cmap, 1, vesa_setcolreg, info);
-	else
-		fb_set_cmap(fb_default_cmap(video_cmap_len), 1, vesa_setcolreg,
-			    info);
-}
-
 static int vesafb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			   struct fb_info *info)
 {
-	if (con == currcon) /* current console? */
+	if (con == info->currcon) /* current console? */
 		return fb_get_cmap(cmap, kspc, vesa_getcolreg, info);
 	else if (fb_display[con].cmap.len) /* non default colormap? */
 		fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
@@ -421,30 +408,14 @@
 	return 0;
 }
 
-static int vesafb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-			   struct fb_info *info)
-{
-	int err;
-
-	if (!fb_display[con].cmap.len) {	/* no colormap allocated? */
-		err = fb_alloc_cmap(&fb_display[con].cmap,video_cmap_len,0);
-		if (err)
-			return err;
-	}
-	if (con == currcon)			/* current console? */
-		return fb_set_cmap(cmap, kspc, vesa_setcolreg, info);
-	else
-		fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
-	return 0;
-}
-
 static struct fb_ops vesafb_ops = {
 	owner:		THIS_MODULE,
 	fb_get_fix:	vesafb_get_fix,
 	fb_get_var:	vesafb_get_var,
 	fb_set_var:	vesafb_set_var,
 	fb_get_cmap:	vesafb_get_cmap,
-	fb_set_cmap:	vesafb_set_cmap,
+	fb_set_cmap:	gen_set_cmap,
+	fb_setcolreg:	vesafb_setcolreg,
 	fb_pan_display:	vesafb_pan_display,
 };
 
@@ -483,24 +454,17 @@
 static int vesafb_switch(int con, struct fb_info *info)
 {
 	/* Do we have to save the colormap? */
-	if (fb_display[currcon].cmap.len)
-		fb_get_cmap(&fb_display[currcon].cmap, 1, vesa_getcolreg,
+	if (fb_display[info->currcon].cmap.len)
+		fb_get_cmap(&fb_display[info->currcon].cmap, 1, vesa_getcolreg,
 			    info);
 	
-	currcon = con;
+	info->currcon = con;
 	/* Install new colormap */
 	do_install_cmap(con, info);
 	vesafb_update_var(con,info);
 	return 1;
 }
 
-/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */
-
-static void vesafb_blank(int blank, struct fb_info *info)
-{
-	/* Not supported */
-}
-
 int __init vesafb_init(void)
 {
 	int i,j;
@@ -650,10 +614,11 @@
 	fb_info.changevar = NULL;
 	fb_info.node = NODEV;
 	fb_info.fbops = &vesafb_ops;
+	fb_info.screen_base = video_vbase;
 	fb_info.disp=&disp;
+	fb_info.currcon = -1;
 	fb_info.switch_con=&vesafb_switch;
 	fb_info.updatevar=&vesafb_update_var;
-	fb_info.blank=&vesafb_blank;
 	fb_info.flags=FBINFO_FLAG_DEFAULT;
 	vesafb_set_disp(-1);
 
diff -Nru a/drivers/video/vfb.c b/drivers/video/vfb.c
--- a/drivers/video/vfb.c	Sun Apr 28 20:10:30 2002
+++ b/drivers/video/vfb.c	Sun Apr 28 20:10:30 2002
@@ -43,7 +43,6 @@
 
 static u_long videomemory, videomemorysize = VIDEOMEMSIZE;
 MODULE_PARM(videomemorysize, "l");
-static int currcon = 0;
 static struct display disp;
 static struct fb_info fb_info;
 static struct { u_char red, green, blue, pad; } palette[256];
@@ -83,13 +82,12 @@
 		       struct fb_info *info);
 static int vfb_set_var(struct fb_var_screeninfo *var, int con,
 		       struct fb_info *info);
+static int vfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+                         u_int transp, struct fb_info *info);
 static int vfb_pan_display(struct fb_var_screeninfo *var, int con,
 			   struct fb_info *info);
 static int vfb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			struct fb_info *info);
-static int vfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-			struct fb_info *info);
-
 
     /*
      *  Interface to the low level console driver
@@ -98,8 +96,6 @@
 int vfb_init(void);
 static int vfbcon_switch(int con, struct fb_info *info);
 static int vfbcon_updatevar(int con, struct fb_info *info);
-static void vfbcon_blank(int blank, struct fb_info *info);
-
 
     /*
      *  Internal routines
@@ -111,10 +107,6 @@
 static void set_color_bitfields(struct fb_var_screeninfo *var);
 static int vfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
                          u_int *transp, struct fb_info *info);
-static int vfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-                         u_int transp, struct fb_info *info);
-static void do_install_cmap(int con, struct fb_info *info);
-
 
 static struct fb_ops vfb_ops = {
 	owner:		THIS_MODULE,
@@ -122,7 +114,8 @@
 	fb_get_var:	vfb_get_var,
 	fb_set_var:	vfb_set_var,
 	fb_get_cmap:	vfb_get_cmap,
-	fb_set_cmap:	vfb_set_cmap,
+	fb_set_cmap:	gen_set_cmap,
+	fb_setcolreg:	vfb_setcolreg,
 	fb_pan_display:	vfb_pan_display,
 };
 
@@ -237,7 +230,6 @@
 	    struct fb_fix_screeninfo fix;
 
 	    vfb_encode_fix(&fix, var);
-	    display->screen_base = (char *)videomemory;
 	    display->visual = fix.visual;
 	    display->type = fix.type;
 	    display->type_aux = fix.type_aux;
@@ -339,7 +331,7 @@
 static int vfb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			struct fb_info *info)
 {
-    if (con == currcon) /* current console? */
+    if (con == info->currcon) /* current console? */
 	return fb_get_cmap(cmap, kspc, vfb_getcolreg, info);
     else if (fb_display[con].cmap.len) /* non default colormap? */
 	fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
@@ -349,28 +341,6 @@
     return 0;
 }
 
-    /*
-     *  Set the Colormap
-     */
-
-static int vfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-			struct fb_info *info)
-{
-    int err;
-
-    if (!fb_display[con].cmap.len) {	/* no colormap allocated? */
-	if ((err = fb_alloc_cmap(&fb_display[con].cmap,
-			      1<<fb_display[con].var.bits_per_pixel, 0)))
-	    return err;
-    }
-    if (con == currcon)			/* current console? */
-	return fb_set_cmap(cmap, kspc, vfb_setcolreg, info);
-    else
-	fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
-    return 0;
-}
-
-
 int __init vfb_setup(char *options)
 {
     char *this_opt;
@@ -406,10 +376,11 @@
     fb_info.changevar = NULL;
     fb_info.node = NODEV;
     fb_info.fbops = &vfb_ops;
+    fb_info.screen_base = (char *)videomemory;
     fb_info.disp = &disp;
+    fb_info.currcon = -1;	
     fb_info.switch_con = &vfbcon_switch;
     fb_info.updatevar = &vfbcon_updatevar;
-    fb_info.blank = &vfbcon_blank;
     fb_info.flags = FBINFO_FLAG_DEFAULT;
 
     vfb_set_var(&vfb_default, -1, &fb_info);
@@ -428,10 +399,10 @@
 static int vfbcon_switch(int con, struct fb_info *info)
 {
     /* Do we have to save the colormap? */
-    if (fb_display[currcon].cmap.len)
-	fb_get_cmap(&fb_display[currcon].cmap, 1, vfb_getcolreg, info);
+    if (fb_display[info->currcon].cmap.len)
+	fb_get_cmap(&fb_display[info->currcon].cmap, 1, vfb_getcolreg, info);
 
-    currcon = con;
+    info->currcon = con;
     /* Install new colormap */
     do_install_cmap(con, info);
     return 0;
@@ -447,15 +418,6 @@
     return 0;
 }
 
-    /*
-     *  Blank the display.
-     */
-
-static void vfbcon_blank(int blank, struct fb_info *info)
-{
-    /* Nothing */
-}
-
 static u_long get_line_length(int xres_virtual, int bpp)
 {
     u_long length;
@@ -585,19 +547,6 @@
     palette[regno].blue = blue;
     return 0;
 }
-
-
-static void do_install_cmap(int con, struct fb_info *info)
-{
-    if (con != currcon)
-	return;
-    if (fb_display[con].cmap.len)
-	fb_set_cmap(&fb_display[con].cmap, 1, vfb_setcolreg, info);
-    else
-	fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), 1,
-		    vfb_setcolreg, info);
-}
-
 
 #ifdef MODULE
 MODULE_LICENSE("GPL");
diff -Nru a/drivers/video/vga16fb.c b/drivers/video/vga16fb.c
--- a/drivers/video/vga16fb.c	Sun Apr 28 20:10:33 2002
+++ b/drivers/video/vga16fb.c	Sun Apr 28 20:10:33 2002
@@ -102,8 +102,6 @@
 static struct display disp;
 static struct { u_short blue, green, red, pad; } palette[256];
 
-static int             currcon   = 0;
-
 /* --------------------------------------------------------------------- */
 
 static void vga16fb_pan_var(struct fb_info *info, struct fb_var_screeninfo *var)
@@ -176,7 +174,6 @@
 	
 	vga16fb_get_fix(&fix, con, &info->fb_info);
 
-	display->screen_base = info->video_vbase;
 	display->visual = fix.visual;
 	display->type = fix.type;
 	display->type_aux = fix.type_aux;
@@ -535,7 +532,7 @@
 			if (info->fb_info.changevar)
 				info->fb_info.changevar(con);
 		}
-		if (con == currcon)
+		if (con == info->fb_info.currcon)
 			vga16fb_set_par(&par, info);
 	}
 
@@ -582,9 +579,9 @@
 	outb(blue  >> 10, dac_val);
 }
 
-static int vga16_setcolreg(unsigned regno, unsigned red, unsigned green,
-			  unsigned blue, unsigned transp,
-			  struct fb_info *fb_info)
+static int vga16fb_setcolreg(unsigned regno, unsigned red, unsigned green,
+			     unsigned blue, unsigned transp,
+			     struct fb_info *fb_info)
 {
 	int gray;
 
@@ -602,10 +599,10 @@
 	palette[regno].green = green;
 	palette[regno].blue  = blue;
 	
-	if (currcon < 0)
+	if (fb_info->currcon < 0)
 		gray = disp.var.grayscale;
 	else
-		gray = fb_display[currcon].var.grayscale;
+		gray = fb_display[fb_info->currcon].var.grayscale;
 	if (gray) {
 		/* gray = 0.30*R + 0.59*G + 0.11*B */
 		red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
@@ -618,21 +615,10 @@
 	return 0;
 }
 
-static void do_install_cmap(int con, struct fb_info *info)
-{
-	if (con != currcon)
-		return;
-	if (fb_display[con].cmap.len)
-		fb_set_cmap(&fb_display[con].cmap, 1, vga16_setcolreg, info);
-	else
-		fb_set_cmap(fb_default_cmap(16), 1, vga16_setcolreg,
-			    info);
-}
-
 static int vga16fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			   struct fb_info *info)
 {
-	if (con == currcon) /* current console? */
+	if (con == info->currcon) /* current console? */
 		return fb_get_cmap(cmap, kspc, vga16_getcolreg, info);
 	else if (fb_display[con].cmap.len) /* non default colormap? */
 		fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
@@ -642,30 +628,13 @@
 	return 0;
 }
 
-static int vga16fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-			   struct fb_info *info)
-{
-	int err;
-
-	if (!fb_display[con].cmap.len) {	/* no colormap allocated? */
-		err = fb_alloc_cmap(&fb_display[con].cmap,16,0);
-		if (err)
-			return err;
-	}
-	if (con == currcon)			/* current console? */
-		return fb_set_cmap(cmap, kspc, vga16_setcolreg, info);
-	else
-		fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
-	return 0;
-}
-
 static int vga16fb_pan_display(struct fb_var_screeninfo *var, int con,
 			       struct fb_info *info) 
 {
 	if (var->xoffset + fb_display[con].var.xres > fb_display[con].var.xres_virtual ||
 	    var->yoffset + fb_display[con].var.yres > fb_display[con].var.yres_virtual)
 		return -EINVAL;
-	if (con == currcon)
+	if (con == info->currcon)
 		vga16fb_pan_var(info, var);
 	fb_display[con].var.xoffset = var->xoffset;
 	fb_display[con].var.yoffset = var->yoffset;
@@ -673,55 +642,6 @@
 	return 0;
 }
 
-static struct fb_ops vga16fb_ops = {
-	owner:		THIS_MODULE,
-	fb_get_fix:	vga16fb_get_fix,
-	fb_get_var:	vga16fb_get_var,
-	fb_set_var:	vga16fb_set_var,
-	fb_get_cmap:	vga16fb_get_cmap,
-	fb_set_cmap:	vga16fb_set_cmap,
-	fb_pan_display:	vga16fb_pan_display,
-};
-
-int vga16fb_setup(char *options)
-{
-	char *this_opt;
-	
-	vga16fb.fb_info.fontname[0] = '\0';
-	
-	if (!options || !*options)
-		return 0;
-	
-	while ((this_opt = strsep(&options, ",")) != NULL) {
-		if (!*this_opt) continue;
-		
-		if (!strncmp(this_opt, "font:", 5))
-			strcpy(vga16fb.fb_info.fontname, this_opt+5);
-	}
-	return 0;
-}
-
-static int vga16fb_switch(int con, struct fb_info *fb)
-{
-	struct vga16fb_par par;
-	struct vga16fb_info *info = (struct vga16fb_info*)fb;
-
-	/* Do we have to save the colormap? */
-	if (fb_display[currcon].cmap.len)
-		fb_get_cmap(&fb_display[currcon].cmap, 1, vga16_getcolreg,
-			    fb);
-	
-	currcon = con;
-	vga16fb_decode_var(&fb_display[con].var, &par, info);
-	vga16fb_set_par(&par, info);
-	vga16fb_set_disp(con, info);
-
-	/* Install new colormap */
-	do_install_cmap(con, fb);
-/*	vga16fb_update_var(con, fb); */
-	return 1;
-}
-
 /* The following VESA blanking code is taken from vgacon.c.  The VGA
    blanking code was originally by Huang shi chao, and modified by
    Christoph Rimek (chrimek@toppoint.de) and todd j. derr
@@ -862,7 +782,7 @@
 }
 
 /* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */
-static void vga16fb_blank(int blank, struct fb_info *fb_info)
+static int vga16fb_blank(int blank, struct fb_info *fb_info)
 {
 	struct vga16fb_info *info = (struct vga16fb_info*)fb_info;
 
@@ -873,7 +793,7 @@
 			info->vesa_blanked = 0;
 		}
 		if (info->palette_blanked) {
-			do_install_cmap(currcon, fb_info);
+			do_install_cmap(fb_info->currcon, fb_info);
 			info->palette_blanked = 0;
 		}
 		break;
@@ -886,6 +806,58 @@
 		info->vesa_blanked = 1;
 		break;
 	}
+	return 0;
+}
+
+static struct fb_ops vga16fb_ops = {
+	owner:		THIS_MODULE,
+	fb_get_fix:	vga16fb_get_fix,
+	fb_get_var:	vga16fb_get_var,
+	fb_set_var:	vga16fb_set_var,
+	fb_get_cmap:	vga16fb_get_cmap,
+	fb_set_cmap:	gen_set_cmap,
+	fb_setcolreg:	vga16fb_setcolreg,
+	fb_pan_display:	vga16fb_pan_display,
+	fb_blank:	vga16fb_blank,
+};
+
+int vga16fb_setup(char *options)
+{
+	char *this_opt;
+	
+	vga16fb.fb_info.fontname[0] = '\0';
+	
+	if (!options || !*options)
+		return 0;
+	
+	while ((this_opt = strsep(&options, ",")) != NULL) {
+		if (!*this_opt) continue;
+		
+		if (!strncmp(this_opt, "font:", 5))
+			strcpy(vga16fb.fb_info.fontname, this_opt+5);
+	}
+	return 0;
+}
+
+static int vga16fb_switch(int con, struct fb_info *fb)
+{
+	struct vga16fb_par par;
+	struct vga16fb_info *info = (struct vga16fb_info*)fb;
+
+	/* Do we have to save the colormap? */
+	if (fb_display[fb->currcon].cmap.len)
+		fb_get_cmap(&fb_display[fb->currcon].cmap, 1, vga16_getcolreg,
+			    fb);
+	
+	fb->currcon = con;
+	vga16fb_decode_var(&fb_display[con].var, &par, info);
+	vga16fb_set_par(&par, info);
+	vga16fb_set_disp(con, info);
+
+	/* Install new colormap */
+	do_install_cmap(con, fb);
+/*	vga16fb_update_var(con, fb); */
+	return 1;
 }
 
 int __init vga16fb_init(void)
@@ -928,10 +900,11 @@
 	vga16fb.fb_info.changevar = NULL;
 	vga16fb.fb_info.node = NODEV;
 	vga16fb.fb_info.fbops = &vga16fb_ops;
+	vga16fb.fb_info.screen_base = vga16fb.video_vbase;
 	vga16fb.fb_info.disp=&disp;
+	vga16fb.fb_info.currcon = -1;
 	vga16fb.fb_info.switch_con=&vga16fb_switch;
 	vga16fb.fb_info.updatevar=&vga16fb_update_var;
-	vga16fb.fb_info.blank=&vga16fb_blank;
 	vga16fb.fb_info.flags=FBINFO_FLAG_DEFAULT;
 	vga16fb_set_disp(-1, &vga16fb);
 
diff -Nru a/drivers/video/virgefb.c b/drivers/video/virgefb.c
--- a/drivers/video/virgefb.c	Sun Apr 28 20:10:34 2002
+++ b/drivers/video/virgefb.c	Sun Apr 28 20:10:34 2002
@@ -108,7 +108,6 @@
 static struct virgefb_par current_par;
 
 static int current_par_valid = 0;
-static int currcon = 0;
 
 static struct display disp;
 static struct fb_info fb_info;
@@ -136,8 +135,6 @@
    int (*encode_var)(struct fb_var_screeninfo *var, struct virgefb_par *par);
    int (*getcolreg)(u_int regno, u_int *red, u_int *green, u_int *blue,
                     u_int *transp, struct fb_info *info);
-   int (*setcolreg)(u_int regno, u_int red, u_int green, u_int blue,
-                    u_int transp, struct fb_info *info);
    void (*blank)(int blank);
 } *fbhw;
 
@@ -304,9 +301,9 @@
 fb_info *info);
 static int virgefb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			    struct fb_info *info);
-static int virgefb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-			    struct fb_info *info);
-
+static int virgefb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+                         u_int transp, struct fb_info *info);
+static int virgefb_blank(int blank, struct fb_info *info);
 
 /*
  *    Interface to the low level console driver
@@ -315,8 +312,6 @@
 int virgefb_init(void);
 static int Cyberfb_switch(int con, struct fb_info *info);
 static int Cyberfb_updatevar(int con, struct fb_info *info);
-static void Cyberfb_blank(int blank, struct fb_info *info);
-
 
 /*
  *    Text console acceleration
@@ -343,8 +338,6 @@
                           struct virgefb_par *par);
 static int Cyber_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
                          u_int *transp, struct fb_info *info);
-static int Cyber_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-                         u_int transp, struct fb_info *info);
 static void Cyber_blank(int blank);
 
 
@@ -355,7 +348,6 @@
 static void virgefb_get_par(struct virgefb_par *par);
 static void virgefb_set_par(struct virgefb_par *par);
 static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive);
-static void do_install_cmap(int con, struct fb_info *info);
 static void virgefb_set_disp(int con, struct fb_info *info);
 static int get_video_mode(const char *name);
 
@@ -573,8 +565,8 @@
  *    entries in the var structure). Return != 0 for invalid regno.
  */
 
-static int Cyber_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-			   u_int transp, struct fb_info *info)
+static int virgefb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+			     u_int transp, struct fb_info *info)
 {
 	if (((current_par.bpp==8) && (regno>255)) ||
 		((current_par.bpp!=8) && (regno>15)))
@@ -817,7 +809,7 @@
 
 static struct fb_hwswitch Cyber_switch = {
 	Cyber_init, Cyber_encode_fix, Cyber_decode_var, Cyber_encode_var,
-	Cyber_getcolreg, Cyber_setcolreg, Cyber_blank
+	Cyber_getcolreg, Cyber_blank
 };
 
 
@@ -878,18 +870,6 @@
 	return 0;
 }
 
-
-static void do_install_cmap(int con, struct fb_info *info)
-{
-	if (con != currcon)
-		return;
-	if (fb_display[con].cmap.len)
-		fb_set_cmap(&fb_display[con].cmap, 1, fbhw->setcolreg, info);
-	else
-		fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
-			    1, fbhw->setcolreg, info);
-}
-
 /*
  *    Get the Fixed Part of the Display
  */
@@ -947,14 +927,14 @@
 	if (con == -1)
 		con = 0;
 	if (cv3d_on_zorro2) {
-		display->screen_base = (char*) CyberMem;
+		info->screen_base = (char*) CyberMem;
 	} else {
 	        switch (display->var.bits_per_pixel) {
 			case 8:
-				display->screen_base = (char*) (CyberMem + CYBMEM_OFFSET_8);
+				info->screen_base = (char*) (CyberMem + CYBMEM_OFFSET_8);
 				break;
 			case 16:
-				display->screen_base = (char*) (CyberMem + CYBMEM_OFFSET_16);
+				info->screen_base = (char*) (CyberMem + CYBMEM_OFFSET_16);
 				break;
 		}
 	}
@@ -1000,7 +980,7 @@
 {
 	int err, oldxres, oldyres, oldvxres, oldvyres, oldbpp, oldaccel;
 
-	if ((err = do_fb_set_var(var, con == currcon)))
+	if ((err = do_fb_set_var(var, con == info->currcon)))
 		return(err);
 	if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
 		oldxres = fb_display[con].var.xres;
@@ -1033,7 +1013,7 @@
 static int virgefb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			    struct fb_info *info)
 {
-	if (con == currcon) /* current console? */
+	if (con == info->currcon) /* current console? */
 		return(fb_get_cmap(cmap, kspc, fbhw->getcolreg, info));
 	else if (fb_display[con].cmap.len) /* non default colormap? */
 		fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
@@ -1043,39 +1023,27 @@
 	return(0);
 }
 
-
 /*
- *    Set the Colormap
+ *    Blank the display.
  */
 
-static int virgefb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-			    struct fb_info *info)
+static int virgefb_blank(int blank, struct fb_info *info)
 {
-	int err;
-
-	if (!fb_display[con].cmap.len) {       /* no colormap allocated? */
-		if ((err = fb_alloc_cmap(&fb_display[con].cmap,
-				1<<fb_display[con].var.bits_per_pixel, 0)))
-			return(err);
-	}
-	if (con == currcon)		 /* current console? */
-		return(fb_set_cmap(cmap, kspc, fbhw->setcolreg, info));
-	else
-		fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
-	return(0);
+	fbhw->blank(blank);
+	return 0;
 }
 
-
 static struct fb_ops virgefb_ops = {
 	owner:		THIS_MODULE,
 	fb_get_fix:	virgefb_get_fix,
 	fb_get_var:	virgefb_get_var,
 	fb_set_var:	virgefb_set_var,
 	fb_get_cmap:	virgefb_get_cmap,
-	fb_set_cmap:	virgefb_set_cmap,
+	fb_set_cmap:	gen_set_cmap,
+	fb_setcolreg:	virgefb_setcolreg,
+	fb_blank:	virgefb_blank,
 };
 
-
 int __init virgefb_setup(char *options)
 {
 	char *this_opt;
@@ -1171,9 +1139,9 @@
 	    fb_info.node = NODEV;
 	    fb_info.fbops = &virgefb_ops;
 	    fb_info.disp = &disp;
+	    fb_info.currcon = -1;	
 	    fb_info.switch_con = &Cyberfb_switch;
 	    fb_info.updatevar = &Cyberfb_updatevar;
-	    fb_info.blank = &Cyberfb_blank;
 	    fb_info.flags = FBINFO_FLAG_DEFAULT;
 
 	    fbhw->init();
@@ -1205,12 +1173,12 @@
 static int Cyberfb_switch(int con, struct fb_info *info)
 {
 	/* Do we have to save the colormap? */
-	if (fb_display[currcon].cmap.len)
-		fb_get_cmap(&fb_display[currcon].cmap, 1, fbhw->getcolreg,
+	if (fb_display[info->currcon].cmap.len)
+		fb_get_cmap(&fb_display[info->currcon].cmap, 1, fbhw->getcolreg,
 			    info);
 
 	do_fb_set_var(&fb_display[con].var, 1);
-	currcon = con;
+	info->currcon = con;
 	/* Install new colormap */
 	do_install_cmap(con, info);
 	return(0);
@@ -1234,11 +1202,10 @@
  *    Blank the display.
  */
 
-static void Cyberfb_blank(int blank, struct fb_info *info)
+static void virgefb_blank(int blank, struct fb_info *info)
 {
 	fbhw->blank(blank);
 }
-
 
 /*
  *    Get a Video Mode
diff -Nru a/fs/Config.help b/fs/Config.help
--- a/fs/Config.help	Sun Apr 28 20:10:37 2002
+++ b/fs/Config.help	Sun Apr 28 20:10:37 2002
@@ -563,33 +563,36 @@
   say N.
 
 CONFIG_NTFS_FS
-  NTFS is the file system of Microsoft Windows NT. Say Y if you want
-  to get read access to files on NTFS partitions of your hard drive.
-  The Linux NTFS driver supports most of the mount options of the VFAT
-  driver, see <file:Documentation/filesystems/ntfs.txt>. Saying Y here
-  will give you read-only access to NTFS partitions.
+  NTFS is the file system of Microsoft Windows NT/2000/XP. For more
+  information see <file:Documentation/filesystems/ntfs.txt>. Saying Y
+  here would allow you to read from NTFS partitions.
 
-  This code is also available as a module ( = code which can be
+  This file system 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 ntfs.o. If you want to compile it as a
   module, say M here and read <file:Documentation/modules.txt>.
 
-CONFIG_NTFS_RW
-  If you say Y here, you will (maybe) be able to write to NTFS file
-  systems as well as read from them. The read-write support in NTFS
-  is far from being complete and is not well tested. If you say Y
-  here, back up your NTFS volume first, since it will probably get
-  damaged. Also, download the Linux-NTFS project distribution from
-  Sourceforge at <http://linux-ntfs.sf.net/> and always run the
-  included ntfsfix utility after writing to an NTFS partition from
-  Linux to fix some of the damage done by the driver. You should run
-  ntfsfix _after_ unmounting the partition in Linux but _before_
-  rebooting into Windows. When Windows next boots, chkdsk will be
-  run automatically to fix the remaining damage.
-  Please note that write support is limited to Windows NT4 and
-  earlier versions.
+  If you are not using Windows NT/2000/XP in addition to Linux on your
+  computer it is safe to say N.
 
-  If unsure, say N.
+CONFIG_NTFS_DEBUG
+  If you are experiencing any problems with the NTFS file system, say
+  Y here. This will result in additional consistency checks to be
+  performed by the driver as well as additional debugging messages to
+  be written to the system log. Note that debugging messages are
+  disabled by default. To enable them, supply the option debug_msgs=1
+  at the kernel command line when booting the kernel or as an option
+  to insmod when loading the ntfs module. Once the driver is active,
+  you can enable debugging messages by doing (as root):
+    echo 1 > /proc/sys/fs/ntfs-debug
+  Replacing the "1" with "0" would disable debug messages.
+
+  If you leave debugging messages disabled, this results in little
+  overhead, but enabling debug messages results in very significant
+  slowdown of the system.
+
+  When reporting bugs, please try to have available a full dump of
+  debugging messages while the misbehaviour was occurring.
 
 CONFIG_SYSV_FS
   SCO, Xenix and Coherent are commercial Unix systems for Intel
diff -Nru a/fs/Config.in b/fs/Config.in
--- a/fs/Config.in	Sun Apr 28 20:10:31 2002
+++ b/fs/Config.in	Sun Apr 28 20:10:31 2002
@@ -62,8 +62,9 @@
 tristate 'Minix fs support' CONFIG_MINIX_FS
 
 tristate 'FreeVxFS file system support (VERITAS VxFS(TM) compatible)' CONFIG_VXFS_FS
+
 tristate 'NTFS file system support (read only)' CONFIG_NTFS_FS
-dep_mbool '  NTFS write support (DANGEROUS)' CONFIG_NTFS_RW $CONFIG_NTFS_FS $CONFIG_EXPERIMENTAL
+dep_mbool '  NTFS debugging support' CONFIG_NTFS_DEBUG $CONFIG_NTFS_FS
 
 tristate 'OS/2 HPFS file system support' CONFIG_HPFS_FS
 
diff -Nru a/fs/bio.c b/fs/bio.c
--- a/fs/bio.c	Sun Apr 28 20:10:30 2002
+++ b/fs/bio.c	Sun Apr 28 20:10:30 2002
@@ -202,7 +202,7 @@
 	bio->bi_io_vec = bio_src->bi_io_vec;
 
 	bio->bi_sector = bio_src->bi_sector;
-	bio->bi_dev = bio_src->bi_dev;
+	bio->bi_bdev = bio_src->bi_bdev;
 	bio->bi_flags |= 1 << BIO_CLONED;
 	bio->bi_rw = bio_src->bi_rw;
 
@@ -300,7 +300,7 @@
 	}
 
 	b->bi_sector = bio->bi_sector;
-	b->bi_dev = bio->bi_dev;
+	b->bi_bdev = bio->bi_bdev;
 	b->bi_rw = bio->bi_rw;
 
 	b->bi_vcnt = bio->bi_vcnt;
@@ -383,7 +383,7 @@
 	}
 
 	bio->bi_sector = sector;
-	bio->bi_dev = dev;
+	bio->bi_bdev = bdev;
 	bio->bi_idx = 0;
 	bio->bi_end_io = bio_end_io_kio;
 	bio->bi_private = kio;
diff -Nru a/fs/block_dev.c b/fs/block_dev.c
--- a/fs/block_dev.c	Sun Apr 28 20:10:37 2002
+++ b/fs/block_dev.c	Sun Apr 28 20:10:37 2002
@@ -106,7 +106,7 @@
 
 int sb_min_blocksize(struct super_block *sb, int size)
 {
-	int minsize = get_hardsect_size(sb->s_dev);
+	int minsize = bdev_hardsect_size(sb->s_bdev);
 	if (size < minsize)
 		size = minsize;
 	return sb_set_blocksize(sb, size);
@@ -117,7 +117,6 @@
 	if (iblock >= max_block(inode->i_rdev))
 		return -EIO;
 
-	bh->b_dev = inode->i_rdev;
 	bh->b_bdev = inode->i_bdev;
 	bh->b_blocknr = iblock;
 	bh->b_state |= 1UL << BH_Mapped;
@@ -353,6 +352,7 @@
 			atomic_set(&new_bdev->bd_count,1);
 			new_bdev->bd_dev = dev;
 			new_bdev->bd_op = NULL;
+			new_bdev->bd_contains = NULL;
 			new_bdev->bd_inode = inode;
 			inode->i_mode = S_IFBLK;
 			inode->i_rdev = kdev;
@@ -563,7 +563,7 @@
 		return 0;
 
 	printk(KERN_DEBUG "VFS: Disk change detected on device %s\n",
-		bdevname(dev));
+		__bdevname(dev));
 
 	if (invalidate_device(dev, 0))
 		printk("VFS: busy inodes on changed media.\n");
@@ -590,28 +590,62 @@
 {
 	int ret = -ENXIO;
 	kdev_t dev = to_kdev_t(bdev->bd_dev);
+	struct module *owner = NULL;
 
 	down(&bdev->bd_sem);
 	lock_kernel();
-	if (!bdev->bd_op)
+	if (!bdev->bd_op) {
 		bdev->bd_op = get_blkfops(major(dev));
-	if (bdev->bd_op) {
-		ret = 0;
-		if (bdev->bd_op->owner)
-			__MOD_INC_USE_COUNT(bdev->bd_op->owner);
-		if (bdev->bd_op->open)
-			ret = bdev->bd_op->open(inode, file);
-		if (!ret) {
-			bdev->bd_openers++;
-			bdev->bd_inode->i_size = blkdev_size(dev);
-			bdev->bd_inode->i_blkbits = blksize_bits(block_size(dev));
-		} else {
-			if (bdev->bd_op->owner)
-				__MOD_DEC_USE_COUNT(bdev->bd_op->owner);
-			if (!bdev->bd_openers)
-				bdev->bd_op = NULL;
+		if (!bdev->bd_op)
+			goto out;
+		owner = bdev->bd_op->owner;
+		if (owner)
+			__MOD_INC_USE_COUNT(owner);
+	}
+	if (!bdev->bd_contains) {
+		unsigned minor = minor(dev);
+		struct gendisk *g = get_gendisk(dev);
+		bdev->bd_contains = bdev;
+		if (g) {
+			int shift = g->minor_shift;
+			unsigned minor0 = (minor >> shift) << shift;
+			if (minor != minor0) {
+				struct block_device *disk;
+				disk = bdget(MKDEV(major(dev), minor0));
+				ret = -ENOMEM;
+				if (!disk)
+					goto out1;
+				ret = blkdev_get(disk, file->f_mode, file->f_flags, BDEV_RAW);
+				if (ret)
+					goto out1;
+				bdev->bd_contains = disk;
+			}
+		}
+	}
+	if (bdev->bd_op->open) {
+		ret = bdev->bd_op->open(inode, file);
+		if (ret)
+			goto out2;
+	}
+	bdev->bd_openers++;
+	bdev->bd_inode->i_size = blkdev_size(dev);
+	bdev->bd_inode->i_blkbits = blksize_bits(block_size(dev));
+	unlock_kernel();
+	up(&bdev->bd_sem);
+	return 0;
+
+out2:
+	if (!bdev->bd_openers) {
+		bdev->bd_op = NULL;
+		if (bdev != bdev->bd_contains) {
+			blkdev_put(bdev->bd_contains, BDEV_RAW);
+			bdev->bd_contains = NULL;
 		}
 	}
+out1:
+	if (owner)
+		__MOD_DEC_USE_COUNT(owner);
+out:
 	unlock_kernel();
 	up(&bdev->bd_sem);
 	if (ret)
@@ -676,8 +710,13 @@
 		ret = bdev->bd_op->release(bd_inode, NULL);
 	if (bdev->bd_op->owner)
 		__MOD_DEC_USE_COUNT(bdev->bd_op->owner);
-	if (!bdev->bd_openers)
+	if (!bdev->bd_openers) {
 		bdev->bd_op = NULL;
+		if (bdev != bdev->bd_contains) {
+			blkdev_put(bdev->bd_contains, BDEV_RAW);
+			bdev->bd_contains = NULL;
+		}
+	}
 	unlock_kernel();
 	up(&bdev->bd_sem);
 	bdput(bdev);
@@ -728,7 +767,7 @@
 	ioctl:		blkdev_ioctl,
 };
 
-const char * bdevname(kdev_t dev)
+const char *__bdevname(kdev_t dev)
 {
 	static char buffer[32];
 	const char * name = blkdevs[major(dev)].name;
diff -Nru a/fs/buffer.c b/fs/buffer.c
--- a/fs/buffer.c	Sun Apr 28 20:10:30 2002
+++ b/fs/buffer.c	Sun Apr 28 20:10:30 2002
@@ -507,7 +507,7 @@
 
 static inline void __insert_into_hash_list(struct buffer_head *bh)
 {
-	struct buffer_head **head = &hash(bh->b_dev, bh->b_blocknr);
+	struct buffer_head **head = &hash(to_kdev_t(bh->b_bdev->bd_dev), bh->b_blocknr);
 	struct buffer_head *next = *head;
 
 	*head = bh;
@@ -1142,7 +1142,6 @@
 	if (nr_unused_buffer_heads >= MAX_UNUSED_BUFFERS) {
 		kmem_cache_free(bh_cachep, bh);
 	} else {
-		bh->b_dev = B_FREE;
 		bh->b_bdev = NULL;
 		bh->b_blocknr = -1;
 		bh->b_this_page = NULL;
@@ -1246,7 +1245,6 @@
 		if (!bh)
 			goto no_grow;
 
-		bh->b_dev = NODEV;
 		bh->b_bdev = NULL;
 		bh->b_this_page = head;
 		head = bh;
@@ -2092,7 +2090,6 @@
 		struct buffer_head bh;
 
 		bh.b_state = 0;
-		bh.b_dev = inode->i_dev;
 		bh.b_size = blocksize;
 
 		retval = get_block(inode, blocknr, &bh, rw & 1);
@@ -2210,7 +2207,6 @@
 		lock_buffer(bh);
 		bh->b_blocknr = *(b++);
 		bh->b_bdev = bdev;
-		bh->b_dev = to_kdev_t(bdev->bd_dev);
 		set_bit(BH_Mapped, &bh->b_state);
 		set_buffer_async_io(bh);
 		bh = bh->b_this_page;
@@ -2325,7 +2321,6 @@
 		if (!(bh->b_state & (1 << BH_Mapped))) {
 			init_buffer(bh, NULL, NULL);
 			bh->b_bdev = bdev;
-			bh->b_dev = to_kdev_t(bdev->bd_dev);
 			bh->b_blocknr = block;
 			bh->b_state = uptodate;
 		}
@@ -2351,7 +2346,7 @@
 	int sizebits;
 
 	/* Size must be multiple of hard sectorsize */
-	if (size & (get_hardsect_size(to_kdev_t(bdev->bd_dev))-1))
+	if (size & (bdev_hardsect_size(bdev)-1))
 		BUG();
 	/* Size must be within 512 bytes and PAGE_SIZE */
 	if (size < 512 || size > PAGE_SIZE)
@@ -2462,9 +2457,6 @@
 	do {
 		struct buffer_head * p = tmp;
 		tmp = tmp->b_this_page;
-
-		if (kdev_same(p->b_dev, B_FREE)) BUG();
-
 		remove_inode_queue(p);
 		__remove_from_queues(p);
 		__put_unused_buffer_head(p);
diff -Nru a/fs/dcache.c b/fs/dcache.c
--- a/fs/dcache.c	Sun Apr 28 20:10:31 2002
+++ b/fs/dcache.c	Sun Apr 28 20:10:31 2002
@@ -846,13 +846,22 @@
  
 struct dentry * d_lookup(struct dentry * parent, struct qstr * name)
 {
+	struct dentry * dentry;
+	spin_lock(&dcache_lock);
+	dentry = __d_lookup(parent,name);
+	spin_unlock(&dcache_lock);
+	return dentry;
+}
+
+struct dentry * __d_lookup(struct dentry * parent, struct qstr * name)  
+{
+
 	unsigned int len = name->len;
 	unsigned int hash = name->hash;
 	const unsigned char *str = name->name;
 	struct list_head *head = d_hash(parent,hash);
 	struct list_head *tmp;
 
-	spin_lock(&dcache_lock);
 	tmp = head->next;
 	for (;;) {
 		struct dentry * dentry = list_entry(tmp, struct dentry, d_hash);
@@ -874,10 +883,8 @@
 		}
 		__dget_locked(dentry);
 		dentry->d_vfs_flags |= DCACHE_REFERENCED;
-		spin_unlock(&dcache_lock);
 		return dentry;
 	}
-	spin_unlock(&dcache_lock);
 	return NULL;
 }
 
diff -Nru a/fs/ext3/inode.c b/fs/ext3/inode.c
--- a/fs/ext3/inode.c	Sun Apr 28 20:10:37 2002
+++ b/fs/ext3/inode.c	Sun Apr 28 20:10:37 2002
@@ -1014,8 +1014,8 @@
 			      unsigned from, unsigned to)
 {
 	struct inode *inode = page->mapping->host;
-	handle_t *handle = ext3_journal_current_handle();
 	int ret, needed_blocks = ext3_writepage_trans_blocks(inode);
+	handle_t *handle;
 
 	lock_kernel();
 	handle = ext3_journal_start(inode, needed_blocks);
diff -Nru a/fs/ext3/super.c b/fs/ext3/super.c
--- a/fs/ext3/super.c	Sun Apr 28 20:10:38 2002
+++ b/fs/ext3/super.c	Sun Apr 28 20:10:38 2002
@@ -58,7 +58,7 @@
 {
 	if (bdev) {
 		printk(KERN_WARNING "Turning device %s read-only\n", 
-		       bdevname(to_kdev_t(bdev->bd_dev)));
+		       bdevname(bdev));
 		*no_write = 0xdead0000 + bdev->bd_dev;
 	}
 }
@@ -351,7 +351,7 @@
 
 fail:
 	printk(KERN_ERR "EXT3: failed to open journal device %s: %d\n",
-			bdevname(dev), err);
+			__bdevname(dev), err);
 	return NULL;
 }
 
@@ -764,7 +764,7 @@
 				sb->s_id);
 	if (EXT3_SB(sb)->s_journal->j_inode == NULL) {
 		printk("external journal on %s\n",
-		    bdevname(to_kdev_t(EXT3_SB(sb)->s_journal->j_dev->bd_dev)));
+		    bdevname(EXT3_SB(sb)->s_journal->j_dev));
 	} else {
 		printk("internal journal\n");
 	}
@@ -943,7 +943,6 @@
 	unsigned long logic_sb_block = 1;
 	unsigned long offset = 0;
 	unsigned long journal_inum = 0;
-	kdev_t dev = sb->s_dev;
 	int blocksize;
 	int hblock;
 	int db_count;
@@ -1033,7 +1032,7 @@
 
 	sb->s_maxbytes = ext3_max_size(sb->s_blocksize_bits);
 
-	hblock = get_hardsect_size(dev);
+	hblock = bdev_hardsect_size(sb->s_bdev);
 	if (sb->s_blocksize != blocksize) {
 		/*
 		 * Make sure the blocksize for the filesystem is larger
@@ -1322,7 +1321,7 @@
 		return NULL;
 
 	blocksize = sb->s_blocksize;
-	hblock = get_hardsect_size(j_dev);
+	hblock = bdev_hardsect_size(bdev);
 	if (blocksize < hblock) {
 		printk(KERN_ERR
 			"EXT3-fs: blocksize too small for journal device.\n");
diff -Nru a/fs/fat/cache.c b/fs/fat/cache.c
--- a/fs/fat/cache.c	Sun Apr 28 20:10:34 2002
+++ b/fs/fat/cache.c	Sun Apr 28 20:10:34 2002
@@ -34,62 +34,59 @@
 	return MSDOS_SB(inode->i_sb)->cvf_format->cvf_bmap(inode,sector);
 }
 
-int default_fat_access(struct super_block *sb,int nr,int new_value)
+int __fat_access(struct super_block *sb, int nr, int new_value)
 {
+	struct msdos_sb_info *sbi = MSDOS_SB(sb);
 	struct buffer_head *bh, *bh2, *c_bh, *c_bh2;
 	unsigned char *p_first, *p_last;
 	int copy, first, last, next, b;
 
-	if ((unsigned) (nr-2) >= MSDOS_SB(sb)->clusters)
-		return 0;
-	if (MSDOS_SB(sb)->fat_bits == 32) {
+	if (sbi->fat_bits == 32) {
 		first = last = nr*4;
-	} else if (MSDOS_SB(sb)->fat_bits == 16) {
+	} else if (sbi->fat_bits == 16) {
 		first = last = nr*2;
 	} else {
 		first = nr*3/2;
 		last = first+1;
 	}
-	b = MSDOS_SB(sb)->fat_start + (first >> sb->s_blocksize_bits);
+	b = sbi->fat_start + (first >> sb->s_blocksize_bits);
 	if (!(bh = fat_bread(sb, b))) {
-		printk("bread in fat_access failed\n");
-		return 0;
+		printk("FAT: bread(block %d) in fat_access failed\n", b);
+		return -EIO;
 	}
 	if ((first >> sb->s_blocksize_bits) == (last >> sb->s_blocksize_bits)) {
 		bh2 = bh;
 	} else {
 		if (!(bh2 = fat_bread(sb, b+1))) {
 			fat_brelse(sb, bh);
-			printk("2nd bread in fat_access failed\n");
-			return 0;
+			printk("FAT: bread(block %d) in fat_access failed\n",
+			       b + 1);
+			return -EIO;
 		}
 	}
-	if (MSDOS_SB(sb)->fat_bits == 32) {
+	if (sbi->fat_bits == 32) {
 		p_first = p_last = NULL; /* GCC needs that stuff */
 		next = CF_LE_L(((__u32 *) bh->b_data)[(first &
 		    (sb->s_blocksize - 1)) >> 2]);
 		/* Fscking Microsoft marketing department. Their "32" is 28. */
-		next &= 0xfffffff;
-		if (next >= 0xffffff7) next = -1;
-		PRINTK(("fat_bread: 0x%x, nr=0x%x, first=0x%x, next=0x%x\n", b, nr, first, next));
-
-	} else if (MSDOS_SB(sb)->fat_bits == 16) {
+		next &= 0x0fffffff;
+	} else if (sbi->fat_bits == 16) {
 		p_first = p_last = NULL; /* GCC needs that stuff */
 		next = CF_LE_W(((__u16 *) bh->b_data)[(first &
 		    (sb->s_blocksize - 1)) >> 1]);
-		if (next >= 0xfff7) next = -1;
 	} else {
 		p_first = &((__u8 *)bh->b_data)[first & (sb->s_blocksize - 1)];
 		p_last = &((__u8 *)bh2->b_data)[(first + 1) & (sb->s_blocksize - 1)];
-		if (nr & 1) next = ((*p_first >> 4) | (*p_last << 4)) & 0xfff;
-		else next = (*p_first+(*p_last << 8)) & 0xfff;
-		if (next >= 0xff7) next = -1;
+		if (nr & 1)
+			next = ((*p_first >> 4) | (*p_last << 4)) & 0xfff;
+		else
+			next = (*p_first+(*p_last << 8)) & 0xfff;
 	}
 	if (new_value != -1) {
-		if (MSDOS_SB(sb)->fat_bits == 32) {
+		if (sbi->fat_bits == 32) {
 			((__u32 *)bh->b_data)[(first & (sb->s_blocksize - 1)) >> 2]
 				= CT_LE_L(new_value);
-		} else if (MSDOS_SB(sb)->fat_bits == 16) {
+		} else if (sbi->fat_bits == 16) {
 			((__u16 *)bh->b_data)[(first & (sb->s_blocksize - 1)) >> 1]
 				= CT_LE_W(new_value);
 		} else {
@@ -104,9 +101,9 @@
 			fat_mark_buffer_dirty(sb, bh2);
 		}
 		fat_mark_buffer_dirty(sb, bh);
-		for (copy = 1; copy < MSDOS_SB(sb)->fats; copy++) {
-			b = MSDOS_SB(sb)->fat_start + (first >> sb->s_blocksize_bits)
-				+ MSDOS_SB(sb)->fat_length * copy;
+		for (copy = 1; copy < sbi->fats; copy++) {
+			b = sbi->fat_start + (first >> sb->s_blocksize_bits)
+				+ sbi->fat_length * copy;
 			if (!(c_bh = fat_bread(sb, b)))
 				break;
 			if (bh != bh2) {
@@ -129,6 +126,27 @@
 	return next;
 }
 
+int default_fat_access(struct super_block *sb, int nr, int new_value)
+{
+	int next;
+
+	next = -EIO;
+	if (nr < 2 || MSDOS_SB(sb)->clusters + 2 <= nr) {
+		fat_fs_panic(sb, "invalid access to FAT (entry 0x%08x)", nr);
+		goto out;
+	}
+	if (new_value == FAT_ENT_EOF)
+		new_value = EOF_FAT(sb);
+
+	next = __fat_access(sb, nr, new_value);
+	if (next < 0)
+		goto out;
+	if (next >= BAD_FAT(sb))
+		next = FAT_ENT_EOF;
+out:
+	return next;
+}
+
 void fat_cache_init(void)
 {
 	static int initialized = 0;
@@ -208,7 +226,7 @@
 		    && walk->start_cluster == first
 		    && walk->file_cluster == f_clu) {
 			if (walk->disk_cluster != d_clu) {
-				printk("FAT cache corruption inode=%ld\n",
+				printk("FAT: cache corruption inode=%lu\n",
 					inode->i_ino);
 				spin_unlock(&fat_cache_lock);
 				fat_cache_inval_inode(inode);
@@ -271,19 +289,30 @@
 }
 
 
-int fat_get_cluster(struct inode *inode,int cluster)
+static int fat_get_cluster(struct inode *inode, int cluster)
 {
+	struct super_block *sb = inode->i_sb;
 	int nr,count;
 
 	if (!(nr = MSDOS_I(inode)->i_start)) return 0;
 	if (!cluster) return nr;
 	count = 0;
-	for (fat_cache_lookup(inode,cluster,&count,&nr); count < cluster;
-	    count++) {
-		if ((nr = fat_access(inode->i_sb,nr,-1)) == -1) return 0;
-		if (!nr) return 0;
+	for (fat_cache_lookup(inode, cluster, &count, &nr);
+	     count < cluster;
+	     count++) {
+		nr = fat_access(sb, nr, -1);
+		if (nr == FAT_ENT_EOF) {
+			fat_fs_panic(sb, "%s: request beyond EOF (ino %lu)",
+				     __FUNCTION__, inode->i_ino);
+			return -EIO;
+		} else if (nr == FAT_ENT_FREE) {
+			fat_fs_panic(sb, "%s: invalid cluster chain (ino %lu)",
+				     __FUNCTION__, inode->i_ino);
+			return -EIO;
+		} else if (nr < 0)
+			return nr;
 	}
-	fat_cache_add(inode,cluster,nr);
+	fat_cache_add(inode, cluster, nr);
 	return nr;
 }
 
@@ -307,9 +336,11 @@
 
 	cluster = sector / sbi->cluster_size;
 	offset  = sector % sbi->cluster_size;
-	if (!(cluster = fat_get_cluster(inode, cluster)))
+	cluster = fat_get_cluster(inode, cluster);
+	if (cluster < 0)
+		return cluster;
+	else if (!cluster)
 		return 0;
-
 	return (cluster - 2) * sbi->cluster_size + sbi->data_start + offset;
 }
 
@@ -319,20 +350,25 @@
 
 int fat_free(struct inode *inode,int skip)
 {
+	struct super_block *sb = inode->i_sb;
 	int nr,last;
 
 	if (!(nr = MSDOS_I(inode)->i_start)) return 0;
 	last = 0;
 	while (skip--) {
 		last = nr;
-		if ((nr = fat_access(inode->i_sb,nr,-1)) == -1) return 0;
-		if (!nr) {
-			printk("fat_free: skipped EOF\n");
+		nr = fat_access(sb, nr, -1);
+		if (nr == FAT_ENT_EOF)
+			return 0;
+		else if (nr == FAT_ENT_FREE) {
+			fat_fs_panic(sb, "%s: invalid cluster chain (ino %lu)",
+				     __FUNCTION__, inode->i_ino);
 			return -EIO;
-		}
+		} else if (nr < 0)
+			return nr;
 	}
 	if (last) {
-		fat_access(inode->i_sb,last,EOF_FAT(inode->i_sb));
+		fat_access(sb, last, FAT_ENT_EOF);
 		fat_cache_inval_inode(inode);
 	} else {
 		fat_cache_inval_inode(inode);
@@ -340,20 +376,26 @@
 		MSDOS_I(inode)->i_logstart = 0;
 		mark_inode_dirty(inode);
 	}
-	lock_fat(inode->i_sb);
-	while (nr != -1) {
-		if (!(nr = fat_access(inode->i_sb,nr,0))) {
-			fat_fs_panic(inode->i_sb,"fat_free: deleting beyond EOF");
-			break;
-		}
-		if (MSDOS_SB(inode->i_sb)->free_clusters != -1) {
-			MSDOS_SB(inode->i_sb)->free_clusters++;
-			if (MSDOS_SB(inode->i_sb)->fat_bits == 32) {
-				fat_clusters_flush(inode->i_sb);
-			}
+
+	lock_fat(sb);
+	while (nr != FAT_ENT_EOF) {
+		nr = fat_access(sb, nr, FAT_ENT_FREE);
+		if (nr < 0)
+			goto error;
+		else if (nr == FAT_ENT_FREE) {
+			fat_fs_panic(sb, "%s: deleting beyond EOF (ino %lu)",
+				     __FUNCTION__, inode->i_ino);
+			nr = -EIO;
+			goto error;
 		}
-		inode->i_blocks -= (1 << MSDOS_SB(inode->i_sb)->cluster_bits) / 512;
-	}
-	unlock_fat(inode->i_sb);
-	return 0;
+		if (MSDOS_SB(sb)->free_clusters != -1)
+			MSDOS_SB(sb)->free_clusters++;
+		inode->i_blocks -= (1 << MSDOS_SB(sb)->cluster_bits) >> 9;
+	}
+	fat_clusters_flush(sb);
+	nr = 0;
+error:
+	unlock_fat(sb);
+
+	return nr;
 }
diff -Nru a/fs/fat/fatfs_syms.c b/fs/fat/fatfs_syms.c
--- a/fs/fat/fatfs_syms.c	Sun Apr 28 20:10:32 2002
+++ b/fs/fat/fatfs_syms.c	Sun Apr 28 20:10:32 2002
@@ -32,7 +32,6 @@
 EXPORT_SYMBOL(fat_write_inode);
 EXPORT_SYMBOL(register_cvf_format);
 EXPORT_SYMBOL(unregister_cvf_format);
-EXPORT_SYMBOL(fat_get_cluster);
 EXPORT_SYMBOL(fat_dir_ioctl);
 EXPORT_SYMBOL(fat_add_entries);
 EXPORT_SYMBOL(fat_dir_empty);
diff -Nru a/fs/fat/file.c b/fs/fat/file.c
--- a/fs/fat/file.c	Sun Apr 28 20:10:32 2002
+++ b/fs/fat/file.c	Sun Apr 28 20:10:32 2002
@@ -46,8 +46,10 @@
 	unsigned long phys;
 
 	phys = fat_bmap(inode, iblock);
+	if (phys < 0)
+		return phys;
 	if (phys) {
-		map_bh(bh_result, inode->i_sb, phys);
+		map_bh(bh_result, sb, phys);
 		return 0;
 	}
 	if (!create)
@@ -56,7 +58,7 @@
 		BUG();
 		return -EIO;
 	}
-	if (!(iblock % MSDOS_SB(inode->i_sb)->cluster_size)) {
+	if (!(iblock % MSDOS_SB(sb)->cluster_size)) {
 		int error;
 
 		error = fat_add_cluster(inode);
@@ -65,10 +67,12 @@
 	}
 	MSDOS_I(inode)->mmu_private += sb->s_blocksize;
 	phys = fat_bmap(inode, iblock);
+	if (phys < 0)
+		return phys;
 	if (!phys)
 		BUG();
 	bh_result->b_state |= (1UL << BH_New);
-	map_bh(bh_result, inode->i_sb, phys);
+	map_bh(bh_result, sb, phys);
 	return 0;
 }
 
diff -Nru a/fs/fat/inode.c b/fs/fat/inode.c
--- a/fs/fat/inode.c	Sun Apr 28 20:10:36 2002
+++ b/fs/fat/inode.c	Sun Apr 28 20:10:36 2002
@@ -17,7 +17,6 @@
 #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>
@@ -121,7 +120,7 @@
 	return inode;
 }
 
-static void fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de);
+static int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de);
 
 struct inode *fat_build_inode(struct super_block *sb,
 				struct msdos_dir_entry *de, int ino, int *res)
@@ -135,10 +134,14 @@
 	*res = -ENOMEM;
 	if (!inode)
 		goto out;
-	*res = 0;
 	inode->i_ino = iunique(sb, MSDOS_ROOT_INO);
 	inode->i_version = 0;
-	fat_fill_inode(inode, de);
+	*res = fat_fill_inode(inode, de);
+	if (*res < 0) {
+		iput(inode);
+		inode = NULL;
+		goto out;
+	}
 	fat_attach(inode, ino);
 	insert_inode_hash(inode);
 out:
@@ -174,9 +177,7 @@
 		dec_cvf_format_use_count_by_version(sbi->cvf_format->cvf_version);
 		sbi->cvf_format->unmount_cvf(sb);
 	}
-	if (sbi->fat_bits == 32) {
-		fat_clusters_flush(sb);
-	}
+	fat_clusters_flush(sb);
 	fat_cache_inval_dev(sb);
 	if (sbi->nls_disk) {
 		unload_nls(sbi->nls_disk);
@@ -358,37 +359,43 @@
 	return ret;
 }
 
-static void fat_calc_dir_size(struct inode *inode)
+static int fat_calc_dir_size(struct inode *inode)
 {
 	struct super_block *sb = inode->i_sb;
 	int nr;
 
 	inode->i_size = 0;
 	if (MSDOS_I(inode)->i_start == 0)
-		return;
+		return 0;
 
 	nr = MSDOS_I(inode)->i_start;
 	do {
 		inode->i_size += 1 << MSDOS_SB(sb)->cluster_bits;
-		if (!(nr = fat_access(sb, nr, -1))) {
-			printk("FAT: Directory %ld: bad FAT\n",
-			       inode->i_ino);
-			break;
+		nr = fat_access(sb, nr, -1);
+		if (nr < 0)
+			return nr;
+		else if (nr == FAT_ENT_FREE) {
+			fat_fs_panic(sb, "Directory %lu: invalid cluster chain",
+				     inode->i_ino);
+			return -EIO;
 		}
 		if (inode->i_size > FAT_MAX_DIR_SIZE) {
-			fat_fs_panic(sb, "Directory %ld: "
+			fat_fs_panic(sb, "Directory %lu: "
 				     "exceeded the maximum size of directory",
 				     inode->i_ino);
 			inode->i_size = FAT_MAX_DIR_SIZE;
 			break;
 		}
-	} while (nr != -1);
+	} while (nr != FAT_ENT_EOF);
+
+	return 0;
 }
 
-static void fat_read_root(struct inode *inode)
+static int fat_read_root(struct inode *inode)
 {
 	struct super_block *sb = inode->i_sb;
 	struct msdos_sb_info *sbi = MSDOS_SB(sb);
+	int error;
 
 	MSDOS_I(inode)->i_location = 0;
 	inode->i_uid = sbi->options.fs_uid;
@@ -400,7 +407,9 @@
 	inode->i_fop = &fat_dir_operations;
 	if (sbi->fat_bits == 32) {
 		MSDOS_I(inode)->i_start = sbi->root_cluster;
-		fat_calc_dir_size(inode);
+		error = fat_calc_dir_size(inode);
+		if (error < 0)
+			return error;
 	} else {
 		MSDOS_I(inode)->i_start = 0;
 		inode->i_size = sbi->dir_entries * sizeof(struct msdos_dir_entry);
@@ -415,6 +424,8 @@
 	inode->i_mtime = inode->i_atime = inode->i_ctime = 0;
 	MSDOS_I(inode)->i_ctime_ms = 0;
 	inode->i_nlink = fat_subdirs(inode)+2;
+
+	return 0;
 }
 
 /*
@@ -573,12 +584,13 @@
 int fat_fill_super(struct super_block *sb, void *data, int silent,
 		   struct inode_operations *fs_dir_inode_ops, int isvfat)
 {
-	struct inode *root_inode;
+	struct inode *root_inode = NULL;
 	struct buffer_head *bh;
 	struct fat_boot_sector *b;
 	struct msdos_sb_info *sbi;
-	int logical_sector_size, fat_clusters, debug, cp;
+	int logical_sector_size, fat_clusters, debug, cp, first;
 	unsigned int total_sectors, rootdir_sectors;
+	unsigned char media;
 	long error = -EIO;
 	char buf[50];
 	int i;
@@ -641,6 +653,13 @@
 		brelse(bh);
 		goto out_invalid;
 	}
+	media = b->media;
+	if (!FAT_VALID_MEDIA(media)) {
+		if (!silent)
+			printk("FAT: invalid media value (0x%02x)\n", media);
+		brelse(bh);
+		goto out_invalid;
+	}
 	logical_sector_size =
 		CF_LE_W(get_unaligned((unsigned short *) &b->sector_size));
 	if (!logical_sector_size
@@ -763,6 +782,21 @@
 
 	brelse(bh);
 
+	/* validity check of FAT */
+	first = __fat_access(sb, 0, -1);
+	if (first < 0) {
+		error = first;
+		goto out_fail;
+	}
+	if (FAT_FIRST_ENT(sb, media) != first) {
+		if (!silent) {
+			printk("FAT: invalid first entry of FAT "
+			       "(0x%x != 0x%x)\n",
+			       FAT_FIRST_ENT(sb, media), first);
+		}
+		goto out_invalid;
+	}
+
 	if (!strcmp(cvf_format, "none"))
 		i = -1;
 	else
@@ -802,18 +836,20 @@
 			       sbi->nls_io->charset);
 	}
 
+	error = -ENOMEM;
 	root_inode = new_inode(sb);
 	if (!root_inode)
 		goto out_fail;
-
 	root_inode->i_ino = MSDOS_ROOT_INO;
 	root_inode->i_version = 0;
-	fat_read_root(root_inode);
+	error = fat_read_root(root_inode);
+	if (error < 0)
+		goto out_fail;
+	error = -ENOMEM;
 	insert_inode_hash(root_inode);
 	sb->s_root = d_alloc_root(root_inode);
 	if (!sb->s_root) {
 		printk("FAT: get root inode failed\n");
-		iput(root_inode);
 		goto out_fail;
 	}
 	if(i >= 0) {
@@ -826,6 +862,8 @@
 	error = -EINVAL;
 
 out_fail:
+	if (root_inode)
+		iput(root_inode);
 	if (sbi->nls_io)
 		unload_nls(sbi->nls_io);
 	if (sbi->nls_disk)
@@ -855,17 +893,20 @@
 		free = MSDOS_SB(sb)->free_clusters;
 	else {
 		free = 0;
-		for (nr = 2; nr < MSDOS_SB(sb)->clusters+2; nr++)
-			if (!fat_access(sb,nr,-1)) free++;
+		for (nr = 2; nr < MSDOS_SB(sb)->clusters + 2; nr++)
+			if (fat_access(sb, nr, -1) == FAT_ENT_FREE)
+				free++;
 		MSDOS_SB(sb)->free_clusters = free;
 	}
 	unlock_fat(sb);
+
 	buf->f_type = sb->s_magic;
 	buf->f_bsize = 1 << MSDOS_SB(sb)->cluster_bits;
 	buf->f_blocks = MSDOS_SB(sb)->clusters;
 	buf->f_bfree = free;
 	buf->f_bavail = free;
 	buf->f_namelen = MSDOS_SB(sb)->options.isvfat ? 260 : 12;
+
 	return 0;
 }
 
@@ -906,10 +947,11 @@
 };
 
 /* doesn't deal with root inode */
-static void fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de)
+static int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de)
 {
 	struct super_block *sb = inode->i_sb;
 	struct msdos_sb_info *sbi = MSDOS_SB(sb);
+	int error;
 
 	MSDOS_I(inode)->i_location = 0;
 	inode->i_uid = sbi->options.fs_uid;
@@ -930,16 +972,19 @@
 				(CF_LE_W(de->starthi) << 16);
 		}
 		MSDOS_I(inode)->i_logstart = MSDOS_I(inode)->i_start;
+		error = fat_calc_dir_size(inode);
+		if (error < 0)
+			return error;
+		MSDOS_I(inode)->mmu_private = inode->i_size;
+
 		inode->i_nlink = fat_subdirs(inode);
 		    /* includes .., compensating for "self" */
 #ifdef DEBUG
 		if (!inode->i_nlink) {
-			printk("directory %d: i_nlink == 0\n",inode->i_ino);
+			printk("directory %lu: i_nlink == 0\n",inode->i_ino);
 			inode->i_nlink = 1;
 		}
 #endif
-		fat_calc_dir_size(inode);
-		MSDOS_I(inode)->mmu_private = inode->i_size;
 	} else { /* not a directory */
 		inode->i_generation |= 1;
 		inode->i_mode = MSDOS_MKMODE(de->attr,
@@ -974,6 +1019,8 @@
 		? date_dos2unix(CF_LE_W(de->ctime),CF_LE_W(de->cdate))
 		: inode->i_mtime;
 	MSDOS_I(inode)->i_ctime_ms = de->ctime_ms;
+
+	return 0;
 }
 
 void fat_write_inode(struct inode *inode, int wait)
diff -Nru a/fs/fat/misc.c b/fs/fat/misc.c
--- a/fs/fat/misc.c	Sun Apr 28 20:10:34 2002
+++ b/fs/fat/misc.c	Sun Apr 28 20:10:34 2002
@@ -97,6 +97,11 @@
 	struct buffer_head *bh;
 	struct fat_boot_fsinfo *fsinfo;
 
+	if (MSDOS_SB(sb)->fat_bits != 32)
+		return;
+	if (MSDOS_SB(sb)->free_clusters == -1)
+		return;
+
 	bh = fat_bread(sb, MSDOS_SB(sb)->fsinfo_sector);
 	if (bh == NULL) {
 		printk("FAT bread failed in fat_clusters_flush\n");
@@ -128,6 +133,41 @@
 	int count, nr, limit, last, curr, file_cluster;
 	int cluster_bits = MSDOS_SB(sb)->cluster_bits;
 	
+	/* 
+	 * We must locate the last cluster of the file to add this new
+	 * one (nr) to the end of the link list (the FAT).
+	 *
+	 * In order to confirm that the cluster chain is valid, we
+	 * find out EOF first.
+	 */
+	nr = -EIO;
+	last = file_cluster = 0;
+	if ((curr = MSDOS_I(inode)->i_start) != 0) {
+		int max_cluster = MSDOS_I(inode)->mmu_private >> cluster_bits;
+
+		fat_cache_lookup(inode, INT_MAX, &last, &curr);
+		file_cluster = last;
+		while (curr && curr != FAT_ENT_EOF) {
+			file_cluster++;
+			curr = fat_access(sb, last = curr, -1);
+			if (curr < 0)
+				return curr;
+			else if (curr == FAT_ENT_FREE) {
+				fat_fs_panic(sb, "%s: invalid cluster chain"
+					     " (ino %lu)",
+					     __FUNCTION__, inode->i_ino);
+				goto out;
+			}
+			if (file_cluster > max_cluster) {
+				fat_fs_panic(sb, "%s: bad cluster counts"
+					     " (ino %lu)",
+					     __FUNCTION__, inode->i_ino);
+				goto out;
+			}
+		}
+	}
+
+	/* find free FAT entry */
 	lock_fat(sb);
 	
 	if (MSDOS_SB(sb)->free_clusters == 0) {
@@ -135,10 +175,9 @@
 		return -ENOSPC;
 	}
 	limit = MSDOS_SB(sb)->clusters;
-	nr = limit; /* to keep GCC happy */
 	for (count = 0; count < limit; count++) {
 		nr = ((count + MSDOS_SB(sb)->prev_free) % limit) + 2;
-		if (fat_access(sb, nr, -1) == 0)
+		if (fat_access(sb, nr, -1) == FAT_ENT_FREE)
 			break;
 	}
 	if (count >= limit) {
@@ -148,43 +187,14 @@
 	}
 	
 	MSDOS_SB(sb)->prev_free = (count + MSDOS_SB(sb)->prev_free + 1) % limit;
-	fat_access(sb, nr, EOF_FAT(sb));
+	fat_access(sb, nr, FAT_ENT_EOF);
 	if (MSDOS_SB(sb)->free_clusters != -1)
 		MSDOS_SB(sb)->free_clusters--;
-	if (MSDOS_SB(sb)->fat_bits == 32)
-		fat_clusters_flush(sb);
+	fat_clusters_flush(sb);
 	
 	unlock_fat(sb);
-	
-	/* We must locate the last cluster of the file to add this
-	   new one (nr) to the end of the link list (the FAT).
-	   
-	   Here file_cluster will be the number of the last cluster of the
-	   file (before we add nr).
-	   
-	   last is the corresponding cluster number on the disk. We will
-	   use last to plug the nr cluster. We will use file_cluster to
-	   update the cache.
-	*/
-	last = file_cluster = 0;
-	if ((curr = MSDOS_I(inode)->i_start) != 0) {
-		int max_cluster = MSDOS_I(inode)->mmu_private >> cluster_bits;
 
-		fat_cache_lookup(inode, INT_MAX, &last, &curr);
-		file_cluster = last;
-		while (curr && curr != -1) {
-			file_cluster++;
-			if (!(curr = fat_access(sb, last = curr, -1))) {
-				fat_fs_panic(sb, "File without EOF");
-				return -EIO;
-			}
-			if (file_cluster > max_cluster) {
-				fat_fs_panic(sb,"inode %lu: bad cluster counts",
-					     inode->i_ino);
-				return -EIO;
-			}
-		}
-	}
+	/* add new one to the last of the cluster chain */
 	if (last) {
 		fat_access(sb, last, nr);
 		fat_cache_add(inode, file_cluster, nr);
@@ -200,6 +210,7 @@
 	}
 	inode->i_blocks += (1 << cluster_bits) >> 9;
 
+out:
 	return nr;
 }
 
@@ -330,33 +341,33 @@
 {
 	struct super_block *sb = dir->i_sb;
 	struct msdos_sb_info *sbi = MSDOS_SB(sb);
-	int sector, offset;
+	int sector, offset, iblock;
 
-	while (1) {
-		offset = *pos;
-		PRINTK (("get_entry offset %d\n",offset));
-		if (*bh)
-			fat_brelse(sb, *bh);
-		*bh = NULL;
-		if ((sector = fat_bmap(dir,offset >> sb->s_blocksize_bits)) == -1)
-			return -1;
-		PRINTK (("get_entry sector %d %p\n",sector,*bh));
-		PRINTK (("get_entry sector apres brelse\n"));
-		if (!sector)
-			return -1; /* beyond EOF */
-		*pos += sizeof(struct msdos_dir_entry);
-		if (!(*bh = fat_bread(sb, sector))) {
-			printk("Directory sread (sector 0x%x) failed\n",sector);
-			continue;
-		}
-		PRINTK (("get_entry apres sread\n"));
-
-		offset &= sb->s_blocksize - 1;
-		*de = (struct msdos_dir_entry *) ((*bh)->b_data + offset);
-		*ino = (sector << sbi->dir_per_block_bits) + (offset >> MSDOS_DIR_BITS);
+next:
+	offset = *pos;
+	if (*bh)
+		fat_brelse(sb, *bh);
+
+	*bh = NULL;
+	iblock = *pos >> sb->s_blocksize_bits;
+	sector = fat_bmap(dir, iblock);
+	if (sector <= 0)
+		return -1;	/* beyond EOF or error */
+
+	*bh = fat_bread(sb, sector);
+	if (*bh == NULL) {
+		printk("FAT: Directory bread(block %d) failed\n", sector);
+		/* skip this block */
+		*pos = (iblock + 1) << sb->s_blocksize_bits;
+		goto next;
+	}
+
+	offset &= sb->s_blocksize - 1;
+	*pos += sizeof(struct msdos_dir_entry);
+	*de = (struct msdos_dir_entry *)((*bh)->b_data + offset);
+	*ino = (sector << sbi->dir_per_block_bits) + (offset >> MSDOS_DIR_BITS);
 
-		return 0;
-	}
+	return 0;
 }
 
 
@@ -503,14 +514,18 @@
 				     start);
 			break;
 		}
-		if (!(start = fat_access(sb,start,-1))) {
-			fat_fs_panic(sb,"FAT error");
+		start = fat_access(sb, start, -1);
+		if (start < 0)
+			return start;
+		else if (start == FAT_ENT_FREE) {
+			fat_fs_panic(sb, "%s: invalid cluster chain",
+				     __FUNCTION__);
 			break;
 		}
 #ifdef DEBUG
 	printk("next start: %d\n",start);
 #endif
-	} while (start != -1);
+	} while (start != FAT_ENT_EOF);
 
 	return -ENOENT;
 }
diff -Nru a/fs/jbd/journal.c b/fs/jbd/journal.c
--- a/fs/jbd/journal.c	Sun Apr 28 20:10:32 2002
+++ b/fs/jbd/journal.c	Sun Apr 28 20:10:32 2002
@@ -476,7 +476,6 @@
 	new_jh->b_transaction = NULL;
 	new_bh->b_size = jh2bh(jh_in)->b_size;
 	new_bh->b_bdev = transaction->t_journal->j_dev;
-	new_bh->b_dev = to_kdev_t(transaction->t_journal->j_dev->bd_dev);
 	new_bh->b_blocknr = blocknr;
 	new_bh->b_state |= (1 << BH_Mapped) | (1 << BH_Dirty);
 
@@ -641,7 +640,7 @@
 				": journal block not found "
 				"at offset %lu on %s\n",
 				blocknr,
-				bdevname(to_kdev_t(journal->j_dev->bd_dev)));
+				bdevname(journal->j_dev));
 			err = -EIO;
 			__journal_abort_soft(journal, err);
 		}
@@ -1366,7 +1365,7 @@
 	else
 		bdev = journal->j_dev;
 
-	return bdevname(to_kdev_t(bdev->bd_dev));
+	return bdevname(bdev);
 }
 
 /*
diff -Nru a/fs/jfs/jfs_logmgr.c b/fs/jfs/jfs_logmgr.c
--- a/fs/jfs/jfs_logmgr.c	Sun Apr 28 20:10:37 2002
+++ b/fs/jfs/jfs_logmgr.c	Sun Apr 28 20:10:37 2002
@@ -1073,7 +1073,6 @@
 
 	log->sb = sb;		/* This should be a list */
 	log->flag = JFS_INLINELOG;
-	log->dev = sb->s_dev;
 	log->base = addressPXD(&JFS_SBI(sb)->logpxd);
 	log->size = lengthPXD(&JFS_SBI(sb)->logpxd) >>
 	    (L2LOGPSIZE - sb->s_blocksize_bits);
@@ -1104,7 +1103,6 @@
 	}
 
 	log->sb = sb;		/* This should be a list */
-	log->dev = JFS_SBI(sb)->logdev;
 	log->bdev = bdev;
 	
 	/*
@@ -1313,7 +1311,7 @@
 	logsuper->state = cpu_to_le32(LOGMOUNT);
 	log->serial = le32_to_cpu(logsuper->serial) + 1;
 	logsuper->serial = cpu_to_le32(log->serial);
-	logsuper->device = cpu_to_le32(kdev_t_to_nr(log->dev));
+	logsuper->device = cpu_to_le32(log->bdev->bd_dev);
 	lbmDirectWrite(log, bpsuper, lbmWRITE | lbmRELEASE | lbmSYNC);
 	if ((rc = lbmIOWait(bpsuper, lbmFREE)))
 		goto errout30;
@@ -1775,7 +1773,7 @@
 	bio = bio_alloc(GFP_NOFS, 1);
 
 	bio->bi_sector = bp->l_blkno << (log->l2bsize - 9);
-	bio->bi_dev = log->dev;
+	bio->bi_bdev = log->bdev;
 	bio->bi_io_vec[0].bv_page = virt_to_page(bp->l_ldata);
 	bio->bi_io_vec[0].bv_len = LOGPSIZE;
 	bio->bi_io_vec[0].bv_offset = 0;
@@ -1917,7 +1915,7 @@
 
 	bio = bio_alloc(GFP_NOFS, 1);
 	bio->bi_sector = bp->l_blkno << (log->l2bsize - 9);
-	bio->bi_dev = log->dev;
+	bio->bi_bdev = log->bdev;
 	bio->bi_io_vec[0].bv_page = virt_to_page(bp->l_ldata);
 	bio->bi_io_vec[0].bv_len = LOGPSIZE;
 	bio->bi_io_vec[0].bv_offset = 0;
diff -Nru a/fs/jfs/jfs_logmgr.h b/fs/jfs/jfs_logmgr.h
--- a/fs/jfs/jfs_logmgr.h	Sun Apr 28 20:10:34 2002
+++ b/fs/jfs/jfs_logmgr.h	Sun Apr 28 20:10:34 2002
@@ -366,7 +366,6 @@
 				 *    need to be a list if we share
 				 *    the log between fs's
 				 */
-	kdev_t dev;		/* 4: log lv number */
 	struct block_device *bdev; /* 4: log lv pointer */
 	s32 serial;		/* 4: log mount serial number */
 
diff -Nru a/fs/jfs/jfs_mount.c b/fs/jfs/jfs_mount.c
--- a/fs/jfs/jfs_mount.c	Sun Apr 28 20:10:32 2002
+++ b/fs/jfs/jfs_mount.c	Sun Apr 28 20:10:32 2002
@@ -444,7 +444,7 @@
 	if (state == FM_MOUNT) {
 		/* record log's dev_t and mount serial number */
 		j_sb->s_logdev =
-			cpu_to_le32(kdev_t_to_nr(JFS_SBI(sb)->log->dev));
+			cpu_to_le32(JFS_SBI(sb)->log->bdev->bd_dev);
 		j_sb->s_logserial = cpu_to_le32(JFS_SBI(sb)->log->serial);
 		/* record our own device number in case the location
 		 * changes after a reboot
diff -Nru a/fs/namei.c b/fs/namei.c
--- a/fs/namei.c	Sun Apr 28 20:10:35 2002
+++ b/fs/namei.c	Sun Apr 28 20:10:35 2002
@@ -268,8 +268,41 @@
 static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name, int flags)
 {
 	struct dentry * dentry = d_lookup(parent, name);
+	
+	if (dentry && dentry->d_op && dentry->d_op->d_revalidate) {
+		if (!dentry->d_op->d_revalidate(dentry, flags) && !d_invalidate(dentry)) {
+			dput(dentry);
+			dentry = NULL;
+		}
+	}
+	return dentry;
+}
 
+/*for fastwalking*/
+static inline void undo_locked(struct nameidata *nd)
+{
+	if(nd->flags & LOOKUP_LOCKED){
+		dget_locked(nd->dentry);
+		mntget(nd->mnt);
+		spin_unlock(&dcache_lock);
+		nd->flags &= ~LOOKUP_LOCKED;
+	}
+}
+
+/*
+ * For fast path lookup while holding the dcache_lock. 
+ * SMP-safe
+ */
+static struct dentry * cached_lookup_nd(struct nameidata * nd, struct qstr * name, int flags)
+{
+	struct dentry * dentry = NULL;
+	if(!(nd->flags & LOOKUP_LOCKED))
+		return cached_lookup(nd->dentry, name, flags);
+	
+	dentry = __d_lookup(nd->dentry, name);
+	
 	if (dentry && dentry->d_op && dentry->d_op->d_revalidate) {
+		undo_locked(nd);
 		if (!dentry->d_op->d_revalidate(dentry, flags) && !d_invalidate(dentry)) {
 			dput(dentry);
 			dentry = NULL;
@@ -279,6 +312,34 @@
 }
 
 /*
+ * Short-cut version of permission(), for calling by
+ * path_walk(), when dcache lock is held.  Combines parts
+ * of permission() and vfs_permission(), and tests ONLY for
+ * MAY_EXEC permission.
+ *
+ * If appropriate, check DAC only.  If not appropriate, or
+ * short-cut DAC fails, then call permission() to do more
+ * complete permission check.
+ */
+static inline int exec_permission_lite(struct inode *inode)
+{
+	umode_t	mode = inode->i_mode;
+
+	if ((inode->i_op && inode->i_op->permission))
+		return -EACCES;
+
+	if (current->fsuid == inode->i_uid)
+		mode >>= 6;
+	else if (in_group_p(inode->i_gid))
+		mode >>= 3;
+
+	if (mode & MAY_EXEC)
+		return 0;
+
+	return -EACCES;
+}
+
+/*
  * This is called when everything else fails, and we actually have
  * to go to the low-level filesystem to find out what we should do..
  *
@@ -472,7 +533,11 @@
 		struct qstr this;
 		unsigned int c;
 
-		err = permission(inode, MAY_EXEC);
+		err = exec_permission_lite(inode);
+		if(err){
+			undo_locked(nd);
+			err = permission(inode, MAY_EXEC);
+		}
 		dentry = ERR_PTR(err);
  		if (err)
 			break;
@@ -507,6 +572,7 @@
 			case 2:	
 				if (this.name[1] != '.')
 					break;
+				undo_locked(nd);
 				follow_dotdot(nd);
 				inode = nd->dentry->d_inode;
 				/* fallthrough */
@@ -523,16 +589,20 @@
 				break;
 		}
 		/* This does the actual lookups.. */
-		dentry = cached_lookup(nd->dentry, &this, LOOKUP_CONTINUE);
+		dentry = cached_lookup_nd(nd, &this, LOOKUP_CONTINUE);
 		if (!dentry) {
+			undo_locked(nd);
 			dentry = real_lookup(nd->dentry, &this, LOOKUP_CONTINUE);
 			err = PTR_ERR(dentry);
 			if (IS_ERR(dentry))
 				break;
 		}
 		/* Check mountpoints.. */
-		while (d_mountpoint(dentry) && __follow_down(&nd->mnt, &dentry))
-			;
+		if(d_mountpoint(dentry)){
+			undo_locked(nd);
+			while (d_mountpoint(dentry) && __follow_down(&nd->mnt, &dentry))
+				;
+		}
 
 		err = -ENOENT;
 		inode = dentry->d_inode;
@@ -543,6 +613,7 @@
 			goto out_dput;
 
 		if (inode->i_op->follow_link) {
+			undo_locked(nd);
 			err = do_follow_link(dentry, nd);
 			dput(dentry);
 			if (err)
@@ -555,7 +626,8 @@
 			if (!inode->i_op)
 				break;
 		} else {
-			dput(nd->dentry);
+			if (!(nd->flags & LOOKUP_LOCKED))
+				dput(nd->dentry);
 			nd->dentry = dentry;
 		}
 		err = -ENOTDIR; 
@@ -575,6 +647,7 @@
 			case 2:	
 				if (this.name[1] != '.')
 					break;
+				undo_locked(nd);
 				follow_dotdot(nd);
 				inode = nd->dentry->d_inode;
 				/* fallthrough */
@@ -586,7 +659,8 @@
 			if (err < 0)
 				break;
 		}
-		dentry = cached_lookup(nd->dentry, &this, 0);
+		dentry = cached_lookup_nd(nd, &this, 0);
+		undo_locked(nd); 
 		if (!dentry) {
 			dentry = real_lookup(nd->dentry, &this, 0);
 			err = PTR_ERR(dentry);
@@ -626,11 +700,14 @@
 		else if (this.len == 2 && this.name[1] == '.')
 			nd->last_type = LAST_DOTDOT;
 return_base:
+		undo_locked(nd);
 		return 0;
 out_dput:
+		undo_locked(nd);
 		dput(dentry);
 		break;
 	}
+	undo_locked(nd);
 	path_release(nd);
 return_err:
 	return err;
@@ -734,6 +811,36 @@
 	nd->dentry = dget(current->fs->pwd);
 	read_unlock(&current->fs->lock);
 	return 1;
+}
+
+int path_lookup(const char *name, unsigned int flags, struct nameidata *nd)
+{
+	nd->last_type = LAST_ROOT; /* if there are only slashes... */
+	nd->flags = flags;
+	if (*name=='/'){
+		read_lock(&current->fs->lock);
+		if (current->fs->altroot && !(nd->flags & LOOKUP_NOALT)) {
+			nd->mnt = mntget(current->fs->altrootmnt);
+			nd->dentry = dget(current->fs->altroot);
+			read_unlock(&current->fs->lock);
+			if (__emul_lookup_dentry(name,nd))
+				return 0;
+			read_lock(&current->fs->lock);
+		}
+		spin_lock(&dcache_lock); /*to avoid cacheline bouncing with d_count*/
+		nd->mnt = current->fs->rootmnt;
+		nd->dentry = current->fs->root;
+		read_unlock(&current->fs->lock);
+	}
+	else{
+		read_lock(&current->fs->lock);
+		spin_lock(&dcache_lock);
+		nd->mnt = current->fs->pwdmnt;
+		nd->dentry = current->fs->pwd;
+		read_unlock(&current->fs->lock);
+	}
+	nd->flags |= LOOKUP_LOCKED;
+	return (path_walk(name, nd));
 }
 
 /*
diff -Nru a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/fs/ntfs/ChangeLog	Sun Apr 28 20:10:39 2002
@@ -0,0 +1,446 @@
+ToDo:
+	- Find and fix bugs.
+	- W.r.t. s_maxbytes still need to be careful on reading/truncating as
+	  there are dragons lurking in the details, e.g. read_inode() currently
+	  does no checks for file size wrt s_maxbytes. So what happens when a
+	  user open()s a file with i_size > s_maxbytes? Should read_inode()
+	  truncate the visible i_size? Will the user just get -E2BIG (or
+	  whatever) on open()? Or will (s)he be able to open() but lseek() and
+	  read() will fail when s_maxbytes is reached? -> Investigate this!
+	- Implement/allow non-resident index bitmaps in dir.c::ntfs_readdir()
+	  and then also consider initialized_size w.r.t. the bitmaps, etc.
+	- vcn_to_lcn() should somehow return the correct pointer within the
+	  ->run_list so we can get at the lcns for the following vcns, this is
+	  strictly a speed optimization. Obviously need to keep the ->run_list
+	  locked or RACE. load_attribute_list() already performs such an
+	  optimization so use the same optimization where desired.
+	- Optimize all our readpage functions to not do i/o on buffer heads
+	  beyond initialized_size, just zero the buffer heads instead.
+	  Question: How to setup the buffer heads so they point to the on disk
+	  location correctly (after all they are allocated) but are not read
+	  from disk?
+	- Consider if ntfs_file_read_compressed_block() shouldn't be coping
+	  with initialized_size < data_size. I don't think it can happen but
+	  it requires more careful consideration.
+	- CLEANUP: Modularise and reuse code in aops.c. At the moment we have
+	  several copies of almost identicall functions and the functions are
+	  quite big. Modularising them a bit, e.g. a-la get_block(), will make
+	  them cleaner and make code reuse easier.
+
+2.0.4 - Cleanups and updates for kernel 2.5.11.
+
+	- Add documentation on how to use the MD driver to be able to use NTFS
+	  stripe and volume sets in Linux and generally cleanup documentation
+	  a bit.
+	Remove all uses of kdev_t in favour of struct block_device *:
+	- Change compress.c::ntfs_file_read_compressed_block() to use
+	  sb_getblk() instead of getblk().
+	- Change super.c::ntfs_fill_suoer() to use bdev_hardsect_size() instead
+	  of get_hardsect_size().
+	- No need to get old blocksize in super.c::ntfs_fill_super() as
+	  fs/super.c::get_sb_bdev() already does this.
+	- Set bh->b_bdev instead of bh->b_dev throughout aops.c.
+
+2.0.3 - Small bug fixes, cleanups, and performance improvements.
+
+	- Remove some dead code from mft.c.
+	- Optimize readpage and read_block functions throughout aops.c so that
+	  only initialized blocks are read. Non-initialized ones have their
+	  buffer head mapped, zeroed, and set up to date, without scheduling
+	  any i/o. Thanks to Al Viro for advice on how to avoid the device i/o.
+	Thanks go to Andrew Morton for spotting the below:
+	- Fix buglet in allocate_compression_buffers() error code path.
+	- Call flush_dcache_page() after modifying page cache page contents in
+	  ntfs_file_readpage().
+	- Check for existence of page buffers throughout aops.c before calling
+	  create_empty_buffers(). This happens when an I/O error occurs and the
+	  read is retried. (It also happens once writing is implemented so that
+	  needed doing anyway but I had left it for later...)
+	- Don't BUG_ON() uptodate and/or mapped buffers throughout aops.c in
+	  readpage and read_block functions. Reasoning same as above (i.e. I/O
+	  error retries and future write code paths.)
+
+2.0.2 - Minor updates and cleanups.
+
+	- Cleanup: rename mst.c::__post_read_mst_fixup to post_write_mst_fixup
+	  and cleanup the code a bit, removing the unused size parameter.
+	- Change default fmask to 0177 and update documentation.
+	- Change attrib.c::get_attr_search_ctx() to return the search context
+	  directly instead of taking the address of a pointer. A return value
+	  of NULL means the allocation failed. Updated all callers
+	  appropriately.
+	- Update to 2.5.9 kernel (preserving backwards compatibility) by
+	  replacing all occurences of page->buffers with page_buffers(page).
+	- Fix minor bugs in run list merging, also minor cleanup.
+	- Updates to bootsector layout and mft mirror contents descriptions.
+	- Small bug fix in error detection in unistr.c and some cleanups.
+	- Grow name buffer allocations in unistr.c in aligned mutlipled of 64
+	  bytes.
+
+2.0.1 - Minor updates.
+
+	- Make default umask correspond to documentation.
+	- Improve documentation.
+	- Set default mode to include execute bit. The {u,f,d}mask can be used
+	  to take it away if desired. This allows binaries to be executed from
+	  a mounted ntfs partition.
+
+2.0.0 - New version number. Remove TNG from the name. Now in the kernel.
+
+	- Add kill_super, just keeping up with the vfs changes in the kernel.
+	- Repeat some changes from tng-0.0.8 that somehow got lost on the way
+	  from the CVS import into BitKeeper.
+	- Begin to implement proper handling of allocated_size vs
+	  initialized_size vs data_size (i.e. i_size). Done are
+	  mft.c::ntfs_mft_readpage(), aops.c::end_buffer_read_index_async(),
+	  and attrib.c::load_attribute_list().
+	- Lock the run list in attrib.c::load_attribute_list() while using it.
+	- Fix memory leak in ntfs_file_read_compressed_block() and generally
+	  clean up compress.c a little, removing some uncommented/unused debug
+	  code.
+	- Tidy up dir.c a little bit.
+	- Don't bother getting the run list in inode.c::ntfs_read_inode().
+	- Merge mft.c::ntfs_mft_readpage() and aops.c::ntfs_index_readpage()
+	  creating aops.c::ntfs_mst_readpage(), improving the handling of
+	  holes and overflow in the process and implementing the correct
+	  equivalent of ntfs_file_get_block() in ntfs_mst_readpage() itself.
+	  I am aiming for correctness at the moment. Modularisation can come
+	  later.
+	- Rename aops.c::end_buffer_read_index_async() to
+	  end_buffer_read_mst_async() and optimize the overflow checking and
+	  handling.
+	- Use the host of the mftbmp address space mapping to hold the ntfs
+	  volume. This is needed so the async i/o completion handler can
+	  retrieve a pointer to the volume. Hopefully this will not cause
+	  problems elsewhere in the kernel... Otherwise will need to use a
+	  fake inode.
+	- Complete implementation of proper handling of allocated_size vs
+	  initialized_size vs data_size (i.e. i_size) in whole driver.
+	  Basically aops.c is now completely rewritten.
+	- Change NTFS driver name to just NTFS and set version number to 2.0.0
+	  to make a clear distinction from the old driver which is still on
+	  version 1.1.22.
+
+tng-0.0.8 - 08/03/2002 - Now using BitKeeper, http://linux-ntfs.bkbits.net/
+
+	- Replace bdevname(sb->s_dev) with sb->s_id.
+	- Remove now superfluous new-line characters in all callers of
+	  ntfs_debug().
+	- Apply kludge in ntfs_read_inode(), setting i_nlink to 1 for
+	  directories. Without this the "find" utility gets very upset which is
+	  fair enough as Linux/Unix do not support directory hard links.
+	- Further run list merging work. (Richard Russon)
+	- Backwards compatibility for gcc-2.95. (Richard Russon)
+	- Update to kernel 2.5.5-pre1 and rediff the now tiny patch.
+	- Convert to new file system declaration using ->ntfs_get_sb() and
+	  replacing ntfs_read_super() with ntfs_fill_super().
+	- Set s_maxbytes to MAX_LFS_FILESIZE to avoid page cache page index
+	  overflow on 32-bit architectures.
+	- Cleanup upcase loading code to use ntfs_(un)map_page().
+	- Disable/reenable preemtion in critical sections of compession engine.
+	- Replace device size determination in ntfs_fill_super() with
+	  sb->s_bdev->bd_inode->i_size (in bytes) and remove now superfluous
+	  function super.c::get_nr_blocks().
+	- Implement a mount time option (show_inodes) allowing choice of which
+	  types of inode names readdir() returns and modify ntfs_filldir()
+	  accordingly. There are several parameters to show_inodes:
+		system:	system files
+	  	win32:	long file names (including POSIX file names) [DEFAULT]
+		long:	same as win32
+	  	dos:	short file names only (excluding POSIX file names)
+		short:	same as dos
+		posix:	same as both win32 and dos
+	  	all:	all file names
+	  Note that the options are additive, i.e. specifying:
+		-o show_inodes=system,show_inodes=win32,show_inodes=dos
+	  is the same as specifying:
+		-o show_inodes=all
+	  Note that the "posix" and "all" options will show all directory
+	  names, BUT the link count on each directory inode entry is set to 1,
+	  due to Linux not supporting directory hard links. This may well
+	  confuse some userspace applications, since the directory names will
+	  have the same inode numbers. Thus it is NOT advisable to use the
+	  "posix" or "all" options. We provide them only for completeness sake.
+	- Add copies of allocated_size, initialized_size, and compressed_size to
+	  the ntfs inode structure and set them up in
+	  inode.c::ntfs_read_inode(). These reflect the unnamed data attribute
+	  for files and the index allocation attribute for directories.
+	- Add copies of allocated_size and initialized_size to ntfs inode for
+	  $BITMAP attribute of large directories and set them up in
+	  inode.c::ntfs_read_inode().
+	- Add copies of allocated_size and initialized_size to ntfs volume for
+	  $BITMAP attribute of $MFT and set them up in
+	  super.c::load_system_files().
+	- Parse deprecated ntfs driver options (iocharset, show_sys_files,
+	  posix, and utf8) and tell user what the new options to use are. Note
+	  we still do support them but they will be removed with kernel 2.7.x.
+	- Change all occurences of integer long long printf formatting to hex
+	  as printk() will not support long long integer format if/when the
+	  div64 patch goes into the kernel.
+	- Make slab caches have stable names and change the names to what they
+	  were intended to be. These changes are required/made possible by the
+	  new slab cache name handling which removes the length limitation by
+	  requiring the caller of kmem_cache_create() to supply a stable name
+	  which is then referenced but not copied.
+	- Rename run_list structure to run_list_element and create a new
+	  run_list structure containing a pointer to a run_list_element
+	  structure and a read/write semaphore. Adapt all users of run lists
+	  to new scheme and take and release the lock as needed. This fixes a
+	  nasty race as the run_list changes even when inodes are locked for
+	  reading and even when the inode isn't locked at all, so we really
+	  needed the serialization. We use a semaphore rather than a spinlock
+	  as memory allocations can sleep and doing everything GFP_ATOMIC
+	  would be silly.
+	- Cleanup read_inode() removing all code checking for lowest_vcn != 0.
+	  This can never happen due to the nature of lookup_attr() and how we
+	  support attribute lists. If it did happen it would imply the inode
+	  being corrupt.
+	- Check for lowest_vcn != 0 in ntfs_read_inode() and mark the inode as
+	  bad if found.
+	- Update to 2.5.6-pre2 changes in struct address_space.
+	- Use parent_ino() when accessing d_parent inode number in dir.c.
+	- Import Sourceforge CVS repository into BitKeeper repository:
+		http://linux-ntfs.bkbits.net/ntfs-tng-2.5
+	- Update fs/Makefile, fs/Config.help, fs/Config.in, and
+	  Documentation/filesystems/ntfs.txt for NTFS TNG.
+	- Create kernel configuration option controlling whether debugging
+	  is enabled or not.
+	- Add the required export of end_buffer_io_sync() from the patches
+	  directory to the kernel code.
+	- Update inode.c::ntfs_show_options() with show_inodes mount option.
+	- Update errors mount option.
+
+tng-0.0.7 - 13/02/2002 - The driver is now feature complete for read-only!
+
+	- Cleanup mft.c and it's debug/error output in particular. Fix a minor
+	  bug in mapping of extent inodes. Update all the comments to fit all
+	  the recent code changes.
+	- Modify vcn_to_lcn() to cope with entirely unmapped run lists.
+	- Cleanups in compress.c, mostly comments and folding help.
+	- Implement attrib.c::map_run_list() as a generic helper.
+	- Make compress.c::ntfs_file_read_compressed_block() use map_run_list()
+	  thus making code shorter and enabling attribute list support.
+	- Cleanup incorrect use of [su]64 with %L printf format specifier in
+	  all source files. Type casts to [unsigned] long long added to correct
+	  the mismatches (important for architectures which have long long not
+	  being 64 bits).
+	- Merge async io completion handlers for directory indexes and $MFT
+	  data into one by setting the index_block_size{_bits} of the ntfs
+	  inode for $MFT to the mft_record_size{_bits} of the ntfs_volume.
+	- Cleanup aops.c, update comments.
+	- Make ntfs_file_get_block() use map_run_list() so all files now
+	  support attribute lists.
+	- Make ntfs_dir_readpage() almost verbatim copy of
+	  block_read_full_page() by using ntfs_file_get_block() with only real
+	  difference being the use of our own async io completion handler
+	  rather than the default one, thus reducing the amount of code and
+	  automatically enabling attribute list support for directory indices.
+	- Fix bug in load_attribute_list() - forgot to call brelse in error
+	  code path.
+	- Change parameters to find_attr() and lookup_attr(). We no longer
+	  pass in the upcase table and its length. These can be gotten from
+	  ctx->ntfs_ino->vol->upcase{_len}. Update all callers.
+	- Cleanups in attrib.c. 
+	- Implement merging of run lists, attrib.c::merge_run_lists() and its
+	  helpers. (Richard Russon)
+	- Attribute lists part 2, attribute extents and multi part run lists:
+	  enable proper support for LCN_RL_NOT_MAPPED and automatic mapping of
+	  further run list parts via attrib.c::map_run_list().
+	- Tiny endianness bug fix in decompress_mapping_pairs().
+
+tng-0.0.6 - Encrypted directories, bug fixes, cleanups, debugging enhancements.
+
+	- Enable encrypted directories. (Their index root is marked encrypted
+	  to indicate that new files in that directory should be created
+	  encrypted.)
+	- Fix bug in NInoBmpNonResident() macro. (Cut and paste error.)
+	- Enable $Extend system directory. Most (if not all) extended system
+	  files do not have unnamed data attributes so ntfs_read_inode() had to
+	  special case them but that is ok, as the special casing recovery
+	  happens inside an error code path so there is zero slow down in the
+	  normal fast path. The special casing is done by introducing a new
+	  function inode.c::ntfs_is_extended_system_file() which checks if any
+	  of the hard links in the inode point to $Extend as being their parent
+	  directory and if they do we assume this is an extended system file.
+	- Create a sysctl/proc interface to allow {dis,en}abling of debug output
+	  when compiled with -DDEBUG. Default is debug messages to be disabled.
+	  To enable them, one writes a non-zero value to /proc/sys/fs/ntfs-debug
+	  (if /proc is enabled) or uses sysctl(2) to effect the same (if sysctl
+	  interface is enabled). Inspired by old ntfs driver.
+	- Add debug_msgs insmod/kernel boot parameter to set whether debug
+	  messages are {dis,en}abled. This is useful to enable debug messages
+	  during ntfs initialization and is the only way to activate debugging
+	  when the sysctl interface is not enabled.
+	- Cleanup debug output in various places.
+	- Remove all dollar signs ($) from the source (except comments) to
+	  enable compilation on architectures whose gcc compiler does not
+	  support dollar signs in the names of variables/constants. Attribute
+	  types now start with AT_ instead of $ and $I30 is now just I30.
+	- Cleanup ntfs_lookup() and add consistency check of sequence numbers.
+	- Load complete run list for $MFT/$BITMAP during mount and cleanup
+	  access functions. This means we now cope with $MFT/$BITMAP being
+	  spread accross several mft records.
+	- Disable modification of mft_zone_multiplier on remount. We can always
+	  reenable this later on if we really want to, but we will need to make
+	  sure we readjust the mft_zone size / layout accordingly.
+
+tng-0.0.5 - Modernize for 2.5.x and further in line-ing with Al Viro's comments.
+
+	- Use sb_set_blocksize() instead of set_blocksize() and verify the
+	  return value.
+	- Use sb_bread() instead of bread() throughout.
+	- Add index_vcn_size{_bits} to ntfs_inode structure to store the size
+	  of a directory index block vcn. Apply resulting simplifications in
+	  dir.c everywhere.
+	- Fix a small bug somewhere (but forgot what it was).
+	- Change ntfs_{debug,error,warning} to enable gcc to do type checking
+	  on the printf-format parameter list and fix bugs reported by gcc
+	  as a result. (Richard Russon)
+	- Move inode allocation strategy to Al's new stuff but maintain the
+	  divorce of ntfs_inode from struct inode. To achieve this we have two
+	  separate slab caches, one for big ntfs inodes containing a struct
+	  inode and pure ntfs inodes and at the same time fix some faulty
+	  error code paths in ntfs_read_inode().
+	- Show mount options in proc (inode.c::ntfs_show_options()).
+
+tng-0.0.4 - Big changes, getting in line with Al Viro's comments.
+
+	- Modified (un)map_mft_record functions to be common for read and write
+	  case. To specify which is which, added extra parameter at front of
+	  parameter list. Pass either READ or WRITE to this, each has the
+	  obvious meaning.
+	- General cleanups to allow for easier folding in vi.
+	- attrib.c::decompress_mapping_pairs() now accepts the old run list
+	  argument, and invokes attrib.c::merge_run_lists() to merge the old
+	  and the new run lists.
+	- Removed attrib.c::find_first_attr().
+	- Implemented loading of attribute list and complete run list for $MFT.
+	  This means we now cope with $MFT being spread across several mft
+	  records.
+	- Adapt to 2.5.2-pre9 and the changed create_empty_buffers() syntax.
+	- Adapt major/minor/kdev_t/[bk]devname stuff to new 2.5.x kernels.
+	- Make ntfs_volume be allocated via kmalloc() instead of using a slab
+	  cache. There are too little ntfs_volume structures at any one time
+	  to justify a private slab cache.
+	- Fix bogus kmap() use in async io completion. Now use kmap_atomic().
+	  Use KM_BIO_IRQ on advice from IRC/kernel...
+	- Use ntfs_map_page() in map_mft_record() and create ->readpage method
+	  for reading $MFT (ntfs_mft_readpage). In the process create dedicated
+	  address space operations (ntfs_mft_aops) for $MFT inode mapping. Also
+	  removed the now superfluous exports from the kernel core patch.
+	- Fix a bug where kfree() was used insted of ntfs_free().
+	- Change map_mft_record() to take ntfs_inode as argument instead of
+	  vfs inode. Dito for unmap_mft_record(). Adapt all callers.
+	- Add pointer to ntfs_volume to ntfs_inode.
+	- Add mft record number and sequence number to ntfs_inode. Stop using
+	  i_ino and i_generation for in-driver purposes.
+	- Implement attrib.c::merge_run_lists(). (Richard Russon)
+	- Remove use of proper inodes by extent inodes. Move i_ino and
+	  i_generation to ntfs_inode to do this. Apply simplifications that
+	  result and remove iget_no_wait(), etc.
+	- Pass ntfs_inode everywhere in the driver (used to be struct inode).
+	- Add reference counting in ntfs_inode for the ntfs inode itself and
+	  for the mapped mft record.
+	- Extend mft record mapping so we can (un)map extent mft records (new
+	  functions (un)map_extent_mft_record), and so mappings are reference
+	  counted and don't have to happen twice if already mapped - just ref
+	  count increases.
+	- Add -o iocharset as alias to -o nls for backwards compatibility.
+	- The latest core patch is now tiny. In fact just a single additional
+	  export is necessary over the base kernel.
+
+tng-0.0.3 - Cleanups, enhancements, bug fixes.
+
+	- Work on attrib.c::decompress_mapping_pairs() to detect base extents
+	  and setup the run list appropriately using knowledge provided by the
+	  sizes in the base attribute record.
+	- Balance the get_/put_attr_search_ctx() calls so we don't leak memory
+	  any more.
+	- Introduce ntfs_malloc_nofs() and ntfs_free() to allocate/free a single
+	  page or use vmalloc depending on the amount of memory requested.
+	- Cleanup error output. The __FUNCTION__ "(): " is now added
+	  automatically. Introduced a new header file debug.h to support this
+	  and also moved ntfs_debug() function into it.
+	- Make reading of compressed files more intelligent and especially get
+	  rid of the vmalloc_nofs() from readpage(). This now uses per CPU
+	  buffers (allocated at first mount with cluster size <= 4kiB and
+	  deallocated on last umount with cluster size <= 4kiB), and
+	  asynchronous io for the compressed data using a list of buffer heads.
+	  Er, we use synchronous io as async io only works on whole pages
+	  covered by buffers and not on individual buffer heads...
+	- Bug fix for reading compressed files with sparse compression blocks.
+
+tng-0.0.2 - Now handles larger/fragmented/compressed volumes/files/dirs.
+
+	- Fixed handling of directories when cluster size exceeds index block
+	  size.
+	- Hide DOS only name space directory entries from readdir() but allow
+	  them in lookup(). This should fix the problem that Linux doesn't
+	  support directory hard links, while still allowing access to entries
+	  via their short file name. This also has the benefit of mimicking
+	  what Windows users are used to, so it is the ideal solution.
+	- Implemented sync_page everywhere so no more hangs in D state when
+	  waiting for a page.
+	- Stop using bforget() in favour of brelse().
+	- Stop locking buffers unnecessarily.
+	- Implemented compressed files (inode->mapping contains uncompressed
+	  data, raw compressed data is currently bread() into a vmalloc()ed
+	  memory buffer).
+	- Enable compressed directories. (Their index root is marked compressed
+	  to indicate that new files in that directory should be created
+	  compressed.)
+	- Use vsnprintf rather than vsprintf in the ntfs_error and ntfs_warning
+	  functions. (Thanks to Will Dyson for pointing this out.)
+	- Moved the ntfs_inode and ntfs_volume (the former ntfs_inode_info and
+	  ntfs_sb_info) out of the common inode and super_block structures and
+	  started using the generic_ip and generic_sbp pointers instead. This
+	  makes ntfs entirely private with respect to the kernel tree.
+	- Detect compiler version and abort with error message if gcc less than
+	  2.96 is used.
+	- Fix bug in name comparison function in unistr.c.
+	- Implement attribute lists part 1, the infrastructure: search contexts
+	  and operations, find_external_attr(), lookup_attr()) and make the
+	  code use the infrastructure.
+	- Fix stupid buffer overflow bug that became apparent on larger run
+	  list containing attributes.
+	- Fix bugs in readdir() that became apparent on larger directories.
+
+	The driver is now really useful and survives the test
+		find . -type f -exec md5sum "{}" \;
+	without any error messages on a over 1GiB sized partition with >16k
+	files on it, including compressed files and directories and many files
+	and directories with attribute lists.
+
+tng-0.0.1 - The first useful version.
+
+	- Added ntfs_lookup().
+	- Added default upcase generation and handling.
+	- Added compile options to be shown on module init.
+	- Many bug fixes that were "hidden" before.
+	- Update to latest kernel.
+	- Added ntfs_readdir().
+	- Added file operations for mmap(), read(), open() and llseek(). We just
+	  use the generic ones. The whole point of going through implementing
+	  readpage() methods and where possible get_block() call backs is that
+	  this allows us to make use of the generic high level methods provided
+	  by the kernel.
+
+	The driver is now actually useful! Yey. (-: It undoubtedly has got bugs
+	though and it doesn't implement accesssing compressed files yet. Also,
+	accessing files with attribute list attributes is not implemented yet
+	either. But for small or simple file systems it should work and allow
+	you to list directories, use stat on directory entries and the file
+	system, open, read, mmap and llseek around in files. A big mile stone
+	has been reached!
+
+tng-0.0.0 - Initial version tag.
+
+	Initial driver implementation. The driver can mount and umount simple
+	NTFS file systems (i.e. ones without attribute lists in the system
+	files). If the mount fails there might be problems in the error handling
+	code paths, so be warned. Otherwise it seems to be loading the system
+	files nicely and the mft record read mapping/unmapping seems to be
+	working nicely, too. Proof of inode metadata in the page cache and non-
+	resident file unnamed stream data in the page cache concepts is thus
+	complete.
+
diff -Nru a/fs/ntfs/Makefile b/fs/ntfs/Makefile
--- a/fs/ntfs/Makefile	Sun Apr 28 20:10:35 2002
+++ b/fs/ntfs/Makefile	Sun Apr 28 20:10:39 2002
@@ -1,11 +1,17 @@
-# Rules for making the NTFS driver
+# Rules for making the NTFS driver.
 
 O_TARGET := ntfs.o
 
-obj-y   := fs.o sysctl.o support.o util.o inode.o dir.o super.o attr.o unistr.o
+obj-y   := aops.o attrib.o compress.o debug.o dir.o file.o inode.o mft.o \
+		mst.o namei.o super.o sysctl.o time.o unistr.o upcase.o
+
 obj-m   := $(O_TARGET)
-# New version format started 3 February 2001.
-EXTRA_CFLAGS = -DNTFS_VERSION=\"1.1.22\" #-DDEBUG
+
+EXTRA_CFLAGS = -DNTFS_VERSION=\"2.0.4\"
+
+ifeq ($(CONFIG_NTFS_DEBUG),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
 
 include $(TOPDIR)/Rules.make
 
diff -Nru a/fs/ntfs/aops.c b/fs/ntfs/aops.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/fs/ntfs/aops.c	Sun Apr 28 20:10:39 2002
@@ -0,0 +1,811 @@
+/**
+ * aops.c - NTFS kernel address space operations and page cache handling.
+ * 	    Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2001,2002 Anton Altaparmakov.
+ * Copyright (C) 2002 Richard Russon.
+ *
+ * This program/include file 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/include file 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 (in the main directory of the Linux-NTFS 
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/swap.h>
+#include <linux/locks.h>
+
+#include "ntfs.h"
+
+#define MAX_BUF_PER_PAGE (PAGE_CACHE_SIZE / 512)
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,8)
+#define page_buffers(page)	(page)->buffers
+#endif
+
+/**
+ * end_buffer_read_file_async -
+ *
+ * Async io completion handler for accessing files. Adapted from
+ * end_buffer_read_mst_async().
+ */
+static void end_buffer_read_file_async(struct buffer_head *bh, int uptodate)
+{
+	static spinlock_t page_uptodate_lock = SPIN_LOCK_UNLOCKED;
+	unsigned long flags;
+	struct buffer_head *tmp;
+	struct page *page;
+
+	mark_buffer_uptodate(bh, uptodate);
+
+	page = bh->b_page;
+
+	if (likely(uptodate)) {
+		s64 file_ofs;
+
+		ntfs_inode *ni = NTFS_I(page->mapping->host);
+
+		file_ofs = (page->index << PAGE_CACHE_SHIFT) + bh_offset(bh);
+		if (file_ofs + bh->b_size > ni->initialized_size) {
+			char *addr;
+			int ofs = 0;
+
+			if (file_ofs < ni->initialized_size)
+				ofs = ni->initialized_size - file_ofs;
+			addr = kmap_atomic(page, KM_BIO_IRQ);
+			memset(addr + bh_offset(bh) + ofs, 0, bh->b_size - ofs);
+			flush_dcache_page(page);
+			kunmap_atomic(addr, KM_BIO_IRQ);
+		}
+	} else
+		SetPageError(page);
+
+	spin_lock_irqsave(&page_uptodate_lock, flags);
+	mark_buffer_async(bh, 0);
+	unlock_buffer(bh);
+
+	tmp = bh->b_this_page;
+	while (tmp != bh) {
+		if (buffer_locked(tmp)) {
+			if (buffer_async(tmp))
+				goto still_busy;
+		} else if (!buffer_uptodate(tmp))
+			SetPageError(page);
+		tmp = tmp->b_this_page;
+	}
+
+	spin_unlock_irqrestore(&page_uptodate_lock, flags);
+	if (!PageError(page))
+		SetPageUptodate(page);
+	UnlockPage(page);
+	return;
+still_busy:
+	spin_unlock_irqrestore(&page_uptodate_lock, flags);
+	return;
+}
+
+/**
+ * ntfs_file_read_block -
+ *
+ * NTFS version of block_read_full_page(). Adapted from ntfs_mst_readpage().
+ */
+static int ntfs_file_read_block(struct page *page)
+{
+	VCN vcn;
+	LCN lcn;
+	ntfs_inode *ni;
+	ntfs_volume *vol;
+	struct buffer_head *bh, *head, *arr[MAX_BUF_PER_PAGE];
+	sector_t iblock, lblock, zblock;
+	unsigned int blocksize, blocks, vcn_ofs;
+	int i, nr;
+	unsigned char blocksize_bits;
+
+	ni = NTFS_I(page->mapping->host);
+	vol = ni->vol;
+
+	blocksize_bits = VFS_I(ni)->i_blkbits;
+	blocksize = 1 << blocksize_bits;
+
+	if (!page_has_buffers(page))
+		create_empty_buffers(page, blocksize);
+	bh = head = page_buffers(page);
+	if (!bh)
+		return -ENOMEM;
+
+	blocks = PAGE_CACHE_SIZE >> blocksize_bits;
+	iblock = page->index << (PAGE_CACHE_SHIFT - blocksize_bits);
+	lblock = (ni->allocated_size + blocksize - 1) >> blocksize_bits;
+	zblock = (ni->initialized_size + blocksize - 1) >> blocksize_bits;
+
+#ifdef DEBUG
+	if (unlikely(!ni->mft_no)) {
+		ntfs_error(vol->sb, "NTFS: Attempt to access $MFT! This is a "
+				"very serious bug! Denying access...");
+		return -EACCES;
+	}
+#endif
+
+	/* Loop through all the buffers in the page. */
+	nr = i = 0;
+	do {
+		if (unlikely(buffer_uptodate(bh)))
+			continue;
+		if (unlikely(buffer_mapped(bh))) {
+			arr[nr++] = bh;
+			continue;
+		}
+		bh->b_bdev = vol->sb->s_bdev;
+		/* Is the block within the allowed limits? */
+		if (iblock < lblock) {
+			BOOL is_retry = FALSE;
+
+			/* Convert iblock into corresponding vcn and offset. */
+			vcn = (VCN)iblock << blocksize_bits >>
+					vol->cluster_size_bits;
+			vcn_ofs = ((VCN)iblock << blocksize_bits) &
+					vol->cluster_size_mask;
+retry_remap:
+			/* Convert the vcn to the corresponding lcn. */
+			down_read(&ni->run_list.lock);
+			lcn = vcn_to_lcn(ni->run_list.rl, vcn);
+			up_read(&ni->run_list.lock);
+			/* Successful remap. */
+			if (lcn >= 0) {
+				/* Setup buffer head to correct block. */
+				bh->b_blocknr = ((lcn << vol->cluster_size_bits)
+						+ vcn_ofs) >> blocksize_bits;
+				bh->b_state |= (1UL << BH_Mapped);
+				/* Only read initialized data blocks. */
+				if (iblock < zblock) {
+					arr[nr++] = bh;
+					continue;
+				}
+				/* Fully non-initialized data block, zero it. */
+				goto handle_zblock;
+			}
+			/* It is a hole, need to zero it. */
+			if (lcn == LCN_HOLE)
+				goto handle_hole;
+			/* If first try and run list unmapped, map and retry. */
+			if (!is_retry && lcn == LCN_RL_NOT_MAPPED) {
+				is_retry = TRUE;
+				if (!map_run_list(ni, vcn))
+					goto retry_remap;
+			}
+			/* Hard error, zero out region. */
+			SetPageError(page);
+			ntfs_error(vol->sb, "vcn_to_lcn(vcn = 0x%Lx) failed "
+					"with error code 0x%Lx%s.",
+					(long long)vcn, (long long)-lcn,
+					is_retry ? " even after retrying" : "");
+			// FIXME: Depending on vol->on_errors, do something.
+		}
+		/*
+		 * Either iblock was outside lblock limits or vcn_to_lcn()
+		 * returned error. Just zero that portion of the page and set
+		 * the buffer uptodate.
+		 */
+handle_hole:
+		bh->b_blocknr = -1UL;
+		bh->b_state &= ~(1UL << BH_Mapped);
+handle_zblock:
+		memset(kmap(page) + i * blocksize, 0, blocksize);
+		flush_dcache_page(page);
+		kunmap(page);
+		set_bit(BH_Uptodate, &bh->b_state);
+	} while (i++, iblock++, (bh = bh->b_this_page) != head);
+
+	/* Check we have at least one buffer ready for i/o. */
+	if (nr) {
+		/* Lock the buffers. */
+		for (i = 0; i < nr; i++) {
+			struct buffer_head *tbh = arr[i];
+			lock_buffer(tbh);
+			tbh->b_end_io = end_buffer_read_file_async;
+			mark_buffer_async(tbh, 1);
+		}
+		/* Finally, start i/o on the buffers. */
+		for (i = 0; i < nr; i++)
+			submit_bh(READ, arr[i]);
+		return 0;
+	}
+	/* No i/o was scheduled on any of the buffers. */
+	if (!PageError(page))
+		SetPageUptodate(page);
+	else /* Signal synchronous i/o error. */
+		nr = -EIO;
+	UnlockPage(page);
+	return nr;
+}
+
+/**
+ * ntfs_file_readpage - fill a @page of a @file with data from the device
+ * @file:	open file to which the page @page belongs or NULL
+ * @page:	page cache page to fill with data
+ *
+ * For non-resident attributes, ntfs_file_readpage() fills the @page of the open
+ * file @file by calling the generic block_read_full_page() function provided by
+ * the kernel which in turn invokes our ntfs_file_get_block() callback in order
+ * to create and read in the buffers associated with the page asynchronously.
+ *
+ * For resident attributes, OTOH, ntfs_file_readpage() fills @page by copying
+ * the data from the mft record (which at this stage is most likely in memory)
+ * and fills the remainder with zeroes. Thus, in this case I/O is synchronous,
+ * as even if the mft record is not cached at this point in time, we need to
+ * wait for it to be read in before we can do the copy.
+ *
+ * Return zero on success or -errno on error.
+ */
+static int ntfs_file_readpage(struct file *file, struct page *page)
+{
+	s64 attr_pos;
+	ntfs_inode *ni;
+	char *addr;
+	attr_search_context *ctx;
+	MFT_RECORD *mrec;
+	u32 attr_len;
+	int err = 0;
+
+	if (!PageLocked(page))
+		PAGE_BUG(page);
+
+	ni = NTFS_I(page->mapping->host);
+
+	/* Is the unnamed $DATA attribute resident? */
+	if (test_bit(NI_NonResident, &ni->state)) {
+		/* Attribute is not resident. */
+
+		/* If the file is encrypted, we deny access, just like NT4. */
+		if (test_bit(NI_Encrypted, &ni->state)) {
+			err = -EACCES;
+			goto unl_err_out;
+		}
+		/* Compressed data stream. Handled in compress.c. */
+		if (test_bit(NI_Compressed, &ni->state))
+			return ntfs_file_read_compressed_block(page);
+		/* Normal data stream. */
+		return ntfs_file_read_block(page);
+	}
+	/* Attribute is resident, implying it is not compressed or encrypted. */
+
+	/* Map, pin and lock the mft record for reading. */
+	mrec = map_mft_record(READ, ni);
+	if (IS_ERR(mrec)) {
+		err = PTR_ERR(mrec);
+		goto unl_err_out;
+	}
+
+	ctx = get_attr_search_ctx(ni, mrec);
+	if (!ctx) {
+		err = -ENOMEM;
+		goto unm_unl_err_out;
+	}
+
+	/* Find the data attribute in the mft record. */
+	if (!lookup_attr(AT_DATA, NULL, 0, 0, 0, NULL, 0, ctx)) {
+		err = -ENOENT;
+		goto put_unm_unl_err_out;
+	}
+
+	/* Starting position of the page within the attribute value. */
+	attr_pos = page->index << PAGE_CACHE_SHIFT;
+
+	/* The total length of the attribute value. */
+	attr_len = le32_to_cpu(ctx->attr->_ARA(value_length));
+
+	addr = kmap(page);
+	/* Copy over in bounds data, zeroing the remainder of the page. */
+	if (attr_pos < attr_len) {
+		u32 bytes = attr_len - attr_pos;
+		if (bytes > PAGE_CACHE_SIZE)
+			bytes = PAGE_CACHE_SIZE;
+		else if (bytes < PAGE_CACHE_SIZE)
+			memset(addr + bytes, 0, PAGE_CACHE_SIZE - bytes);
+		/* Copy the data to the page. */
+		memcpy(addr, attr_pos + (char*)ctx->attr +
+				le16_to_cpu(ctx->attr->_ARA(value_offset)),
+				bytes);
+	} else
+		memset(addr, 0, PAGE_CACHE_SIZE);
+	flush_dcache_page(page);
+	kunmap(page);
+
+	SetPageUptodate(page);
+put_unm_unl_err_out:
+	put_attr_search_ctx(ctx);
+unm_unl_err_out:
+	unmap_mft_record(READ, ni);
+unl_err_out:
+	UnlockPage(page);
+	return err;
+}
+
+/**
+ * end_buffer_read_mftbmp_async -
+ *
+ * Async io completion handler for accessing mft bitmap. Adapted from
+ * end_buffer_read_mst_async().
+ */
+static void end_buffer_read_mftbmp_async(struct buffer_head *bh, int uptodate)
+{
+	static spinlock_t page_uptodate_lock = SPIN_LOCK_UNLOCKED;
+	unsigned long flags;
+	struct buffer_head *tmp;
+	struct page *page;
+
+	mark_buffer_uptodate(bh, uptodate);
+
+	page = bh->b_page;
+
+	if (likely(uptodate)) {
+		s64 file_ofs;
+
+		/* Host is the ntfs volume. Our mft bitmap access kludge... */
+		ntfs_volume *vol = (ntfs_volume*)page->mapping->host;
+
+		file_ofs = (page->index << PAGE_CACHE_SHIFT) + bh_offset(bh);
+		if (file_ofs + bh->b_size > vol->mftbmp_initialized_size) {
+			char *addr;
+			int ofs = 0;
+
+			if (file_ofs < vol->mftbmp_initialized_size)
+				ofs = vol->mftbmp_initialized_size - file_ofs;
+			addr = kmap_atomic(page, KM_BIO_IRQ);
+			memset(addr + bh_offset(bh) + ofs, 0, bh->b_size - ofs);
+			flush_dcache_page(page);
+			kunmap_atomic(addr, KM_BIO_IRQ);
+		}
+	} else
+		SetPageError(page);
+
+	spin_lock_irqsave(&page_uptodate_lock, flags);
+	mark_buffer_async(bh, 0);
+	unlock_buffer(bh);
+
+	tmp = bh->b_this_page;
+	while (tmp != bh) {
+		if (buffer_locked(tmp)) {
+			if (buffer_async(tmp))
+				goto still_busy;
+		} else if (!buffer_uptodate(tmp))
+			SetPageError(page);
+		tmp = tmp->b_this_page;
+	}
+
+	spin_unlock_irqrestore(&page_uptodate_lock, flags);
+	if (!PageError(page))
+		SetPageUptodate(page);
+	UnlockPage(page);
+	return;
+still_busy:
+	spin_unlock_irqrestore(&page_uptodate_lock, flags);
+	return;
+}
+
+/**
+ * ntfs_mftbmp_readpage -
+ *
+ * Readpage for accessing mft bitmap. Adapted from ntfs_mst_readpage().
+ */
+static int ntfs_mftbmp_readpage(ntfs_volume *vol, struct page *page)
+{
+	VCN vcn;
+	LCN lcn;
+	struct buffer_head *bh, *head, *arr[MAX_BUF_PER_PAGE];
+	sector_t iblock, lblock, zblock;
+	unsigned int blocksize, blocks, vcn_ofs;
+	int nr, i;
+	unsigned char blocksize_bits;
+
+	if (!PageLocked(page))
+		PAGE_BUG(page);
+
+	blocksize = vol->sb->s_blocksize;
+	blocksize_bits = vol->sb->s_blocksize_bits;
+
+	if (!page_has_buffers(page))
+		create_empty_buffers(page, blocksize);
+	bh = head = page_buffers(page);
+	if (!bh)
+		return -ENOMEM;
+
+	blocks = PAGE_CACHE_SIZE >> blocksize_bits;
+	iblock = page->index << (PAGE_CACHE_SHIFT - blocksize_bits);
+	lblock = (vol->mftbmp_allocated_size + blocksize - 1) >> blocksize_bits;
+	zblock = (vol->mftbmp_initialized_size + blocksize - 1) >>
+			blocksize_bits;
+
+	/* Loop through all the buffers in the page. */
+	nr = i = 0;
+	do {
+		if (unlikely(buffer_uptodate(bh)))
+			continue;
+		if (unlikely(buffer_mapped(bh))) {
+			arr[nr++] = bh;
+			continue;
+		}
+		bh->b_bdev = vol->sb->s_bdev;
+		/* Is the block within the allowed limits? */
+		if (iblock < lblock) {
+			/* Convert iblock into corresponding vcn and offset. */
+			vcn = (VCN)iblock << blocksize_bits >>
+					vol->cluster_size_bits;
+			vcn_ofs = ((VCN)iblock << blocksize_bits) &
+					vol->cluster_size_mask;
+			/* Convert the vcn to the corresponding lcn. */
+			down_read(&vol->mftbmp_rl.lock);
+			lcn = vcn_to_lcn(vol->mftbmp_rl.rl, vcn);
+			up_read(&vol->mftbmp_rl.lock);
+			/* Successful remap. */
+			if (lcn >= 0) {
+				/* Setup buffer head to correct block. */
+				bh->b_blocknr = ((lcn << vol->cluster_size_bits)
+						+ vcn_ofs) >> blocksize_bits;
+				bh->b_state |= (1UL << BH_Mapped);
+				/* Only read initialized data blocks. */
+				if (iblock < zblock) {
+					arr[nr++] = bh;
+					continue;
+				}
+				/* Fully non-initialized data block, zero it. */
+				goto handle_zblock;
+			}
+			if (lcn != LCN_HOLE) {
+				/* Hard error, zero out region. */
+				SetPageError(page);
+				ntfs_error(vol->sb, "vcn_to_lcn(vcn = 0x%Lx) "
+						"failed with error code "
+						"0x%Lx.", (long long)vcn,
+						(long long)-lcn);
+				// FIXME: Depending on vol->on_errors, do
+				// something.
+			}
+		}
+		/*
+		 * Either iblock was outside lblock limits or vcn_to_lcn()
+		 * returned error. Just zero that portion of the page and set
+		 * the buffer uptodate.
+		 */
+		bh->b_blocknr = -1UL;
+		bh->b_state &= ~(1UL << BH_Mapped);
+handle_zblock:
+		memset(kmap(page) + i * blocksize, 0, blocksize);
+		flush_dcache_page(page);
+		kunmap(page);
+		set_bit(BH_Uptodate, &bh->b_state);
+	} while (i++, iblock++, (bh = bh->b_this_page) != head);
+
+	/* Check we have at least one buffer ready for i/o. */
+	if (nr) {
+		/* Lock the buffers. */
+		for (i = 0; i < nr; i++) {
+			struct buffer_head *tbh = arr[i];
+			lock_buffer(tbh);
+			tbh->b_end_io = end_buffer_read_mftbmp_async;
+			mark_buffer_async(tbh, 1);
+		}
+		/* Finally, start i/o on the buffers. */
+		for (i = 0; i < nr; i++)
+			submit_bh(READ, arr[i]);
+		return 0;
+	}
+	/* No i/o was scheduled on any of the buffers. */
+	if (!PageError(page))
+		SetPageUptodate(page);
+	else /* Signal synchronous i/o error. */
+		nr = -EIO;
+	UnlockPage(page);
+	return nr;
+}
+
+/**
+ * end_buffer_read_mst_async - async io completion for reading index records
+ * @bh:		buffer head on which io is completed
+ * @uptodate:	whether @bh is now uptodate or not
+ *
+ * Asynchronous I/O completion handler for reading pages belonging to the
+ * index allocation attribute address space of directory inodes.
+ *
+ * Perform the post read mst fixups when all IO on the page has been completed
+ * and marks the page uptodate or sets the error bit on the page.
+ *
+ * Adapted from fs/buffer.c.
+ *
+ * NOTE: We use this function as async io completion handler for reading pages
+ * belonging to the mft data attribute address space, too as this saves
+ * duplicating an almost identical function. We do this by cheating a little
+ * bit in setting the index_block_size in the mft ntfs_inode to the mft record
+ * size of the volume (vol->mft_record_size), and index_block_size_bits to
+ * mft_record_size_bits, respectively.
+ */
+static void end_buffer_read_mst_async(struct buffer_head *bh, int uptodate)
+{
+	static spinlock_t page_uptodate_lock = SPIN_LOCK_UNLOCKED;
+	unsigned long flags;
+	struct buffer_head *tmp;
+	struct page *page;
+	ntfs_inode *ni;
+
+	mark_buffer_uptodate(bh, uptodate);
+
+	page = bh->b_page;
+
+	ni = NTFS_I(page->mapping->host);
+
+	if (likely(uptodate)) {
+		s64 file_ofs;
+
+		file_ofs = (page->index << PAGE_CACHE_SHIFT) + bh_offset(bh);
+		/* Check for the current buffer head overflowing. */
+		if (file_ofs + bh->b_size > ni->initialized_size) {
+			char *addr;
+			int ofs = 0;
+
+			if (file_ofs < ni->initialized_size)
+				ofs = ni->initialized_size - file_ofs;
+			addr = kmap_atomic(page, KM_BIO_IRQ);
+			memset(addr + bh_offset(bh) + ofs, 0, bh->b_size - ofs);
+			flush_dcache_page(page);
+			kunmap_atomic(addr, KM_BIO_IRQ);
+		}
+	} else
+		SetPageError(page);
+
+	spin_lock_irqsave(&page_uptodate_lock, flags);
+	mark_buffer_async(bh, 0);
+	unlock_buffer(bh);
+
+	tmp = bh->b_this_page;
+	while (tmp != bh) {
+		if (buffer_locked(tmp)) {
+			if (buffer_async(tmp))
+				goto still_busy;
+		} else if (!buffer_uptodate(tmp))
+			SetPageError(page);
+		tmp = tmp->b_this_page;
+	}
+
+	spin_unlock_irqrestore(&page_uptodate_lock, flags);
+	/*
+	 * If none of the buffers had errors then we can set the page uptodate,
+	 * but we first have to perform the post read mst fixups.
+	 */
+	if (!PageError(page)) {
+		char *addr;
+		unsigned int i, recs, nr_err = 0;
+		u32 rec_size;
+
+		rec_size = ni->_IDM(index_block_size);
+		recs = PAGE_CACHE_SIZE / rec_size;
+		addr = kmap_atomic(page, KM_BIO_IRQ);
+		for (i = 0; i < recs; i++) {
+			if (!post_read_mst_fixup((NTFS_RECORD*)(addr +
+					i * rec_size), rec_size))
+				continue;
+			nr_err++;
+			ntfs_error(ni->vol->sb, "post_read_mst_fixup() failed, "
+					"corrupt %s record 0x%Lx. Run chkdsk.",
+					ni->mft_no ? "index" : "mft",
+					(long long)((page->index <<
+					PAGE_CACHE_SHIFT >>
+					ni->_IDM(index_block_size_bits)) + i));
+		}
+		flush_dcache_page(page);
+		kunmap_atomic(addr, KM_BIO_IRQ);
+		if (likely(!nr_err && recs))
+			SetPageUptodate(page);
+		else {
+			ntfs_error(ni->vol->sb, "Setting page error, index "
+					"0x%lx.", page->index);
+			SetPageError(page);
+		}
+	}
+	UnlockPage(page);
+	return;
+still_busy:
+	spin_unlock_irqrestore(&page_uptodate_lock, flags);
+	return;
+}
+
+/**
+ * ntfs_mst_readpage - fill a @page of the mft or a directory with data
+ * @file:	open file/directory to which the page @page belongs or NULL
+ * @page:	page cache page to fill with data
+ *
+ * Readpage method for the VFS address space operations.
+ *
+ * Fill the page @page of the $MFT or the open directory @dir. We read each
+ * buffer asynchronously and when all buffers are read in our io completion
+ * handler end_buffer_read_mst_async() automatically applies the mst fixups to
+ * the page before finally marking it uptodate and unlocking it.
+ *
+ * Contains an adapted version of fs/buffer.c::block_read_full_page().
+ */
+int ntfs_mst_readpage(struct file *dir, struct page *page)
+{
+	VCN vcn;
+	LCN lcn;
+	ntfs_inode *ni;
+	ntfs_volume *vol;
+	struct buffer_head *bh, *head, *arr[MAX_BUF_PER_PAGE];
+	sector_t iblock, lblock, zblock;
+	unsigned int blocksize, blocks, vcn_ofs;
+	int i, nr;
+	unsigned char blocksize_bits;
+
+	if (!PageLocked(page))
+		PAGE_BUG(page);
+
+	ni = NTFS_I(page->mapping->host);
+	vol = ni->vol;
+
+	blocksize_bits = VFS_I(ni)->i_blkbits;
+	blocksize = 1 << blocksize_bits;
+
+	if (!page_has_buffers(page))
+		create_empty_buffers(page, blocksize);
+	bh = head = page_buffers(page);
+	if (!bh)
+		return -ENOMEM;
+
+	blocks = PAGE_CACHE_SIZE >> blocksize_bits;
+	iblock = page->index << (PAGE_CACHE_SHIFT - blocksize_bits);
+	lblock = (ni->allocated_size + blocksize - 1) >> blocksize_bits;
+	zblock = (ni->initialized_size + blocksize - 1) >> blocksize_bits;
+
+#ifdef DEBUG
+	if (unlikely(!ni->run_list.rl && !ni->mft_no))
+		panic("NTFS: $MFT/$DATA run list has been unmapped! This is a "
+				"very serious bug! Cannot continue...");
+#endif
+
+	/* Loop through all the buffers in the page. */
+	nr = i = 0;
+	do {
+		if (unlikely(buffer_uptodate(bh)))
+			continue;
+		if (unlikely(buffer_mapped(bh))) {
+			arr[nr++] = bh;
+			continue;
+		}
+		bh->b_bdev = vol->sb->s_bdev;
+		/* Is the block within the allowed limits? */
+		if (iblock < lblock) {
+			BOOL is_retry = FALSE;
+
+			/* Convert iblock into corresponding vcn and offset. */
+			vcn = (VCN)iblock << blocksize_bits >>
+					vol->cluster_size_bits;
+			vcn_ofs = ((VCN)iblock << blocksize_bits) &
+					vol->cluster_size_mask;
+retry_remap:
+			/* Convert the vcn to the corresponding lcn. */
+			down_read(&ni->run_list.lock);
+			lcn = vcn_to_lcn(ni->run_list.rl, vcn);
+			up_read(&ni->run_list.lock);
+			/* Successful remap. */
+			if (lcn >= 0) {
+				/* Setup buffer head to correct block. */
+				bh->b_blocknr = ((lcn << vol->cluster_size_bits)
+						+ vcn_ofs) >> blocksize_bits;
+				bh->b_state |= (1UL << BH_Mapped);
+				/* Only read initialized data blocks. */
+				if (iblock < zblock) {
+					arr[nr++] = bh;
+					continue;
+				}
+				/* Fully non-initialized data block, zero it. */
+				goto handle_zblock;
+			}
+			/* It is a hole, need to zero it. */
+			if (lcn == LCN_HOLE)
+				goto handle_hole;
+			/* If first try and run list unmapped, map and retry. */
+			if (!is_retry && lcn == LCN_RL_NOT_MAPPED) {
+				is_retry = TRUE;
+				if (!map_run_list(ni, vcn))
+					goto retry_remap;
+			}
+			/* Hard error, zero out region. */
+			SetPageError(page);
+			ntfs_error(vol->sb, "vcn_to_lcn(vcn = 0x%Lx) failed "
+					"with error code 0x%Lx%s.",
+					(long long)vcn, (long long)-lcn,
+					is_retry ? " even after retrying" : "");
+			// FIXME: Depending on vol->on_errors, do something.
+		}
+		/*
+		 * Either iblock was outside lblock limits or vcn_to_lcn()
+		 * returned error. Just zero that portion of the page and set
+		 * the buffer uptodate.
+		 */
+handle_hole:
+		bh->b_blocknr = -1UL;
+		bh->b_state &= ~(1UL << BH_Mapped);
+handle_zblock:
+		memset(kmap(page) + i * blocksize, 0, blocksize);
+		flush_dcache_page(page);
+		kunmap(page);
+		set_bit(BH_Uptodate, &bh->b_state);
+	} while (i++, iblock++, (bh = bh->b_this_page) != head);
+
+	/* Check we have at least one buffer ready for i/o. */
+	if (nr) {
+		/* Lock the buffers. */
+		for (i = 0; i < nr; i++) {
+			struct buffer_head *tbh = arr[i];
+			lock_buffer(tbh);
+			tbh->b_end_io = end_buffer_read_mst_async;
+			mark_buffer_async(tbh, 1);
+		}
+		/* Finally, start i/o on the buffers. */
+		for (i = 0; i < nr; i++)
+			submit_bh(READ, arr[i]);
+		return 0;
+	}
+	/* No i/o was scheduled on any of the buffers. */
+	if (!PageError(page))
+		SetPageUptodate(page);
+	else /* Signal synchronous i/o error. */
+		nr = -EIO;
+	UnlockPage(page);
+	return nr;
+}
+
+/**
+ * ntfs_file_aops - address space operations for accessing normal file data
+ */
+struct address_space_operations ntfs_file_aops = {
+	writepage:	NULL,			/* Write dirty page to disk. */
+	readpage:	ntfs_file_readpage,	/* Fill page with data. */
+	sync_page:	block_sync_page,	/* Currently, just unplugs the
+						   disk request queue. */
+	prepare_write:	NULL,			/* . */
+	commit_write:	NULL,			/* . */
+};
+
+typedef int readpage_t(struct file *, struct page *);
+
+/**
+ * ntfs_mftbmp_aops - address space operations for accessing mftbmp
+ */
+struct address_space_operations ntfs_mftbmp_aops = {
+	writepage:	NULL,			/* Write dirty page to disk. */
+	readpage:	(readpage_t*)ntfs_mftbmp_readpage, /* Fill page with
+							      data. */
+	sync_page:	block_sync_page,	/* Currently, just unplugs the
+						   disk request queue. */
+	prepare_write:	NULL,			/* . */
+	commit_write:	NULL,			/* . */
+};
+
+/**
+ * ntfs_dir_aops -
+ *
+ * Address space operations for accessing normal directory data (i.e. index
+ * allocation attribute). We can't just use the same operations as for files
+ * because 1) the attribute is different and even more importantly 2) the index
+ * records have to be multi sector transfer deprotected (i.e. fixed-up).
+ */
+struct address_space_operations ntfs_dir_aops = {
+	writepage:	NULL,			/* Write dirty page to disk. */
+	readpage:	ntfs_mst_readpage,	/* Fill page with data. */
+	sync_page:	block_sync_page,	/* Currently, just unplugs the
+						   disk request queue. */
+	prepare_write:	NULL,			/* . */
+	commit_write:	NULL,			/* . */
+};
+
diff -Nru a/fs/ntfs/attr.c b/fs/ntfs/attr.c
--- a/fs/ntfs/attr.c	Sun Apr 28 20:10:37 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,872 +0,0 @@
-/*
- * attr.c
- *
- * Copyright (C) 1996-1999 Martin von Löwis
- * Copyright (C) 1996-1997 Régis Duchesne
- * Copyright (C) 1998 Joseph Malicki
- * Copyright (C) 1999 Steve Dodd
- * Copyright (C) 2001 Anton Altaparmakov (AIA)
- */
-
-#include "ntfstypes.h"
-#include "struct.h"
-#include "attr.h"
-
-#include <linux/errno.h>
-#include <linux/ntfs_fs.h>
-#include "macros.h"
-#include "support.h"
-#include "util.h"
-#include "super.h"
-#include "inode.h"
-#include "unistr.h"
-
-/**
- * ntfs_find_attr_in_mft_rec - find attribute in mft record
- * @vol:	volume on which attr resides
- * @m:		mft record to search
- * @type:	attribute type to find
- * @name:	attribute name to find (optional, i.e. NULL means don't care)
- * @name_len:	attribute name length (only needed if @name present)
- * @ic:		ignore case if 1 or case sensitive if 0 (ignored if @name NULL)
- * @instance:	instance number to find
- *
- * Only search the specified mft record and it ignores the presence of an
- * attribute list attribute (unless it is the one being searched for,
- * obviously, in which case it is returned).
- */
-ntfs_u8* ntfs_find_attr_in_mft_rec(ntfs_volume *vol, ntfs_u8 *m, __u32 type,
-		wchar_t *name, __u32 name_len, int ic, __u16 instance)
-{
-	ntfs_u8 *a;
-	
-	/* Iterate over attributes in mft record @m. */
-	a = m + NTFS_GETU16(m + 20);	/* attrs_offset */
-	for (; a >= m && a <= m + vol->mft_record_size;
-				a += NTFS_GETU32(a + 4 /* length */)) {
-		/* We catch $END with this more general check, too... */
-		if (NTFS_GETU32(a + 0 /* type */) > type)
-			return NULL;
-		if (!NTFS_GETU32(a + 4 /* length */))
-			break;
-		if (NTFS_GETU32(a + 0 /* type */) != type)
-			continue;
-		/* If @name is present, compare the two names. */
-		if (name && !ntfs_are_names_equal(name, name_len, (wchar_t*)
-				(a + NTFS_GETU16(a + 10 /* name_offset */)),
-				a[9] /* name_length */, ic, vol->upcase,
-				vol->upcase_length)) {
-			register int rc;
-			
-			rc = ntfs_collate_names(vol->upcase, vol->upcase_length,
-					name, name_len, (wchar_t*)(a +
-					NTFS_GETU16(a + 10 /* name_offset */)),
-					a[9] /* name_length */, 1, 1);
-			/*
-			 * If @name collates before a->name, there is no
-			 * matching attribute.
-			 */
-			if (rc == -1)
-				return NULL;
-			/* If the strings are not equal, continue search. */
-			if (rc)
-	 			continue;
-			rc = ntfs_collate_names(vol->upcase, vol->upcase_length,
-					name, name_len, (wchar_t*)(a +
-					NTFS_GETU16(a + 10 /* name_offset */)),
-					a[9] /* name_length */, 0, 1);
-			if (rc == -1)
-				return NULL;
-			if (rc)
-				continue;
-		}
-		/*
-		 * The names match or @name not present. Check instance number.
-		 * and if it matches we have found the attribute and are done.
-		 */
-		if (instance != NTFS_GETU16(a + 14 /* instance */))
-			continue;
-		ntfs_debug(DEBUG_FILE3, "ntfs_find_attr_in_mft_record: found: "
-			"attr type 0x%x, instance number = 0x%x.\n",
-			NTFS_GETU32(a + 0), instance);
-		return a;
-	}
-	ntfs_error("ntfs_find_attr_in_mft_record: mft record 0x%x is corrupt"
-			". Run chkdsk.\n", m);
-	return NULL;
-}
-
-/* Look if an attribute already exists in the inode, and if not, create it. */
-int ntfs_new_attr(ntfs_inode *ino, int type, void *name, int namelen,
-		  void *value, int value_len, int *pos, int *found)
-{
-	int do_insert = 0;
-	int i, m;
-	ntfs_attribute *a;
-
-	for (i = 0; i < ino->attr_count; i++)
-	{
-		a = ino->attrs + i;
-		if (a->type < type)
-			continue;
-		if (a->type > type) {
-			do_insert = 1;
-			break;
-		}
-		/* If @name is present, compare the two names. */
-		if (namelen && !ntfs_are_names_equal((wchar_t*)name, namelen,
-				a->name, a->namelen /* name_length */,
-				1 /* ignore case*/, ino->vol->upcase,
-				ino->vol->upcase_length)) {
-			register int rc;
-
-			rc = ntfs_collate_names(ino->vol->upcase,
-					ino->vol->upcase_length, a->name,
-					a->namelen, (wchar_t*)name, namelen,
-					1 /* ignore case */, 1);
-			if (rc == -1)
-				continue;
-			if (rc == 1) {
-	 			do_insert = 1;
-				break;
-			}
-			rc = ntfs_collate_names(ino->vol->upcase,
-					ino->vol->upcase_length, a->name,
-					a->namelen, (wchar_t*)name, namelen,
-					0 /* case sensitive */, 1);
-			if (rc == -1)
-				continue;
-			if (rc == 1) {
-				do_insert = 1;
-				break;
-			}
-		}
-		/* Names are equal or no name was asked for. */
-		/* If a value was specified compare the values. */
-		if (value_len && a->resident) {
-			if (!a->resident) {
-				ntfs_error("ntfs_new_attr: Value specified but "
-					"attribute non-resident. Bug!\n");
-				return -EINVAL;
-			}
-			m = value_len;
-			if (m > a->size)
-				m = a->size;
-			m = memcmp(value, a->d.data, m);
-			if (m > 0)
-				continue;
-			if (m < 0) {
-				do_insert = 1;
-				break;
-			}
-			/* Values match until min of value lengths. */
-			if (value_len > a->size)
-				continue;
-			if (value_len < a->size) {
-				do_insert = 1;
-				break;
-			}
-		}
-		/* Full match! */
-		*found = 1;
-		*pos = i;
-		return 0;
-	}
-	/* Re-allocate space. */
-	if (ino->attr_count % 8 == 0)
-	{
-		ntfs_attribute* new;
-		new = (ntfs_attribute*)ntfs_malloc((ino->attr_count + 8) *
-							sizeof(ntfs_attribute));
-		if (!new)
-			return -ENOMEM;
-		if (ino->attrs) {
-			ntfs_memcpy(new, ino->attrs, ino->attr_count *
-							sizeof(ntfs_attribute));
-			ntfs_free(ino->attrs);
-		}
-		ino->attrs = new;
-	}
-	if (do_insert)
-		ntfs_memmove(ino->attrs + i + 1, ino->attrs + i,
-			     (ino->attr_count - i) * sizeof(ntfs_attribute));
-	ino->attr_count++;
-	ino->attrs[i].type = type;
-	ino->attrs[i].namelen = namelen;
-	ino->attrs[i].name = name;
-	*pos = i;
-	*found = 0;
-	return 0;
-}
-
-int ntfs_make_attr_resident(ntfs_inode *ino, ntfs_attribute *attr)
-{
-	__s64 size = attr->size;
-	if (size > 0) {
-		/* FIXME: read data, free clusters */
-		return -EOPNOTSUPP;
-	}
-	attr->resident = 1;
-	return 0;
-}
-
-/* Store in the inode readable information about a run. */
-int ntfs_insert_run(ntfs_attribute *attr, int cnum, ntfs_cluster_t cluster,
-		     int len)
-{
-	/* (re-)allocate space if necessary. */
-	if ((attr->d.r.len * sizeof(ntfs_runlist)) % PAGE_SIZE == 0) {
-		ntfs_runlist* new;
-		unsigned long new_size;
-
-		ntfs_debug(DEBUG_MALLOC, "ntfs_insert_run: re-allocating "
-				"space: old attr->d.r.len = 0x%x\n",
-				attr->d.r.len);
-		new_size = attr->d.r.len * sizeof(ntfs_runlist) + PAGE_SIZE;
-		if ((new_size >> PAGE_SHIFT) > num_physpages) {
-			ntfs_error("ntfs_insert_run: attempted to allocate "
-					"more pages than num_physpages."
-					"This might be a bug or a corrupt"
-					"file system.\n");
-			return -1;
-		}
-		new = ntfs_vmalloc(new_size);
-		if (!new) {
-			ntfs_error("ntfs_insert_run: ntfs_vmalloc(new_size = "
-					"0x%x) failed\n", new_size);
-			return -1;
-		}
-		if (attr->d.r.runlist) {
-			ntfs_memcpy(new, attr->d.r.runlist, attr->d.r.len
-					* sizeof(ntfs_runlist));
-			ntfs_vfree(attr->d.r.runlist);
-		}
-		attr->d.r.runlist = new;
-	}
-	if (attr->d.r.len > cnum)
-		ntfs_memmove(attr->d.r.runlist + cnum + 1,
-			     attr->d.r.runlist + cnum,
-			     (attr->d.r.len - cnum) * sizeof(ntfs_runlist));
-	attr->d.r.runlist[cnum].lcn = cluster;
-	attr->d.r.runlist[cnum].len = len;
-	attr->d.r.len++;
-	return 0;
-}
-
-/**
- * ntfs_extend_attr - extend allocated size of an attribute
- * @ino:	ntfs inode containing the attribute to extend
- * @attr:	attribute which to extend
- * @len:	desired new length for @attr (_not_ the amount to extend by)
- *
- * Extends an attribute. Allocate clusters on the volume which @ino belongs to.
- * Extends the run list accordingly, preferably by extending the last run of
- * the existing run list, first.
- *
- * Only modifies attr->allocated, i.e. doesn't touch attr->size, nor
- * attr->initialized.
- */
-int ntfs_extend_attr(ntfs_inode *ino, ntfs_attribute *attr, const __s64 len)
-{
-	int rlen, rl2_len, err = 0;
-	ntfs_cluster_t cluster, clen;
-	ntfs_runlist *rl, *rl2;
-
-	if ((attr->flags & (ATTR_IS_COMPRESSED | ATTR_IS_ENCRYPTED)) ||
-			ino->record_count > 1)
-		return -EOPNOTSUPP;
-	/*
-	 * FIXME: Don't make non-resident if the attribute type is not right.
-	 * For example cannot make index attribute non-resident! (AIA)
-	 */
-	if (attr->resident) {
-		err = ntfs_make_attr_nonresident(ino, attr);
-		if (err)
-			return err;
-	}
-	if (len <= attr->allocated)
-		return 0;	/* Truly stupid things do sometimes happen. */
-	rl = attr->d.r.runlist;
-	rlen = attr->d.r.len;
-	if (rlen > 0)
-		cluster = rl[rlen - 1].lcn + rl[rlen - 1].len;
-	else
-		/* No preference for allocation space. */
-		cluster = (ntfs_cluster_t)-1;
-	/*
-	 * Calculate the extra space we need, and round up to multiple of
-	 * cluster size to get number of new clusters needed.
-	 */
-	clen = (len - attr->allocated + ino->vol->cluster_size - 1) >>
-			ino->vol->cluster_size_bits;
-	if (!clen)
-		return 0;
-	err = ntfs_allocate_clusters(ino->vol, &cluster, &clen, &rl2,
-			&rl2_len, DATA_ZONE);
-	if (err)
-		return err;
-	attr->allocated += (__s64)clen << ino->vol->cluster_size_bits;
-	if (rlen > 0) {
-		err = splice_runlists(&rl, &rlen, rl2, rl2_len);
-		ntfs_vfree(rl2);
-		if (err)
-			return err;
-	} else {
-		if (rl)
-			ntfs_vfree(rl);
-		rl = rl2;
-		rlen = rl2_len;
-	}
-	attr->d.r.runlist = rl;
-	attr->d.r.len = rlen;
-	return 0;
-}
-
-int ntfs_make_attr_nonresident(ntfs_inode *ino, ntfs_attribute *attr)
-{
-	int error;
-	ntfs_io io;
-	void *data = attr->d.data;
-	__s64 len = attr->size;
-
-	attr->d.r.len = 0;
-	attr->d.r.runlist = NULL;
-	attr->resident = 0;
-	/*
-	 * ->allocated is updated by ntfs_extend_attr(), while ->initialized
-	 * and ->size are updated by ntfs_readwrite_attr(). (AIA)
-	 */
-	attr->allocated = attr->initialized = 0;
-	error = ntfs_extend_attr(ino, attr, len);
-	if (error)
-		return error; /* FIXME: On error, restore old values. */
-	io.fn_put = ntfs_put;
-	io.fn_get = ntfs_get;
-	io.param = data;
-	io.size = len;
-	io.do_read = 0;
-	return ntfs_readwrite_attr(ino, attr, 0, &io);
-}
-
-int ntfs_attr_allnonresident(ntfs_inode *ino)
-{
-	int i, error = 0;
-        ntfs_volume *vol = ino->vol;
-
-	for (i = 0; !error && i < ino->attr_count; i++)
-	{
-		if (ino->attrs[i].type != vol->at_security_descriptor &&
-		    ino->attrs[i].type != vol->at_data)
-			continue;
-		error = ntfs_make_attr_nonresident(ino, ino->attrs + i);
-	}
-	return error;
-}
-
-/*
- * Resize the attribute to a newsize. attr->allocated and attr->size are
- * updated, but attr->initialized is not changed unless it becomes bigger than
- * attr->size, in which case it is set to attr->size.
- */
-int ntfs_resize_attr(ntfs_inode *ino, ntfs_attribute *attr, __s64 newsize)
-{
-	int error = 0;
-	__s64 oldsize = attr->size;
-	int clustersizebits = ino->vol->cluster_size_bits;
-	int i, count, newcount;
-	ntfs_runlist *rl, *rlt;
-
-	if (newsize == oldsize)
-		return 0;
-	if (attr->flags & (ATTR_IS_COMPRESSED | ATTR_IS_ENCRYPTED))
-		return -EOPNOTSUPP;
-	if (attr->resident) {
-		void *v;
-		if (newsize > ino->vol->mft_record_size) {
-			error = ntfs_make_attr_nonresident(ino, attr);
-			if (error)
-				return error;
-			return ntfs_resize_attr(ino, attr, newsize);
-		}
-		v = attr->d.data;
-		if (newsize) {
-			__s64 minsize = newsize;
-			attr->d.data = ntfs_malloc(newsize);
-			if (!attr->d.data) {
-				ntfs_free(v);
-				return -ENOMEM;
-			}
-			if (newsize > oldsize) {
-				minsize = oldsize;
-				ntfs_bzero((char*)attr->d.data + oldsize,
-					   newsize - oldsize);
-			}
-			ntfs_memcpy((char*)attr->d.data, v, minsize);
-		} else
-			attr->d.data = 0;
-		ntfs_free(v);
-		attr->size = newsize;
-		return 0;
-	}
-	/* Non-resident attribute. */
-	rl = attr->d.r.runlist;
-	if (newsize < oldsize) {
-		int rl_size;
-		/*
-		 * FIXME: We might be going awfully wrong for newsize = 0,
-		 * possibly even leaking memory really badly. But considering
-		 * in that case there is more breakage due to -EOPNOTSUPP stuff
-		 * further down the code path, who cares for the moment... (AIA)
-		 */
-		for (i = 0, count = 0; i < attr->d.r.len; i++) {
-			if ((__s64)(count + rl[i].len) << clustersizebits >
-					newsize) {
-				i++;
-				break;
-			}
-			count += (int)rl[i].len;
-		}
-		newcount = count;
-		/* Free unused clusters in current run, unless sparse. */
-		if (rl[--i].lcn != (ntfs_cluster_t)-1) {
-			ntfs_cluster_t rounded = newsize - ((__s64)count <<
-					clustersizebits);
-			rounded = (rounded + ino->vol->cluster_size - 1) >>
-					clustersizebits;
-			error = ntfs_deallocate_cluster_run(ino->vol, 
-					rl[i].lcn + rounded,
-					rl[i].len - rounded);
-			if (error)
-				return error; /* FIXME: Incomplete operation. */
-			rl[i].len = rounded;
-			newcount = count + rounded;
-		}
-		/* Free all other runs. */
-		i++;
-		error = ntfs_deallocate_clusters(ino->vol, rl + i,
-				attr->d.r.len - i);
-		if (error)
-			return error; /* FIXME: Incomplete operation. */
-		/*
-		 * Free space for extra runs in memory if enough memory left
-		 * to do so. FIXME: Only do it if it would free memory. (AIA)
-		 */
-		rl_size = ((i + 1) * sizeof(ntfs_runlist) + PAGE_SIZE - 1) &
-				PAGE_MASK;
-		if (rl_size < ((attr->d.r.len * sizeof(ntfs_runlist) +
-				PAGE_SIZE - 1) & PAGE_MASK)) {
-			rlt = ntfs_vmalloc(rl_size);
-			if (rlt) {
-				ntfs_memcpy(rlt, rl, i * sizeof(ntfs_runlist));
-				ntfs_vfree(rl);
-				attr->d.r.runlist = rl = rlt;
-			}
-		}
-		rl[i].lcn = (ntfs_cluster_t)-1;
-		rl[i].len = (ntfs_cluster_t)0;
-		attr->d.r.len = i;
-	} else {
-		error = ntfs_extend_attr(ino, attr, newsize);
-		if (error)
-			return error; /* FIXME: Incomplete operation. */
-		newcount = (newsize + ino->vol->cluster_size - 1) >>
-				clustersizebits;
-	}
-	/* Fill in new sizes. */
-	attr->allocated = (__s64)newcount << clustersizebits;
-	attr->size = newsize;
-	if (attr->initialized > newsize)
-		attr->initialized = newsize;
-	if (!newsize)
-		error = ntfs_make_attr_resident(ino, attr);
-	return error;
-}
-
-int ntfs_create_attr(ntfs_inode *ino, int anum, char *aname, void *data,
-		int dsize, ntfs_attribute **rattr)
-{
-	void *name;
-	int namelen;
-	int found, i;
-	int error;
-	ntfs_attribute *attr;
-	
-	if (dsize > ino->vol->mft_record_size)
-		/* FIXME: Non-resident attributes. */
-		return -EOPNOTSUPP;
-	if (aname) {
-		namelen = strlen(aname);
-		name = ntfs_malloc(2 * namelen);
-		if (!name)
-			return -ENOMEM;
-		ntfs_ascii2uni(name, aname, namelen);
-	} else {
-		name = 0;
-		namelen = 0;
-	}
-	error = ntfs_new_attr(ino, anum, name, namelen, data, dsize, &i,
-			&found);
-	if (error || found) {
-		ntfs_free(name);
-		return error ? error : -EEXIST;
-	}
-	*rattr = attr = ino->attrs + i;
-	/* Allocate a new number.
-	 * FIXME: Should this happen on inode writeback?
-	 * FIXME: Extension records not supported. */
-	error = ntfs_allocate_attr_number(ino, &i);
-	if (error)
-		return error;
-	attr->attrno = i;
-	if (attr->attrno + 1 != NTFS_GETU16(ino->attr + 0x28))
-		ntfs_error("UH OH! attr->attrno (%i) != NTFS_GETU16(ino->attr "
-				"+ 0x28) (%i)\n", attr->attrno,
-				NTFS_GETU16(ino->attr + 0x28));
-	attr->resident = 1;
-	attr->flags = 0;
-	attr->cengine = 0;
-	attr->size = attr->allocated = attr->initialized = dsize;
-
-	/* FIXME: INDEXED information should come from $AttrDef
-	 * Currently, only file names are indexed. As of NTFS v3.0 (Win2k),
-	 * this is no longer true. Different attributes can be indexed now. */
-	if (anum == ino->vol->at_file_name)
-		attr->indexed = 1;
-	else
-		attr->indexed = 0;
-	attr->d.data = ntfs_malloc(dsize);
-	if (!attr->d.data)
-		return -ENOMEM;
-	ntfs_memcpy(attr->d.data, data, dsize);
-	return 0;
-}
-
-/*
- * Non-resident attributes are stored in runs (intervals of clusters).
- *
- * This function stores in the inode readable information about a non-resident
- * attribute.
- */
-static int ntfs_process_runs(ntfs_inode *ino, ntfs_attribute* attr,
-		unsigned char *data)
-{
-	int startvcn, endvcn;
-	int vcn, cnum;
-	ntfs_cluster_t cluster;
-	int len, ctype;
-	int er = 0;
-	startvcn = NTFS_GETS64(data + 0x10);
-	endvcn = NTFS_GETS64(data + 0x18);
-
-	/* Check whether this chunk really belongs to the end. Problem with
-	 * this: this functions can get called on the last extent first, before
-	 * it is called on the other extents in sequence. This happens when the
-	 * base mft record contains the last extent instead of the first one
-	 * and the first extent is stored, like any intermediate extents in
-	 * extension mft records. This would be difficult to allow the way the
-	 * runlist is stored in memory. Thus we fix elsewhere by causing the
-	 * attribute list attribute to be processed immediately when found. The
-	 * extents will then be processed starting with the first one. */
-	for (cnum = 0, vcn = 0; cnum < attr->d.r.len; cnum++)
-		vcn += attr->d.r.runlist[cnum].len;
-	if (vcn != startvcn) {
-		ntfs_debug(DEBUG_FILE3, "ntfs_process_runs: ino = 0x%x, "
-			"attr->type = 0x%x, startvcn = 0x%x, endvcn = 0x%x, "
-			"vcn = 0x%x, cnum = 0x%x\n", ino->i_number, attr->type,
-			startvcn, endvcn, vcn, cnum);
-		if (vcn < startvcn) {
-			ntfs_error("Problem with runlist in extended record\n");
-			return -1;
-		}
-		/* Tried to insert an already inserted runlist. */
-		return 0;
-	}
-	if (!endvcn) {
-		if (!startvcn) {
-			/* Allocated length. */
-			endvcn = NTFS_GETS64(data + 0x28) - 1;
-			endvcn >>= ino->vol->cluster_size_bits;
-		} else {
-			/* This is an extent. Allocated length is not defined!
-			 * Extents must have an endvcn though so this is an
-			 * error. */
-			ntfs_error("Corrupt attribute extent. (endvcn is "
-				"missing)\n");
-			return -1;
-		}
-	}
-	data = data + NTFS_GETU16(data + 0x20);
-	cnum = attr->d.r.len;
-	cluster = 0;
-	for (vcn = startvcn; vcn <= endvcn; vcn += len)	{
-		if (ntfs_decompress_run(&data, &len, &cluster, &ctype)) {
-			ntfs_debug(DEBUG_FILE3, "ntfs_process_runs: "
-				"ntfs_decompress_run failed. i_number = 0x%x\n",
-				ino->i_number);
-			return -1;
-		}
-		if (ctype)
-			er = ntfs_insert_run(attr, cnum, -1, len);
-		else
-			er = ntfs_insert_run(attr, cnum, cluster, len);
-		if (er)
-			break;
-		cnum++;
-	}
-	if (er)
-		ntfs_error("ntfs_process_runs: ntfs_insert_run failed\n");
-	ntfs_debug(DEBUG_FILE3, "ntfs_process_runs: startvcn = 0x%x, vcn = 0x%x"
-				", endvcn = 0x%x, cnum = %i\n", startvcn, vcn,
-				endvcn, cnum);
-	return er;
-}
-  
-/* Insert the attribute starting at attr in the inode ino. */
-int ntfs_insert_attribute(ntfs_inode *ino, unsigned char *attrdata)
-{
-	int i, found;
-	int type;
-	short int *name;
-	int namelen;
-	void *data;
-	ntfs_attribute *attr;
-	int error;
-
-	type = NTFS_GETU32(attrdata);
-	namelen = NTFS_GETU8(attrdata + 9);
-	ntfs_debug(DEBUG_FILE3, "ntfs_insert_attribute: ino->i_number 0x%x, "
-			"attr type 0x%x\n", ino->i_number, type);
-	/* Read the attribute's name if it has one. */
-	if (!namelen)
-		name = 0;
-	else {
-		/* 1 Unicode character fits in 2 bytes. */
-		name = ntfs_malloc(2 * namelen);
-		if (!name)
-			return -ENOMEM;
-		ntfs_memcpy(name, attrdata + NTFS_GETU16(attrdata + 10),
-			    2 * namelen);
-	}
-	/* If resident look for value, too. */
-	if (NTFS_GETU8(attrdata + 8) == 0)
-		error = ntfs_new_attr(ino, type, name, namelen,
-				attrdata + NTFS_GETU16(attrdata + 0x14),
-				NTFS_GETU16(attrdata + 0x10), &i, &found);
-	else
-		error = ntfs_new_attr(ino, type, name, namelen, NULL, 0, &i,
-				&found);
-	if (error) {
-		ntfs_debug(DEBUG_FILE3, "ntfs_insert_attribute: ntfs_new_attr "
-				"failed.\n");
-		if (name)
-			ntfs_free(name);
-		return error;
-	}
-	if (found) {
-		/* It's already there, if not resident just process the runs. */
-		if (!ino->attrs[i].resident) {
-			ntfs_debug(DEBUG_FILE3, "ntfs_insert_attribute:"
-						" processing runs 1.\n");
-			/* FIXME: Check error code! (AIA) */
-			ntfs_process_runs(ino, ino->attrs + i, attrdata);
-		}
-		return 0;
-	}
-	attr = ino->attrs + i;
-	attr->resident = NTFS_GETU8(attrdata + 8) == 0;
-	attr->flags = *(__u16*)(attrdata + 0xC);
-	attr->attrno = NTFS_GETU16(attrdata + 0xE);
-  
-	if (attr->resident) {
-		attr->size = NTFS_GETU16(attrdata + 0x10);
-		data = attrdata + NTFS_GETU16(attrdata + 0x14);
-		attr->d.data = (void*)ntfs_malloc(attr->size);
-		if (!attr->d.data)
-			return -ENOMEM;
-		ntfs_memcpy(attr->d.data, data, attr->size);
-		attr->indexed = NTFS_GETU8(attrdata + 0x16);
-	} else {
-		attr->allocated = NTFS_GETS64(attrdata + 0x28);
-		attr->size = NTFS_GETS64(attrdata + 0x30);
-		attr->initialized = NTFS_GETS64(attrdata + 0x38);
-		attr->cengine = NTFS_GETU16(attrdata + 0x22);
-		if (attr->flags & ATTR_IS_COMPRESSED)
-			attr->compsize = NTFS_GETS64(attrdata + 0x40);
-		ntfs_debug(DEBUG_FILE3, "ntfs_insert_attribute: "
-			"attr->allocated = 0x%Lx, attr->size = 0x%Lx, "
-			"attr->initialized = 0x%Lx\n", attr->allocated,
-			attr->size, attr->initialized);
-		ino->attrs[i].d.r.runlist = 0;
-		ino->attrs[i].d.r.len = 0;
-		ntfs_debug(DEBUG_FILE3, "ntfs_insert_attribute: processing "
-				"runs 2.\n");
-		/* FIXME: Check error code! (AIA) */
-		ntfs_process_runs(ino, attr, attrdata);
-	}
-	return 0;
-}
-
-int ntfs_read_zero(ntfs_io *dest, int size)
-{
-	int i;
-	char *sparse = ntfs_calloc(512);
-	if (!sparse)
-		return -ENOMEM;
-	i = 512;
-	while (size) {
-		if (i > size)
-			i = size;
-		dest->fn_put(dest, sparse, i);
-		size -= i;
-	}
-	ntfs_free(sparse);
-	return 0;
-}
-
-/* Process compressed attributes. */
-int ntfs_read_compressed(ntfs_inode *ino, ntfs_attribute *attr, __s64 offset,
-			 ntfs_io *dest)
-{
-	int error = 0;
-	int clustersizebits;
-	int s_vcn, rnum, vcn, got, l1;
-	__s64 copied, len, chunk, offs1, l, chunk2;
-	ntfs_cluster_t cluster, cl1;
-	char *comp = 0, *comp1;
-	char *decomp = 0;
-	ntfs_io io;
-	ntfs_runlist *rl;
-
-	l = dest->size;
-	clustersizebits = ino->vol->cluster_size_bits;
-	/* Starting cluster of potential chunk. There are three situations:
-	   a) In a large uncompressible or sparse chunk, s_vcn is in the middle
-	      of a run.
-	   b) s_vcn is right on a run border.
-	   c) When several runs make a chunk, s_vcn is before the chunks. */
-	s_vcn = offset >> clustersizebits;
-	/* Round down to multiple of 16. */
-	s_vcn &= ~15;
-	rl = attr->d.r.runlist;
-	for (rnum = vcn = 0; rnum < attr->d.r.len && vcn + rl->len <= s_vcn;
-								rnum++, rl++)
-		vcn += rl->len;
-	if (rnum == attr->d.r.len) {
-		/* Beyond end of file. */
-		/* FIXME: Check allocated / initialized. */
-		dest->size = 0;
-		return 0;
-	}
-	io.do_read = 1;
-	io.fn_put = ntfs_put;
-	io.fn_get = 0;
-	cluster = rl->lcn;
-	len = rl->len;
-	copied = 0;
-	while (l) {
-		chunk = 0;
-		if (cluster == (ntfs_cluster_t)-1) {
-			/* Sparse cluster. */
-			__s64 ll;
-
-			if ((len - (s_vcn - vcn)) & 15)
-				ntfs_error("Unexpected sparse chunk size.");
-			ll = ((__s64)(vcn + len) << clustersizebits) - offset;
-			if (ll > l)
-				ll = l;
-			chunk = ll;
-			error = ntfs_read_zero(dest, ll);
-			if (error)
-				goto out;
-		} else if (dest->do_read) {
-			if (!comp) {
-				comp = ntfs_malloc(16 << clustersizebits);
-				if (!comp) {
-					error = -ENOMEM;
-					goto out;
-				}
-			}
-			got = 0;
-			/* We might need to start in the middle of a run. */
-			cl1 = cluster + s_vcn - vcn;
-			comp1 = comp;
-			do {
-				int delta;
-
-				io.param = comp1;
-				delta = s_vcn - vcn;
-				if (delta < 0)
-					delta = 0;
-				l1 = len - delta;
-				if (l1 > 16 - got)
-					l1 = 16 - got;
-				io.size = (__s64)l1 << clustersizebits;
-				error = ntfs_getput_clusters(ino->vol, cl1, 0,
-					       		     &io);
-				if (error)
-					goto out;
-				if (l1 + delta == len) {
-					rnum++;
-					rl++;
-					vcn += len;
-					cluster = cl1 = rl->lcn;
-					len = rl->len;
-				}
-				got += l1;
-				comp1 += (__s64)l1 << clustersizebits;
-			} while (cluster != (ntfs_cluster_t)-1 && got < 16);
-							/* Until empty run. */
-			chunk = 16 << clustersizebits;
-			if (cluster != (ntfs_cluster_t)-1 || got == 16)
-				/* Uncompressible */
-				comp1 = comp;
-			else {
-				if (!decomp) {
-					decomp = ntfs_malloc(16 << 
-							clustersizebits);
-					if (!decomp) {
-						error = -ENOMEM;
-						goto out;
-					}
-				}
-				/* Make sure there are null bytes after the
-				 * last block. */
-				*(ntfs_u32*)comp1 = 0;
-				ntfs_decompress(decomp, comp, chunk);
-				comp1 = decomp;
-			}
-			offs1 = offset - ((__s64)s_vcn << clustersizebits);
-			chunk2 = (16 << clustersizebits) - offs1;
-			if (chunk2 > l)
-				chunk2 = l;
-			if (chunk > chunk2)
-				chunk = chunk2;
-			dest->fn_put(dest, comp1 + offs1, chunk);
-		}
-		l -= chunk;
-		copied += chunk;
-		offset += chunk;
-		s_vcn = (offset >> clustersizebits) & ~15;
-		if (l && offset >= ((__s64)(vcn + len) << clustersizebits)) {
-			rnum++;
-			rl++;
-			vcn += len;
-			cluster = rl->lcn;
-			len = rl->len;
-		}
-	}
-out:
-	if (comp)
-		ntfs_free(comp);
-	if (decomp)
-		ntfs_free(decomp);
-	dest->size = copied;
-	return error;
-}
-
-int ntfs_write_compressed(ntfs_inode *ino, ntfs_attribute *attr, __s64 offset,
-		ntfs_io *dest)
-{
-	return -EOPNOTSUPP;
-}
-
diff -Nru a/fs/ntfs/attr.h b/fs/ntfs/attr.h
--- a/fs/ntfs/attr.h	Sun Apr 28 20:10:30 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,38 +0,0 @@
-/*
- * attr.h -  Header file for attr.c
- *
- * Copyright (C) 1997 Régis Duchesne
- * Copyright (c) 2001 Anton Altaparmakov (AIA)
- */
-#include <linux/nls.h>
-
-ntfs_u8* ntfs_find_attr_in_mft_rec(ntfs_volume *vol, ntfs_u8 *m, __u32 type,
-			wchar_t *name, __u32 name_len, int ic, __u16 instance);
-
-int ntfs_extend_attr(ntfs_inode *ino, ntfs_attribute *attr, const __s64 len);
-
-int ntfs_resize_attr(ntfs_inode *ino, ntfs_attribute *attr, __s64 newsize);
-
-int ntfs_insert_attribute(ntfs_inode *ino, unsigned char* attrdata);
-
-int ntfs_read_compressed(ntfs_inode *ino, ntfs_attribute *attr, __s64 offset,
-			 ntfs_io *dest);
-
-int ntfs_write_compressed(ntfs_inode *ino, ntfs_attribute *attr, __s64 offset,
-			  ntfs_io *dest);
-
-int ntfs_create_attr(ntfs_inode *ino, int anum, char *aname, void *data,
-		     int dsize, ntfs_attribute **rattr);
-
-int ntfs_read_zero(ntfs_io *dest, int size);
-
-int ntfs_make_attr_nonresident(ntfs_inode *ino, ntfs_attribute *attr);
-
-int ntfs_attr_allnonresident(ntfs_inode *ino);
-
-int ntfs_new_attr(ntfs_inode *ino, int type, void *name, int namelen,
-		  void *value, int value_len, int *pos, int *found);
-
-int ntfs_insert_run(ntfs_attribute *attr, int cnum, ntfs_cluster_t cluster,
-		     int len);
-
diff -Nru a/fs/ntfs/attraops.c b/fs/ntfs/attraops.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/fs/ntfs/attraops.c	Sun Apr 28 20:10:39 2002
@@ -0,0 +1,48 @@
+#include "ntfs.h"
+
+/*
+ * We need to define the attribute object structure. FIXME: Move these to
+ * ntfs.h.
+ */
+typedef struct {
+	ntfs_inode *a_ni;
+	ntfs_volume *a_vol;
+	atomic_t a_count;
+	s64 a_size;
+	struct rw_semaphore a_sem;
+	struct address_space a_mapping;
+	unsigned long a_flags;
+} attr_obj;
+
+/**
+ * ntfs_attr_readpage - fill a page @page of an attribute object @aobj with data
+ * @aobj:	attribute object to which the page @page belongs
+ * @page:	page cache page to fill with data
+ *
+ */
+//static int ntfs_attr_readpage(attr_obj *aobj, struct page *page)
+static int ntfs_attr_readpage(struct file *aobj, struct page *page)
+{
+	return -EOPNOTSUPP;
+}
+
+/*
+ * Address space operations for accessing attributes. Note that these functions
+ * do not accept an inode as the first parameter but an attribute object. We
+ * use this to implement a generic interface that is not bound to inodes in
+ * order to support multiple named streams per file, multiple bitmaps per file
+ * and directory, etc. Basically, this gives access to any attribute within an
+ * mft record.
+ *
+ * We make use of a slab cache for attribute object allocations.
+ */
+struct address_space_operations ntfs_attr_aops = {
+	writepage:	NULL,			/* Write dirty page to disk. */
+	readpage:	ntfs_attr_readpage,	/* Fill page with data. */
+	sync_page:	block_sync_page,	/* Currently, just unplugs the
+						   disk request queue. */
+	prepare_write:	NULL,			/* . */
+	commit_write:	NULL,			/* . */
+	//truncatepage:	NULL,			/* . */
+};
+
diff -Nru a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/fs/ntfs/attrib.c	Sun Apr 28 20:10:39 2002
@@ -0,0 +1,1596 @@
+/**
+ * attrib.c - NTFS attribute operations. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2001,2002 Anton Altaparmakov.
+ * Copyright (C) 2002 Richard Russon.
+ *
+ * This program/include file 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/include file 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 (in the main directory of the Linux-NTFS 
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "ntfs.h"
+
+/* Temporary helper functions -- might become macros */
+
+/**
+ * rl_mm - run_list memmove
+ *
+ * It is up to the caller to serialize access to the run list @base.
+ */
+static inline void rl_mm(run_list_element *base, int dst, int src, int size)
+{
+	if ((dst != src) && (size > 0))
+		memmove (base + dst, base + src, size * sizeof (*base));
+}
+
+/**
+ * rl_mc - run_list memory copy
+ *
+ * It is up to the caller to serialize access to the run lists @dstbase and
+ * @srcbase.
+ */
+static inline void rl_mc(run_list_element *dstbase, int dst,
+		run_list_element *srcbase, int src, int size)
+{
+	if (size > 0)
+		memcpy (dstbase+dst, srcbase+src, size * sizeof (*dstbase));
+}
+
+/**
+ * ntfs_rl_realloc - Reallocate memory for run_lists
+ * @orig:  The original memory allocation
+ * @old:   The number of run_lists in the original
+ * @new:   The number of run_lists we need space for
+ *
+ * As the run_lists grow, more memory will be required.  To prevent the
+ * kernel having to allocate and reallocate large numbers of small bits of
+ * memory, this function returns and entire page of memory.
+ *
+ * It is up to the caller to serialize access to the run list @orig.
+ *
+ * N.B.  If the new allocation doesn't require a different number of pages in
+ *       memory, the function will return the original pointer.
+ *
+ * Return: Pointer  The newly allocated, or recycled,  memory.
+ *
+ * Errors: -ENOMEM, Not enough memory to allocate run list array.
+ *         -EINVAL, Invalid parameters were passed in.
+ */
+static inline run_list_element *ntfs_rl_realloc(run_list_element *orig,
+		int old, int new)
+{
+	run_list_element *nrl;
+
+	old = PAGE_ALIGN (old * sizeof (*orig));
+	new = PAGE_ALIGN (new * sizeof (*orig));
+	if (old == new)
+		return orig;
+
+	nrl = ntfs_malloc_nofs (new);
+	if (!nrl)
+		return ERR_PTR (-ENOMEM);
+
+	if (orig) {
+		memcpy (nrl, orig, min (old, new));
+		ntfs_free (orig);
+	}
+	return nrl;
+}
+
+/**
+ * ntfs_rl_merge - Join together two run_lists
+ * @one:  The first run_list and destination
+ * @two:  The second run_list
+ *
+ * If possible merge together two run_lists.  For this, their VCNs and LCNs
+ * must be adjacent.
+ *
+ * It is up to the caller to serialize access to the run lists @one and @two.
+ *
+ * Return: TRUE   Success, the run_lists were merged
+ *         FALSE  Failure, the run_lists were not merged
+ */
+static inline BOOL ntfs_rl_merge(run_list_element *one, run_list_element *two)
+{
+	BUG_ON (!one || !two);
+
+	if ((one->lcn < 0) || (two->lcn < 0))     /* Are we merging holes? */
+		return FALSE;
+	if ((one->lcn + one->length) != two->lcn) /* Are the runs contiguous? */
+		return FALSE;
+	if ((one->vcn + one->length) != two->vcn) /* Are the runs misaligned? */
+		return FALSE;
+
+	one->length += two->length;
+	return TRUE;
+}
+
+/**
+ * ntfs_rl_append - Append a run_list after the given element
+ * @orig:   The original run_list to be worked on.
+ * @osize:  The number of elements in @orig (including end marker).
+ * @new:    The run_list to be inserted.
+ * @nsize:  The number of elements in @new (excluding end marker).
+ * @loc:    Append the new run_list after this element in @orig.
+ *
+ * Append a run_list after element @loc in @orig.  Merge the right end of
+ * the new run_list, if necessary.  Adjust the size of the hole before the
+ * appended run_list.
+ *
+ * It is up to the caller to serialize access to the run lists @orig and @new.
+ *
+ * Return: Pointer, The new, combined, run_list
+ *
+ * Errors: -ENOMEM, Not enough memory to allocate run list array.
+ *         -EINVAL, Invalid parameters were passed in.
+ */
+static inline run_list_element *ntfs_rl_append(run_list_element *orig,
+		int osize, run_list_element *new, int nsize, int loc)
+{
+	run_list_element *res;
+	BOOL right;
+
+	BUG_ON (!orig || !new);
+
+	/* First, merge the right hand end, if necessary. */
+	right = ntfs_rl_merge (new + nsize - 1, orig + loc + 1);
+
+	/* Space required: Orig size + New size, less one if we merged. */
+	res = ntfs_rl_realloc (orig, osize, osize + nsize - right);
+	if (IS_ERR (res))
+		return res;
+
+	/* Move the tail of Orig out of the way, then copy in New. */
+	rl_mm (res, loc + 1 + nsize, loc + 1 + right, osize - loc - 1 - right);
+	rl_mc (res, loc + 1, new, 0, nsize);
+
+	/* Adjust the size of the preceding hole. */
+	res[loc].length = res[loc+1].vcn - res[loc].vcn;
+
+	/* We may have changed the length of the file, so fix the end marker */
+	if (res[loc+nsize+1].lcn == LCN_ENOENT)
+		res[loc+nsize+1].vcn = res[loc+nsize].vcn + res[loc+nsize].length;
+
+	return res;
+}
+
+/**
+ * ntfs_rl_insert - Insert a run_list into another
+ * @orig:   The original run_list to be worked on.
+ * @osize:  The number of elements in @orig (including end marker).
+ * @new:    The run_list to be inserted.
+ * @nsize:  The number of elements in @new (excluding end marker).
+ * @loc:    Insert the new run_list before this element in @orig.
+ *
+ * Insert a run_list before element @loc in @orig.  Merge the left end of
+ * the new run_list, if necessary.  Adjust the size of the hole after the
+ * inserted run_list.
+ *
+ * It is up to the caller to serialize access to the run lists @orig and @new.
+ *
+ * Return: Pointer, The new, combined, run_list
+ *
+ * Errors: -ENOMEM, Not enough memory to allocate run list array.
+ *         -EINVAL, Invalid parameters were passed in.
+ */
+static inline run_list_element *ntfs_rl_insert(run_list_element *orig,
+		int osize, run_list_element *new, int nsize, int loc)
+{
+	run_list_element *res;
+	BOOL left = FALSE;
+	BOOL disc = FALSE;	/* Discontinuity */
+	BOOL hole = FALSE;	/* Following a hole */
+
+	BUG_ON (!orig || !new);
+
+	/* disc => Discontinuity between the end of Orig and the start of New.
+	 *         This means we might need to insert a hole.
+	 * hole => Orig ends with a hole or an unmapped region which we can
+	 *         extend to match the discontinuity. */
+	if (loc == 0) {
+		disc = (new[0].vcn > 0);
+	} else {
+		left = ntfs_rl_merge (orig + loc - 1, new);
+
+		disc = (new[0].vcn > (orig[loc-1].vcn + orig[loc-1].length));
+		if (disc)
+			hole = (orig[loc-1].lcn == LCN_HOLE);
+	}
+
+	/* Space required: Orig size + New size, less one if we merged,
+	 * plus one if there was a discontinuity, less one for a trailing hole */
+	res = ntfs_rl_realloc (orig, osize, osize + nsize - left + disc - hole);
+	if (IS_ERR (res))
+		return res;
+
+	/* Move the tail of Orig out of the way, then copy in New. */
+	rl_mm (res, loc + nsize - left + disc - hole, loc, osize - loc);
+	rl_mc (res, loc + disc - hole, new, left, nsize - left);
+
+	/* Adjust the VCN of the last run ... */
+	if (res[loc+nsize-left+disc-hole].lcn <= LCN_HOLE) {
+		res[loc+nsize-left+disc-hole].vcn =
+			res[loc+nsize-left+disc-hole-1].vcn +
+			res[loc+nsize-left+disc-hole-1].length;
+	}
+	/* ... and the length. */
+	if ((res[loc+nsize-left+disc-hole].lcn == LCN_HOLE) ||
+	    (res[loc+nsize-left+disc-hole].lcn == LCN_RL_NOT_MAPPED)) {
+		res[loc+nsize-left+disc-hole].length =
+			res[loc+nsize-left+disc-hole+1].vcn -
+			res[loc+nsize-left+disc-hole].vcn;
+	}
+
+	/* Writing beyond the end of the file and there's a discontinuity. */
+	if (disc) {
+		if (hole) {
+			res[loc-1].length = res[loc].vcn - res[loc-1].vcn;
+		} else {
+			if (loc > 0) {
+				res[loc].vcn = res[loc-1].vcn +
+					res[loc-1].length;
+				res[loc].length = res[loc+1].vcn - res[loc].vcn;
+			} else {
+				res[loc].vcn = 0;
+				res[loc].length = res[loc+1].vcn;
+			}
+			res[loc].lcn = LCN_RL_NOT_MAPPED;
+		}
+
+		if (res[loc+nsize-left+disc].lcn == LCN_ENOENT)
+			res[loc+nsize-left+disc].vcn = res[loc+nsize-left+disc-1].vcn +
+				res[loc+nsize-left+disc-1].length;
+	}
+
+	return res;
+}
+
+/**
+ * ntfs_rl_replace - Overwrite a run_list element with another run_list
+ * @orig:   The original run_list to be worked on.
+ * @osize:  The number of elements in @orig (including end marker).
+ * @new:    The run_list to be inserted.
+ * @nsize:  The number of elements in @new (excluding end marker).
+ * @loc:    Index of run_list @orig to overwrite with @new.
+ *
+ * Replace the run_list at @loc with @new.  Merge the left and right ends of
+ * the inserted run_list, if necessary.
+ *
+ * It is up to the caller to serialize access to the run lists @orig and @new.
+ *
+ * Return: Pointer, The new, combined, run_list
+ *
+ * Errors: -ENOMEM, Not enough memory to allocate run list array.
+ *         -EINVAL, Invalid parameters were passed in.
+ */
+static inline run_list_element *ntfs_rl_replace(run_list_element *orig,
+		int osize, run_list_element *new, int nsize, int loc)
+{
+	run_list_element *res;
+	BOOL left = FALSE;
+	BOOL right;
+
+	BUG_ON (!orig || !new);
+
+	/* First, merge the left and right ends, if necessary. */
+	right = ntfs_rl_merge (new + nsize - 1, orig + loc + 1);
+	if (loc > 0)
+		left = ntfs_rl_merge (orig + loc - 1, new);
+
+	/* Allocate some space.  We'll need less if the left, right
+	 * or both ends were merged. */
+	res = ntfs_rl_realloc (orig, osize, osize + nsize - left - right);
+	if (IS_ERR (res))
+		return res;
+
+	/* Move the tail of Orig out of the way, then copy in New. */
+	rl_mm (res, loc + nsize - left, loc + right + 1,
+		osize - loc - right - 1);
+	rl_mc (res, loc, new, left, nsize - left);
+
+	/* We may have changed the length of the file, so fix the end marker */
+	if (res[loc+nsize-left].lcn == LCN_ENOENT)
+		res[loc+nsize-left].vcn = res[loc+nsize-left-1].vcn +
+					  res[loc+nsize-left-1].length;
+	return res;
+}
+
+/**
+ * ntfs_rl_split - Insert a run_list into the centre of a hole
+ * @orig:   The original run_list to be worked on.
+ * @osize:  The number of elements in @orig (including end marker).
+ * @new:    The run_list to be inserted.
+ * @nsize:  The number of elements in @new (excluding end marker).
+ * @loc:    Index of run_list in @orig to split with @new.
+ *
+ * Split the run_list at @loc into two and insert @new.  No merging of
+ * run_lists is necessary.  Adjust the size of the holes either side.
+ *
+ * It is up to the caller to serialize access to the run lists @orig and @new.
+ *
+ * Return: Pointer, The new, combined, run_list
+ *
+ * Errors: -ENOMEM, Not enough memory to allocate run list array.
+ *         -EINVAL, Invalid parameters were passed in.
+ */
+static inline run_list_element *ntfs_rl_split(run_list_element *orig, int osize,
+		run_list_element *new, int nsize, int loc)
+{
+	run_list_element *res;
+
+	BUG_ON (!orig || !new);
+
+	/* Space required: Orig size + New size + One new hole. */
+	res = ntfs_rl_realloc (orig, osize, osize + nsize + 1);
+	if (IS_ERR (res))
+		return res;
+
+	/* Move the tail of Orig out of the way, then copy in New. */
+	rl_mm (res, loc + 1 + nsize, loc, osize - loc);
+	rl_mc (res, loc + 1, new, 0, nsize);
+
+	/* Adjust the size of the holes either size of New. */
+	res[loc].length         = res[loc+1].vcn       - res[loc].vcn;
+	res[loc+nsize+1].vcn    = res[loc+nsize].vcn   + res[loc+nsize].length;
+	res[loc+nsize+1].length = res[loc+nsize+2].vcn - res[loc+nsize+1].vcn;
+
+	return res;
+}
+
+/**
+ * merge_run_lists - merge two run_lists into one
+ * @drl:  The original run_list.
+ * @srl:  The new run_list to be merge into @drl.
+ *
+ * First we sanity check the two run_lists to make sure that they are sensible
+ * and can be merged.  The @srl run_list must be either after the @drl run_list
+ * or completely within a hole in @drl.
+ *
+ * It is up to the caller to serialize access to the run lists @drl and @srl.
+ *
+ * Merging of run lists is necessary in two cases:
+ *   1. When attribute lists are used and a further extent is being mapped.
+ *   2. When new clusters are allocated to fill a hole or extend a file.
+ *
+ * There are four possible ways @srl can be merged.  It can be inserted at
+ * the beginning of a hole; split the hole in two; appended at the end of
+ * a hole; replace the whole hole.  It can also be appended to the end of
+ * the run_list, which is just a variant of the insert case.
+ *
+ * N.B.  Either, or both, of the input pointers may be freed if the function
+ *       is successful.  Only the returned pointer may be used.
+ *
+ *       If the function fails, neither of the input run_lists may be safe.
+ *
+ * Return: Pointer, The resultant merged run_list.
+ *
+ * Errors: -ENOMEM, Not enough memory to allocate run list array.
+ *         -EINVAL, Invalid parameters were passed in.
+ *         -ERANGE, The run_lists overlap and cannot be merged.
+ */
+run_list_element *merge_run_lists(run_list_element *drl, run_list_element *srl)
+{
+	run_list_element *nrl;	/* New run list. */
+	int di, si;		/* Current index into @[ds]rl. */
+	int sstart;		/* First index with lcn > LCN_RL_NOT_MAPPED. */
+	int dins;		/* Index into @drl at which to insert @srl. */
+	int dend, send;		/* Last index into @[ds]rl. */
+	int dfinal, sfinal;	/* The last index into @[ds]rl with
+				   lcn >= LCN_HOLE. */
+	int marker = 0;
+
+#if 1
+	ntfs_debug ("dst:");
+	ntfs_debug_dump_runlist (drl);
+	ntfs_debug ("src:");
+	ntfs_debug_dump_runlist (srl);
+#endif
+
+ 	/* Check for silly calling... */
+	if (unlikely (!srl))
+		return drl;
+	if (unlikely (IS_ERR (srl) || IS_ERR (drl)))
+		return ERR_PTR (-EINVAL);
+
+	/* Check for the case where the first mapping is being done now. */
+	if (unlikely (!drl)) {
+		nrl = srl;
+
+		/* Complete the source run list if necessary. */
+		if (unlikely (srl[0].vcn)) {
+			/* Scan to the end of the source run list. */
+			for (send = 0; likely (srl[send].length); send++)
+				;
+			nrl = ntfs_rl_realloc (srl, send, send + 1);
+			if (!nrl)
+				return ERR_PTR (-ENOMEM);
+
+			rl_mm (nrl, 1, 0, send);
+			nrl[0].vcn = 0;			/* Add start element. */
+			nrl[0].lcn = LCN_RL_NOT_MAPPED;
+			nrl[0].length = nrl[1].vcn;
+		}
+		goto finished;
+	}
+
+	si = di = 0;
+
+	/* Skip the unmapped start element(s) in each run_list if present. */
+	while (srl[si].length && srl[si].lcn < (LCN)LCN_HOLE)
+		si++;
+
+	/* Can't have an entirely unmapped srl run_list. */
+	BUG_ON (!srl[si].length);
+
+	/* Record the starting points. */
+	sstart = si;
+
+	/*
+	 * Skip forward in @drl until we reach the position where @srl needs to
+	 * be inserted. If we reach the end of @drl, @srl just needs to be
+	 * appended to @drl.
+	 */
+	for (; drl[di].length; di++) {
+		if ((drl[di].vcn + drl[di].length) > srl[sstart].vcn)
+			break;
+	}
+	dins = di;
+
+	/* Sanity check for illegal overlaps. */
+	if ((drl[di].vcn == srl[si].vcn) &&
+	    (drl[di].lcn >= 0) &&
+	    (srl[si].lcn >= 0)) {
+		ntfs_error (NULL, "Run lists overlap. Cannot merge! Returning "
+				"ERANGE.");
+		nrl = ERR_PTR (-ERANGE);
+		goto exit;
+	}
+
+	/* Scan to the end of both run lists in order to know their sizes. */
+	for (send = si; srl[send].length; send++)
+		;
+	for (dend = di; drl[dend].length; dend++)
+		;
+
+	if (srl[send].lcn == LCN_ENOENT) {
+		marker = send;
+	}
+
+	/* Scan to the last element with lcn >= LCN_HOLE. */
+	for (sfinal = send; sfinal >= 0 && srl[sfinal].lcn < LCN_HOLE; sfinal--)
+		;
+	for (dfinal = dend; dfinal >= 0 && drl[dfinal].lcn < LCN_HOLE; dfinal--)
+		;
+
+	{
+	BOOL start;
+	BOOL finish;
+	int ds = dend   + 1;		/* Number of elements in drl & srl */
+	int ss = sfinal - sstart + 1;
+
+	start  = ((drl[dins].lcn <  LCN_RL_NOT_MAPPED) ||    /* End of file   */
+		  (drl[dins].vcn == srl[sstart].vcn));	     /* Start of hole */
+	finish = ((drl[dins].lcn >= LCN_RL_NOT_MAPPED) &&    /* End of file   */
+		 ((drl[dins].vcn + drl[dins].length) <=      /* End of hole   */
+		  (srl[send-1].vcn + srl[send-1].length)));
+
+	/* Or we'll lose an end marker */
+	if (start && finish && (drl[dins].length == 0))
+		ss++;
+	if (marker && (drl[dins].vcn + drl[dins].length > srl[send-1].vcn))
+		finish = FALSE;
+
+#if 0
+	ntfs_debug("dfinal = %i, dend = %i", dfinal, dend);
+	ntfs_debug("sstart = %i, sfinal = %i, send = %i", sstart, sfinal, send);
+	ntfs_debug("start = %i, finish = %i", start, finish);
+	ntfs_debug("ds = %i, ss = %i, dins = %i", ds, ss, dins);
+#endif
+	if (start)
+		if (finish)
+			nrl = ntfs_rl_replace (drl, ds, srl + sstart, ss, dins);
+		else
+			nrl = ntfs_rl_insert  (drl, ds, srl + sstart, ss, dins);
+	else
+		if (finish)
+			nrl = ntfs_rl_append  (drl, ds, srl + sstart, ss, dins);
+		else
+			nrl = ntfs_rl_split   (drl, ds, srl + sstart, ss, dins);
+
+	if (marker && !IS_ERR(nrl)) {
+		for (ds = 0; nrl[ds].length; ds++)
+			;
+		nrl = ntfs_rl_insert(nrl, ds + 1, srl + marker, 1, ds);
+	}
+	}
+
+	if (likely (!IS_ERR (nrl))) {
+		/* The merge was completed successfully. */
+finished:
+		if (nrl != srl)
+			ntfs_free (srl);
+		/*ntfs_debug ("Done.");*/
+		/*ntfs_debug ("Merged run list:");*/
+
+#if 1
+		ntfs_debug ("res:");
+		ntfs_debug_dump_runlist (nrl);
+#endif
+	} else {
+		ntfs_error (NULL, "Merge failed, returning error code %ld.",
+				-PTR_ERR (nrl));
+	}
+exit:
+	return nrl;
+}
+
+/**
+ * decompress_mapping_pairs - convert mapping pairs array to run list
+ * @vol:	ntfs volume on which the attribute resides
+ * @attr:	attribute record whose mapping pairs array to decompress
+ * @run_list:	optional run list in which to insert @attr's run list
+ *
+ * Decompress the attribute @attr's mapping pairs array into a run_list and
+ * return the run list or -errno on error. If @run_list is not NULL then
+ * the mapping pairs array of @attr is decompressed and the run list inserted
+ * into the appropriate place in @run_list. If this is the case and the
+ * function returns success, the original pointer passed into @run_list is no
+ * longer valid.
+ *
+ * It is up to the caller to serialize access to the run list @old_rl.
+ *
+ * Check the return value for error with IS_ERR(ret_val). If this is FALSE,
+ * the function was successful, the return value is the new run list, and if
+ * an existing run list pointer was passed in, this is no longer valid.
+ * If IS_ERR(ret_val) returns true, there was an error, the return value is not
+ * a run_list pointer and the existing run list pointer if one was passed in
+ * has not been touched. In this case use PTR_ERR(ret_val) to obtain the error
+ * code. Following error codes are defined:
+ * 	-ENOMEM		Not enough memory to allocate run list array.
+ * 	-EIO		Corrupt run list.
+ * 	-EINVAL		Invalid parameters were passed in.
+ * 	-ERANGE		The two run lists overlap.
+ *
+ * FIXME: For now we take the conceptionally simplest approach of creating the
+ * new run list disregarding the already existing one and then splicing the
+ * two into one if that is possible (we check for overlap and discard the new
+ * run list if overlap present and return error).
+ */
+run_list_element *decompress_mapping_pairs(const ntfs_volume *vol,
+		const ATTR_RECORD *attr, run_list_element *old_rl)
+{
+	VCN vcn;		/* Current vcn. */
+	LCN lcn; 		/* Current lcn. */
+	s64 deltaxcn;		/* Change in [vl]cn. */
+	run_list_element *rl = NULL;	/* The output run_list. */
+	run_list_element *rl2;	/* Temporary run_list. */
+	u8 *buf;		/* Current position in mapping pairs array. */
+	u8 *attr_end;		/* End of attribute. */
+	int rlsize;		/* Size of run_list buffer. */
+	int rlpos;		/* Current run_list position. */
+	u8 b;			/* Current byte offset in buf. */
+
+#ifdef DEBUG
+	/* Make sure attr exists and is non-resident. */
+	if (!attr || !attr->non_resident ||
+			sle64_to_cpu(attr->_ANR(lowest_vcn)) < (VCN)0) {
+		ntfs_error(vol->sb, "Invalid arguments.");
+		return ERR_PTR(-EINVAL);
+	}
+#endif
+	/* Start at vcn = lowest_vcn and lcn 0. */
+	vcn = sle64_to_cpu(attr->_ANR(lowest_vcn));
+	lcn = 0;
+	/* Get start of the mapping pairs array. */
+	buf = (u8*)attr + le16_to_cpu(attr->_ANR(mapping_pairs_offset));
+	attr_end = (u8*)attr + le32_to_cpu(attr->length);
+	if (unlikely(buf < (u8*)attr || buf > attr_end)) {
+		ntfs_error(vol->sb, "Corrupt attribute.");
+		return ERR_PTR(-EIO);
+	}
+	/* Current position in run_list array. */
+	rlpos = 0;
+	/* Allocate first page. */
+	rl = ntfs_malloc_nofs(PAGE_SIZE);
+	if (unlikely(!rl))
+		return ERR_PTR(-ENOMEM);
+	/* Current run_list buffer size in bytes. */
+	rlsize = PAGE_SIZE;
+	/* Insert unmapped starting element if necessary. */
+	if (vcn) {
+		rl->vcn = (VCN)0;
+		rl->lcn = (LCN)LCN_RL_NOT_MAPPED;
+		rl->length = vcn;
+		rlpos++;
+	}
+	while (buf < attr_end && *buf) {
+		/*
+		 * Allocate more memory if needed, including space for the
+		 * not-mapped and terminator elements. ntfs_malloc_nofs()
+		 * operates on whole pages only.
+		 */
+		if (((rlpos + 3) * sizeof(*old_rl)) > rlsize) {
+			rl2 = ntfs_malloc_nofs(rlsize + (int)PAGE_SIZE);
+			if (unlikely(!rl2)) {
+				ntfs_free(rl);
+				return ERR_PTR(-ENOMEM);
+			}
+			memmove(rl2, rl, rlsize);
+			ntfs_free(rl);
+			rl = rl2;
+			rlsize += PAGE_SIZE;
+		}
+		/* Enter the current vcn into the current run_list element. */
+		(rl + rlpos)->vcn = vcn;
+		/*
+		 * Get the change in vcn, i.e. the run length in clusters.
+		 * Doing it this way ensures that we signextend negative values.
+		 * A negative run length doesn't make any sense, but hey, I
+		 * didn't make up the NTFS specs and Windows NT4 treats the run
+		 * length as a signed value so that's how it is...
+		 */
+		b = *buf & 0xf;
+		if (b) {
+			if (unlikely(buf + b > attr_end))
+				goto io_error;
+			for (deltaxcn = (s8)buf[b--]; b; b--)
+				deltaxcn = (deltaxcn << 8) + buf[b];
+		} else { /* The length entry is compulsory. */
+			ntfs_error(vol->sb, "Missing length entry in mapping "
+					"pairs array.");
+			deltaxcn = (s64)-1;
+		}
+		/*
+		 * Assume a negative length to indicate data corruption and
+		 * hence clean-up and return NULL.
+		 */
+		if (unlikely(deltaxcn < 0)) {
+			ntfs_error(vol->sb, "Invalid length in mapping pairs "
+					"array.");
+			goto err_out;
+		}
+		/*
+		 * Enter the current run length into the current run_list
+		 * element.
+		 */
+		(rl + rlpos)->length = deltaxcn;
+		/* Increment the current vcn by the current run length. */
+		vcn += deltaxcn;
+		/*
+		 * There might be no lcn change at all, as is the case for
+		 * sparse clusters on NTFS 3.0+, in which case we set the lcn
+		 * to LCN_HOLE.
+		 */
+		if (!(*buf & 0xf0))
+			(rl + rlpos)->lcn = (LCN)LCN_HOLE;
+		else {
+			/* Get the lcn change which really can be negative. */
+			u8 b2 = *buf & 0xf;
+			b = b2 + ((*buf >> 4) & 0xf);
+			if (buf + b > attr_end)
+				goto io_error;
+			for (deltaxcn = (s8)buf[b--]; b > b2; b--)
+				deltaxcn = (deltaxcn << 8) + buf[b];
+			/* Change the current lcn to it's new value. */
+			lcn += deltaxcn;
+#ifdef DEBUG
+			/*
+			 * On NTFS 1.2-, apparently can have lcn == -1 to
+			 * indicate a hole. But we haven't verified ourselves
+			 * whether it is really the lcn or the deltaxcn that is
+			 * -1. So if either is found give us a message so we
+			 * can investigate it further!
+			 */
+			if (vol->major_ver < 3) {
+				if (unlikely(deltaxcn == (LCN)-1))
+					ntfs_error(vol->sb, "lcn delta == -1");
+				if (unlikely(lcn == (LCN)-1))
+					ntfs_error(vol->sb, "lcn == -1");
+			}
+#endif
+			/* Check lcn is not below -1. */
+			if (unlikely(lcn < (LCN)-1)) {
+				ntfs_error(vol->sb, "Invalid LCN < -1 in "
+						"mapping pairs array.");
+				goto err_out;
+			}
+			/* Enter the current lcn into the run_list element. */
+			(rl + rlpos)->lcn = lcn;
+		}
+		/* Get to the next run_list element. */
+		rlpos++;
+		/* Increment the buffer position to the next mapping pair. */
+		buf += (*buf & 0xf) + ((*buf >> 4) & 0xf) + 1;
+	}
+	if (unlikely(buf >= attr_end))
+		goto io_error;
+	/*
+	 * If there is a highest_vcn specified, it must be equal to the final
+	 * vcn in the run list - 1, or something has gone badly wrong.
+	 */
+	deltaxcn = sle64_to_cpu(attr->_ANR(highest_vcn));
+	if (unlikely(deltaxcn && vcn - 1 != deltaxcn)) {
+mpa_err:
+		ntfs_error(vol->sb, "Corrupt mapping pairs array in "
+				"non-resident attribute.");
+		goto err_out;
+	}
+	/* Setup not mapped run_list element if this is the base extent. */
+	if (!attr->_ANR(lowest_vcn)) {
+		VCN max_cluster;
+
+		max_cluster = (sle64_to_cpu(attr->_ANR(allocated_size)) +
+				vol->cluster_size - 1) >>
+				vol->cluster_size_bits;
+		/*
+		 * If there is a difference between the highest_vcn and the
+		 * highest cluster, the run list is either corrupt or, more
+		 * likely, there are more extents following this one.
+		 */
+		if (deltaxcn < --max_cluster) {
+			//RAR ntfs_debug("More extents to follow; deltaxcn = 0x%Lx, "
+					//RAR "max_cluster = 0x%Lx",
+					//RAR (long long)deltaxcn,
+					//RAR (long long)max_cluster);
+			(rl + rlpos)->vcn = vcn;
+			vcn += (rl + rlpos)->length = max_cluster - deltaxcn;
+			(rl + rlpos)->lcn = (LCN)LCN_RL_NOT_MAPPED;
+			rlpos++;
+		} else if (unlikely(deltaxcn > max_cluster)) {
+			ntfs_error(vol->sb, "Corrupt attribute. deltaxcn = "
+					"0x%Lx, max_cluster = 0x%Lx",
+					(long long)deltaxcn,
+					(long long)max_cluster);
+			goto mpa_err;
+		}
+		(rl + rlpos)->lcn = (LCN)LCN_ENOENT;
+	} else /* Not the base extent. There may be more extents to follow. */
+		(rl + rlpos)->lcn = (LCN)LCN_RL_NOT_MAPPED;
+
+	/* Setup terminating run_list element. */
+	(rl + rlpos)->vcn = vcn;
+	(rl + rlpos)->length = (s64)0;
+	//RAR ntfs_debug("Mapping pairs array successfully decompressed.");
+	//RAR ntfs_debug_dump_runlist(rl);
+	/* If no existing run list was specified, we are done. */
+	if (!old_rl)
+		return rl;
+	/* Now combine the new and old run lists checking for overlaps. */
+	rl2 = merge_run_lists(old_rl, rl);
+	if (likely(!IS_ERR(rl2)))
+		return rl2;
+	ntfs_free(rl);
+	ntfs_error(vol->sb, "Failed to merge run lists.");
+	return rl2;
+io_error:
+	ntfs_error(vol->sb, "Corrupt attribute.");
+err_out:
+	ntfs_free(rl);
+	return ERR_PTR(-EIO);
+}
+
+/**
+ * map_run_list - map (a part of) a run list of an ntfs inode
+ * @ni:		ntfs inode for which to map (part of) a run list 
+ * @vcn:	map run list part containing this vcn
+ *
+ * Map the part of a run list containing the @vcn of an the ntfs inode @ni.
+ *
+ * Return 0 on success and -errno on error.
+ */
+int map_run_list(ntfs_inode *ni, VCN vcn)
+{
+	attr_search_context *ctx;
+	MFT_RECORD *mrec;
+	const uchar_t *name;
+	u32 name_len;
+	ATTR_TYPES at;
+	int err = 0;
+	
+	ntfs_debug("Mapping run list part containing vcn 0x%Lx.",
+			(long long)vcn);
+
+	/* Map, pin and lock the mft record for reading. */
+	mrec = map_mft_record(READ, ni);
+	if (IS_ERR(mrec))
+		return PTR_ERR(mrec);
+
+	ctx = get_attr_search_ctx(ni, mrec);
+	if (!ctx) {
+		err = -ENOMEM;
+		goto unm_err_out;
+	}
+
+	/* The attribute type is determined from the inode type. */
+	if (S_ISDIR(VFS_I(ni)->i_mode)) {
+		at = AT_INDEX_ALLOCATION;
+		name = I30;
+		name_len = 4;
+	} else {
+		at = AT_DATA;
+		name = NULL;
+		name_len = 0;
+	}
+
+	/* Find the attribute in the mft record. */
+	if (!lookup_attr(at, name, name_len, CASE_SENSITIVE, vcn, NULL, 0,
+			ctx)) {
+		put_attr_search_ctx(ctx);
+		err = -ENOENT;
+		goto unm_err_out;
+	}
+
+	/* Lock the run list. */
+	down_write(&ni->run_list.lock);
+
+	/* Make sure someone else didn't do the work while we were spinning. */
+	if (likely(vcn_to_lcn(ni->run_list.rl, vcn) <= LCN_RL_NOT_MAPPED)) {
+		run_list_element *rl;
+
+		/* Decode the run list. */
+		rl = decompress_mapping_pairs(ni->vol, ctx->attr,
+				ni->run_list.rl);
+
+		/* Flag any errors or set the run list if successful. */
+		if (unlikely(IS_ERR(rl)))
+			err = PTR_ERR(rl);
+		else
+			ni->run_list.rl = rl;
+	}
+
+	/* Unlock the run list. */
+	up_write(&ni->run_list.lock);
+	
+	put_attr_search_ctx(ctx);
+
+	/* Unlock, unpin and release the mft record. */
+	unmap_mft_record(READ, ni);
+
+	/* If an error occured, return it. */
+	ntfs_debug("Done.");
+	return err;
+
+unm_err_out:
+	unmap_mft_record(READ, ni);
+	return err;
+}
+
+/**
+ * vcn_to_lcn - convert a vcn into a lcn given a run list
+ * @rl:		run list to use for conversion
+ * @vcn:	vcn to convert
+ *
+ * Convert the virtual cluster number @vcn of an attribute into a logical
+ * cluster number (lcn) of a device using the run list @rl to map vcns to their
+ * corresponding lcns.
+ *
+ * It is up to the caller to serialize access to the run list @rl.
+ *
+ * Since lcns must be >= 0, we use negative return values with special meaning:
+ *
+ * Return value			Meaning / Description
+ * ==================================================
+ *  -1 = LCN_HOLE		Hole / not allocated on disk.
+ *  -2 = LCN_RL_NOT_MAPPED	This is part of the run list which has not been
+ *				inserted into the run list yet.
+ *  -3 = LCN_ENOENT		There is no such vcn in the attribute.
+ *  -4 = LCN_EINVAL		Input parameter error (if debug enabled).
+ */
+LCN vcn_to_lcn(const run_list_element *rl, const VCN vcn)
+{
+	int i;
+
+#ifdef DEBUG
+	if (vcn < (VCN)0)
+		return (LCN)LCN_EINVAL;
+#endif
+	/*
+	 * If rl is NULL, assume that we have found an unmapped run list. The
+	 * caller can then attempt to map it and fail appropriately if
+	 * necessary.
+	 */
+	if (unlikely(!rl))
+		return (LCN)LCN_RL_NOT_MAPPED;
+
+	/* Catch out of lower bounds vcn. */
+	if (unlikely(vcn < rl[0].vcn))
+		return (LCN)LCN_ENOENT;
+
+	for (i = 0; likely(rl[i].length); i++) {
+		if (unlikely(vcn < rl[i+1].vcn)) {
+			if (likely(rl[i].lcn >= (LCN)0))
+				return rl[i].lcn + (vcn - rl[i].vcn);
+			return rl[i].lcn;
+		}
+	}
+	/*
+	 * The terminator element is setup to the correct value, i.e. one of
+	 * LCN_HOLE, LCN_RL_NOT_MAPPED, or LCN_ENOENT.
+	 */
+	if (likely(rl[i].lcn < (LCN)0))
+		return rl[i].lcn;
+	/* Just in case... We could replace this with BUG() some day. */
+	return (LCN)LCN_ENOENT;
+}
+
+/**
+ * find_attr - find (next) attribute in mft record
+ * @type:	attribute type to find
+ * @name:	attribute name to find (optional, i.e. NULL means don't care)
+ * @name_len:	attribute name length (only needed if @name present)
+ * @ic:		IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present)
+ * @val:	attribute value to find (optional, resident attributes only)
+ * @val_len:	attribute value length
+ * @ctx:	search context with mft record and attribute to search from
+ *
+ * You shouldn't need to call this function directly. Use lookup_attr() instead.
+ *
+ * find_attr() takes a search context @ctx as parameter and searches the mft
+ * record specified by @ctx->mrec, beginning at @ctx->attr, for an attribute of
+ * @type, optionally @name and @val. If found, find_attr() returns TRUE and
+ * @ctx->attr will point to the found attribute. If not found, find_attr()
+ * returns FALSE and @ctx->attr is undefined (i.e. do not rely on it not
+ * changing).
+ *
+ * If @ctx->is_first is TRUE, the search begins with @ctx->attr itself. If it
+ * is FALSE, the search begins after @ctx->attr.
+ *
+ * If @ic is IGNORE_CASE, the @name comparisson is not case sensitive and
+ * @ctx->ntfs_ino must be set to the ntfs inode to which the mft record
+ * @ctx->mrec belongs. This is so we can get at the ntfs volume and hence at
+ * the upcase table. If @ic is CASE_SENSITIVE, the comparison is case
+ * sensitive. When @name is present, @name_len is the @name length in Unicode
+ * characters.
+ *
+ * If @name is not present (NULL), we assume that the unnamed attribute is
+ * being searched for.
+ *
+ * Finally, the resident attribute value @val is looked for, if present. If @val
+ * is not present (NULL), @val_len is ignored.
+ *
+ * find_attr() only searches the specified mft record and it ignores the
+ * presence of an attribute list attribute (unless it is the one being searched
+ * for, obviously). If you need to take attribute lists into consideration, use
+ * lookup_attr() instead (see below). This also means that you cannot use
+ * find_attr() to search for extent records of non-resident attributes, as
+ * extents with lowest_vcn != 0 are usually described by the attribute list
+ * attribute only. - Note that it is possible that the first extent is only in
+ * the attribute list while the last extent is in the base mft record, so don't
+ * rely on being able to find the first extent in the base mft record.
+ *
+ * Warning: Never use @val when looking for attribute types which can be
+ *	    non-resident as this most likely will result in a crash!
+ */
+BOOL find_attr(const ATTR_TYPES type, const uchar_t *name, const u32 name_len,
+		const IGNORE_CASE_BOOL ic, const u8 *val, const u32 val_len,
+		attr_search_context *ctx)
+{
+	ATTR_RECORD *a;
+	ntfs_volume *vol;
+	uchar_t *upcase;
+	u32 upcase_len;
+
+	if (ic == IGNORE_CASE) {
+		vol = ctx->ntfs_ino->vol;
+		upcase = vol->upcase;
+		upcase_len = vol->upcase_len;
+	} else {
+		vol = NULL;
+		upcase = NULL;
+		upcase_len = 0;
+	}
+	/*
+	 * Iterate over attributes in mft record starting at @ctx->attr, or the
+	 * attribute following that, if @ctx->is_first is TRUE.
+	 */
+	if (ctx->is_first) {
+		a = ctx->attr;
+		ctx->is_first = FALSE;
+	} else
+		a = (ATTR_RECORD*)((u8*)ctx->attr +
+				le32_to_cpu(ctx->attr->length));
+	for (;;	a = (ATTR_RECORD*)((u8*)a + le32_to_cpu(a->length))) {
+		if ((u8*)a < (u8*)ctx->mrec || (u8*)a > (u8*)ctx->mrec +
+				le32_to_cpu(ctx->mrec->bytes_allocated))
+			break;
+		ctx->attr = a;
+		/* We catch $END with this more general check, too... */
+		if (le32_to_cpu(a->type) > le32_to_cpu(type))
+			return FALSE;
+		if (unlikely(!a->length))
+			break;
+		if (a->type != type)
+			continue;
+		/* 
+		 * If @name is present, compare the two names. If @name is
+		 * missing, assume we want an unnamed attribute.
+		 */
+		if (!name) {
+			/* The search failed if the found attribute is named. */
+			if (a->name_length)
+				return FALSE;
+		} else if (!ntfs_are_names_equal(name, name_len,
+			    (uchar_t*)((u8*)a + le16_to_cpu(a->name_offset)),
+			    a->name_length, ic, upcase, upcase_len)) {
+			register int rc;
+			
+			rc = ntfs_collate_names(name, name_len,
+					(uchar_t*)((u8*)a +
+						le16_to_cpu(a->name_offset)),
+					a->name_length, 1, IGNORE_CASE,
+					upcase, upcase_len);
+			/*
+			 * If @name collates before a->name, there is no
+			 * matching attribute.
+			 */
+			if (rc == -1)
+				return FALSE;
+			/* If the strings are not equal, continue search. */
+			if (rc)
+	 			continue;
+			rc = ntfs_collate_names(name, name_len,
+					(uchar_t*)((u8*)a +
+						le16_to_cpu(a->name_offset)),
+					a->name_length, 1, CASE_SENSITIVE,
+					upcase, upcase_len);
+			if (rc == -1)
+				return FALSE;
+			if (rc)
+				continue;
+		}
+		/*
+		 * The names match or @name not present and attribute is
+		 * unnamed. If no @val specified, we have found the attribute
+		 * and are done.
+		 */
+		if (!val)
+			return TRUE;
+		/* @val is present; compare values. */
+		else {
+			register int rc;
+			
+			rc = memcmp(val, (u8*)a +le16_to_cpu(a->_ARA(value_offset)),
+				min(val_len, le32_to_cpu(a->_ARA(value_length))));
+			/*
+			 * If @val collates before the current attribute's
+			 * value, there is no matching attribute.
+			 */
+			if (!rc) {
+				register u32 avl;
+				avl = le32_to_cpu(a->_ARA(value_length));
+				if (val_len == avl)
+					return TRUE;
+				if (val_len < avl)
+					return FALSE;
+			} else if (rc < 0)
+				return FALSE;
+		}
+	}
+	ntfs_error(NULL, "Inode is corrupt. Run chkdsk.");
+	return FALSE;
+}
+
+/**
+ * load_attribute_list - load an attribute list into memory
+ * @vol:		ntfs volume from which to read
+ * @run_list:		run list of the attribute list
+ * @al:			destination buffer
+ * @size:		size of the destination buffer in bytes
+ * @initialized_size:	initialized size of the attribute list
+ *
+ * Walk the run list @run_list and load all clusters from it copying them into
+ * the linear buffer @al. The maximum number of bytes copied to @al is @size
+ * bytes. Note, @size does not need to be a multiple of the cluster size. If
+ * @initialized_size is less than @size, the region in @al between
+ * @initialized_size and @size will be zeroed and not read from disk.
+ *
+ * Return 0 on success or -errno on error.
+ */
+int load_attribute_list(ntfs_volume *vol, run_list *run_list, u8 *al,
+		const s64 size, const s64 initialized_size)
+{
+	LCN lcn;
+	u8 *al_end = al + initialized_size;
+	run_list_element *rl;
+	struct buffer_head *bh;
+	struct super_block *sb = vol->sb;
+	unsigned long block_size = sb->s_blocksize;
+	unsigned long block, max_block;
+	int err = 0;
+	unsigned char block_size_bits = sb->s_blocksize_bits;
+
+	ntfs_debug("Entering.");
+#ifdef DEBUG
+	if (!vol || !run_list || !al || size <= 0 || initialized_size < 0 ||
+			initialized_size > size)
+		return -EINVAL;
+#endif
+	if (!initialized_size) {
+		memset(al, 0, size);
+		return 0;
+	}
+	down_read(&run_list->lock);
+	rl = run_list->rl;
+	/* Read all clusters specified by the run list one run at a time. */
+	while (rl->length) {
+		lcn = vcn_to_lcn(rl, rl->vcn);
+		ntfs_debug("Reading vcn = 0x%Lx, lcn = 0x%Lx.",
+				(long long)rl->vcn, (long long)lcn);
+		/* The attribute list cannot be sparse. */
+		if (lcn < 0) {
+			ntfs_error(sb, "vcn_to_lcn() failed. Cannot read "
+					"attribute list.");
+			goto err_out;
+		}
+		block = lcn << vol->cluster_size_bits >> block_size_bits;
+		/* Read the run from device in chunks of block_size bytes. */
+		max_block = block + (rl->length << vol->cluster_size_bits >>
+				block_size_bits);
+		ntfs_debug("max_block = 0x%lx.", max_block);
+		do {
+			ntfs_debug("Reading block = 0x%lx.", block);
+			bh = sb_bread(sb, block);
+			if (!bh) {
+				ntfs_error(sb, "sb_bread() failed. Cannot "
+						"read attribute list.");
+				goto err_out;
+			}
+			if (al + block_size > al_end)
+				goto do_partial;
+			memcpy(al, bh->b_data, block_size);
+			brelse(bh);
+			al += block_size;
+		} while (++block < max_block);
+		rl++;
+	}
+	if (initialized_size < size) {
+initialize:
+		memset(al + initialized_size, 0, size - initialized_size);
+	}
+done:
+	up_read(&run_list->lock);
+	return err;
+do_partial:
+	if (al < al_end) {
+		/* Partial block. */
+		memcpy(al, bh->b_data, al_end - al);
+		brelse(bh);
+		/*
+		 * Skip sanity checking if initialized_size < size as it is
+		 * too much trouble.
+		 */
+		if (initialized_size < size)
+			goto initialize;
+		/* If the final lcn is partial all is fine. */
+		if (((s64)(block - (lcn << vol->cluster_size_bits >>
+				block_size_bits)) << block_size_bits >>
+				vol->cluster_size_bits) == rl->length - 1) {
+			if (!rl[1].length || (rl[1].lcn == LCN_RL_NOT_MAPPED
+					&& !rl[2].length))
+				goto done;
+		}
+	} else
+		brelse(bh);
+	/* Real overflow! */
+	ntfs_error(sb, "Attribute list buffer overflow. Read attribute list "
+			"is truncated.");
+err_out:
+	err = -EIO;
+	goto done;
+}
+
+/**
+ * find_external_attr - find an attribute in the attribute list of an ntfs inode
+ * @type:	attribute type to find
+ * @name:	attribute name to find (optional, i.e. NULL means don't care)
+ * @name_len:	attribute name length (only needed if @name present)
+ * @ic:		IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present)
+ * @lowest_vcn:	lowest vcn to find (optional, non-resident attributes only)
+ * @val:	attribute value to find (optional, resident attributes only)
+ * @val_len:	attribute value length
+ * @ctx:	search context with mft record and attribute to search from
+ *
+ * You shouldn't need to call this function directly. Use lookup_attr() instead.
+ *
+ * Find an attribute by searching the attribute list for the corresponding
+ * attribute list entry. Having found the entry, map the mft record for read
+ * if the attribute is in a different mft record/inode, find_attr the attribute
+ * in there and return it.
+ *
+ * On first search @ctx->ntfs_ino must be the base mft record and @ctx must
+ * have been obtained from a call to get_attr_search_ctx(). On subsequent calls
+ * @ctx->ntfs_ino can be any extent inode, too (@ctx->base_ntfs_ino is then the
+ * base inode).
+ *
+ * After finishing with the attribute/mft record you need to call
+ * release_attr_search_ctx() to cleanup the search context (unmapping any
+ * mapped inodes, etc).
+ *
+ * Return TRUE if the search was successful and FALSE if not. When TRUE,
+ * @ctx->attr is the found attribute and it is in mft record @ctx->mrec. When
+ * FALSE, @ctx->attr is the attribute which collates just after the attribute
+ * being searched for in the base ntfs inode, i.e. if one wants to add the
+ * attribute to the mft record this is the correct place to insert it into
+ * and if there is not enough space, the attribute should be placed in an
+ * extent mft record.
+ */
+static BOOL find_external_attr(const ATTR_TYPES type, const uchar_t *name,
+		const u32 name_len, const IGNORE_CASE_BOOL ic,
+		const VCN lowest_vcn, const u8 *val, const u32 val_len,
+		attr_search_context *ctx)
+{
+	ntfs_inode *base_ni, *ni;
+	ntfs_volume *vol;
+	ATTR_LIST_ENTRY *al_entry, *next_al_entry;
+	u8 *al_start, *al_end;
+	ATTR_RECORD *a;
+	uchar_t *al_name;
+	u32 al_name_len;
+
+	ni = ctx->ntfs_ino;
+	base_ni = ctx->base_ntfs_ino;
+	ntfs_debug("Entering for inode 0x%Lx, type 0x%x.",
+			(unsigned long long)ni->mft_no, type);
+	if (!base_ni) {
+		/* First call happens with the base mft record. */
+		base_ni = ctx->base_ntfs_ino = ctx->ntfs_ino;
+		ctx->base_mrec = ctx->mrec;
+	}
+	if (ni == base_ni)
+		ctx->base_attr = ctx->attr;
+	vol = base_ni->vol;
+	al_start = base_ni->attr_list;
+	al_end = al_start + base_ni->attr_list_size;
+	if (!ctx->al_entry)
+		ctx->al_entry = (ATTR_LIST_ENTRY*)al_start;
+	/*
+	 * Iterate over entries in attribute list starting at @ctx->al_entry,
+	 * or the entry following that, if @ctx->is_first is TRUE.
+	 */
+	if (ctx->is_first) {
+		al_entry = ctx->al_entry;
+		ctx->is_first = FALSE;
+	} else
+		al_entry = (ATTR_LIST_ENTRY*)((u8*)ctx->al_entry +
+				le16_to_cpu(ctx->al_entry->length));
+	for (;; al_entry = next_al_entry) {
+		/* Out of bounds check. */
+		if ((u8*)al_entry < base_ni->attr_list ||
+				(u8*)al_entry > al_end)
+			break;	/* Inode is corrupt. */
+		ctx->al_entry = al_entry;
+		/* Catch the end of the attribute list. */
+		if ((u8*)al_entry == al_end)
+			goto not_found;
+		if (!al_entry->length)
+			break;
+		if ((u8*)al_entry + 6 > al_end || (u8*)al_entry +
+				le16_to_cpu(al_entry->length) > al_end)
+			break;
+		next_al_entry = (ATTR_LIST_ENTRY*)((u8*)al_entry +
+				le16_to_cpu(al_entry->length));
+		if (le32_to_cpu(al_entry->type) > le32_to_cpu(type))
+			goto not_found;
+		if (type != al_entry->type)
+			continue;
+		/*
+		 * If @name is present, compare the two names. If @name is
+		 * missing, assume we want an unnamed attribute.
+		 */
+		al_name_len = al_entry->name_length;
+		al_name = (uchar_t*)((u8*)al_entry + al_entry->name_offset);
+		if (!name) {
+			if (al_name_len)
+				goto not_found;
+		} else if (!ntfs_are_names_equal(al_name, al_name_len, name,
+				name_len, ic, vol->upcase, vol->upcase_len)) {
+			register int rc;
+
+			rc = ntfs_collate_names(name, name_len, al_name,
+					al_name_len, 1, IGNORE_CASE,
+					vol->upcase, vol->upcase_len);
+			/*
+			 * If @name collates before al_name, there is no
+			 * matching attribute.
+			 */
+			if (rc == -1)
+				goto not_found;
+			/* If the strings are not equal, continue search. */
+			if (rc)
+				continue;
+			/*
+			 * FIXME: Reverse engineering showed 0, IGNORE_CASE but
+			 * that is inconsistent with find_attr(). The subsequent
+			 * rc checks were also different. Perhaps I made a
+			 * mistake in one of the two. Need to recheck which is
+			 * correct or at least see what is going on... (AIA)
+			 */
+			rc = ntfs_collate_names(name, name_len, al_name,
+					al_name_len, 1, CASE_SENSITIVE,
+					vol->upcase, vol->upcase_len);
+			if (rc == -1)
+				goto not_found;
+			if (rc)
+				continue;
+		}
+		/*
+		 * The names match or @name not present and attribute is
+		 * unnamed. Now check @lowest_vcn. Continue search if the
+		 * next attribute list entry still fits @lowest_vcn. Otherwise
+		 * we have reached the right one or the search has failed.
+		 */
+		if (lowest_vcn && (u8*)next_al_entry >= al_start	    &&
+				(u8*)next_al_entry + 6 < al_end		    &&
+				(u8*)next_al_entry + le16_to_cpu(
+					next_al_entry->length) <= al_end    &&	
+				sle64_to_cpu(next_al_entry->lowest_vcn) <=
+					sle64_to_cpu(lowest_vcn)	    &&
+				next_al_entry->type == al_entry->type	    &&
+				next_al_entry->name_length == al_name_len   &&
+				ntfs_are_names_equal((uchar_t*)((u8*)
+					next_al_entry +
+					next_al_entry->name_offset),
+					next_al_entry->name_length,
+					al_name, al_name_len, CASE_SENSITIVE,
+					vol->upcase, vol->upcase_len))
+			continue;
+		if (MREF_LE(al_entry->mft_reference) == ni->mft_no) {
+			if (MSEQNO_LE(al_entry->mft_reference) != ni->seq_no) {
+				ntfs_error(vol->sb, "Found stale mft "
+						"reference in attribute list!");
+				break;
+			}
+		} else { /* Mft references do not match. */
+			/* If there is a mapped record unmap it first. */
+			if (ni != base_ni)
+				unmap_extent_mft_record(ni);
+			/* Do we want the base record back? */
+			if (MREF_LE(al_entry->mft_reference) ==
+					base_ni->mft_no) {
+				ni = ctx->ntfs_ino = base_ni;
+				ctx->mrec = ctx->base_mrec;
+			} else {
+				/* We want an extent record. */
+				ctx->mrec = map_extent_mft_record(base_ni,
+						al_entry->mft_reference, &ni);
+				ctx->ntfs_ino = ni;
+				if (IS_ERR(ctx->mrec)) {
+					ntfs_error(vol->sb, "Failed to map mft "
+							"record, error code "
+							"%ld.",
+							-PTR_ERR(ctx->mrec));
+					break;
+				}
+			}
+			ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec +
+					le16_to_cpu(ctx->mrec->attrs_offset));
+		}
+		/*
+		 * ctx->vfs_ino, ctx->mrec, and ctx->attr now point to the
+		 * mft record containing the attribute represented by the
+		 * current al_entry.
+		 */
+		/*
+		 * We could call into find_attr() to find the right attribute
+		 * in this mft record but this would be less efficient and not
+		 * quite accurate as find_attr() ignores the attribute instance
+		 * numbers for example which become important when one plays
+		 * with attribute lists. Also, because a proper match has been
+		 * found in the attribute list entry above, the comparison can
+		 * now be optimized. So it is worth re-implementing a
+		 * simplified find_attr() here.
+		 */
+		a = ctx->attr;
+		/*
+		 * Use a manual loop so we can still use break and continue
+		 * with the same meanings as above.
+		 */
+do_next_attr_loop:
+		if ((u8*)a < (u8*)ctx->mrec || (u8*)a > (u8*)ctx->mrec +
+				le32_to_cpu(ctx->mrec->bytes_allocated))
+			break;
+		if (a->type == AT_END)
+			continue;
+		if (!a->length)
+			break;
+		if (al_entry->instance != a->instance)
+			goto do_next_attr;
+		if (al_entry->type != a->type)
+			continue;
+		if (name) {
+			if (a->name_length != al_name_len)
+				continue;
+			if (!ntfs_are_names_equal((uchar_t*)((u8*)a +
+					le16_to_cpu(a->name_offset)),
+					a->name_length, al_name, al_name_len,
+					CASE_SENSITIVE, vol->upcase,
+					vol->upcase_len))
+				continue;
+		}
+		ctx->attr = a;
+		/*
+		 * If no @val specified or @val specified and it matches, we
+		 * have found it!
+		 */
+		if (!val || (!a->non_resident && le32_to_cpu(a->_ARA(value_length))
+				== val_len && !memcmp((u8*)a +
+				le16_to_cpu(a->_ARA(value_offset)), val, val_len))) {
+			ntfs_debug("Done, found.");
+			return TRUE;
+		}
+do_next_attr:
+		/* Proceed to the next attribute in the current mft record. */
+		a = (ATTR_RECORD*)((u8*)a + le32_to_cpu(a->length));
+		goto do_next_attr_loop;
+	}
+	ntfs_error(base_ni->vol->sb, "Inode contains corrupt attribute list "
+			"attribute.\n");
+	if (ni != base_ni) {
+		unmap_extent_mft_record(ni);
+		ctx->ntfs_ino = base_ni;
+		ctx->mrec = ctx->base_mrec;
+		ctx->attr = ctx->base_attr;
+	}
+	/*
+	 * FIXME: We absolutely have to return ERROR status instead of just
+	 * false or we will blow up or even worse cause corruption when we add
+	 * write support and we reach this code path!
+	 */
+	printk(KERN_CRIT "NTFS: FIXME: Hit unfinished error code path!!!\n");
+	return FALSE;
+not_found:
+	/*
+	 * Seek to the end of the base mft record, i.e. when we return false,
+	 * ctx->mrec and ctx->attr indicate where the attribute should be
+	 * inserted into the attribute record.
+	 * And of course ctx->al_entry points to the end of the attribute
+	 * list inside NTFS_I(ctx->base_vfs_ino)->attr_list.
+	 *
+	 * FIXME: Do we really want to do this here? Think about it... (AIA)
+	 */
+	reinit_attr_search_ctx(ctx);
+	find_attr(type, name, name_len, ic, val, val_len, ctx);
+	ntfs_debug("Done, not found.");
+	return FALSE;
+}
+
+/**
+ * lookup_attr - find an attribute in an ntfs inode
+ * @type:	attribute type to find
+ * @name:	attribute name to find (optional, i.e. NULL means don't care)
+ * @name_len:	attribute name length (only needed if @name present)
+ * @ic:		IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present)
+ * @lowest_vcn:	lowest vcn to find (optional, non-resident attributes only)
+ * @val:	attribute value to find (optional, resident attributes only)
+ * @val_len:	attribute value length
+ * @ctx:	search context with mft record and attribute to search from
+ *
+ * Find an attribute in an ntfs inode. On first search @ctx->ntfs_ino must
+ * be the base mft record and @ctx must have been obtained from a call to
+ * get_attr_search_ctx().
+ *
+ * This function transparently handles attribute lists and @ctx is used to
+ * continue searches where they were left off at.
+ *
+ * After finishing with the attribute/mft record you need to call
+ * release_attr_search_ctx() to cleanup the search context (unmapping any
+ * mapped inodes, etc).
+ *
+ * Return TRUE if the search was successful and FALSE if not. When TRUE,
+ * @ctx->attr is the found attribute and it is in mft record @ctx->mrec. When
+ * FALSE, @ctx->attr is the attribute which collates just after the attribute
+ * being searched for, i.e. if one wants to add the attribute to the mft
+ * record this is the correct place to insert it into.
+ */
+BOOL lookup_attr(const ATTR_TYPES type, const uchar_t *name, const u32 name_len,
+		const IGNORE_CASE_BOOL ic, const VCN lowest_vcn, const u8 *val,
+		const u32 val_len, attr_search_context *ctx)
+{
+	ntfs_inode *base_ni;
+
+	ntfs_debug("Entering.");
+	if (ctx->base_ntfs_ino)
+		base_ni = ctx->base_ntfs_ino;
+	else
+		base_ni = ctx->ntfs_ino;
+	/* Sanity check, just for debugging really. */
+	BUG_ON(!base_ni);
+	if (!NInoAttrList(base_ni))
+		return find_attr(type, name, name_len, ic, val, val_len, ctx);
+	return find_external_attr(type, name, name_len, ic, lowest_vcn, val,
+			val_len, ctx);
+}
+
+/**
+ * init_attr_search_ctx - initialize an attribute search context
+ * @ctx:	attribute search context to initialize
+ * @ni:		ntfs inode with which to initialize the search context
+ * @mrec:	mft record with which to initialize the search context
+ *
+ * Initialize the attribute search context @ctx with @ni and @mrec.
+ */
+static inline void init_attr_search_ctx(attr_search_context *ctx,
+		ntfs_inode *ni, MFT_RECORD *mrec)
+{
+	ctx->mrec = mrec;
+	/* Sanity checks are performed elsewhere. */
+	ctx->attr = (ATTR_RECORD*)((u8*)mrec + le16_to_cpu(mrec->attrs_offset));
+	ctx->is_first = TRUE;
+	ctx->ntfs_ino = ni;
+	ctx->al_entry = NULL;
+	ctx->base_ntfs_ino = NULL;
+	ctx->base_mrec = NULL;
+	ctx->base_attr = NULL;
+}
+
+/**
+ * reinit_attr_search_ctx - reinitialize an attribute search context
+ * @ctx:	attribute search context to reinitialize
+ *
+ * Reinitialize the attribute search context @ctx, unmapping an associated
+ * extent mft record if present, and initialize the search context again.
+ *
+ * This is used when a search for a new attribute is being started to reset
+ * the search context to the beginning.
+ */
+void reinit_attr_search_ctx(attr_search_context *ctx)
+{
+	if (likely(!ctx->base_ntfs_ino)) {
+		/* No attribute list. */
+		ctx->is_first = TRUE;
+		/* Sanity checks are performed elsewhere. */
+		ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec +
+				le16_to_cpu(ctx->mrec->attrs_offset));
+		return;
+	} /* Attribute list. */
+	if (ctx->ntfs_ino != ctx->base_ntfs_ino)
+		unmap_mft_record(READ, ctx->ntfs_ino);
+	init_attr_search_ctx(ctx, ctx->base_ntfs_ino, ctx->base_mrec);
+	return;
+}
+
+/**
+ * get_attr_search_ctx - allocate and initialize a new attribute search context
+ * @ni:		ntfs inode with which to initialize the search context
+ * @mrec:	mft record with which to initialize the search context
+ *
+ * Allocate a new attribute search context, initialize it with @ni and @mrec,
+ * and return it. Return NULL if allocation failed.
+ */
+attr_search_context *get_attr_search_ctx(ntfs_inode *ni, MFT_RECORD *mrec)
+{
+	attr_search_context *ctx;
+
+	ctx = kmem_cache_alloc(ntfs_attr_ctx_cache, SLAB_NOFS);
+	if (ctx)
+		init_attr_search_ctx(ctx, ni, mrec);
+	return ctx;
+}
+
+/**
+ * put_attr_search_ctx - release an attribute search context
+ * @ctx:	attribute search context to free
+ *
+ * Release the attribute search context @ctx, unmapping an associated extent
+ * mft record if present.
+ */
+void put_attr_search_ctx(attr_search_context *ctx)
+{
+	if (ctx->base_ntfs_ino && ctx->ntfs_ino != ctx->base_ntfs_ino)
+		unmap_mft_record(READ, ctx->ntfs_ino);
+	kmem_cache_free(ntfs_attr_ctx_cache, ctx);
+	return;
+}
+
diff -Nru a/fs/ntfs/attrib.h b/fs/ntfs/attrib.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/fs/ntfs/attrib.h	Sun Apr 28 20:10:39 2002
@@ -0,0 +1,106 @@
+/*
+ * attrib.h - Defines for attribute handling in NTFS Linux kernel driver.
+ *	      Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2001,2002 Anton Altaparmakov.
+ * Copyright (C) 2002 Richard Russon.
+ *
+ * This program/include file 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/include file 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 (in the main directory of the Linux-NTFS 
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _LINUX_NTFS_ATTRIB_H
+#define _LINUX_NTFS_ATTRIB_H
+
+#include <linux/fs.h>
+
+#include "endian.h"
+#include "types.h"
+#include "layout.h"
+
+static inline void init_run_list(run_list *rl)
+{
+	rl->rl = NULL;
+	init_rwsem(&rl->lock);
+}
+
+typedef enum {
+	LCN_HOLE		= -1,	/* Keep this as highest value or die! */
+	LCN_RL_NOT_MAPPED	= -2,
+	LCN_ENOENT		= -3,
+	LCN_EINVAL		= -4,
+} LCN_SPECIAL_VALUES;
+
+/**
+ * attr_search_context - used in attribute search functions
+ * @mrec:	buffer containing mft record to search
+ * @attr:	attribute record in @mrec where to begin/continue search
+ * @is_first:	if true lookup_attr() begins search with @attr, else after @attr
+ *
+ * Structure must be initialized to zero before the first call to one of the
+ * attribute search functions. Initialize @mrec to point to the mft record to
+ * search, and @attr to point to the first attribute within @mrec (not necessary
+ * if calling the _first() functions), and set @is_first to TRUE (not necessary
+ * if calling the _first() functions).
+ *
+ * If @is_first is TRUE, the search begins with @attr. If @is_first is FALSE,
+ * the search begins after @attr. This is so that, after the first call to one
+ * of the search attribute functions, we can call the function again, without
+ * any modification of the search context, to automagically get the next
+ * matching attribute.
+ */
+typedef struct {
+	MFT_RECORD *mrec;
+	ATTR_RECORD *attr;
+	BOOL is_first;
+	ntfs_inode *ntfs_ino;
+	ATTR_LIST_ENTRY *al_entry;
+	ntfs_inode *base_ntfs_ino;
+	MFT_RECORD *base_mrec;
+	ATTR_RECORD *base_attr;
+} attr_search_context;
+
+extern run_list_element *decompress_mapping_pairs(const ntfs_volume *vol,
+		const ATTR_RECORD *attr, run_list_element *old_rl);
+
+extern int map_run_list(ntfs_inode *ni, VCN vcn);
+
+extern LCN vcn_to_lcn(const run_list_element *rl, const VCN vcn);
+
+extern BOOL find_attr(const ATTR_TYPES type, const uchar_t *name,
+		const u32 name_len, const IGNORE_CASE_BOOL ic, const u8 *val,
+		const u32 val_len, attr_search_context *ctx);
+
+BOOL lookup_attr(const ATTR_TYPES type, const uchar_t *name, const u32 name_len,
+		const IGNORE_CASE_BOOL ic, const VCN lowest_vcn, const u8 *val,
+		const u32 val_len, attr_search_context *ctx);
+
+extern int load_attribute_list(ntfs_volume *vol, run_list *rl, u8 *al,
+		const s64 size, const s64 initialized_size);
+
+static inline s64 attribute_value_length(const ATTR_RECORD *a)
+{
+	if (!a->non_resident)
+		return (s64)le32_to_cpu(a->_ARA(value_length));
+	return sle64_to_cpu(a->_ANR(data_size));
+}
+
+extern void reinit_attr_search_ctx(attr_search_context *ctx);
+extern attr_search_context *get_attr_search_ctx(ntfs_inode *ni,
+		MFT_RECORD *mrec);
+extern void put_attr_search_ctx(attr_search_context *ctx);
+
+#endif /* _LINUX_NTFS_ATTRIB_H */
+
diff -Nru a/fs/ntfs/compress.c b/fs/ntfs/compress.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/fs/ntfs/compress.c	Sun Apr 28 20:10:39 2002
@@ -0,0 +1,864 @@
+/**
+ * compress.c - NTFS kernel compressed attributes handling.
+ *		Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2001,2002 Anton Altaparmakov.
+ * Copyright (C) 2002 Richard Russon.
+ *
+ * This program/include file 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/include file 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 (in the main directory of the Linux-NTFS 
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/locks.h>
+#include <linux/fs.h>
+
+#include "ntfs.h"
+
+/**
+ * ntfs_compression_constants - enum of constants used in the compression code
+ */
+typedef enum {
+	/* Token types and access mask. */
+	NTFS_SYMBOL_TOKEN	=	0,
+	NTFS_PHRASE_TOKEN	=	1,
+	NTFS_TOKEN_MASK		=	1,
+
+	/* Compression sub-block constants. */
+	NTFS_SB_SIZE_MASK	=	0x0fff,
+	NTFS_SB_SIZE		=	0x1000,
+	NTFS_SB_IS_COMPRESSED	=	0x8000,
+
+	/*
+	 * The maximum compression block size is by definition 16 * the cluster
+	 * size, with the maximum supported cluster size being 4kiB. Thus the
+	 * maximum compression buffer size is 64kiB, so we use this when
+	 * initializing the per-CPU buffers.
+	 */
+	NTFS_MAX_CB_SIZE	= 64 * 1024,
+} ntfs_compression_constants;
+
+/**
+ * ntfs_compression_buffers - per-CPU buffers for the decompression engine.
+ */
+static u8 **ntfs_compression_buffers = NULL;
+
+/**
+ * allocate_compression_buffers - allocate the per-CPU decompression buffers
+ *
+ * Allocate the per-CPU buffers for the decompression engine.
+ *
+ * Caller has to hold the ntfs_lock semaphore.
+ *
+ * Return 0 on success or -ENOMEM if the allocations failed.
+ */
+int allocate_compression_buffers(void)
+{
+	int i, j;
+
+	BUG_ON(ntfs_compression_buffers);
+
+	ntfs_compression_buffers =  (u8**)kmalloc(smp_num_cpus * sizeof(u8*),
+			GFP_KERNEL);
+	if (!ntfs_compression_buffers)
+		return -ENOMEM;
+	for (i = 0; i < smp_num_cpus; i++) {
+		ntfs_compression_buffers[i] = (u8*)vmalloc(NTFS_MAX_CB_SIZE);
+		if (!ntfs_compression_buffers[i])
+			break;
+	}
+	if (i == smp_num_cpus)
+		return 0;
+	/* Allocation failed, cleanup and return error. */
+	for (j = 0; j < i; j++)
+		vfree(ntfs_compression_buffers[j]);
+	kfree(ntfs_compression_buffers);
+	return -ENOMEM;
+}
+
+/**
+ * free_compression_buffers - free the per-CPU decompression buffers
+ *
+ * Free the per-CPU buffers used by the decompression engine.
+ *
+ * Caller has to hold the ntfs_lock semaphore.
+ */
+void free_compression_buffers(void)
+{
+	int i;
+
+	BUG_ON(!ntfs_compression_buffers);
+
+	for (i = 0; i < smp_num_cpus; i++)
+		vfree(ntfs_compression_buffers[i]);
+	kfree(ntfs_compression_buffers);
+	ntfs_compression_buffers = NULL;
+}
+
+/**
+ * ntfs_decompress - decompress a compression block into an array of pages
+ * @dest_pages:		destination array of pages
+ * @dest_index:		current index into @dest_pages (IN/OUT)
+ * @dest_ofs:		current offset within @dest_pages[@dest_index] (IN/OUT)
+ * @dest_max_index:	maximum index into @dest_pages (IN)
+ * @dest_max_ofs:	maximum offset within @dest_pages[@dest_max_index] (IN)
+ * @xpage:		the target page (-1 if none) (IN)
+ * @xpage_done:		set to 1 if xpage was completed successfully (IN/OUT)
+ * @cb_start:		compression block to decompress (IN)
+ * @cb_size:		size of compression block @cb_start in bytes (IN)
+ *
+ * The caller must have disabled preemption. ntfs_decompress() reenables it when
+ * the critical section is finished.
+ *
+ * This decompresses the compression block @cb_start into the array of
+ * destination pages @dest_pages starting at index @dest_index into @dest_pages
+ * and at offset @dest_pos into the page @dest_pages[@dest_index].
+ *
+ * When the page @dest_pages[@xpage] is completed, @xpage_done is set to 1.
+ * If xpage is -1 or @xpage has not been completed, @xpage_done is not modified.
+ *
+ * @cb_start is a pointer to the compression block which needs decompressing
+ * and @cb_size is the size of @cb_start in bytes (8-64kiB).
+ *
+ * Return 0 if success or -EOVERFLOW on error in the compressed stream.
+ * @xpage_done indicates whether the target page (@dest_pages[@xpage]) was
+ * completed during the decompression of the compression block (@cb_start).
+ *
+ * Warning: This function *REQUIRES* PAGE_CACHE_SIZE >= 4096 or it will blow up
+ * unpredicatbly! You have been warned!
+ *
+ * Note to hackers: This function may not sleep until it has finished accessing
+ * the compression block @cb_start as it is a per-CPU buffer.
+ */
+static int ntfs_decompress(struct page *dest_pages[], int *dest_index,
+		int *dest_ofs, const int dest_max_index, const int dest_max_ofs,
+		const int xpage, char *xpage_done, u8 *const cb_start,
+		const u32 cb_size)
+{
+	/*
+	 * Pointers into the compressed data, i.e. the compression block (cb),
+	 * and the therein contained sub-blocks (sb).
+	 */
+	u8 *cb_end = cb_start + cb_size; /* End of cb. */
+	u8 *cb = cb_start;	/* Current position in cb. */
+	u8 *cb_sb_start = cb;	/* Beginning of the current sb in the cb. */
+	u8 *cb_sb_end;		/* End of current sb / beginning of next sb. */
+
+	/* Variables for uncompressed data / destination. */
+	struct page *dp;	/* Current destination page being worked on. */
+	u8 *dp_addr;		/* Current pointer into dp. */
+	u8 *dp_sb_start;	/* Start of current sub-block in dp. */
+	u8 *dp_sb_end;		/* End of current sb in dp (dp_sb_start +
+				   NTFS_SB_SIZE). */
+	u16 do_sb_start;	/* @dest_ofs when starting this sub-block. */
+	u16 do_sb_end;		/* @dest_ofs of end of this sb (do_sb_start +
+				   NTFS_SB_SIZE). */
+
+	/* Variables for tag and token parsing. */
+	u8 tag;			/* Current tag. */
+	int token;		/* Loop counter for the eight tokens in tag. */
+
+	/* Need this because we can't sleep, so need two stages. */
+	int completed_pages[dest_max_index - *dest_index + 1];
+	int nr_completed_pages = 0;
+
+	/* Default error code. */
+	int err = -EOVERFLOW;
+	
+	ntfs_debug("Entering, cb_size = 0x%x.", cb_size);
+do_next_sb:
+	ntfs_debug("Beginning sub-block at offset = 0x%x in the cb.",
+			cb - cb_start);
+
+	/* Have we reached the end of the compression block? */
+	if (cb == cb_end || !le16_to_cpup(cb)) {
+		int i;
+
+		ntfs_debug("Completed. Returning success (0).");
+		err = 0;
+return_error:
+		/* We can sleep from now on, so we reenable preemption. */
+		preempt_enable();
+		/* Second stage: finalize completed pages. */
+		for (i = 0; i < nr_completed_pages; i++) {
+			int di = completed_pages[i];
+
+			dp = dest_pages[di];
+			flush_dcache_page(dp);
+			kunmap(dp);
+			SetPageUptodate(dp);
+			UnlockPage(dp);
+			if (di == xpage)
+				*xpage_done = 1;
+			else
+				page_cache_release(dp);
+			dest_pages[di] = NULL;
+		}
+		return err;
+	}
+
+	/* Setup offsets for the current sub-block destination. */
+	do_sb_start = *dest_ofs;
+	do_sb_end = do_sb_start + NTFS_SB_SIZE;
+	
+	/* Check that we are still within allowed boundaries. */
+	if (*dest_index == dest_max_index && do_sb_end > dest_max_ofs)
+		goto return_overflow;
+
+	/* Does the minimum size of a compressed sb overflow valid range? */
+	if (cb + 6 > cb_end)
+		goto return_overflow;
+
+	/* Setup the current sub-block source pointers and validate range. */
+	cb_sb_start = cb;
+	cb_sb_end = cb_sb_start + (le16_to_cpup(cb) & NTFS_SB_SIZE_MASK) + 3;
+	if (cb_sb_end > cb_end)
+		goto return_overflow;
+
+	/* Get the current destination page. */
+	dp = dest_pages[*dest_index];
+	if (!dp) {
+		/* No page present. Skip decompression of this sub-block. */
+		cb = cb_sb_end;
+
+		/* Advance destination position to next sub-block. */
+		*dest_ofs = (*dest_ofs + NTFS_SB_SIZE) & ~PAGE_CACHE_MASK;
+		if (!*dest_ofs && (++*dest_index > dest_max_index))
+			goto return_overflow;
+		goto do_next_sb;
+	}
+
+	/* We have a valid destination page. Setup the destination pointers. */
+	dp_addr = (u8*)page_address(dp) + do_sb_start;
+
+	/* Now, we are ready to process the current sub-block (sb). */
+	if (!(le16_to_cpup(cb) & NTFS_SB_IS_COMPRESSED)) {
+		ntfs_debug("Found uncompressed sub-block.");
+		/* This sb is not compressed, just copy it into destination. */
+
+		/* Advance source position to first data byte. */
+		cb += 2;
+
+		/* An uncompressed sb must be full size. */
+		if (cb_sb_end - cb != NTFS_SB_SIZE)
+			goto return_overflow;
+
+		/* Copy the block and advance the source position. */
+		memcpy(dp_addr, cb, NTFS_SB_SIZE);
+		cb += NTFS_SB_SIZE;
+
+		/* Advance destination position to next sub-block. */
+		*dest_ofs += NTFS_SB_SIZE;
+		if (!(*dest_ofs &= ~PAGE_CACHE_MASK)) {
+finalize_page:
+			/*
+			 * First stage: add current page index to array of
+			 * completed pages.
+			 */
+			completed_pages[nr_completed_pages++] = *dest_index;
+			if (++*dest_index > dest_max_index)
+				goto return_overflow;
+		}
+		goto do_next_sb;
+	}
+	ntfs_debug("Found compressed sub-block.");
+	/* This sb is compressed, decompress it into destination. */
+
+	/* Setup destination pointers. */
+	dp_sb_start = dp_addr;
+	dp_sb_end = dp_sb_start + NTFS_SB_SIZE;
+
+	/* Forward to the first tag in the sub-block. */
+	cb += 2;
+do_next_tag:
+	if (cb == cb_sb_end) {
+		/* Check if the decompressed sub-block was not full-length. */
+		if (dp_addr < dp_sb_end) {
+			int nr_bytes = do_sb_end - *dest_ofs;
+
+			ntfs_debug("Filling incomplete sub-block with "
+					"zeroes.");
+			/* Zero remainder and update destination position. */
+			memset(dp_addr, 0, nr_bytes);
+			*dest_ofs += nr_bytes;
+		}
+		/* We have finished the current sub-block. */
+		if (!(*dest_ofs &= ~PAGE_CACHE_MASK))
+			goto finalize_page;
+		goto do_next_sb;
+	}
+
+	/* Check we are still in range. */
+	if (cb > cb_sb_end || dp_addr > dp_sb_end)
+		goto return_overflow;
+
+	/* Get the next tag and advance to first token. */
+	tag = *cb++;
+
+	/* Parse the eight tokens described by the tag. */
+	for (token = 0; token < 8; token++, tag >>= 1) {
+		u16 lg, pt, length, max_non_overlap;
+		register u16 i;
+		u8 *dp_back_addr;
+
+		/* Check if we are done / still in range. */
+		if (cb >= cb_sb_end || dp_addr > dp_sb_end)
+			break;
+
+		/* Determine token type and parse appropriately.*/
+		if ((tag & NTFS_TOKEN_MASK) == NTFS_SYMBOL_TOKEN) {
+			/*
+			 * We have a symbol token, copy the symbol across, and
+			 * advance the source and destination positions.
+			 */
+			*dp_addr++ = *cb++;
+			++*dest_ofs;
+
+			/* Continue with the next token. */
+			continue;
+		}
+
+		/* 
+		 * We have a phrase token. Make sure it is not the first tag in
+		 * the sb as this is illegal and would confuse the code below.
+		 */
+		if (dp_addr == dp_sb_start)
+			goto return_overflow;
+
+		/*
+		 * Determine the number of bytes to go back (p) and the number
+		 * of bytes to copy (l). We use an optimized algorithm in which
+		 * we first calculate log2(current destination position in sb),
+		 * which allows determination of l and p in O(1) rather than
+		 * O(n). We just need an arch-optimized log2() function now.
+		 */
+		lg = 0;
+		for (i = *dest_ofs - do_sb_start - 1; i >= 0x10; i >>= 1)
+			lg++;
+
+		/* Get the phrase token into i. */
+		pt = le16_to_cpup(cb);
+
+		/*
+		 * Calculate starting position of the byte sequence in
+		 * the destination using the fact that p = (pt >> (12 - lg)) + 1
+		 * and make sure we don't go too far back.
+		 */
+		dp_back_addr = dp_addr - (pt >> (12 - lg)) - 1;
+		if (dp_back_addr < dp_sb_start)
+			goto return_overflow;
+
+		/* Now calculate the length of the byte sequence. */
+		length = (pt & (0xfff >> lg)) + 3;
+
+		/* Advance destination position and verify it is in range. */
+		*dest_ofs += length;
+		if (*dest_ofs > do_sb_end)
+			goto return_overflow;
+
+		/* The number of non-overlapping bytes. */
+		max_non_overlap = dp_addr - dp_back_addr;
+
+		if (length <= max_non_overlap) {
+			/* The byte sequence doesn't overlap, just copy it. */
+			memcpy(dp_addr, dp_back_addr, length);
+
+			/* Advance destination pointer. */
+			dp_addr += length;
+		} else {
+			/*
+			 * The byte sequence does overlap, copy non-overlapping
+			 * part and then do a slow byte by byte copy for the
+			 * overlapping part. Also, advance the destination
+			 * pointer.
+			 */
+			memcpy(dp_addr, dp_back_addr, max_non_overlap);
+			dp_addr += max_non_overlap;
+			dp_back_addr += max_non_overlap;
+			length -= max_non_overlap;
+			while (length--)
+				*dp_addr++ = *dp_back_addr++;
+		}
+
+		/* Advance source position and continue with the next token. */
+		cb += 2;
+	}
+
+	/* No tokens left in the current tag. Continue with the next tag. */
+	goto do_next_tag;
+
+return_overflow:
+	ntfs_error(NULL, "Failed. Returning -EOVERFLOW.\n");
+	goto return_error;
+}
+
+/**
+ * ntfs_file_read_compressed_block - read a compressed block into the page cache
+ * @page:	locked page in the compression block(s) we need to read
+ *
+ * When we are called the page has already been verified to be locked and the
+ * attribute is known to be non-resident, not encrypted, but compressed.
+ *
+ * 1. Determine which compression block(s) @page is in.
+ * 2. Get hold of all pages corresponding to this/these compression block(s).
+ * 3. Read the (first) compression block.
+ * 4. Decompress it into the corresponding pages.
+ * 5. Throw the compressed data away and proceed to 3. for the next compression
+ *    block or return success if no more compression blocks left.
+ *
+ * Warning: We have to be careful what we do about existing pages. They might
+ * have been written to so that we would lose data if we were to just overwrite
+ * them with the out-of-date uncompressed data.
+ *
+ * FIXME: For PAGE_CACHE_SIZE > cb_size we are not doing the Right Thing(TM) at
+ * the end of the file I think. We need to detect this case and zero the out
+ * of bounds remainder of the page in question and mark it as handled. At the
+ * moment we would just return -EIO on such a page. This bug will only become
+ * apparent if pages are above 8kiB and the NTFS volume only uses 512 byte
+ * clusters so is probably not going to be seen by anyone. Still this should
+ * be fixed. (AIA)
+ *
+ * FIXME: Again for PAGE_CACHE_SIZE > cb_size we are screwing up both in
+ * handling sparse and compressed cbs. (AIA)
+ *
+ * FIXME: At the moment we don't do any zeroing out in the case that
+ * initialized_size is less than data_size. This should be safe because of the
+ * nature of the compression algorithm used. Just in case we check and output
+ * an error message in read inode if the two sizes are not equal for a
+ * compressed file.
+ */
+int ntfs_file_read_compressed_block(struct page *page)
+{
+	struct address_space *mapping = page->mapping;
+	ntfs_inode *ni = NTFS_I(mapping->host);
+	ntfs_volume *vol = ni->vol;
+	struct super_block *sb = vol->sb;
+	unsigned long block_size = sb->s_blocksize;
+	unsigned char block_size_bits = sb->s_blocksize_bits;
+	u8 *cb, *cb_pos, *cb_end;
+	struct buffer_head **bhs;
+	unsigned long offset, index = page->index;
+	u32 cb_size = ni->_ICF(compression_block_size);
+	u64 cb_size_mask = cb_size - 1UL;
+	VCN vcn;
+	LCN lcn;
+	/* The first wanted vcn (minimum alignment is PAGE_CACHE_SIZE). */
+	VCN start_vcn = (((s64)index << PAGE_CACHE_SHIFT) & ~cb_size_mask) >>
+			vol->cluster_size_bits;
+	/*
+	 * The first vcn after the last wanted vcn (minumum alignment is again
+	 * PAGE_CACHE_SIZE.
+	 */
+	VCN end_vcn = ((((s64)(index + 1UL) << PAGE_CACHE_SHIFT) + cb_size - 1)
+			& ~cb_size_mask) >> vol->cluster_size_bits;
+	/* Number of compression blocks (cbs) in the wanted vcn range. */
+	unsigned int nr_cbs = (end_vcn - start_vcn) << vol->cluster_size_bits
+			>> ni->_ICF(compression_block_size_bits);
+	/*
+	 * Number of pages required to store the uncompressed data from all
+	 * compression blocks (cbs) overlapping @page. Due to alignment
+	 * guarantees of start_vcn and end_vcn, no need to round up here.
+	 */
+	unsigned int nr_pages = (end_vcn - start_vcn) <<
+			vol->cluster_size_bits >> PAGE_CACHE_SHIFT;
+	unsigned int xpage, max_page, cur_page, cur_ofs, i;
+	unsigned int cb_clusters, cb_max_ofs;
+	int block, max_block, cb_max_page, bhs_size, nr_bhs, err = 0;
+	struct page **pages;
+	unsigned char xpage_done = 0;
+
+	ntfs_debug("Entering, page->index = 0x%lx, cb_size = 0x%x, nr_pages = "
+			"%i.", index, cb_size, nr_pages);
+
+	pages = kmalloc(nr_pages * sizeof(struct page *), GFP_NOFS);
+
+	/* Allocate memory to store the buffer heads we need. */
+	bhs_size = cb_size / block_size * sizeof(struct buffer_head *);
+	bhs = kmalloc(bhs_size, GFP_NOFS);
+
+	if (unlikely(!pages || !bhs)) {
+		kfree(bhs);
+		kfree(pages);
+		SetPageError(page);
+		UnlockPage(page);
+		ntfs_error(vol->sb, "Failed to allocate internal buffers.");
+		return -ENOMEM;
+	}
+
+	/*
+	 * We have already been given one page, this is the one we must do.
+	 * Once again, the alignment guarantees keep it simple.
+	 */
+	offset = start_vcn << vol->cluster_size_bits >> PAGE_CACHE_SHIFT;
+	xpage = index - offset;
+	pages[xpage] = page;
+	/*
+	 * The remaining pages need to be allocated and inserted into the page
+	 * cache, alignment guarantees keep all the below much simpler. (-8
+	 */
+	max_page = ((VFS_I(ni)->i_size + PAGE_CACHE_SIZE - 1) >>
+			PAGE_CACHE_SHIFT) - offset;
+	if (nr_pages < max_page)
+		max_page = nr_pages;
+	for (i = 0; i < max_page; i++, offset++) {
+		if (i != xpage)
+			pages[i] = grab_cache_page_nowait(mapping, offset);
+		page = pages[i];
+		if (page) {
+			/*
+			 * We only (re)read the page if it isn't already read
+			 * in and/or dirty or we would be losing data or at
+			 * least wasting our time.
+			 */
+			if (!PageDirty(page) && (!Page_Uptodate(page) ||
+					PageError(page))) {
+				ClearPageError(page);
+				kmap(page);
+				continue;
+			}
+			UnlockPage(page);
+			page_cache_release(page);
+			pages[i] = NULL;
+		}
+	}
+
+	/*
+	 * We have the run list, and all the destination pages we need to fill.
+	 * Now read the first compression block.
+	 */
+	cur_page = 0;
+	cur_ofs = 0;
+	cb_clusters = ni->_ICF(compression_block_clusters);
+do_next_cb:
+	nr_cbs--;
+	nr_bhs = 0;
+
+	/* Read all cb buffer heads one cluster at a time. */
+	for (vcn = start_vcn, start_vcn += cb_clusters; vcn < start_vcn;
+			vcn++) {
+		BOOL is_retry = FALSE;
+retry_remap:
+		/* Find lcn of vcn and convert it into blocks. */
+		down_read(&ni->run_list.lock);
+		lcn = vcn_to_lcn(ni->run_list.rl, vcn);
+		up_read(&ni->run_list.lock);
+		ntfs_debug("Reading vcn = 0x%Lx, lcn = 0x%Lx.",
+				(long long)vcn, (long long)lcn);
+		if (lcn < 0) {
+			/*
+			 * When we reach the first sparse cluster we have
+			 * finished with the cb.
+			 */
+			if (lcn == LCN_HOLE)
+				break;
+			if (is_retry || lcn != LCN_RL_NOT_MAPPED)
+				goto rl_err;
+			is_retry = TRUE;
+			/* Map run list of current extent and retry. */
+			if (!map_run_list(ni, vcn))
+				goto retry_remap;
+			goto map_rl_err;
+		}
+		block = lcn << vol->cluster_size_bits >> block_size_bits;
+		/* Read the lcn from device in chunks of block_size bytes. */
+		max_block = block + (vol->cluster_size >> block_size_bits);
+		do {
+			ntfs_debug("block = 0x%x.", block);
+			if (unlikely(!(bhs[nr_bhs] = sb_getblk(sb, block))))
+				goto getblk_err;
+			nr_bhs++;
+		} while (++block < max_block);
+	}
+
+	/* Setup and initiate io on all buffer heads. */
+	for (i = 0; i < nr_bhs; i++) {
+		struct buffer_head *tbh = bhs[i];
+
+		if (buffer_uptodate(tbh))
+			continue;
+
+		lock_buffer(tbh);
+		get_bh(tbh);
+		tbh->b_end_io = end_buffer_io_sync;
+		submit_bh(READ, tbh);
+	}
+
+	/* Wait for io completion on all buffer heads. */
+	for (i = 0; i < nr_bhs; i++) {
+		struct buffer_head *tbh = bhs[i];
+
+		if (buffer_uptodate(tbh))
+			continue;
+
+		wait_on_buffer(tbh);
+		if (!buffer_uptodate(tbh))
+			goto read_err;
+	}
+
+	/*
+	 * Get the compression buffer corresponding to the current CPU. We must
+	 * not sleep any more until we are finished with the compression buffer.
+	 * If on a preemptible kernel, now disable preemption.
+	 */
+	preempt_disable();
+	cb = ntfs_compression_buffers[smp_processor_id()];
+
+	BUG_ON(!cb);
+
+	cb_pos = cb;
+	cb_end = cb + cb_size;
+
+	/* Copy the buffer heads into the contiguous buffer. */
+	for (i = 0; i < nr_bhs; i++) {
+		memcpy(cb_pos, bhs[i]->b_data, block_size);
+		cb_pos += block_size;
+	}
+
+	/* Just a precaution. */
+	if (cb_pos + 2 <= cb + cb_size)
+		*(u16*)cb_pos = 0;
+
+	/* Reset cb_pos back to the beginning. */
+	cb_pos = cb;
+
+	/* We now have both source (if present) and destination. */
+	ntfs_debug("Successfully read the compression block.");
+
+	/* The last page and maximum offset within it for the current cb. */
+	cb_max_page = (cur_page << PAGE_CACHE_SHIFT) + cur_ofs + cb_size;
+	cb_max_ofs = cb_max_page & ~PAGE_CACHE_MASK;
+	cb_max_page >>= PAGE_CACHE_SHIFT;
+
+	/* Catch end of file inside a compression block. */
+	if (cb_max_page > max_page)
+		cb_max_page = max_page;
+
+	if (vcn == start_vcn - cb_clusters) {
+		/* Sparse cb, zero out page range overlapping the cb. */
+		ntfs_debug("Found sparse compression block.");
+		/* We can sleep from now on, so we reenable preemption. */
+		preempt_enable();
+		if (cb_max_ofs)
+			cb_max_page--;
+		for (; cur_page < cb_max_page; cur_page++) {
+			page = pages[cur_page];
+			if (page) {
+				/*
+				 * FIXME: Using clear_page() will become wrong
+				 * when we get PAGE_CACHE_SIZE != PAGE_SIZE but
+				 * for now there is no problem.
+				 */
+				if (likely(!cur_ofs))
+					clear_page(page_address(page));
+				else
+					memset(page_address(page) + cur_ofs, 0,
+							PAGE_CACHE_SIZE -
+							cur_ofs);
+				flush_dcache_page(page);
+				kunmap(page);
+				SetPageUptodate(page);
+				UnlockPage(page);
+				if (cur_page == xpage)
+					xpage_done = 1;
+				else
+					page_cache_release(page);
+				pages[cur_page] = NULL;
+			}
+			cb_pos += PAGE_CACHE_SIZE - cur_ofs;
+			cur_ofs = 0;
+			if (cb_pos >= cb_end)
+				break;
+		}
+		/* If we have a partial final page, deal with it now. */
+		if (cb_max_ofs && cb_pos < cb_end) {
+			page = pages[cur_page];
+			if (page)
+				memset(page_address(page) + cur_ofs, 0,
+						cb_max_ofs - cur_ofs);
+			cb_pos += cb_max_ofs - cur_ofs;
+			cur_ofs = cb_max_ofs;
+		}
+	} else if (vcn == start_vcn) {
+		/* We can't sleep so we need two stages. */
+		unsigned int cur2_page = cur_page;
+		unsigned int cur_ofs2 = cur_ofs;
+		u8 *cb_pos2 = cb_pos;
+
+		ntfs_debug("Found uncompressed compression block.");
+		/* Uncompressed cb, copy it to the destination pages. */
+		/*
+		 * TODO: As a big optimization, we could detect this case
+		 * before we read all the pages and use block_read_full_page()
+		 * on all full pages instead (we still have to treat partial
+		 * pages especially but at least we are getting rid of the
+		 * synchronous io for the majority of pages.
+		 * Or if we choose not to do the read-ahead/-behind stuff, we
+		 * could just return block_read_full_page(pages[xpage]) as long
+		 * as PAGE_CACHE_SIZE <= cb_size.
+		 */
+		if (cb_max_ofs)
+			cb_max_page--;
+		/* First stage: copy data into destination pages. */
+		for (; cur_page < cb_max_page; cur_page++) {
+			page = pages[cur_page];
+			if (page)
+				memcpy(page_address(page) + cur_ofs, cb_pos,
+						PAGE_CACHE_SIZE - cur_ofs);
+			cb_pos += PAGE_CACHE_SIZE - cur_ofs;
+			cur_ofs = 0;
+			if (cb_pos >= cb_end)
+				break;
+		}
+		/* If we have a partial final page, deal with it now. */
+		if (cb_max_ofs && cb_pos < cb_end) {
+			page = pages[cur_page];
+			if (page)
+				memcpy(page_address(page) + cur_ofs, cb_pos,
+						cb_max_ofs - cur_ofs);
+			cb_pos += cb_max_ofs - cur_ofs;
+			cur_ofs = cb_max_ofs;
+		}
+		/* We can sleep from now on, so we reenable preemption. */
+		preempt_enable();
+		/* Second stage: finalize pages. */
+		for (; cur2_page < cb_max_page; cur2_page++) {
+			page = pages[cur2_page];
+			if (page) {
+				flush_dcache_page(page);
+				kunmap(page);
+				SetPageUptodate(page);
+				UnlockPage(page);
+				if (cur2_page == xpage)
+					xpage_done = 1;
+				else
+					page_cache_release(page);
+				pages[cur2_page] = NULL;
+			}
+			cb_pos2 += PAGE_CACHE_SIZE - cur_ofs2;
+			cur_ofs2 = 0;
+			if (cb_pos2 >= cb_end)
+				break;
+		}
+	} else {
+		/* Compressed cb, decompress it into the destination page(s). */
+		unsigned int prev_cur_page = cur_page;
+
+		ntfs_debug("Found compressed compression block.");
+		err = ntfs_decompress(pages, &cur_page, &cur_ofs,
+				cb_max_page, cb_max_ofs, xpage, &xpage_done,
+				cb_pos,	cb_size - (cb_pos - cb));
+		/*
+		 * We can sleep from now on, preemption already reenabled by
+		 * ntfs_decompess.
+		 */
+		if (err) {
+			ntfs_error(vol->sb, "ntfs_decompress() failed with "
+					"error code %i. Skipping this "
+					"compression block.\n", -err);
+			/* Release the unfinished pages. */
+			for (; prev_cur_page < cur_page; prev_cur_page++) {
+				page = pages[prev_cur_page];
+				if (page) {
+					if (prev_cur_page == xpage &&
+							!xpage_done)
+						SetPageError(page);
+					flush_dcache_page(page);
+					kunmap(page);
+					UnlockPage(page);
+					if (prev_cur_page != xpage)
+						page_cache_release(page);
+					pages[prev_cur_page] = NULL;
+				}
+			}
+		}
+	}
+
+	/* Release the buffer heads. */
+	for (i = 0; i < nr_bhs; i++)
+		brelse(bhs[i]);
+
+	/* Do we have more work to do? */
+	if (nr_cbs)
+		goto do_next_cb;
+
+	/* We no longer need the list of buffer heads. */
+	kfree(bhs);
+
+	/* Clean up if we have any pages left. Should never happen. */
+	for (cur_page = 0; cur_page < max_page; cur_page++) {
+		page = pages[cur_page];
+		if (page) {
+			ntfs_error(vol->sb, "Still have pages left! "
+					"Terminating them with extreme "
+					"prejudice.");
+			if (cur_page == xpage && !xpage_done)
+				SetPageError(page);
+			flush_dcache_page(page);
+			kunmap(page);
+			UnlockPage(page);
+			if (cur_page != xpage)
+				page_cache_release(page);
+			pages[cur_page] = NULL;
+		}
+	}
+
+	/* We no longer need the list of pages. */
+	kfree(pages);
+
+	/* If we have completed the requested page, we return success. */
+	if (likely(xpage_done))
+		return 0;
+
+	ntfs_debug("Failed. Returning error code %s.", err == -EOVERFLOW ?
+			"EOVERFLOW" : (!err ? "EIO" : "unkown error"));
+	return err < 0 ? err : -EIO;
+
+read_err:
+	ntfs_error(vol->sb, "IO error while reading compressed data.");
+	/* Release the buffer heads. */
+	for (i = 0; i < nr_bhs; i++)
+		brelse(bhs[i]);
+	goto err_out;
+
+map_rl_err:
+	ntfs_error(vol->sb, "map_run_list() failed. Cannot read compression "
+			"block.");
+	goto err_out;
+
+rl_err:
+	ntfs_error(vol->sb, "vcn_to_lcn() failed. Cannot read compression "
+			"block.");
+	goto err_out;
+
+getblk_err:
+	ntfs_error(vol->sb, "getblk() failed. Cannot read compression block.");
+
+err_out:
+	kfree(bhs);
+	for (i = cur_page; i < max_page; i++) {
+		page = pages[i];
+		if (page) {
+			if (i == xpage && !xpage_done)
+				SetPageError(page);
+			flush_dcache_page(page);
+			kunmap(page);
+			UnlockPage(page);
+			if (i != xpage)
+				page_cache_release(page);
+		}
+	}
+	kfree(pages);
+	return -EIO;
+}
+
diff -Nru a/fs/ntfs/debug.c b/fs/ntfs/debug.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/fs/ntfs/debug.c	Sun Apr 28 20:10:39 2002
@@ -0,0 +1,175 @@
+/*
+ * debug.c - NTFS kernel debug support. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2001,2002 Anton Altaparmakov.
+ *
+ * This program/include file 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/include file 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 (in the main directory of the Linux-NTFS 
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "debug.h"
+
+/*
+ * A static buffer to hold the error string being displayed and a spinlock
+ * to protect concurrent accesses to it.
+ */
+static char err_buf[1024];
+static spinlock_t err_buf_lock = SPIN_LOCK_UNLOCKED;
+
+/**
+ * __ntfs_warning - output a warning to the syslog
+ * @function:	name of function outputting the warning
+ * @sb:		super block of mounted ntfs filesystem
+ * @fmt:	warning string containing format specifications
+ * @...:	a variable number of arguments specified in @fmt
+ *
+ * Outputs a warning to the syslog for the mounted ntfs filesystem described
+ * by @sb.
+ *
+ * @fmt and the corresponding @... is printf style format string containing
+ * the warning string and the corresponding format arguments, respectively.
+ *
+ * @function is the name of the function from which __ntfs_warning is being
+ * called.
+ *
+ * Note, you should be using debug.h::ntfs_warning(@sb, @fmt, @...) instead
+ * as this provides the @function parameter automatically.
+ */
+void __ntfs_warning(const char *function, const struct super_block *sb,
+		const char *fmt, ...)
+{
+	va_list args;
+	int flen = 0;
+
+	if (function)
+		flen = strlen(function);
+	spin_lock(&err_buf_lock);
+	va_start(args, fmt);
+	vsnprintf(err_buf, sizeof(err_buf), fmt, args);
+	va_end(args);
+	if (sb)
+		printk(KERN_ERR "NTFS-fs warning (device %s): %s(): %s\n",
+				sb->s_id, flen ? function : "", err_buf);
+	else
+		printk(KERN_ERR "NTFS-fs warning: %s(): %s\n",
+				flen ? function : "", err_buf);
+	spin_unlock(&err_buf_lock);
+}
+
+/**
+ * __ntfs_error - output an error to the syslog
+ * @function:	name of function outputting the error
+ * @sb:		super block of mounted ntfs filesystem
+ * @fmt:	error string containing format specifications
+ * @...:	a variable number of arguments specified in @fmt
+ *
+ * Outputs an error to the syslog for the mounted ntfs filesystem described
+ * by @sb.
+ *
+ * @fmt and the corresponding @... is printf style format string containing
+ * the error string and the corresponding format arguments, respectively.
+ *
+ * @function is the name of the function from which __ntfs_error is being
+ * called.
+ *
+ * Note, you should be using debug.h::ntfs_error(@sb, @fmt, @...) instead
+ * as this provides the @function parameter automatically.
+ */
+void __ntfs_error(const char *function, const struct super_block *sb,
+		const char *fmt, ...)
+{
+	va_list args;
+	int flen = 0;
+
+	if (function)
+		flen = strlen(function);
+	spin_lock(&err_buf_lock);
+	va_start(args, fmt);
+	vsnprintf(err_buf, sizeof(err_buf), fmt, args);
+	va_end(args);
+	if (sb)
+		printk(KERN_ERR "NTFS-fs error (device %s): %s(): %s\n",
+				sb->s_id, flen ? function : "", err_buf);
+	else
+		printk(KERN_ERR "NTFS-fs error: %s(): %s\n",
+				flen ? function : "", err_buf);
+	spin_unlock(&err_buf_lock);
+}
+
+#ifdef DEBUG
+
+/* If 1, output debug messages, and if 0, don't. */
+int debug_msgs = 0;
+
+void __ntfs_debug (const char *file, int line, const char *function,
+		const char *fmt, ...)
+{
+	va_list args;
+	int flen = 0;
+
+	if (!debug_msgs)
+		return;
+	if (function)
+		flen = strlen(function);
+	spin_lock(&err_buf_lock);
+	va_start(args, fmt);
+	vsnprintf(err_buf, sizeof(err_buf), fmt, args);
+	va_end(args);
+	printk(KERN_DEBUG "NTFS-fs DEBUG (%s, %d): %s: %s\n",
+		file, line, flen ? function : "", err_buf);
+	spin_unlock(&err_buf_lock);
+}
+
+/* Dump a run list. Caller has to provide synchronisation for @rl. */
+void ntfs_debug_dump_runlist(const run_list_element *rl)
+{
+	int i;
+	const char *lcn_str[5] = { "LCN_HOLE         ", "LCN_RL_NOT_MAPPED",
+				   "LCN_ENOENT       ", "LCN_EINVAL       ",
+				   "LCN_unknown      " };
+
+	if (!debug_msgs)
+		return;
+	printk(KERN_DEBUG "NTFS-fs DEBUG: Dumping run list (values "
+			"in hex):\n");
+	if (!rl) {
+		printk(KERN_DEBUG "Run list not present.\n");
+		return;
+	}
+	printk(KERN_DEBUG "VCN              LCN               Run length\n");
+	for (i = 0; ; i++) {
+		LCN lcn = (rl + i)->lcn;
+
+		if (lcn < (LCN)0) {
+			int index = -lcn - 1;
+
+			if (index > -LCN_EINVAL - 1)
+				index = 4;
+			printk(KERN_DEBUG "%-16Lx %s %-16Lx%s\n",
+				(rl + i)->vcn, lcn_str[index],
+				(rl + i)->length, (rl + i)->length ?
+				"" : " (run list end)");
+		} else
+			printk(KERN_DEBUG "%-16Lx %-16Lx  %-16Lx%s\n",
+				(rl + i)->vcn, (rl + i)->lcn,
+				(rl + i)->length, (rl + i)->length ?
+				"" : " (run list end)");
+		if (!(rl + i)->length)
+			break;
+	}
+}
+
+#endif
+
diff -Nru a/fs/ntfs/debug.h b/fs/ntfs/debug.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/fs/ntfs/debug.h	Sun Apr 28 20:10:39 2002
@@ -0,0 +1,72 @@
+/*
+ * debug.h - NTFS kernel debug support. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2001,2002 Anton Altaparmakov.
+ *
+ * This program/include file 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/include file 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 (in the main directory of the Linux-NTFS 
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _LINUX_NTFS_DEBUG_H
+#define _LINUX_NTFS_DEBUG_H
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/spinlock.h>
+#include <linux/fs.h>
+
+#include "inode.h"
+#include "attrib.h"
+
+#ifdef DEBUG
+
+extern int debug_msgs;
+
+#if 0 /* Fool kernel-doc since it doesn't do macros yet */
+/**
+ * ntfs_debug - write a debug level message to syslog
+ * @f:		a printf format string containing the message
+ * @...:	the variables to substitute into @f
+ *
+ * ntfs_debug() writes a DEBUG level message to the syslog but only if the
+ * driver was compiled with -DDEBUG. Otherwise, the call turns into a NOP.
+ */
+static void ntfs_debug(const char *f, ...);
+#endif
+
+extern void __ntfs_debug (const char *file, int line, const char *function,
+	const char *format, ...) __attribute__ ((format (printf, 4, 5)));
+#define ntfs_debug(f, a...)						\
+	__ntfs_debug(__FILE__, __LINE__, __FUNCTION__, f, ##a)
+
+extern void ntfs_debug_dump_runlist(const run_list_element *rl);
+
+#else	/* !DEBUG */
+
+#define ntfs_debug(f, a...)		do {} while (0)
+#define ntfs_debug_dump_runlist(rl)	do {} while (0)
+
+#endif	/* !DEBUG */
+
+extern void __ntfs_warning(const char *function, const struct super_block *sb,
+		const char *fmt, ...) __attribute__ ((format (printf, 3, 4)));
+#define ntfs_warning(sb, f, a...)	__ntfs_warning(__FUNCTION__, sb, f, ##a)
+
+extern void __ntfs_error(const char *function, const struct super_block *sb,
+		const char *fmt, ...) __attribute__ ((format (printf, 3, 4)));
+#define ntfs_error(sb, f, a...)		__ntfs_error(__FUNCTION__, sb, f, ##a)
+
+#endif /* _LINUX_NTFS_DEBUG_H */
+
diff -Nru a/fs/ntfs/dir.c b/fs/ntfs/dir.c
--- a/fs/ntfs/dir.c	Sun Apr 28 20:10:37 2002
+++ b/fs/ntfs/dir.c	Sun Apr 28 20:10:39 2002
@@ -1,1104 +1,820 @@
-/*
- * dir.c
+/**
+ * dir.c - NTFS kernel directory operations. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2001,2002 Anton Altaparmakov.
+ * Copyright (C) 2002 Richard Russon.
+ *
+ * This program/include file 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/include file 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.
  *
- * Copyright (C) 1995-1997, 1999 Martin von Löwis
- * Copyright (C) 1999 Steve Dodd
- * Copyright (C) 1999 Joseph Malicki
- * Copyright (C) 2001 Anton Altaparmakov (AIA)
+ * You should have received a copy of the GNU General Public License
+ * along with this program (in the main directory of the Linux-NTFS 
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
-#include "ntfstypes.h"
-#include "struct.h"
-#include "dir.h"
-#include "macros.h"
-
-#include <linux/errno.h>
-#include "super.h"
-#include "inode.h"
-#include "attr.h"
-#include "support.h"
-#include "util.h"
-#include <linux/smp_lock.h>
-#include <linux/bitops.h>
-
-static char I30[] = "$I30";
-
-/* An index record should start with INDX, and the last word in each block
- * should contain the check value. If it passes, the original values need to
- * be restored. */
-int ntfs_check_index_record(ntfs_inode *ino, char *record)
-{
-	return ntfs_fixup_record(record, "INDX", ino->u.index.recordsize);
-}
-
-static inline int ntfs_is_top(ntfs_u64 stack)
-{
-	return stack == 14;
-}
-
-static int ntfs_pop(ntfs_u64 *stack)
-{
-	static int width[16] = {1,2,1,3,1,2,1,4,1,2,1,3,1,2,1,-1};
-	int res = -1;
-	
-	switch (width[*stack & 15]) {
-	case 1:
-		res = (int)((*stack & 15) >> 1);
-		*stack >>= 4;
-		break;
-	case 2:
-		res = (int)(((*stack & 63) >> 2) + 7);
-		*stack >>= 6;
-		break;
-	case 3:
-		res = (int)(((*stack & 255) >> 3) + 23);
-		*stack >>= 8;
-		break;
-	case 4:
-		res = (int)(((*stack & 1023) >> 4) + 55);
-		*stack >>= 10;
-		break;
-	default:
-		ntfs_error("Unknown encoding\n");
-	}
-	return res;
-}
-
-static inline unsigned int ntfs_top(void)
-{
-	return 14;
-}
-
-static ntfs_u64 ntfs_push(ntfs_u64 stack, int i)
-{
-	if (i < 7)
-		return (stack << 4) | (i << 1);
-	if (i < 23)
-		return (stack << 6) | ((i - 7) << 2) | 1;
-	if (i < 55)
-		return (stack << 8) | ((i - 23) << 3) | 3;
-	if (i < 120)
-		return (stack << 10) | ((i - 55) << 4) | 7;
-	ntfs_error("Too many entries\n");
-	return ~((ntfs_u64)0);
-}
-
-#if 0
-static void ntfs_display_stack(ntfs_u64 stack)
-{
-	while(!ntfs_is_top(stack))
-	{
-		printf("%d ", ntfs_pop(&stack));
-	}
-	printf("\n");
-}
-#endif
-
-/* True if the entry points to another block of entries. */
-static inline int ntfs_entry_has_subnodes(char *entry)
-{
-	return (NTFS_GETU16(entry + 0xc) & 1);
-}
+#include "ntfs.h"
 
-/* True if it is not the 'end of dir' entry. */
-static inline int ntfs_entry_is_used(char *entry)
-{
-	return !(NTFS_GETU16(entry + 0xc) & 2);
-}
-
-/*
- * Removed RACE for allocating index blocks. But stil not too happy.
- * There might be more races afterwards. (AIA)
+/**
+ * The little endian Unicode string $I30 as a global constant.
  */
-static int ntfs_allocate_index_block(ntfs_iterate_s *walk)
+const uchar_t I30[5] = { const_cpu_to_le16('$'), const_cpu_to_le16('I'),
+		const_cpu_to_le16('3'),	const_cpu_to_le16('0'),
+		const_cpu_to_le16(0) };
+
+/**
+ * ntfs_lookup_inode_by_name - find an inode in a directory given its name
+ * @dir_ni:	ntfs inode of the directory in which to search for the name
+ * @uname:	Unicode name for which to search in the directory
+ * @uname_len:	length of the name @uname in Unicode characters
+ *
+ * Look for an inode with name @uname in the directory with inode @dir_ni.
+ * ntfs_lookup_inode_by_name() walks the contents of the directory looking for
+ * the Unicode name. If the name is found in the directory, the corresponding
+ * inode number (>= 0) is returned as a mft reference in cpu format, i.e. it
+ * is a 64-bit number containing the sequence number.
+ *
+ * On error, a negative value is returned corresponding to the error code. In
+ * particular if the inode is not found -ENOENT is returned. Note that you
+ * can't just check the return value for being negative, you have to check the
+ * inode number for being negative which you can extract using MREC(return
+ * value).
+ *
+ * Note, @uname_len does not include the (optional) terminating NULL character.
+ */
+u64 ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const uchar_t *uname,
+		const int uname_len)
 {
-	ntfs_attribute *allocation, *bitmap = 0;
-	int error, size, i, bit;
-	ntfs_u8 *bmap;
-	ntfs_io io;
-	ntfs_volume *vol = walk->dir->vol;
-
-	/* Check for allocation attribute. */
-	allocation = ntfs_find_attr(walk->dir, vol->at_index_allocation, I30);
-	if (!allocation) {
-		ntfs_u8 bmp[8];
-		/* Create index allocation attribute. */
-		error = ntfs_create_attr(walk->dir, vol->at_index_allocation,
-					 I30, 0, 0, &allocation);
-		if (error)
-			goto err_ret;
-		ntfs_bzero(bmp, sizeof(bmp));
-		error = ntfs_create_attr(walk->dir, vol->at_bitmap, I30, bmp,
-					 sizeof(bmp), &bitmap);
-		if (error)
-			goto err_ret;
-	} else
-		bitmap = ntfs_find_attr(walk->dir, vol->at_bitmap, I30);
-	if (!bitmap) {
-		ntfs_error("Directory w/o bitmap\n");
-		error = -EINVAL;
-		goto err_ret;
-	}
-	size = bitmap->size;
-	bmap = ntfs_malloc(size);
-	if (!bmap) {
-		error = -ENOMEM;
-		goto err_ret;
-	}
-	io.fn_put = ntfs_put;
-	io.fn_get = ntfs_get;
-try_again:
-	io.param = bmap;
-	io.size = size;
-	error = ntfs_read_attr(walk->dir, vol->at_bitmap, I30, 0, &io);
-	if (error || (io.size != size && (error = -EIO, 1)))
-		goto err_fb_out;
-	/* Allocate a bit. */
-	for (bit = i = 0; i < size; i++) {
-		if (bmap[i] == 0xFF)
-			continue;
-		bit = ffz(bmap[i]);
-		if (bit < 8)
-			break;
-	}
-	if (i >= size) {
-		/* FIXME: Extend bitmap. */
-		error = -EOPNOTSUPP;
-		goto err_fb_out;
-	}
-	/* Get the byte containing our bit again, now taking the BKL. */
-	io.param = bmap;
-	io.size = 1;
-	lock_kernel();
-	error = ntfs_read_attr(walk->dir, vol->at_bitmap, I30, i, &io);
-	if (error || (io.size != 1 && (error = -EIO, 1)))
-		goto err_unl_out;
-	if (ntfs_test_and_set_bit(bmap, bit)) {
-		unlock_kernel();
-		/* Give other process(es) a chance to finish. */
-		schedule();
-		goto try_again;
-	}
-	walk->newblock = (i * 8 + bit) * walk->dir->u.index.clusters_per_record;
-	io.param = bmap;
-	error = ntfs_write_attr(walk->dir, vol->at_bitmap, I30, i, &io);
-	if (error || (io.size != size && (error = -EIO, 1)))
-		goto err_unl_out;
-	/* Change inode on disk, required when bitmap is resident. */
-	error = ntfs_update_inode(walk->dir);
-	if (error)
-		goto err_unl_out;
-	unlock_kernel();
-	ntfs_free(bmap);
-	/* Check whether record is out of allocated range. */
-	size = allocation->size;
-	if (walk->newblock * vol->cluster_size >= size) {
-		/* Build index record. */
-		int hsize;
-		int s1 = walk->dir->u.index.recordsize;
-		int nr_fix = (s1 >> vol->sector_size) + 1;
-		char *record = ntfs_malloc(s1);
-		if (!record) {
-			error = -ENOMEM;
-			goto err_ret;
-		}
-		ntfs_bzero(record, s1);
-		/* Magic */
-		ntfs_memcpy(record, "INDX", 4);
-		/* Offset to fixups */
-		NTFS_PUTU16(record + 4, 0x28);
-		/* Number of fixups. */
-		NTFS_PUTU16(record + 6, nr_fix);
-		/* Log file sequence number - We don't do journalling so we
-		 * just set it to zero which should be the Right Thing. (AIA) */
-		NTFS_PUTU64(record + 8, 0);
-		/* VCN of buffer */
-		NTFS_PUTU64(record + 0x10, walk->newblock);
-		/* Header size. */
-		hsize = 0x10 + 2 * nr_fix;
-		hsize = (hsize + 7) & ~7; /* Align. */
-		NTFS_PUTU16(record + 0x18, hsize);
-		/* Total size of record. */
-		NTFS_PUTU32(record + 0x20, s1 - 0x18);
-		/* Writing the data will extend the attribute. */
-		io.param = record;
-		io.size = s1;
-		io.do_read = 0;
-		error = ntfs_readwrite_attr(walk->dir, allocation, size, &io);
-		ntfs_free(record);
-		if (error || (io.size != s1 && (error = -EIO, 1)))
-			goto err_ret;
-		error = ntfs_update_inode(walk->dir);
-		if (error)
-			goto err_ret;
+	ntfs_volume *vol = dir_ni->vol;
+	struct super_block *sb = vol->sb;
+	MFT_RECORD *m;
+	INDEX_ROOT *ir;
+	INDEX_ENTRY *ie;
+	INDEX_ALLOCATION *ia;
+	u8 *index_end;
+	u64 mref;
+	attr_search_context *ctx;
+	int err = 0, rc;
+	IGNORE_CASE_BOOL ic;
+	VCN vcn, old_vcn;
+	struct address_space *ia_mapping;
+	struct page *page;
+	u8 *kaddr;
+
+	/* Get hold of the mft record for the directory. */
+	m = map_mft_record(READ, dir_ni);
+	if (IS_ERR(m))
+		goto map_err_out;
+
+	ctx = get_attr_search_ctx(dir_ni, m);
+	if (!ctx) {
+		err = -ENOMEM;
+		goto unm_err_out;
+	}
+
+	/* Find the index root attribute in the mft record. */
+	if (!lookup_attr(AT_INDEX_ROOT, I30, 4, CASE_SENSITIVE, 0, NULL, 0,
+			ctx)) {
+		ntfs_error(sb, "Index root attribute missing in directory "
+				"inode 0x%Lx.",
+				(unsigned long long)dir_ni->mft_no);
+		err = -EIO;
+		goto put_unm_err_out;
 	}
-	return 0;
-err_unl_out:
-	unlock_kernel();
-err_fb_out:
-	ntfs_free(bmap);
-err_ret:
-	return error;
-}
-
-/* Write an index block (root or allocation) back to storage.
- * Used is the total number of bytes in buf, including all headers. */
-static int ntfs_index_writeback(ntfs_iterate_s *walk, ntfs_u8 *buf, int block,
-				int used)
-{
-	ntfs_io io;
-	int error;
-	ntfs_attribute *a;
-	ntfs_volume *vol = walk->dir->vol;
-	
-	io.fn_put = 0;
-	io.fn_get = ntfs_get;
-	io.param = buf;
-	if (block == -1) {	/* Index root. */
-		NTFS_PUTU16(buf + 0x14, used - 0x10);
-		/* 0x18 is a copy thereof. */
-		NTFS_PUTU16(buf + 0x18, used - 0x10);
-		io.size = used;
-		error = ntfs_write_attr(walk->dir, vol->at_index_root, I30, 0,
-					&io);
-		if (error || (io.size != used && (error = -EIO, 1)))
-			return error;
-		/* Shrink if necessary. */
-		a = ntfs_find_attr(walk->dir, vol->at_index_root, I30);
-		ntfs_resize_attr(walk->dir, a, used);
-	} else {
-		NTFS_PUTU16(buf + 0x1C, used - 0x18);
-		io.size = walk->dir->u.index.recordsize;
-		error = ntfs_insert_fixups(buf, io.size);
-		if (error) {
-			printk(KERN_ALERT "NTFS: ntfs_index_writeback() caught "
-					"corrupt index record ntfs record "
-					"header. Refusing to write corrupt "
-					"data to disk. Unmount and run chkdsk "
-					"immediately!\n");
-			return -EIO;
+	/* Get to the index root value (it's been verified in read_inode). */
+	ir = (INDEX_ROOT*)((u8*)ctx->attr +
+			le16_to_cpu(ctx->attr->_ARA(value_offset)));
+	index_end = (u8*)&ir->index + le32_to_cpu(ir->index.index_length);
+	/* The first index entry. */
+	ie = (INDEX_ENTRY*)((u8*)&ir->index +
+			le32_to_cpu(ir->index.entries_offset));
+	/*
+	 * Loop until we exceed valid memory (corruption case) or until we
+	 * reach the last entry.
+	 */
+	for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->_IEH(length)))) {
+		/* Bounds checks. */
+		if ((u8*)ie < (u8*)ctx->mrec || (u8*)ie +
+				sizeof(INDEX_ENTRY_HEADER) > index_end ||
+				(u8*)ie + le16_to_cpu(ie->_IEH(key_length)) >
+				index_end)
+			goto dir_err_out;
+		/*
+		 * The last entry cannot contain a name. It can however contain
+		 * a pointer to a child node in the B+tree so we just break out.
+		 */
+		if (ie->_IEH(flags) & INDEX_ENTRY_END)
+			break;
+		/*
+		 * If the current entry has a name type of POSIX, the name is
+		 * case sensitive and not otherwise. This has the effect of us
+		 * not being able to access any POSIX file names which collate
+		 * after the non-POSIX one when they only differ in case, but
+		 * anyone doing screwy stuff like that deserves to burn in
+		 * hell... Doing that kind of stuff on NT4 actually causes
+		 * corruption on the partition even when using SP6a and Linux
+		 * is not involved at all.
+		 */
+		ic = ie->key.file_name.file_name_type ? IGNORE_CASE :
+				CASE_SENSITIVE;
+		/*
+		 * If the names match perfectly, we are done and return the
+		 * mft reference of the inode (i.e. the inode number together
+		 * with the sequence number for consistency checking. We
+		 * convert it to cpu format before returning.
+		 */
+		if (ntfs_are_names_equal(uname, uname_len,
+				(uchar_t*)&ie->key.file_name.file_name,
+				ie->key.file_name.file_name_length, ic,
+				vol->upcase, vol->upcase_len)) {
+found_it:
+			mref = le64_to_cpu(ie->_IIF(indexed_file));
+			put_attr_search_ctx(ctx);
+			unmap_mft_record(READ, dir_ni);
+			return mref;
 		}
-		error = ntfs_write_attr(walk->dir, vol->at_index_allocation,
-				I30, (__s64)block << vol->cluster_size_bits,
-				&io);
-		if (error || (io.size != walk->dir->u.index.recordsize &&
-				(error = -EIO, 1)))
-			return error;
-	}
-	return 0;
-}
-
-static int ntfs_split_record(ntfs_iterate_s *walk, char *start, int bsize,
-			     int usize)
-{
-	char *entry, *prev;
-	ntfs_u8 *newbuf = 0, *middle = 0;
-	int error, othersize, mlen;
-	ntfs_io io;
-	ntfs_volume *vol = walk->dir->vol;
-	int oldblock;
-
-	error = ntfs_allocate_index_block(walk);
-	if (error)
-		return error;
-	/* This should not happen. */
-	if (walk->block == -1) {
-		ntfs_error("Trying to split root");
-		return -EOPNOTSUPP;
-	}
-	entry = start + NTFS_GETU16(start + 0x18) + 0x18; 
-	for (prev = entry; entry - start < usize / 2; 
-					       entry += NTFS_GETU16(entry + 8))
-		prev = entry;
-	newbuf = ntfs_malloc(vol->index_record_size);
-	if (!newbuf)
-		return -ENOMEM;
-	io.fn_put = ntfs_put;
-	io.fn_get = ntfs_get;
-	io.param = newbuf;
-	io.size = vol->index_record_size;
-	/* Read in old header. FIXME: Reading everything is overkill. */
-	error = ntfs_read_attr(walk->dir, vol->at_index_allocation, I30,
-			(__s64)walk->newblock << vol->cluster_size_bits, &io);
-	if (error)
-		goto out;
-	if (io.size != vol->index_record_size) {
-		error = -EIO;
-		goto out;
-	}
-	/* FIXME: Adjust header. */
-	/* Copy everything from entry to new block. */
-	othersize = usize - (entry - start);
-	ntfs_memcpy(newbuf + NTFS_GETU16(newbuf + 0x18) + 0x18, entry,
-								    othersize);
-	/* Copy flags. */
-	NTFS_PUTU32(newbuf + 0x24, NTFS_GETU32(start + 0x24));
-	error = ntfs_index_writeback(walk, newbuf, walk->newblock,
-				othersize + NTFS_GETU16(newbuf + 0x18) + 0x18);
-	if (error)
-		goto out;
-	/* Move prev to walk. */
-	mlen = NTFS_GETU16(prev + 0x8);
-	/* Remember old child node. */
-	if (ntfs_entry_has_subnodes(prev))
-		oldblock = NTFS_GETU32(prev + mlen - 8);
-	else
-		oldblock = -1;
-	/* Allow for pointer to subnode. */
-	middle = ntfs_malloc(ntfs_entry_has_subnodes(prev) ? mlen : mlen + 8);
-	if (!middle){
-		error = -ENOMEM;
-		goto out;
-	}
-	ntfs_memcpy(middle, prev, mlen);
-	/* Set has_subnodes flag. */
-	NTFS_PUTU8(middle + 0xC, NTFS_GETU8(middle + 0xC) | 1);
-	/* Middle entry points to block, parent entry will point to newblock. */
-	NTFS_PUTU64(middle + mlen - 8, walk->block);
-	if (walk->new_entry)
-		ntfs_error("Entry not reset");
-	walk->new_entry = middle;
-	walk->u.flags |= ITERATE_SPLIT_DONE;
-	/* Terminate old block. */
-	othersize = usize - (prev-start);
-	NTFS_PUTU64(prev, 0);
-	if (oldblock == -1) {
-		NTFS_PUTU32(prev + 8, 0x10);
-		NTFS_PUTU32(prev + 0xC, 2);
-		othersize += 0x10;
-	} else {
-		NTFS_PUTU32(prev + 8, 0x18);
-		NTFS_PUTU32(prev + 0xC, 3);
-		NTFS_PUTU64(prev + 0x10, oldblock);
-		othersize += 0x18;
-	}
-	/* Write back original block. */
-	error = ntfs_index_writeback(walk, start, walk->block, othersize);
- out:
-	if (newbuf)
-		ntfs_free(newbuf);
-	if (middle)
-		ntfs_free(middle);
-	return error;
-}
-
-static int ntfs_dir_insert(ntfs_iterate_s *walk, char *start, char* entry)
-{
-	int blocksize, usedsize, error, offset;
-	int do_split = 0;
-	offset = entry - start;
-	if (walk->block == -1) { /* index root */
-		blocksize = walk->dir->vol->mft_record_size;
-		usedsize = NTFS_GETU16(start + 0x14) + 0x10;
-	} else {
-		blocksize = walk->dir->u.index.recordsize;
-		usedsize = NTFS_GETU16(start + 0x1C) + 0x18;
+		/*
+		 * Not a perfect match, need to do full blown collation so we
+		 * know which way in the B+tree we have to go.
+		 */
+		rc = ntfs_collate_names(uname, uname_len,
+				(uchar_t*)&ie->key.file_name.file_name,
+				ie->key.file_name.file_name_length, 1,
+				IGNORE_CASE, vol->upcase, vol->upcase_len);
+		/*
+		 * If uname collates before the name of the current entry, there
+		 * is definitely no such name in this index but we might need to
+		 * descend into the B+tree so we just break out of the loop.
+		 */
+		if (rc == -1)
+			break;
+		/* The names are not equal, continue the search. */
+		if (rc)
+			continue;
+		/*
+		 * Names match with case insensitive comparison, now try the
+		 * case sensitive comparison, which is required for proper
+		 * collation.
+		 */
+		rc = ntfs_collate_names(uname, uname_len,
+				(uchar_t*)&ie->key.file_name.file_name,
+				ie->key.file_name.file_name_length, 1,
+				CASE_SENSITIVE, vol->upcase, vol->upcase_len);
+		if (rc == -1)
+			break;
+		if (rc)
+			continue;
+		/*
+		 * Perfect match, this will never happen as the
+		 * ntfs_are_names_equal() call will have gotten a match but we
+		 * still treat it correctly.
+		 */
+		goto found_it;
 	}
-	if (usedsize + walk->new_entry_size > blocksize) {
-		char* s1 = ntfs_malloc(blocksize + walk->new_entry_size);
-		if (!s1)
-			return -ENOMEM;
-		ntfs_memcpy(s1, start, usedsize);
-		do_split = 1;
-		/* Adjust entry to s1. */
-		entry = s1 + (entry - start);
-		start = s1;
-	}
-	ntfs_memmove(entry + walk->new_entry_size, entry, usedsize - offset);
-	ntfs_memcpy(entry, walk->new_entry, walk->new_entry_size);
-	usedsize += walk->new_entry_size;
-	ntfs_free(walk->new_entry);
-	walk->new_entry = 0;
-	if (do_split) {
-		error = ntfs_split_record(walk, start, blocksize, usedsize);
-		ntfs_free(start);
-	} else {
-		error = ntfs_index_writeback(walk, start, walk->block,usedsize);
-		if (error)
-			return error;
+	/*
+	 * We have finished with this index without success. Check for the
+	 * presence of a child node.
+	 */
+	if (!(ie->_IEH(flags) & INDEX_ENTRY_NODE)) {
+		/* No child node, return -ENOENT. */
+		err = -ENOENT;
+		goto put_unm_err_out;
+	} /* Child node present, descend into it. */
+	/* Consistency check: Verify that an index allocation exists. */
+	if (!NInoIndexAllocPresent(dir_ni)) {
+		ntfs_error(sb, "No index allocation attribute but index entry "
+				"requires one. Directory inode 0x%Lx is "
+				"corrupt or driver bug.",
+				(unsigned long long)dir_ni->mft_no);
+		err = -EIO;
+		goto put_unm_err_out;
 	}
-	return 0;
-}
-
-/* Try to split INDEX_ROOT attributes. Return -E2BIG if nothing changed. */
-int ntfs_split_indexroot(ntfs_inode *ino)
-{
-	ntfs_attribute *ra;
-	ntfs_u8 *root = 0, *index = 0;
-	ntfs_io io;
-	int error, off, i, bsize, isize;
-	ntfs_iterate_s walk;
-
-	ra = ntfs_find_attr(ino, ino->vol->at_index_root, I30);
-	if (!ra)
-		return -ENOTDIR;
-	bsize = ino->vol->mft_record_size;
-	root = ntfs_malloc(bsize);
-	if (!root)
-		return -E2BIG;
-	io.fn_put = ntfs_put;
-	io.param = root;
-	io.size = bsize;
-	error = ntfs_read_attr(ino, ino->vol->at_index_root, I30, 0, &io);
-	if (error)
-		goto out;
-	off = 0x20;
-	/* Count number of entries. */
-	for (i = 0; ntfs_entry_is_used(root + off); i++)
-		off += NTFS_GETU16(root + off + 8);
-	if (i <= 2) {
-		/* We don't split small index roots. */
-		error = -E2BIG;
-		goto out;
-	}
-	index = ntfs_malloc(ino->vol->index_record_size);
-	if (!index) {
-		error = -ENOMEM;
-		goto out;
-	}
-	walk.dir = ino;
-	walk.block = -1;
-	walk.result = walk.new_entry = 0;
-	walk.name = 0;
-	error = ntfs_allocate_index_block(&walk);
-	if (error)
-		goto out;
-	/* Write old root to new index block. */
-	io.param = index;
-	io.size = ino->vol->index_record_size;
-	error = ntfs_read_attr(ino, ino->vol->at_index_allocation, I30,
-		(__s64)walk.newblock << ino->vol->cluster_size_bits, &io);
-	if (error)
-		goto out;
-	isize = NTFS_GETU16(root + 0x18) - 0x10;
-	ntfs_memcpy(index + NTFS_GETU16(index + 0x18) + 0x18, root+0x20, isize);
-	/* Copy flags. */
-	NTFS_PUTU32(index + 0x24, NTFS_GETU32(root + 0x1C));
-	error = ntfs_index_writeback(&walk, index, walk.newblock, 
-				     isize + NTFS_GETU16(index + 0x18) + 0x18);
-	if (error)
-		goto out;
-	/* Mark root as split. */
-	NTFS_PUTU32(root + 0x1C, 1);
-	/* Truncate index root. */
-	NTFS_PUTU64(root + 0x20, 0);
-	NTFS_PUTU32(root + 0x28, 0x18);
-	NTFS_PUTU32(root + 0x2C, 3);
-	NTFS_PUTU64(root + 0x30, walk.newblock);
-	error = ntfs_index_writeback(&walk, root, -1, 0x38);
- out:
-	ntfs_free(root);
-	ntfs_free(index);
-	return error;
-}
-
-/* The entry has been found. Copy the result in the caller's buffer */
-static int ntfs_copyresult(char *dest, char *source)
-{
-	int length = NTFS_GETU16(source + 8);
-	ntfs_memcpy(dest, source, length);
-	return 1;
-}
-
-/* Use $UpCase some day. */
-static inline unsigned short ntfs_my_toupper(ntfs_volume *vol, ntfs_u16 x)
-{
-	/* We should read any pending rest of $UpCase here. */
-	if (x >= vol->upcase_length)
-		return x;
-	return vol->upcase[x];
-}
-
-/* Everything passed in walk and entry. */
-static int ntfs_my_strcmp(ntfs_iterate_s *walk, const unsigned char *entry)
-{
-	int lu = *(entry + 0x50);
-	int i;
-
-	ntfs_u16* name = (ntfs_u16*)(entry + 0x52);
-	ntfs_volume *vol = walk->dir->vol;
-	for (i = 0; i < lu && i < walk->namelen; i++)
-		if (ntfs_my_toupper(vol, NTFS_GETU16(name + i)) != 
-			     ntfs_my_toupper(vol, NTFS_GETU16(walk->name + i)))
-			break;
-	if (i == lu && i == walk->namelen)
-		return 0;
-	if (i == lu)
-		return 1;
-	if (i == walk->namelen)
-		return -1;
-	if (ntfs_my_toupper(vol, NTFS_GETU16(name + i)) < 
-			    ntfs_my_toupper(vol, NTFS_GETU16(walk->name + i)))
-		return 1;
-	return -1;
-}
-
-/* Necessary forward declaration. */
-static int ntfs_getdir_iterate(ntfs_iterate_s *walk, char *start, char *entry);
-
-/* Parse a block of entries. Load the block, fix it up, and iterate over the
- * entries. The block is given as virtual cluster number. */
-static int ntfs_getdir_record(ntfs_iterate_s *walk, int block)
-{
-	int length = walk->dir->u.index.recordsize;
-	char *record = (char*)ntfs_malloc(length);
-	char *offset;
-	int retval,error;
-	int oldblock;
-	ntfs_io io;
-
-	if (!record)
-		return -ENOMEM;
-	io.fn_put = ntfs_put;
-	io.param = record;
-	io.size = length;
-	/* Read the block from the index allocation attribute. */
-	error = ntfs_read_attr(walk->dir, walk->dir->vol->at_index_allocation,
-		I30, (__s64)block << walk->dir->vol->cluster_size_bits, &io);
-	if (error || io.size != length) {
-		ntfs_error("read failed\n");
-		ntfs_free(record);
-		return 0;
+	/* Get the starting vcn of the index_block holding the child node. */
+	vcn = sle64_to_cpup((u8*)ie + le16_to_cpu(ie->_IEH(length)) - 8);
+	ia_mapping = VFS_I(dir_ni)->i_mapping;
+descend_into_child_node:
+	/*
+	 * Convert vcn to index into the index allocation attribute in units
+	 * of PAGE_CACHE_SIZE and map the page cache page, reading it from
+	 * disk if necessary.
+	 */
+	page = ntfs_map_page(ia_mapping, vcn <<
+			dir_ni->_IDM(index_vcn_size_bits) >> PAGE_CACHE_SHIFT);
+	if (IS_ERR(page)) {
+		ntfs_error(sb, "Failed to map directory index page, error %ld.",
+				-PTR_ERR(page));
+		goto put_unm_err_out;
+	}
+	kaddr = (u8*)page_address(page);
+fast_descend_into_child_node:
+	/* Get to the index allocation block. */
+	ia = (INDEX_ALLOCATION*)(kaddr + ((vcn <<
+			dir_ni->_IDM(index_vcn_size_bits)) & ~PAGE_CACHE_MASK));
+	/* Bounds checks. */
+	if ((u8*)ia < kaddr || (u8*)ia > kaddr + PAGE_CACHE_SIZE) {
+		ntfs_error(sb, "Out of bounds check failed. Corrupt directory "
+				"inode 0x%Lx or driver bug.",
+				(unsigned long long)dir_ni->mft_no);
+		err = -EIO;
+		goto unm_unm_err_out;
 	}
-	if (!ntfs_check_index_record(walk->dir, record)) {
-		ntfs_error("%x is not an index record\n", block);
-		ntfs_free(record);
-		return 0;
+	if (sle64_to_cpu(ia->index_block_vcn) != vcn) {
+		ntfs_error(sb, "Actual VCN (0x%Lx) of index buffer is "
+				"different from expected VCN (0x%Lx). "
+				"Directory inode 0x%Lx is corrupt or driver "
+				"bug.",
+				(long long)sle64_to_cpu(ia->index_block_vcn),
+				(long long)vcn,
+				(unsigned long long)dir_ni->mft_no);
+		err = -EIO;
+		goto unm_unm_err_out;
 	}
-	offset = record + NTFS_GETU16(record + 0x18) + 0x18;
-	oldblock = walk->block;
-	walk->block = block;
-	retval = ntfs_getdir_iterate(walk, record, offset);
-	walk->block = oldblock;
-	ntfs_free(record);
-	return retval;
-}
-
-/* Go down to the next block of entries. These collate before the current
- * entry. */
-static int ntfs_descend(ntfs_iterate_s *walk, ntfs_u8 *start, ntfs_u8 *entry)
-{
-	int length = NTFS_GETU16(entry + 8);
-	int nextblock = NTFS_GETU32(entry + length - 8);
-	int error;
-
-	if (!ntfs_entry_has_subnodes(entry)) {
-		ntfs_error("illegal ntfs_descend call\n");
-		return 0;
+	if (le32_to_cpu(ia->index.allocated_size) + 0x18 !=
+			dir_ni->_IDM(index_block_size)) {
+		ntfs_error(sb, "Index buffer (VCN 0x%Lx) of directory inode "
+				"0x%Lx has a size (%u) differing from the "
+				"directory specified size (%u). Directory "
+				"inode is corrupt or driver bug.",
+				(long long)vcn,
+				(unsigned long long)dir_ni->mft_no,
+				le32_to_cpu(ia->index.allocated_size) + 0x18,
+				dir_ni->_IDM(index_block_size));
+		err = -EIO;
+		goto unm_unm_err_out;
 	}
-	error = ntfs_getdir_record(walk, nextblock);
-	if (!error && walk->type == DIR_INSERT && 
-	    (walk->u.flags & ITERATE_SPLIT_DONE)) {
-		/* Split has occurred. Adjust entry, insert new_entry. */
-		NTFS_PUTU32(entry + length - 8, walk->newblock);
-		/* Reset flags, as the current block might be split again. */
-		walk->u.flags &= ~ITERATE_SPLIT_DONE;
-		error = ntfs_dir_insert(walk, start, entry);
+	index_end = (u8*)ia + dir_ni->_IDM(index_block_size);
+	if (index_end > kaddr + PAGE_CACHE_SIZE) {
+		ntfs_error(sb, "Index buffer (VCN 0x%Lx) of directory inode "
+				"0x%Lx crosses page boundary. Impossible! "
+				"Cannot access! This is probably a bug in the "
+				"driver.", (long long)vcn,
+				(unsigned long long)dir_ni->mft_no);
+		err = -EIO;
+		goto unm_unm_err_out;
 	}
-	return error;
-}
-
-static int ntfs_getdir_iterate_byposition(ntfs_iterate_s *walk, char* start,
-					  char *entry)
-{
-	int retval = 0;
-	int curpos = 0, destpos = 0;
-	int length;
-	if (walk->u.pos != 0) {
-		if (ntfs_is_top(walk->u.pos))
-			return 0;
-		destpos = ntfs_pop(&walk->u.pos);
+	index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length);
+	if (index_end > (u8*)ia + dir_ni->_IDM(index_block_size)) {
+		ntfs_error(sb, "Size of index buffer (VCN 0x%Lx) of directory "
+				"inode 0x%Lx exceeds maximum size.",
+				(long long)vcn,
+				(unsigned long long)dir_ni->mft_no);
+		err = -EIO;
+		goto unm_unm_err_out;
 	}
-	while (1) {
-		if (walk->u.pos == 0) {
-			if (ntfs_entry_has_subnodes(entry))
-				ntfs_descend(walk, start, entry);
-			else
-				walk->u.pos = ntfs_top();
-			if (ntfs_is_top(walk->u.pos) && 
-			    !ntfs_entry_is_used(entry))
-				return 1;
-			walk->u.pos = ntfs_push(walk->u.pos, curpos);
-			return 1;
-		}
-		if (curpos == destpos) {
-			if (!ntfs_is_top(walk->u.pos) && 
-			    ntfs_entry_has_subnodes(entry)) {
-				retval = ntfs_descend(walk, start, entry);
-				if (retval) {
-					walk->u.pos = ntfs_push(walk->u.pos,
-								curpos);
-					return retval;
-				}
-				if (!ntfs_entry_is_used(entry))
-					return 0;
-				walk->u.pos = 0;
-			}
-			if (ntfs_entry_is_used(entry)) {
-				retval = ntfs_copyresult(walk->result, entry);
-				walk->u.pos = 0;
-			} else {
-				walk->u.pos = ntfs_top();
-				return 0;
-			}
+	/* The first index entry. */
+	ie = (INDEX_ENTRY*)((u8*)&ia->index +
+			le32_to_cpu(ia->index.entries_offset));
+	/*
+	 * Iterate similar to above big loop but applied to index buffer, thus
+	 * loop until we exceed valid memory (corruption case) or until we
+	 * reach the last entry.
+	 */
+	for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->_IEH(length)))) {
+		/* Bounds check. */
+		if ((u8*)ie < (u8*)ia || (u8*)ie +
+				sizeof(INDEX_ENTRY_HEADER) > index_end ||
+				(u8*)ie + le16_to_cpu(ie->_IEH(key_length)) >
+				index_end) {
+			ntfs_error(sb, "Index entry out of bounds in "
+					"directory inode 0x%Lx.",
+					(unsigned long long)dir_ni->mft_no);
+			err = -EIO;
+			goto unm_unm_err_out;
 		}
-		curpos++;
-		if (!ntfs_entry_is_used(entry))
-			break;
-		length = NTFS_GETU16(entry + 8);
-		if (!length) {
-			ntfs_error("infinite loop\n");
+		/*
+		 * The last entry cannot contain a name. It can however contain
+		 * a pointer to a child node in the B+tree so we just break out.
+		 */
+		if (ie->_IEH(flags) & INDEX_ENTRY_END)
 			break;
+		/*
+		 * If the current entry has a name type of POSIX, the name is
+		 * case sensitive and not otherwise. This has the effect of us
+		 * not being able to access any POSIX file names which collate
+		 * after the non-POSIX one when they only differ in case, but
+		 * anyone doing screwy stuff like that deserves to burn in
+		 * hell... Doing that kind of stuff on NT4 actually causes
+		 * corruption on the partition even when using SP6a and Linux
+		 * is not involved at all.
+		 */
+		ic = ie->key.file_name.file_name_type ? IGNORE_CASE :
+				CASE_SENSITIVE;
+		/*
+		 * If the names match perfectly, we are done and return the
+		 * mft reference of the inode (i.e. the inode number together
+		 * with the sequence number for consistency checking. We
+		 * convert it to cpu format before returning.
+		 */
+		if (ntfs_are_names_equal(uname, uname_len,
+				(uchar_t*)&ie->key.file_name.file_name,
+				ie->key.file_name.file_name_length, ic,
+				vol->upcase, vol->upcase_len)) {
+found_it2:
+			mref = le64_to_cpu(ie->_IIF(indexed_file));
+			ntfs_unmap_page(page);
+			put_attr_search_ctx(ctx);
+			unmap_mft_record(READ, dir_ni);
+			return mref;
 		}
-		entry += length;
-	}
-	return -1;
-}
-	
-/* Iterate over a list of entries, either from an index block, or from the
- * index root.
- * If searching BY_POSITION, pop the top index from the position. If the
- * position stack is empty then, return the item at the index and set the
- * position to the next entry. If the position stack is not empty, 
- * recursively proceed for subnodes. If the entry at the position is the
- * 'end of dir' entry, return 'not found' and the empty stack.
- * If searching BY_NAME, walk through the items until found or until
- * one item is collated after the requested item. In the former case, return
- * the result. In the latter case, recursively proceed to the subnodes.
- * If 'end of dir' is reached, the name is not in the directory */
-static int ntfs_getdir_iterate(ntfs_iterate_s *walk, char *start, char *entry)
-{
-	int length;
-	int cmp;
-
-	if (walk->type == BY_POSITION)
-		return ntfs_getdir_iterate_byposition(walk, start, entry);
-	do {
-		/* If the current entry is a real one, compare with the
-		 * requested item. If the current entry is the last item, it
-		 * is always larger than the requested item. */
-		cmp = ntfs_entry_is_used(entry) ? 
-						ntfs_my_strcmp(walk,entry) : -1;
-		switch (walk->type) {
-		case BY_NAME:
-			switch (cmp) {
-			case -1:
-				return ntfs_entry_has_subnodes(entry) ?
-					ntfs_descend(walk, start, entry) : 0;
-			case  0:
-				return ntfs_copyresult(walk->result, entry);
-			case  1:
-				break;
-			}
+		/*
+		 * Not a perfect match, need to do full blown collation so we
+		 * know which way in the B+tree we have to go.
+		 */
+		rc = ntfs_collate_names(uname, uname_len,
+				(uchar_t*)&ie->key.file_name.file_name,
+				ie->key.file_name.file_name_length, 1,
+				IGNORE_CASE, vol->upcase, vol->upcase_len);
+		/*
+		 * If uname collates before the name of the current entry, there
+		 * is definitely no such name in this index but we might need to
+		 * descend into the B+tree so we just break out of the loop.
+		 */
+		if (rc == -1)
 			break;
-		case DIR_INSERT:
-			switch (cmp) {
-			case -1:
-				return ntfs_entry_has_subnodes(entry) ?
-					ntfs_descend(walk, start, entry) :
-					ntfs_dir_insert(walk, start, entry);
-			case  0:
-				return -EEXIST;
-			case  1:
-				break;
-			}
+		/* The names are not equal, continue the search. */
+		if (rc)
+			continue;
+		/*
+		 * Names match with case insensitive comparison, now try the
+		 * case sensitive comparison, which is required for proper
+		 * collation.
+		 */
+		rc = ntfs_collate_names(uname, uname_len,
+				(uchar_t*)&ie->key.file_name.file_name,
+				ie->key.file_name.file_name_length, 1,
+				CASE_SENSITIVE, vol->upcase, vol->upcase_len);
+		if (rc == -1)
 			break;
-		default:
-			ntfs_error("TODO\n"); /* FIXME: ? */
+		if (rc)
+			continue;
+		/*
+		 * Perfect match, this will never happen as the
+		 * ntfs_are_names_equal() call will have gotten a match but we
+		 * still treat it correctly.
+		 */
+		goto found_it2;
+	}
+	/*
+	 * We have finished with this index buffer without success. Check for
+	 * the presence of a child node.
+	 */
+	if (ie->_IEH(flags) & INDEX_ENTRY_NODE) {
+		if ((ia->index.flags & NODE_MASK) == LEAF_NODE) {
+			ntfs_error(sb, "Index entry with child node found in "
+					"a leaf node in directory inode 0x%Lx.",
+					(unsigned long long)dir_ni->mft_no);
+			err = -EIO;
+			goto unm_unm_err_out;
+		}
+		/* Child node present, descend into it. */
+		old_vcn = vcn;
+		vcn = sle64_to_cpup((u8*)ie +
+				le16_to_cpu(ie->_IEH(length)) - 8);
+		if (vcn >= 0) {
+			/* If vcn is in the same page cache page as old_vcn we
+			 * recycle the mapped page. */
+			if (old_vcn << vol->cluster_size_bits >>
+					PAGE_CACHE_SHIFT == vcn <<
+					vol->cluster_size_bits >>
+					PAGE_CACHE_SHIFT)
+				goto fast_descend_into_child_node;
+			ntfs_unmap_page(page);
+			goto descend_into_child_node;
 		}
-		if (!ntfs_entry_is_used(entry))
-			break;
-		length = NTFS_GETU16(entry + 8);
-		if (!length) {
-			ntfs_error("infinite loop\n");
-			break;
+		ntfs_error(sb, "Negative child node vcn in directory inode "
+				"0x%Lx.", (unsigned long long)dir_ni->mft_no);
+		err = -EIO;
+		goto unm_unm_err_out;
+	}
+	/* No child node, return -ENOENT. */
+	ntfs_debug("Entry not found.");
+	err = -ENOENT;
+unm_unm_err_out:
+	ntfs_unmap_page(page);
+put_unm_err_out:
+	put_attr_search_ctx(ctx);
+unm_err_out:
+	unmap_mft_record(READ, dir_ni);
+	return ERR_MREF(err);
+map_err_out:
+	ntfs_error(sb, "map_mft_record(READ) failed with error code %ld.",
+			-PTR_ERR(m));
+	return ERR_MREF(PTR_ERR(m));
+dir_err_out:
+	ntfs_error(sb, "Corrupt directory. Aborting lookup.");
+	err = -EIO;
+	goto put_unm_err_out;
+}
+
+typedef union {
+	INDEX_ROOT *ir;
+	INDEX_ALLOCATION *ia;
+} index_union __attribute__ ((__transparent_union__));
+
+typedef enum {
+	INDEX_TYPE_ROOT,	/* index root */
+	INDEX_TYPE_ALLOCATION,	/* index allocation */
+} INDEX_TYPE;
+
+/**
+ * ntfs_filldir - ntfs specific filldir method
+ * @vol:	current ntfs volume
+ * @filp:	open file descriptor for the current directory
+ * @ndir:	ntfs inode of current directory
+ * @index_type:	specifies whether @iu is an index root or an index allocation
+ * @iu:		index root or index allocation attribute to which @ie belongs
+ * @ie:		current index entry
+ * @name:	buffer to use for the converted name
+ * @dirent:	vfs filldir callback context
+ * filldir:	vfs filldir callback
+ *
+ * Convert the Unicode name to the loaded NLS and pass it to
+ * the filldir callback.
+ */
+static inline int ntfs_filldir(ntfs_volume *vol, struct file *filp,
+		ntfs_inode *ndir, const INDEX_TYPE index_type,
+		index_union iu, INDEX_ENTRY *ie, u8 *name,
+		void *dirent, filldir_t filldir)
+{
+	int name_len;
+	unsigned dt_type;
+	FILE_NAME_TYPE_FLAGS name_type;
+	READDIR_OPTIONS readdir_opts;
+
+	/* Advance the position even if going to skip the entry. */
+	if (index_type == INDEX_TYPE_ALLOCATION)
+		filp->f_pos = (u8*)ie - (u8*)iu.ia +
+				(sle64_to_cpu(iu.ia->index_block_vcn) <<
+				ndir->_IDM(index_vcn_size_bits)) +
+				vol->mft_record_size;
+	else /* if (index_type == INDEX_TYPE_ROOT) */
+		filp->f_pos = (u8*)ie - (u8*)iu.ir;
+	readdir_opts = vol->readdir_opts;
+	name_type = ie->key.file_name.file_name_type;
+	if (name_type == FILE_NAME_DOS && RHideDosNames(readdir_opts)) {
+		ntfs_debug("Skipping DOS name space entry.");
+		return 0;
+	}
+	if (RHideLongNames(readdir_opts)) {
+		if (name_type == FILE_NAME_WIN32 ||
+				name_type == FILE_NAME_POSIX) {
+			ntfs_debug("Skipping WIN32/POSIX name space entry.");
+			return 0;
 		}
-		entry += length;
-	} while (1);
-	return 0;
-}
-
-/*  Tree walking is done using position numbers. The following numbers have a
- *  special meaning:
- *       0   start (.)
- *      -1   no more entries
- *      -2   ..
- *  All other numbers encode sequences of indices. The sequence a, b, c is 
- *  encoded as <stop><c><b><a>, where <foo> is the encoding of foo. The
- *  first few integers are encoded as follows:
- *      0:    0000    1:    0010    2:    0100    3:    0110
- *      4:    1000    5:    1010    6:    1100 stop:    1110
- *      7:  000001    8:  000101    9:  001001   10:  001101
- *  The least significant bits give the width of this encoding, the other bits
- *  encode the value, starting from the first value of the interval.
- *   tag     width  first value  last value
- *   0       3      0            6
- *   01      4      7            22
- *   011     5      23           54
- *   0111    6      55           119
- *   More values are hopefully not needed, as the file position has currently
- *   64 bits in total. */
-
-/* Find an entry in the directory. Return 0 if not found, otherwise copy the
- * entry to the result buffer. */
-int ntfs_getdir(ntfs_iterate_s *walk)
-{
-	int length = walk->dir->vol->mft_record_size;
-	int retval, error;
-	/* Start at the index root. */
-	char *root = ntfs_malloc(length);
-	ntfs_io io;
-
-	if (!root)
-		return -ENOMEM;
-	io.fn_put = ntfs_put;
-	io.param = root;
-	io.size = length;
-	error = ntfs_read_attr(walk->dir, walk->dir->vol->at_index_root, I30,
-			       0, &io);
-	if (error) {
-		ntfs_error("Not a directory\n");
+	}
+	if (MREF_LE(ie->_IIF(indexed_file)) == FILE_root) {
+		ntfs_debug("Skipping root directory self reference entry.");
 		return 0;
 	}
-	walk->block = -1;
-	/* FIXME: Move these to walk. */
-	walk->dir->u.index.recordsize = NTFS_GETU32(root + 0x8);
-	walk->dir->u.index.clusters_per_record = NTFS_GETU32(root + 0xC);
-	/* FIXME: Consistency check. */
-	/* Skip header. */
-	retval = ntfs_getdir_iterate(walk, root, root + 0x20);
-	ntfs_free(root);
-	return retval;
-}
-
-/* Find an entry in the directory by its position stack. Iteration starts
- * if the stack is 0, in which case the position is set to the first item
- * in the directory. If the position is nonzero, return the item at the
- * position and change the position to the next item. The position is -1
- * if there are no more items. */
-int ntfs_getdir_byposition(ntfs_iterate_s *walk)
-{
-	walk->type = BY_POSITION;
-	return ntfs_getdir(walk);
-}
-
-/* Find an entry in the directory by its name. Return 0 if not found. */
-int ntfs_getdir_byname(ntfs_iterate_s *walk)
-{
-	walk->type = BY_NAME;
-	return ntfs_getdir(walk);
+	if (MREF_LE(ie->_IIF(indexed_file)) < FILE_first_user &&
+			RHideSystemFiles(readdir_opts)) {
+		ntfs_debug("Skipping system file.");
+		return 0;
+	}
+	name_len = ntfs_ucstonls(vol, (uchar_t*)&ie->key.file_name.file_name,
+			ie->key.file_name.file_name_length, &name,
+			NTFS_MAX_NAME_LEN * 3 + 1);
+	if (name_len <= 0) {
+		ntfs_debug("Skipping unrepresentable file.");
+		return 0;
+	}
+	if (ie->key.file_name.file_attributes &
+			FILE_ATTR_DUP_FILE_NAME_INDEX_PRESENT)
+		dt_type = DT_DIR;
+	else
+		dt_type = DT_REG;
+	ntfs_debug("Calling filldir for %s with len %i, f_pos 0x%Lx, inode "
+			"0x%Lx, DT_%s.", name, name_len, filp->f_pos,
+			(unsigned long long)MREF_LE(ie->_IIF(indexed_file)),
+			dt_type == DT_DIR ? "DIR" : "REG");
+	return filldir(dirent, name, name_len, filp->f_pos,
+			(unsigned long)MREF_LE(ie->_IIF(indexed_file)), dt_type);
 }
 
-int ntfs_getdir_unsorted(ntfs_inode *ino, u32 *p_high, u32 *p_low,
-		int (*cb)(ntfs_u8 *, void *), void *param)
+/*
+ * VFS calls readdir with BKL held so no possible RACE conditions.
+ * We use the same basic approach as the old NTFS driver, i.e. we parse the
+ * index root entries and then the index allocation entries that are marked
+ * as in use in the index bitmap.
+ * While this will return the names in random order this doesn't matter for
+ * readdir but OTOH results in faster readdir.
+ */
+static int ntfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
 {
-	s64 ib_ofs;
-	char *buf = 0, *entry = 0;
-	ntfs_attribute *attr;
-	ntfs_volume *vol;
-	int byte, bit, err = 0;
-	u32 start, finish, ibs, max_size;
-	ntfs_io io;
-	u8 ibs_bits;
-
-	if (!ino) {
-		ntfs_error(__FUNCTION__ "(): No inode! Returning -EINVAL.\n");
-		return -EINVAL;
-	}
-	vol = ino->vol;
-	if (!vol) {
-		ntfs_error(__FUNCTION__ "(): Inode 0x%lx has no volume. "
-				"Returning -EINVAL.\n", ino->i_number);
-		return -EINVAL;
-	}
-	ntfs_debug(DEBUG_DIR3, __FUNCTION__ "(): Unsorted 1: Entering for "
-			"inode 0x%lx, p_high = 0x%x, p_low = 0x%x.\n",
-			ino->i_number, *p_high, *p_low);
-	if (!*p_high) {
-		/* We are still in the index root. */
-		buf = ntfs_malloc(io.size = vol->mft_record_size);
-		if (!buf)
-			return -ENOMEM;
-		io.fn_put = ntfs_put;
-		io.param = buf;
-		err = ntfs_read_attr(ino, vol->at_index_root, I30, 0, &io);
-		if (err || !io.size)
-			goto read_err_ret;
-		ino->u.index.recordsize = ibs = NTFS_GETU32(buf + 0x8);
-		ino->u.index.clusters_per_record = NTFS_GETU32(buf + 0xC);
-		entry = buf + 0x20;
-		ntfs_debug(DEBUG_DIR3, __FUNCTION__ "(): Unsorted 2: In index "
-				"root.\n");
-		ibs_bits = ffs(ibs) - 1;
-		/* Compensate for faked "." and "..". */
-		start = 2;
-	} else { /* We are in an index record. */
-		io.size = ibs = ino->u.index.recordsize;
-		buf = ntfs_malloc(ibs);
-		if (!buf)
-			return -ENOMEM;
-		ibs_bits = ffs(ibs) - 1;
-		io.fn_put = ntfs_put;
-		io.param = buf;
+	s64 ia_pos, ia_start, prev_ia_pos;
+	struct inode *vdir = filp->f_dentry->d_inode;
+	struct super_block *sb = vdir->i_sb;
+	ntfs_inode *ndir = NTFS_I(vdir);
+	ntfs_volume *vol = NTFS_SB(sb);
+	MFT_RECORD *m;
+	INDEX_ROOT *ir;
+	INDEX_ENTRY *ie;
+	INDEX_ALLOCATION *ia;
+	u8 *name;
+	int rc, err, ir_pos, bmp_pos;
+	struct address_space *ia_mapping;
+	struct page *page;
+	u8 *kaddr, *bmp, *index_end;
+	attr_search_context *ctx;
+
+	ntfs_debug("Entering for inode 0x%Lx, f_pos 0x%Lx.",
+			(unsigned long long)ndir->mft_no, filp->f_pos);
+	rc = err = 0;
+	/* Are we at end of dir yet? */
+	if (filp->f_pos >= vdir->i_size + vol->mft_record_size)
+		goto done;
+	/* Emulate . and .. for all directories. */
+	if (!filp->f_pos) {
+		ntfs_debug("Calling filldir for . with len 1, f_pos 0x0, "
+				"inode 0x%Lx, DT_DIR.",
+				(unsigned long long)ndir->mft_no);
+		rc = filldir(dirent, ".", 1, filp->f_pos, vdir->i_ino, DT_DIR);
+		if (rc)
+			goto done;
+		filp->f_pos++;
+	}
+	if (filp->f_pos == 1) {
+		ntfs_debug("Calling filldir for .. with len 2, f_pos 0x1, "
+				"inode 0x%lx, DT_DIR.",
+				parent_ino(filp->f_dentry));
+		rc = filldir(dirent, "..", 2, filp->f_pos,
+				parent_ino(filp->f_dentry), DT_DIR);
+		if (rc)
+			goto done;
+		filp->f_pos++;
+	}
+	/* Get hold of the mft record for the directory. */
+	m = map_mft_record(READ, ndir);
+	if (IS_ERR(m)) {
+		err = PTR_ERR(m);
+		goto err_out;
+	}
+
+	ctx = get_attr_search_ctx(ndir, m);
+	if (!ctx) {
+		err = -ENOMEM;
+		goto unm_err_out;
+	}
+
+	/*
+	 * Allocate a buffer to store the current name being processed
+	 * converted to format determined by current NLS.
+	 */
+	name = (u8*)kmalloc(NTFS_MAX_NAME_LEN * 3 + 1, GFP_NOFS);
+	if (!name) {
+		err = -ENOMEM;
+		goto put_unm_err_out;
+	}
+	/* Are we jumping straight into the index allocation attribute? */
+	if (filp->f_pos >= vol->mft_record_size)
+		goto skip_index_root;
+	/* Get the offset into the index root attribute. */
+	ir_pos = (s64)filp->f_pos;
+	/* Find the index root attribute in the mft record. */
+	if (!lookup_attr(AT_INDEX_ROOT, I30, 4, CASE_SENSITIVE, 0, NULL, 0,
+			ctx)) {
+		ntfs_error(sb, "Index root attribute missing in directory "
+				"inode 0x%Lx.",
+				(unsigned long long)ndir->mft_no);
+		err = -EIO;
+		goto kf_unm_err_out;
+	}
+	/* Get to the index root value (it's been verified in read_inode). */
+	ir = (INDEX_ROOT*)((u8*)ctx->attr +
+			le16_to_cpu(ctx->attr->_ARA(value_offset)));
+	index_end = (u8*)&ir->index + le32_to_cpu(ir->index.index_length);
+	/* The first index entry. */
+	ie = (INDEX_ENTRY*)((u8*)&ir->index +
+			le32_to_cpu(ir->index.entries_offset));
+	/*
+	 * Loop until we exceed valid memory (corruption case) or until we
+	 * reach the last entry or until filldir tells us it has had enough
+	 * or signals an error (both covered by the rc test).
+	 */
+	for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->_IEH(length)))) {
+		ntfs_debug("In index root, offset 0x%x.", (u8*)ie - (u8*)ir);
+		/* Bounds checks. */
+		if ((u8*)ie < (u8*)ctx->mrec || (u8*)ie +
+				sizeof(INDEX_ENTRY_HEADER) > index_end ||
+				(u8*)ie + le16_to_cpu(ie->_IEH(key_length)) >
+				index_end)
+			goto dir_err_out;
+		/* The last entry cannot contain a name. */
+		if (ie->_IEH(flags) & INDEX_ENTRY_END)
+			break;
+		/* Skip index root entry if continuing previous readdir. */
+		if (ir_pos > (u8*)ie - (u8*)ir)
+			continue;
+		/* Submit the name to the filldir callback. */
+		rc = ntfs_filldir(vol, filp, ndir, INDEX_TYPE_ROOT, ir, ie,
+				name, dirent, filldir);
+		if (rc)
+			goto abort;
+	}
+	/* If there is no index allocation attribute we are finished. */
+	if (!NInoIndexAllocPresent(ndir))
+		goto EOD;
+	/* Advance f_pos to the beginning of the index allocation. */
+	filp->f_pos = vol->mft_record_size;
+	/* Reinitialize the search context. */
+	reinit_attr_search_ctx(ctx);
+skip_index_root:
+	if (NInoBmpNonResident(ndir)) {
 		/*
-		 * 0 is index root, index allocation starts at 1 and works in
-		 * units of index block size (ibs).
+		 * Read the page of the bitmap that contains the current index
+		 * block.
 		 */
-		ib_ofs = (s64)(*p_high - 1) << ibs_bits;
-		err = ntfs_read_attr(ino, vol->at_index_allocation, I30, ib_ofs,
-				&io);
-		if (err || io.size != ibs)
-			goto read_err_ret;
-		if (!ntfs_check_index_record(ino, buf)) {
-			ntfs_error(__FUNCTION__ "(): Index block 0x%x is not "
-					"an index record. Returning "
-					"-ENOTDIR.\n", *p_high - 1);
-			ntfs_free(buf);
-			return -ENOTDIR;
-		}
-		entry = buf + 0x18 + NTFS_GETU16(buf + 0x18);
-		ntfs_debug(DEBUG_DIR3, __FUNCTION__ "(): Unsorted 3: In index "
-				"allocation.\n");
-		start = 0;
-	}
-	/* Process the entries. */
-	finish = *p_low;
-	for (; entry < (buf + ibs) && ntfs_entry_is_used(entry);
-			entry += NTFS_GETU16(entry + 8)) {
-		if (start < finish) {
-			/* Skip entries that were already processed. */
-			ntfs_debug(DEBUG_DIR3, __FUNCTION__ "(): Unsorted 4: "
-					"Skipping already processed entry "
-					"p_high 0x%x, p_low 0x%x.\n", *p_high,
-					start);
-			start++;
-			continue;
-		}
-		ntfs_debug(DEBUG_DIR3, __FUNCTION__ "(): Unsorted 5: "
-				"Processing entry p_high 0x%x, p_low 0x%x.\n",
-				*p_high, *p_low);
-		if ((err = cb(entry, param))) {
-			/* filldir signalled us to stop. */
-			ntfs_debug(DEBUG_DIR3, __FUNCTION__ "(): "
-					"Unsorted 6: cb returned %i, "
-					"returning 0, p_high 0x%x, p_low 0x%x."
-					"\n", *p_high, *p_low);
-			ntfs_free(buf);
-			return 0;
-		}
-		++*p_low;
+		// TODO: FIXME: Implement this!
+		ntfs_error(sb, "Index bitmap is non-resident, which is not "
+				"supported yet. Pretending that end of "
+				"directory has been reached.\n");
+		goto EOD;
+	} else {
+		/* Find the index bitmap attribute in the mft record. */
+		if (!lookup_attr(AT_BITMAP, I30, 4, CASE_SENSITIVE, 0, NULL, 0,
+				ctx)) {
+			ntfs_error(sb, "Index bitmap attribute missing in "
+					"directory inode 0x%Lx.",
+					(unsigned long long)ndir->mft_no);
+			err = -EIO;
+			goto kf_unm_err_out;
+		}
+		bmp = (u8*)ctx->attr +
+				le16_to_cpu(ctx->attr->_ARA(value_offset));
+	}
+	/* Get the offset into the index allocation attribute. */
+	ia_pos = (s64)filp->f_pos - vol->mft_record_size;
+	ia_mapping = vdir->i_mapping;
+	/* If the index block is not in use find the next one that is. */
+	bmp_pos = ia_pos >> ndir->_IDM(index_block_size_bits);
+	page = NULL;
+	kaddr = NULL;
+	prev_ia_pos = -1LL;
+	if (bmp_pos >> 3 >= ndir->_IDM(bmp_size)) {
+		ntfs_error(sb, "Current index allocation position exceeds "
+				"index bitmap size.");
+		goto kf_unm_err_out;
+	}
+	while (!(bmp[bmp_pos >> 3] & (1 << (bmp_pos & 7)))) {
+find_next_index_buffer:
+		bmp_pos++;
+		/* If we have reached the end of the bitmap, we are done. */
+		if (bmp_pos >> 3 >= ndir->_IDM(bmp_size))
+			goto EOD;
+		ia_pos = (s64)bmp_pos << ndir->_IDM(index_block_size_bits);
+	}
+	ntfs_debug("Handling index buffer 0x%x.", bmp_pos);
+	/* If the current index buffer is in the same page we reuse the page. */
+	if ((prev_ia_pos & PAGE_CACHE_MASK) != (ia_pos & PAGE_CACHE_MASK)) {
+		prev_ia_pos = ia_pos;
+		if (page)
+			ntfs_unmap_page(page);
+		/*
+		 * Map the page cache page containing the current ia_pos,
+		 * reading it from disk if necessary.
+		 */
+		page = ntfs_map_page(ia_mapping, ia_pos >> PAGE_CACHE_SHIFT);
+		if (IS_ERR(page))
+			goto map_page_err_out;
+		kaddr = (u8*)page_address(page);
+	}
+	/* Get the current index buffer. */
+	ia = (INDEX_ALLOCATION*)(kaddr + (ia_pos & ~PAGE_CACHE_MASK &
+			~(s64)(ndir->_IDM(index_block_size) - 1)));
+	/* Bounds checks. */
+	if ((u8*)ia < kaddr || (u8*)ia > kaddr + PAGE_CACHE_SIZE) {
+		ntfs_error(sb, "Out of bounds check failed. Corrupt directory "
+				"inode 0x%Lx or driver bug.",
+				(unsigned long long)ndir->mft_no);
+		err = -EIO;
+		goto unm_dir_err_out;
 	}
-	ntfs_debug(DEBUG_DIR3, __FUNCTION__ "(): Unsorted 7: After processing "
-			"entries, p_high 0x%x, p_low 0x%x.\n", *p_high, *p_low);
-	/* We have to locate the next record. */
-	ntfs_free(buf);
-	buf = 0;
-	*p_low = 0;
-	attr = ntfs_find_attr(ino, vol->at_bitmap, I30);
-	if (!attr) {
-		/* Directory does not have index bitmap and index allocation. */
-		*p_high = 0x7fff;
-		ntfs_debug(DEBUG_DIR3, __FUNCTION__ "(): Unsorted 8: No index "
-				"allocation. Returning 0, p_high 0x7fff, "
-				"p_low 0x0.\n");
-		return 0;
+	if (sle64_to_cpu(ia->index_block_vcn) != (ia_pos &
+			~(s64)(ndir->_IDM(index_block_size) - 1)) >>
+			ndir->_IDM(index_vcn_size_bits)) {
+		ntfs_error(sb, "Actual VCN (0x%Lx) of index buffer is "
+				"different from expected VCN (0x%Lx). "
+				"Directory inode 0x%Lx is corrupt or driver "
+				"bug. ",
+				(long long)sle64_to_cpu(ia->index_block_vcn),
+				(long long)ia_pos >>
+				ndir->_IDM(index_vcn_size_bits),
+				(unsigned long long)ndir->mft_no);
+		err = -EIO;
+		goto unm_dir_err_out;
 	}
-	max_size = attr->size;
-	if (max_size > 0x7fff >> 3) {
-		ntfs_error(__FUNCTION__ "(): Directory too large. Visible "
-				"length is truncated.\n");
-		max_size = 0x7fff >> 3;
-	}
-	buf = ntfs_malloc(max_size);
-	if (!buf)
-		return -ENOMEM;
-	io.param = buf;
-	io.size = max_size;
-	err = ntfs_read_attr(ino, vol->at_bitmap, I30, 0, &io);
-	if (err || io.size != max_size)
-		goto read_err_ret;
-	attr = ntfs_find_attr(ino, vol->at_index_allocation, I30);
-	if (!attr) {
-		ntfs_free(buf);
-		ntfs_debug(DEBUG_DIR3, __FUNCTION__ "(): Unsorted 9: Find "
-				"attr failed. Returning -EIO.\n");
-		return -EIO;
-	}
-	if (attr->resident) {
-		ntfs_free(buf);
-		ntfs_debug(DEBUG_DIR3, __FUNCTION__ "(): Unsorted 9.5: IA is "
-				"resident. Not allowed. Returning EINVAL.\n");
-		return -EINVAL;
-	}
-	/* Loop while going through non-allocated index records. */
-	max_size <<= 3;
-	while (1) {
-		if (++*p_high >= 0x7fff) {
-			ntfs_error(__FUNCTION__ "(): Unsorted 10: Directory "
-					"inode 0x%lx overflowed the maximum "
-					"number of index allocation buffers "
-					"the driver can cope with. Pretending "
-					"to be at end of directory.\n",
-					ino->i_number);
-			goto fake_eod;
-		}
-		if (*p_high > max_size || (s64)*p_high << ibs_bits >
-				attr->initialized) {
-fake_eod:
-			/* No more index records. */
-			*p_high = 0x7fff;
-			*p_low = 0;
-			ntfs_free(buf);
-			ntfs_debug(DEBUG_DIR3, __FUNCTION__ "(): Unsorted "
-					"10.5: No more index records. "
-					"Returning 0, p_high 0x7fff, p_low "
-					"0.\n");
-			return 0;
-		}
-		byte = (ntfs_cluster_t)(*p_high - 1);
-		bit = 1 << (byte & 7);
-		byte >>= 3;
-		if ((buf[byte] & bit))
+	if (le32_to_cpu(ia->index.allocated_size) + 0x18 !=
+			ndir->_IDM(index_block_size)) {
+		ntfs_error(sb, "Index buffer (VCN 0x%Lx) of directory inode "
+				"0x%Lx has a size (%u) differing from the "
+				"directory specified size (%u). Directory "
+				"inode is corrupt or driver bug.",
+				(long long)ia_pos >> ndir->_IDM(index_vcn_size_bits),
+				(unsigned long long)ndir->mft_no,
+				le32_to_cpu(ia->index.allocated_size) + 0x18,
+				ndir->_IDM(index_block_size));
+		err = -EIO;
+		goto unm_dir_err_out;
+	}
+	index_end = (u8*)ia + ndir->_IDM(index_block_size);
+	if (index_end > kaddr + PAGE_CACHE_SIZE) {
+		ntfs_error(sb, "Index buffer (VCN 0x%Lx) of directory inode "
+				"0x%Lx crosses page boundary. Impossible! "
+				"Cannot access! This is probably a bug in the "
+				"driver.", (long long)ia_pos >>
+				ndir->_IDM(index_vcn_size_bits),
+				(unsigned long long)ndir->mft_no);
+		err = -EIO;
+		goto unm_dir_err_out;
+	}
+	ia_start = ia_pos & ~(s64)(ndir->_IDM(index_block_size) - 1);
+	index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length);
+	if (index_end > (u8*)ia + ndir->_IDM(index_block_size)) {
+		ntfs_error(sb, "Size of index buffer (VCN 0x%Lx) of directory "
+				"inode 0x%Lx exceeds maximum size.",
+				(long long)ia_pos >>
+				ndir->_IDM(index_vcn_size_bits),
+				(unsigned long long)ndir->mft_no);
+		err = -EIO;
+		goto unm_dir_err_out;
+	}
+	/* The first index entry in this index buffer. */
+	ie = (INDEX_ENTRY*)((u8*)&ia->index +
+			le32_to_cpu(ia->index.entries_offset));
+	/*
+	 * Loop until we exceed valid memory (corruption case) or until we
+	 * reach the last entry or until filldir tells us it has had enough
+	 * or signals an error (both covered by the rc test).
+	 */
+	for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->_IEH(length)))) {
+		ntfs_debug("In index allocation, offset 0x%Lx.",
+				(long long)ia_start + ((u8*)ie - (u8*)ia));
+		/* Bounds checks. */
+		if ((u8*)ie < (u8*)ia || (u8*)ie +
+				sizeof(INDEX_ENTRY_HEADER) > index_end ||
+				(u8*)ie + le16_to_cpu(ie->_IEH(key_length)) >
+				index_end)
+			goto unm_dir_err_out;
+		/* The last entry cannot contain a name. */
+		if (ie->_IEH(flags) & INDEX_ENTRY_END)
 			break;
-	};
-	ntfs_debug(DEBUG_DIR3, __FUNCTION__ "(): Unsorted 11: Done. "
-			"Returning 0, p_high 0x%x, p_low 0x%x.\n", *p_high,
-			*p_low);
-	ntfs_free(buf);
+		/* Skip index block entry if continuing previous readdir. */
+		if (ia_pos - ia_start > (u8*)ie - (u8*)ia)
+			continue;
+		/* Submit the name to the filldir callback. */
+		rc = ntfs_filldir(vol, filp, ndir, INDEX_TYPE_ALLOCATION, ia,
+				ie, name, dirent, filldir);
+		if (rc) {
+			ntfs_unmap_page(page);
+			goto abort;
+		}
+	}
+	goto find_next_index_buffer;
+EOD:
+	/* We are finished, set f_pos to EOD. */
+	filp->f_pos = vdir->i_size + vol->mft_record_size;
+abort:
+	put_attr_search_ctx(ctx);
+	unmap_mft_record(READ, ndir);
+	kfree(name);
+done:
+#ifdef DEBUG
+	if (!rc)
+		ntfs_debug("EOD, f_pos 0x%Lx, returning 0.", filp->f_pos);
+	else
+		ntfs_debug("filldir returned %i, f_pos 0x%Lx, returning 0.",
+				rc, filp->f_pos);
+#endif
 	return 0;
-read_err_ret:
-	if (!err)
-		err = -EIO;
-	ntfs_error(__FUNCTION__ "(): Read failed. Returning error code %i.\n",
-			err);
-	ntfs_free(buf);
+map_page_err_out:
+	ntfs_error(sb, "Reading index allocation data failed.");
+	err = PTR_ERR(page);
+kf_unm_err_out:
+	kfree(name);
+put_unm_err_out:
+	put_attr_search_ctx(ctx);
+unm_err_out:
+	unmap_mft_record(READ, ndir);
+err_out:
+	ntfs_debug("Failed. Returning error code %i.", -err);
 	return err;
+unm_dir_err_out:
+	ntfs_unmap_page(page);
+dir_err_out:
+	ntfs_error(sb, "Corrupt directory. Aborting. You should run chkdsk.");
+	err = -EIO;
+	goto kf_unm_err_out;
 }
 
-int ntfs_dir_add(ntfs_inode *dir, ntfs_inode *new, ntfs_attribute *name)
-{
-	ntfs_iterate_s walk;
-	int nsize, esize;
-	ntfs_u8* entry, *ndata;
-	int error;
-
-	walk.type = DIR_INSERT;
-	walk.dir = dir;
-	walk.u.flags = 0;
-	nsize = name->size;
-	ndata = name->d.data;
-	walk.name = (ntfs_u16*)(ndata + 0x42);
-	walk.namelen = NTFS_GETU8(ndata + 0x40);
-	walk.new_entry_size = esize = (nsize + 0x10 + 7) & ~7;
-	walk.new_entry = entry = ntfs_malloc(esize);
-	if (!entry)
-		return -ENOMEM;
-	NTFS_PUTINUM(entry, new);
-	NTFS_PUTU16(entry + 0x8, esize); /* Size of entry. */
-	NTFS_PUTU16(entry + 0xA, nsize); /* Size of original name attribute. */
-	NTFS_PUTU16(entry + 0xC, 0);     /* Flags. */
-	NTFS_PUTU16(entry + 0xE, 0);	 /* Reserved. */
-	ntfs_memcpy(entry + 0x10, ndata, nsize);
-	ntfs_bzero(entry + 0x10 + nsize, esize - 0x10 - nsize);
-	error = ntfs_getdir(&walk);
-	if (walk.new_entry)
-		ntfs_free(walk.new_entry);
-	return error;
-}
-
-#if 0
-int ntfs_dir_add1(ntfs_inode *dir, const char* name, int namelen,
-		  ntfs_inode *ino)
-{
-	ntfs_iterate_s walk;
-	int error;
-	int nsize;
-	char *entry;
-	ntfs_attribute *name_attr;
-	error = ntfs_decodeuni(dir->vol, name, namelen, &walk.name,
-			       &walk.namelen);
-	if (error)
-		return error;
-	/* FIXME: Set flags. */
-	walk.type = DIR_INSERT;
-	walk.dir = dir;
-	/* walk.new = ino; */
-	/* Prepare new entry. */
-	/* Round up to a multiple of 8. */
-	walk.new_entry_size = nsize = ((0x52 + 2 * walk.namelen + 7) / 8) * 8;
-	walk.new_entry = entry = ntfs_malloc(nsize);
-	if (!entry)
-		return -ENOMEM;
-	ntfs_bzero(entry, nsize);
-	NTFS_PUTINUM(entry, ino);
-	NTFS_PUTU16(entry + 8, nsize);
-	NTFS_PUTU16(entry + 0xA, 0x42 + 2 * namelen); /* FIXME: Size of name 
-						       * attribute. */
-	NTFS_PUTU32(entry + 0xC, 0); /* FIXME: D-F? */
-	name_attr = ntfs_find_attr(ino, vol->at_file_name, 0);
-						    /* FIXME: multiple names */
-	if (!name_attr || !name_attr->resident)
-		return -EIDRM;
-	/* Directory, file stamps, sizes, filename. */
-	ntfs_memcpy(entry + 0x10, name_attr->d.data, 0x42 + 2 * namelen);
-	error = ntfs_getdir(&walk);
-	ntfs_free(walk.name);
-	return error;
-}
-#endif
-
-/* Fills out and creates an INDEX_ROOT attribute. */
-int ntfs_add_index_root(ntfs_inode *ino, int type)
-{
-	ntfs_attribute *da;
-	ntfs_u8 data[0x30]; /* 0x20 header, 0x10 last entry. */
-	char name[10];
-
-	NTFS_PUTU32(data, type);
-	/* Collation rule. 1 == COLLATION_FILENAME */
-	NTFS_PUTU32(data + 4, 1);
-	NTFS_PUTU32(data + 8, ino->vol->index_record_size);
-	NTFS_PUTU32(data + 0xC, ino->vol->index_clusters_per_record);
-	/* Byte offset to first INDEX_ENTRY. */
-	NTFS_PUTU32(data + 0x10, 0x10);
-	/* Size of entries, including header. */
-	NTFS_PUTU32(data + 0x14, 0x20);
-	NTFS_PUTU32(data + 0x18, 0x20);
-	/* No index allocation, yet. */
-	NTFS_PUTU32(data + 0x1C, 0);
-	/* Add last entry. */
-	/* Indexed MFT record. */
-	NTFS_PUTU64(data + 0x20, 0);
-	/* Size of entry. */
-	NTFS_PUTU32(data + 0x28, 0x10);
-	/* Flags: Last entry, no child nodes. */
-	NTFS_PUTU32(data + 0x2C, 2);
-	/* Compute name. */
-	ntfs_indexname(name, type);
-	return ntfs_create_attr(ino, ino->vol->at_index_root, name,
-				data, sizeof(data), &da);
-}
-
-int ntfs_mkdir(ntfs_inode *dir, const char *name, int namelen,
-	       ntfs_inode *result)
-{
-	int error;
-	
-	error = ntfs_alloc_inode(dir, result, name, namelen, NTFS_AFLAG_DIR);
-	if (error)
-		goto out;
-	error = ntfs_add_index_root(result, 0x30);
-	if (error)
-		goto out;
-	/* Set directory bit. */
-	result->attr[0x16] |= 2;
-	error = ntfs_update_inode(dir);
-	if (error)
-		goto out;
-	error = ntfs_update_inode(result);
-	if (error)
-		goto out;
- out:
-	return error;
-}
+struct file_operations ntfs_dir_ops = {
+	read:			generic_read_dir,	/* Return -EISDIR. */
+	readdir:		ntfs_readdir,		/* Read directory. */
+};
 
diff -Nru a/fs/ntfs/dir.h b/fs/ntfs/dir.h
--- a/fs/ntfs/dir.h	Sun Apr 28 20:10:30 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,48 +0,0 @@
-/*
- * dir.h - Header file for dir.c
- *
- * Copyright (C) 1997 Régis Duchesne
- */
-#define ITERATE_SPLIT_DONE      1
-
-enum ntfs_iterate_e {
-    BY_POSITION,
-    BY_NAME,
-    DIR_INSERT
-};
-
-/* not all fields are used for all operations */
-typedef struct ntfs_iterate_s {
-	enum ntfs_iterate_e type;
-	ntfs_inode *dir;
-	union{
-		ntfs_u64 pos;
-		int flags;
-	}u;
-	char *result;      /* pointer to entry if found */
-	ntfs_u16* name;
-	int namelen;
-	int block;         /* current index record */
-	int newblock;      /* index record created in a split */
-	char *new_entry;
-	int new_entry_size;
-	/*ntfs_inode* new;*/
-} ntfs_iterate_s;
-
-int ntfs_getdir_unsorted(ntfs_inode *ino, ntfs_u32 *p_high, ntfs_u32* p_low,
-			 int (*cb)(ntfs_u8*, void*), void *param);
-
-int ntfs_getdir_byname(ntfs_iterate_s *walk);
-
-int ntfs_dir_add(ntfs_inode *dir, ntfs_inode *new, ntfs_attribute *name);
-
-int ntfs_check_index_record(ntfs_inode *ino, char *record);
-
-int ntfs_getdir_byposition(ntfs_iterate_s *walk);
-
-int ntfs_mkdir(ntfs_inode* dir,const char* name,int namelen, ntfs_inode *ino);
-
-int ntfs_split_indexroot(ntfs_inode *ino);
-
-int ntfs_add_index_root(ntfs_inode *ino, int type);
-
diff -Nru a/fs/ntfs/endian.h b/fs/ntfs/endian.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/fs/ntfs/endian.h	Sun Apr 28 20:10:39 2002
@@ -0,0 +1,48 @@
+/*
+ * endian.h - Defines for endianness handling in NTFS Linux kernel driver.
+ *	      Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2001 Anton Altaparmakov.
+ *
+ * This program/include file 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/include file 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 (in the main directory of the Linux-NTFS 
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _LINUX_NTFS_ENDIAN_H
+#define _LINUX_NTFS_ENDIAN_H
+
+#include <asm/byteorder.h>
+
+/*
+ * Signed endianness conversion defines.
+ */
+#define sle16_to_cpu(x)		((s16)__le16_to_cpu((s16)(x)))
+#define sle32_to_cpu(x)		((s32)__le32_to_cpu((s32)(x)))
+#define sle64_to_cpu(x)		((s64)__le64_to_cpu((s64)(x)))
+
+#define sle16_to_cpup(x)	((s16)__le16_to_cpu(*(s16*)(x)))
+#define sle32_to_cpup(x)	((s32)__le32_to_cpu(*(s32*)(x)))
+#define sle64_to_cpup(x)	((s64)__le64_to_cpu(*(s64*)(x)))
+
+#define cpu_to_sle16(x)		((s16)__cpu_to_le16((s16)(x)))
+#define cpu_to_sle32(x)		((s32)__cpu_to_le32((s32)(x)))
+#define cpu_to_sle64(x)		((s64)__cpu_to_le64((s64)(x)))
+
+#define cpu_to_sle16p(x)	((s16)__cpu_to_le16(*(s16*)(x)))
+#define cpu_to_sle32p(x)	((s32)__cpu_to_le32(*(s32*)(x)))
+#define cpu_to_sle64p(x)	((s64)__cpu_to_le64(*(s64*)(x)))
+
+#endif /* _LINUX_NTFS_ENDIAN_H */
+
diff -Nru a/fs/ntfs/file.c b/fs/ntfs/file.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/fs/ntfs/file.c	Sun Apr 28 20:10:39 2002
@@ -0,0 +1,150 @@
+/*
+ * file.c - NTFS kernel file operations. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2001 Anton Altaparmakov.
+ *
+ * This program/include file 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/include file 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 (in the main directory of the Linux-NTFS 
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "ntfs.h"
+
+struct file_operations ntfs_file_ops = {
+	llseek:			generic_file_llseek,	/* Seek inside file. */
+	read:			generic_file_read,	/* Read from file. */
+	write:			NULL,			/* . */
+	readdir:		NULL,			/* . */
+	poll:			NULL,			/* . */
+	ioctl:			NULL,			/* . */
+	mmap:			generic_file_mmap,	/* Mmap file. */
+	open:			generic_file_open,	/* Open file. */
+	flush:			NULL,			/* . */
+	release:		NULL,			/* . */
+	fsync:			NULL,			/* . */
+	fasync:			NULL,			/* . */
+	lock:			NULL,			/* . */
+	readv:			NULL,			/* . */
+	writev:			NULL,			/* . */
+	sendpage:		NULL,			/* . */
+	get_unmapped_area:	NULL,			/* . */
+};
+
+struct inode_operations ntfs_file_inode_ops = {
+	create:		NULL,		/* . */
+	lookup:		NULL,		/* . */
+	link:		NULL,		/* . */
+	unlink:		NULL,		/* . */
+	symlink:	NULL,		/* . */
+	mkdir:		NULL,		/* . */
+	rmdir:		NULL,		/* . */
+	mknod:		NULL,		/* . */
+	rename:		NULL,		/* . */
+	readlink:	NULL,		/* . */
+	follow_link:	NULL,		/* . */
+	truncate:	NULL,		/* . */
+	permission:	NULL,		/* . */
+	revalidate:	NULL,		/* . */
+	setattr:	NULL,		/* . */
+	getattr:	NULL,		/* . */
+};
+
+#if 0
+/* NOTE: read, write, poll, fsync, readv, writev can be called without the big
+ * kernel lock held in all filesystems. */
+struct file_operations {
+	struct module *owner;
+	loff_t (*llseek) (struct file *, loff_t, int);
+	ssize_t (*read) (struct file *, char *, size_t, loff_t *);
+	ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
+	int (*readdir) (struct file *, void *, filldir_t);
+	unsigned int (*poll) (struct file *, struct poll_table_struct *);
+	int (*ioctl) (struct inode *, struct file *, unsigned int,
+			unsigned long);
+	int (*mmap) (struct file *, struct vm_area_struct *);
+	int (*flush) (struct file *);
+	int (*release) (struct inode *, struct file *);
+	int (*fsync) (struct file *, struct dentry *, int datasync);
+	int (*fasync) (int, struct file *, int);
+	int (*lock) (struct file *, int, struct file_lock *);
+	ssize_t (*readv) (struct file *, const struct iovec *, unsigned long,
+			loff_t *);
+	ssize_t (*writev) (struct file *, const struct iovec *, unsigned long,
+			loff_t *);
+	ssize_t (*sendpage) (struct file *, struct page *, int, size_t,
+			loff_t *, int);
+	unsigned long (*get_unmapped_area)(struct file *, unsigned long,
+			unsigned long, unsigned long, unsigned long);
+};
+
+struct inode_operations {
+	int (*create) (struct inode *,struct dentry *,int);
+	struct dentry * (*lookup) (struct inode *,struct dentry *);
+	int (*link) (struct dentry *,struct inode *,struct dentry *);
+	int (*unlink) (struct inode *,struct dentry *);
+	int (*symlink) (struct inode *,struct dentry *,const char *);
+	int (*mkdir) (struct inode *,struct dentry *,int);
+	int (*rmdir) (struct inode *,struct dentry *);
+	int (*mknod) (struct inode *,struct dentry *,int,int);
+	int (*rename) (struct inode *, struct dentry *,
+			struct inode *, struct dentry *);
+	int (*readlink) (struct dentry *, char *,int);
+	int (*follow_link) (struct dentry *, struct nameidata *);
+	void (*truncate) (struct inode *);
+	int (*permission) (struct inode *, int);
+	int (*revalidate) (struct dentry *);
+	int (*setattr) (struct dentry *, struct iattr *);
+	int (*getattr) (struct dentry *, struct iattr *);
+};
+#endif
+
+struct file_operations ntfs_empty_file_ops = {
+	llseek:			NULL,			/* . */
+	read:			NULL,			/* . */
+	write:			NULL,			/* . */
+	readdir:		NULL,			/* . */
+	poll:			NULL,			/* . */
+	ioctl:			NULL,			/* . */
+	mmap:			NULL,			/* . */
+	open:			NULL,			/* . */
+	flush:			NULL,			/* . */
+	release:		NULL,			/* . */
+	fsync:			NULL,			/* . */
+	fasync:			NULL,			/* . */
+	lock:			NULL,			/* . */
+	readv:			NULL,			/* . */
+	writev:			NULL,			/* . */
+	sendpage:		NULL,			/* . */
+	get_unmapped_area:	NULL,			/* . */
+};
+
+struct inode_operations ntfs_empty_inode_ops = {
+	create:		NULL,		/* . */
+	lookup:		NULL,		/* . */
+	link:		NULL,		/* . */
+	unlink:		NULL,		/* . */
+	symlink:	NULL,		/* . */
+	mkdir:		NULL,		/* . */
+	rmdir:		NULL,		/* . */
+	mknod:		NULL,		/* . */
+	rename:		NULL,		/* . */
+	readlink:	NULL,		/* . */
+	follow_link:	NULL,		/* . */
+	truncate:	NULL,		/* . */
+	permission:	NULL,		/* . */
+	revalidate:	NULL,		/* . */
+	setattr:	NULL,		/* . */
+	getattr:	NULL,		/* . */
+};
+
diff -Nru a/fs/ntfs/fs.c b/fs/ntfs/fs.c
--- a/fs/ntfs/fs.c	Sun Apr 28 20:10:32 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,1237 +0,0 @@
-/*
- * fs.c - NTFS driver for Linux 2.4.x
- *
- * Legato Systems, Inc. (http://www.legato.com) have sponsored Anton
- * Altaparmakov to develop NTFS on Linux since June 2001.
- *
- * Copyright (C) 1995-1997, 1999 Martin von Löwis
- * Copyright (C) 1996 Richard Russon
- * Copyright (C) 1996-1997 Régis Duchesne
- * Copyright (C) 2000-2001, Anton Altaparmakov (AIA)
- */
-
-#include <linux/config.h>
-#include <linux/errno.h>
-#include "ntfstypes.h"
-#include "struct.h"
-#include "util.h"
-#include "inode.h"
-#include "super.h"
-#include "dir.h"
-#include "support.h"
-#include "macros.h"
-#include "sysctl.h"
-#include "attr.h"
-#include <linux/module.h>
-#include <asm/uaccess.h>
-#include <linux/locks.h>
-#include <linux/init.h>
-#include <linux/smp_lock.h>
-#include <linux/blkdev.h>
-#include <asm/page.h>
-#include <linux/nls.h>
-#include <linux/ntfs_fs.h>
-
-/* Forward declarations. */
-static struct inode_operations ntfs_dir_inode_operations;
-static struct file_operations ntfs_dir_operations;
-
-#define ITEM_SIZE 2040
-
-/* Io functions to user space. */
-static void ntfs_putuser(ntfs_io* dest, void *src, ntfs_size_t len)
-{
-	copy_to_user(dest->param, src, len);
-	dest->param += len;
-}
-
-#ifdef CONFIG_NTFS_RW
-struct ntfs_getuser_update_vm_s {
-	const char *user;
-	struct inode *ino;
-	loff_t off;
-};
-
-static void ntfs_getuser_update_vm(void *dest, ntfs_io *src, ntfs_size_t len)
-{
-	struct ntfs_getuser_update_vm_s *p = src->param;
-	
-	copy_from_user(dest, p->user, len);
-	p->user += len;
-	p->off += len;
-}
-#endif
-
-/* loff_t is 64 bit signed, so is cool. */
-static ssize_t ntfs_read(struct file *filp, char *buf, size_t count,loff_t *off)
-{
-	int error;
-	ntfs_io io;
-	ntfs_attribute *attr;
-	ntfs_inode *ino = NTFS_I(filp->f_dentry->d_inode);
-
-	/* Inode is not properly initialized. */
-	if (!ino)
-		return -EINVAL;
-	ntfs_debug(DEBUG_OTHER, "ntfs_read %x, %Lx, %x ->",
-		   (unsigned)ino->i_number, (unsigned long long)*off,
-		   (unsigned)count);
-	attr = ntfs_find_attr(ino, ino->vol->at_data, NULL);
-	/* Inode has no unnamed data attribute. */
-	if (!attr) {
-		ntfs_debug(DEBUG_OTHER, "ntfs_read: $DATA not found!\n");
-		return -EINVAL;
-	}
-	if (attr->flags & ATTR_IS_ENCRYPTED)
-		return -EACCES;
-	/* Read the data. */
-	io.fn_put = ntfs_putuser;
-	io.fn_get = 0;
-	io.param = buf;
-	io.size = count;
-	error = ntfs_read_attr(ino, ino->vol->at_data, NULL, *off, &io);
-	if (error && !io.size) {
-		ntfs_debug(DEBUG_OTHER, "ntfs_read: read_attr failed with "
-				"error %i, io size %u.\n", error, io.size);
-		return error;
-	}
-	*off += io.size;
-	ntfs_debug(DEBUG_OTHER, "ntfs_read: finished. read %u bytes.\n",
-								io.size);
-	return io.size;
-}
-
-#ifdef CONFIG_NTFS_RW
-static ssize_t ntfs_write(struct file *filp, const char *buf, size_t count,
-		loff_t *pos)
-{
-	int err;
-	struct inode *vfs_ino = filp->f_dentry->d_inode;
-	ntfs_inode *ntfs_ino = NTFS_I(vfs_ino);
-	ntfs_attribute *data;
-	ntfs_io io;
-	struct ntfs_getuser_update_vm_s param;
-
-	if (!ntfs_ino)
-		return -EINVAL;
-	ntfs_debug(DEBUG_LINUX, __FUNCTION__ "(): Entering for inode 0x%lx, "
-			"*pos 0x%Lx, count 0x%x.\n", ntfs_ino->i_number, *pos,
-			count);
-	/* Allows to lock fs ro at any time. */
-	if (vfs_ino->i_sb->s_flags & MS_RDONLY)
-		return -EROFS;
-	data = ntfs_find_attr(ntfs_ino, ntfs_ino->vol->at_data, NULL);
-	if (!data)
-		return -EINVAL;
-	/* Evaluating O_APPEND is the file system's job... */
-	if (filp->f_flags & O_APPEND)
-		*pos = vfs_ino->i_size;
-	if (!data->resident && *pos + count > data->allocated) {
-		err = ntfs_extend_attr(ntfs_ino, data, *pos + count);
-		if (err < 0)
-			return err;
-	}
-	param.user = buf;
-	param.ino = vfs_ino;
-	param.off = *pos;
-	io.fn_put = 0;
-	io.fn_get = ntfs_getuser_update_vm;
-	io.param = &param;
-	io.size = count;
-	io.do_read = 0;
-	err = ntfs_readwrite_attr(ntfs_ino, data, *pos, &io);
-	ntfs_debug(DEBUG_LINUX, __FUNCTION__ "(): Returning %i\n", -err);
-	if (!err) {
-		*pos += io.size;
-		if (*pos > vfs_ino->i_size)
-			vfs_ino->i_size = *pos;
-		mark_inode_dirty(vfs_ino);
-		return io.size;
-	}
-	return err;
-}
-#endif
-
-struct ntfs_filldir {
-	struct inode *dir;
-	filldir_t filldir;
-	unsigned int type;
-	u32 ph, pl;
-	void *dirent;
-	char *name;
-	int namelen;
-	int ret_code;
-};
-
-static int ntfs_printcb(ntfs_u8 *entry, void *param)
-{
-	unsigned long inum = NTFS_GETU64(entry) & 0xffffffffffff;
-	struct ntfs_filldir *nf = param;
-	u32 flags = NTFS_GETU32(entry + 0x48);
-	char show_sys_files = 0;
-	u8 name_len = NTFS_GETU8(entry + 0x50);
-	u8 name_type = NTFS_GETU8(entry + 0x51);
-	int err;
-	unsigned file_type;
-
-	switch (nf->type) {
-	case ngt_dos:
-		/* Don't display long names. */
-		if (!(name_type & 2))
-			return 0;
-		break;
-	case ngt_nt:
-		/* Don't display short-only names. */
-		if ((name_type & 3) == 2)
-			return 0;
-		break;
-	case ngt_posix:
-		break;
-	case ngt_full:
-		show_sys_files = 1;
-		break;
-	default:
-		BUG();
-	}
-	err = ntfs_encodeuni(NTFS_INO2VOL(nf->dir), (ntfs_u16*)(entry + 0x52),
-			name_len, &nf->name, &nf->namelen);
-	if (err) {
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Skipping "
-				"unrepresentable file.\n");
-		err = 0;
-		goto err_ret;
-	}
-	if (!show_sys_files && inum < 0x10UL) {
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Skipping system "
-				"file (%s).\n", nf->name);
-		err = 0;
-		goto err_ret;
-	}
-	/* Do not return ".", as this is faked. */
-	if (nf->namelen == 1 && nf->name[0] == '.') {
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Skipping \".\"\n");
-		err = 0;
-		goto err_ret;
-	}
-	nf->name[nf->namelen] = 0;
-	if (flags & 0x10000000) /* FILE_ATTR_DUP_FILE_NAME_INDEX_PRESENT */
-		file_type = DT_DIR;
-	else
-		file_type = DT_REG;
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Calling filldir for %s with "
-			"len %i, f_pos 0x%Lx, inode %lu, %s.\n",
-			nf->name, nf->namelen, (loff_t)(nf->ph << 16) | nf->pl,
-			inum, file_type == DT_DIR ? "DT_DIR" : "DT_REG");
-	/*
-	 * Userspace side of filldir expects an off_t rather than an loff_t.
-	 * And it also doesn't like the most significant bit being set as it
-	 * then considers the value to be negative. Thus this implementation
-	 * limits the number of index records to 32766, which should be plenty.
-	 */
-	err = nf->filldir(nf->dirent, nf->name, nf->namelen,
-			(loff_t)(nf->ph << 16) | nf->pl, inum, file_type);
-	if (err)
-		nf->ret_code = err;
-err_ret:
-	nf->namelen = 0;
-	ntfs_free(nf->name);
-	nf->name = NULL;
-	return err;
-}
-
-/*
- * readdir returns '.', then '..', then the directory entries in sequence.
- * As the root directory contains an entry for itself, '.' is not emulated for
- * the root directory.
- */
-static int ntfs_readdir(struct file* filp, void *dirent, filldir_t filldir)
-{
-	struct inode *dir = filp->f_dentry->d_inode;
-	int err;
-	struct ntfs_filldir cb;
-
-	cb.ret_code = 0;
-	cb.pl = filp->f_pos & 0xffff;
-	cb.ph = (filp->f_pos >> 16) & 0x7fff;
-	filp->f_pos = (loff_t)(cb.ph << 16) | cb.pl;
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Entering for inode %lu, "
-			"f_pos 0x%Lx, i_mode 0x%x, i_count %lu.\n", dir->i_ino,
-			filp->f_pos, (unsigned int)dir->i_mode,
-			atomic_read(&dir->i_count));
-	if (!cb.ph) {
-		/* Start of directory. Emulate "." and "..". */
-		if (!cb.pl) {
-			ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Calling "
-				    "filldir for . with len 1, f_pos 0x%Lx, "
-				    "inode %lu, DT_DIR.\n", filp->f_pos,
-				    dir->i_ino);
-			cb.ret_code = filldir(dirent, ".", 1, filp->f_pos,
-				    dir->i_ino, DT_DIR);
-			if (cb.ret_code)
-				goto done;
-			cb.pl++;
-			filp->f_pos = (loff_t)(cb.ph << 16) | cb.pl;
-		}
-		if (cb.pl == (u32)1) {
-			ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Calling "
-				    "filldir for .. with len 2, f_pos 0x%Lx, "
-				    "inode %lu, DT_DIR.\n", filp->f_pos,
-				    parent_ino(filp->f_dentry));
-			cb.ret_code = filldir(dirent, "..", 2, filp->f_pos,
-				    parent_ino(filp->f_dentry), DT_DIR);
-			if (cb.ret_code)
-				goto done;
-			cb.pl++;
-			filp->f_pos = (loff_t)(cb.ph << 16) | cb.pl;
-		}
-	} else if (cb.ph >= 0x7fff)
-		/* End of directory. */
-		goto done;
-	cb.dir = dir;
-	cb.filldir = filldir;
-	cb.dirent = dirent;
-	cb.type = NTFS_INO2VOL(dir)->ngt;
-	do {
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Looking for next "
-				"file using ntfs_getdir_unsorted(), f_pos "
-				"0x%Lx.\n", (loff_t)(cb.ph << 16) | cb.pl);
-		err = ntfs_getdir_unsorted(NTFS_I(dir), &cb.ph, &cb.pl,
-				ntfs_printcb, &cb);
-	} while (!err && !cb.ret_code && cb.ph < 0x7fff);
-	filp->f_pos = (loff_t)(cb.ph << 16) | cb.pl;
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After ntfs_getdir_unsorted()"
-			" calls, f_pos 0x%Lx.\n", filp->f_pos);
-	if (!err) {
-done:
-#ifdef DEBUG
-		if (!cb.ret_code)
-			ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): EOD, f_pos "
-					"0x%Lx, returning 0.\n", filp->f_pos);
-		else 
-			ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): filldir "
-					"returned %i, returning 0, f_pos "
-					"0x%Lx.\n", cb.ret_code, filp->f_pos);
-#endif
-		return 0;
-	}
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Returning %i, f_pos 0x%Lx.\n",
-			err, filp->f_pos);
-	return err;
-}
-
-/* Copied from vfat driver. */
-static int simple_getbool(char *s, int *setval)
-{
-	if (s) {
-		if (!strcmp(s, "1") || !strcmp(s, "yes") || !strcmp(s, "true"))
-			*setval = 1;
-		else if (!strcmp(s, "0") || !strcmp(s, "no") ||
-							!strcmp(s, "false"))
-			*setval = 0;
-		else
-			return 0;
-	} else
-		*setval = 1;
-	return 1;
-}
-
-/*
- * This needs to be outside parse_options() otherwise a remount will reset
- * these unintentionally.
- */
-static void init_ntfs_super_block(ntfs_volume* vol)
-{
-	vol->uid = vol->gid = 0;
-	vol->umask = 0077;
-	vol->ngt = ngt_nt;
-	vol->nls_map = (void*)-1;
-	vol->mft_zone_multiplier = -1;
-}
-
-/* Parse the (re)mount options. */
-static int parse_options(ntfs_volume *vol, char *opt)
-{
-	char *value;		/* Defaults if not specified and !remount. */
-	ntfs_uid_t uid = -1;	/* 0, root user only */
-	ntfs_gid_t gid = -1;	/* 0, root user only */
-	int umask = -1;		/* 0077, owner access only */
-	unsigned int ngt = -1;	/* ngt_nt */
-	void *nls_map = NULL;	/* Try to load the default NLS. */
-	int use_utf8 = -1;	/* If no NLS specified and loading the default
-				   NLS failed use utf8. */
-	int mft_zone_mul = -1;	/* 1 */
-	char *opts = opt;
-
-	if (!opt)
-		goto done;
-	while ((opt = strsep(&opts, ",")) != NULL) {
-		if (!*opt)
-			continue;
-		if ((value = strchr(opt, '=')) != NULL)
-			*value ++= '\0';
-		if (strcmp(opt, "uid") == 0) {
-			if (!value || !*value)
-				goto needs_arg;
-			uid = simple_strtoul(value, &value, 0);
-			if (*value) {
-				printk(KERN_ERR "NTFS: uid invalid argument\n");
-				return 0;
-			}
-		} else if (strcmp(opt, "gid") == 0) {
-			if (!value || !*value)
-				goto needs_arg;
-			gid = simple_strtoul(value, &value, 0);
-			if (*value) {
-				printk(KERN_ERR "NTFS: gid invalid argument\n");
-				return 0;
-			}
-		} else if (strcmp(opt, "umask") == 0) {
-			if (!value || !*value)
-				goto needs_arg;
-			umask = simple_strtoul(value, &value, 0);
-			if (*value) {
-				printk(KERN_ERR "NTFS: umask invalid "
-						"argument\n");
-				return 0;
-			}
-		} else if (strcmp(opt, "mft_zone_multiplier") == 0) {
-			unsigned long ul;
-
-			if (!value || !*value)
-				goto needs_arg;
-			ul = simple_strtoul(value, &value, 0);
-			if (*value) {
-				printk(KERN_ERR "NTFS: mft_zone_multiplier "
-						"invalid argument\n");
-				return 0;
-			}
-			if (ul >= 1 && ul <= 4)
-				mft_zone_mul = ul;
-			else {
-				mft_zone_mul = 1;
-				printk(KERN_WARNING "NTFS: mft_zone_multiplier "
-					      "out of range. Setting to 1.\n");
-			}
-		} else if (strcmp(opt, "posix") == 0) {
-			int val;
-			if (!value || !*value)
-				goto needs_arg;
-			if (!simple_getbool(value, &val))
-				goto needs_bool;
-			ngt = val ? ngt_posix : ngt_nt;
-		} else if (strcmp(opt, "show_sys_files") == 0) {
-			int val = 0;
-			if (!value || !*value)
-				val = 1;
-			else if (!simple_getbool(value, &val))
-				goto needs_bool;
-			ngt = val ? ngt_full : ngt_nt;
-		} else if (strcmp(opt, "iocharset") == 0) {
-			if (!value || !*value)
-				goto needs_arg;
-			nls_map = load_nls(value);
-			if (!nls_map) {
-				printk(KERN_ERR "NTFS: charset not found");
-				return 0;
-			}
-		} else if (strcmp(opt, "utf8") == 0) {
-			int val = 0;
-			if (!value || !*value)
-				val = 1;
-			else if (!simple_getbool(value, &val))
-				goto needs_bool;
-			use_utf8 = val;
-		} else {
-			printk(KERN_ERR "NTFS: unkown option '%s'\n", opt);
-			return 0;
-		}
-	}
-done:
-	if (use_utf8 == -1) {
-		/* utf8 was not specified at all. */
-		if (!nls_map) {
-			/*
-			 * No NLS was specified. If first mount, load the
-			 * default NLS, otherwise don't change the NLS setting.
-			 */
-			if (vol->nls_map == (void*)-1)
-				vol->nls_map = load_nls_default();
-		} else {
-			/* If an NLS was already loaded, unload it first. */
-			if (vol->nls_map && vol->nls_map != (void*)-1)
-				unload_nls(vol->nls_map);
-			/* Use the specified NLS. */
-			vol->nls_map = nls_map;
-		}
-	} else {
-		/* utf8 was specified. */
-		if (use_utf8 && nls_map) {
-			unload_nls(nls_map);
-			printk(KERN_ERR "NTFS: utf8 cannot be combined with "
-					"iocharset.\n");
-			return 0;
-		}
-		/* If an NLS was already loaded, unload it first. */
-		if (vol->nls_map && vol->nls_map != (void*)-1)
-			unload_nls(vol->nls_map);
-		if (!use_utf8) {
-			/* utf8 was specified as false. */
-			if (!nls_map)
-				/* No NLS was specified, load the default. */
-				vol->nls_map = load_nls_default();
-			else
-				/* Use the specified NLS. */
-				vol->nls_map = nls_map;
-		} else
-			/* utf8 was specified as true. */
-			vol->nls_map = NULL;
-	}
-	if (uid != -1)
-		vol->uid = uid;
-	if (gid != -1)
-		vol->gid = gid;
-	if (umask != -1)
-		vol->umask = (ntmode_t)umask;
-	if (ngt != -1)
-		vol->ngt = ngt;
-	if (mft_zone_mul != -1) {
-		/* mft_zone_multiplier was specified. */
-		if (vol->mft_zone_multiplier != -1) {
-			/* This is a remount, ignore a change and warn user. */
-			if (vol->mft_zone_multiplier != mft_zone_mul)
-				printk(KERN_WARNING "NTFS: Ignoring changes in "
-						"mft_zone_multiplier on "
-						"remount. If you want to "
-						"change this you need to "
-						"umount and mount again.\n");
-		} else
-			/* Use the specified multiplier. */
-			vol->mft_zone_multiplier = mft_zone_mul;
-	} else if (vol->mft_zone_multiplier == -1)
-		/* No multiplier specified and first mount, so set default. */
-		vol->mft_zone_multiplier = 1;
-	return 1;
-needs_arg:
-	printk(KERN_ERR "NTFS: %s needs an argument", opt);
-	return 0;
-needs_bool:
-	printk(KERN_ERR "NTFS: %s needs boolean argument", opt);
-	return 0;
-}
-			
-static struct dentry *ntfs_lookup(struct inode *dir, struct dentry *d)
-{
-	struct inode *res = 0;
-	char *item = 0;
-	ntfs_iterate_s walk;
-	int err;
-	
-	ntfs_debug(DEBUG_NAME1, __FUNCTION__ "(): Looking up %s in directory "
-			"ino 0x%x.\n", d->d_name.name, (unsigned)dir->i_ino);
-	walk.name = NULL;
-	walk.namelen = 0;
-	/* Convert to wide string. */
-	lock_kernel();
-	err = ntfs_decodeuni(NTFS_INO2VOL(dir), (char*)d->d_name.name,
-			       d->d_name.len, &walk.name, &walk.namelen);
-	if (err)
-		goto err_ret;
-	item = ntfs_malloc(ITEM_SIZE);
-	if (!item) {
-		err = -ENOMEM;
-		goto err_ret;
-	}
-	/* ntfs_getdir will place the directory entry into item, and the first
-	 * long long is the MFT record number. */
-	walk.type = BY_NAME;
-	walk.dir = NTFS_I(dir);
-	walk.result = item;
-	if (ntfs_getdir_byname(&walk))
-		res = iget(dir->i_sb, NTFS_GETU32(item));
-	d_add(d, res);
-	ntfs_free(item);
-	ntfs_free(walk.name);
-	unlock_kernel();
-	/* Always return success, the dcache will handle negative entries. */
-	return NULL;
-err_ret:
-	ntfs_free(walk.name);
-	unlock_kernel();
-	return ERR_PTR(err);
-}
-
-static struct file_operations ntfs_file_operations = {
-	llseek:		generic_file_llseek,
-	read:		ntfs_read,
-#ifdef CONFIG_NTFS_RW
-	write:		ntfs_write,
-#endif
-	open:		generic_file_open,
-};
-
-static struct inode_operations ntfs_inode_operations;
-
-#ifdef CONFIG_NTFS_RW
-static int ntfs_create(struct inode* dir, struct dentry *d, int mode)
-{
-	struct inode *r = 0;
-	ntfs_inode *ino = 0;
-	ntfs_volume *vol;
-	int error = 0;
-	ntfs_attribute *si;
-
-	lock_kernel();
-	r = new_inode(dir->i_sb);
-	if (!r) {
-		error = -ENOMEM;
-		goto fail;
-	}
-	ntfs_debug(DEBUG_OTHER, "ntfs_create %s\n", d->d_name.name);
-	vol = NTFS_INO2VOL(dir);
-	ino = NTFS_I(r);
-	ino->u.index.recordsize = 0;
-	ino->u.index.clusters_per_record = 0;
-	error = ntfs_alloc_file(NTFS_I(dir), ino, (char*)d->d_name.name,
-				d->d_name.len);
-	if (error) {
-		ntfs_error("ntfs_alloc_file FAILED: error = %i", error);
-		goto fail;
-	}
-	/* Not doing this one was causing a huge amount of corruption! Now the
-	 * bugger bytes the dust! (-8 (AIA) */
-	r->i_ino = ino->i_number;
-	error = ntfs_update_inode(ino);
-	if (error)
-		goto fail;
-	error = ntfs_update_inode(NTFS_I(dir));
-	if (error)
-		goto fail;
-	r->i_uid = vol->uid;
-	r->i_gid = vol->gid;
-	/* FIXME: dirty? dev? */
-	/* Get the file modification times from the standard information. */
-	si = ntfs_find_attr(ino, vol->at_standard_information, NULL);
-	if (si) {
-		char *attr = si->d.data;
-		r->i_atime = ntfs_ntutc2unixutc(NTFS_GETU64(attr + 0x18));
-		r->i_ctime = ntfs_ntutc2unixutc(NTFS_GETU64(attr));
-		r->i_mtime = ntfs_ntutc2unixutc(NTFS_GETU64(attr + 8));
-	}
-	/* It's not a directory */
-	r->i_op = &ntfs_inode_operations;
-	r->i_fop = &ntfs_file_operations;
-	r->i_mode = S_IFREG | S_IRUGO;
-#ifdef CONFIG_NTFS_RW
-	r->i_mode |= S_IWUGO;
-#endif
-	r->i_mode &= ~vol->umask;
-	unlock_kernel();
-	insert_inode_hash(r);
-	d_instantiate(d, r);
-	return 0;
- fail:
-	unlock_kernel();
-	if (r)
-		iput(r);
-	return error;
-}
-
-static int _linux_ntfs_mkdir(struct inode *dir, struct dentry* d, int mode)
-{
-	int error;
-	struct inode *r = 0;
-	ntfs_volume *vol;
-	ntfs_inode *ino;
-	ntfs_attribute *si;
-
-	lock_kernel();
-	ntfs_debug (DEBUG_DIR1, "mkdir %s in %x\n", d->d_name.name, dir->i_ino);
-	error = -ENAMETOOLONG;
-	if (d->d_name.len > /* FIXME: */ 255)
-		goto out;
-	error = -EIO;
-	r = new_inode(dir->i_sb);
-	if (!r)
-		goto out;
-	vol = NTFS_INO2VOL(dir);
-	ino = NTFS_I(r);
-	error = ntfs_mkdir(NTFS_I(dir), d->d_name.name, d->d_name.len,
-			   ino);
-	if (error)
-		goto out;
-	/* Not doing this one was causing a huge amount of corruption! Now the
-	 * bugger bytes the dust! (-8 (AIA) */
-	r->i_ino = ino->i_number;
-	r->i_uid = vol->uid;
-	r->i_gid = vol->gid;
-	si = ntfs_find_attr(ino, vol->at_standard_information, NULL);
-	if (si) {
-		char *attr = si->d.data;
-		r->i_atime = ntfs_ntutc2unixutc(NTFS_GETU64(attr + 0x18));
-		r->i_ctime = ntfs_ntutc2unixutc(NTFS_GETU64(attr));
-		r->i_mtime = ntfs_ntutc2unixutc(NTFS_GETU64(attr + 8));
-	}
-	/* It's a directory. */
-	r->i_op = &ntfs_dir_inode_operations;
-	r->i_fop = &ntfs_dir_operations;
-	r->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
-#ifdef CONFIG_NTFS_RW
-	r->i_mode |= S_IWUGO;
-#endif
-	r->i_mode &= ~vol->umask;	
-	
-	insert_inode_hash(r);
-	d_instantiate(d, r);
-	error = 0;
- out:
- 	ntfs_debug (DEBUG_DIR1, "mkdir returns %d\n", error);
-	unlock_kernel();
-	return error;
-}
-#endif
-
-static struct file_operations ntfs_dir_operations = {
-	read:		generic_read_dir,
-	readdir:	ntfs_readdir,
-};
-
-static struct inode_operations ntfs_dir_inode_operations = {
-	lookup:		ntfs_lookup,
-#ifdef CONFIG_NTFS_RW
-	create:		ntfs_create,
-	mkdir:		_linux_ntfs_mkdir,
-#endif
-};
-
-/* ntfs_read_inode() is called by the Virtual File System (the kernel layer 
- * that deals with filesystems) when iget is called requesting an inode not
- * already present in the inode table. Typically filesystems have separate
- * inode_operations for directories, files and symlinks. */
-static void ntfs_read_inode(struct inode* inode)
-{
-	ntfs_volume *vol;
-	ntfs_inode *ino;
-	ntfs_attribute *data;
-	ntfs_attribute *si;
-
-	vol = NTFS_INO2VOL(inode);
-	inode->i_mode = 0;
-	ntfs_debug(DEBUG_OTHER, "ntfs_read_inode 0x%lx\n", inode->i_ino);
-	switch (inode->i_ino) {
-		/* Those are loaded special files. */
-	case FILE_Mft:
-		if (!vol->mft_ino || ((vol->ino_flags & 1) == 0))
-			goto sys_file_error;
-		ntfs_memcpy(NTFS_I(inode), vol->mft_ino, sizeof(ntfs_inode));
-		ino = vol->mft_ino;
-		vol->mft_ino = NTFS_I(inode);
-		vol->ino_flags &= ~1;
-		ntfs_free(ino);
-		ino = vol->mft_ino;
-		ntfs_debug(DEBUG_OTHER, "Opening $MFT!\n");
-		break;
-	case FILE_MftMirr:
-		if (!vol->mftmirr || ((vol->ino_flags & 2) == 0))
-			goto sys_file_error;
-		ntfs_memcpy(NTFS_I(inode), vol->mftmirr, sizeof(ntfs_inode));
-		ino = vol->mftmirr;
-		vol->mftmirr = NTFS_I(inode);
-		vol->ino_flags &= ~2;
-		ntfs_free(ino);
-		ino = vol->mftmirr;
-		ntfs_debug(DEBUG_OTHER, "Opening $MFTMirr!\n");
-		break;
-	case FILE_BitMap:
-		if (!vol->bitmap || ((vol->ino_flags & 4) == 0))
-			goto sys_file_error;
-		ntfs_memcpy(NTFS_I(inode), vol->bitmap, sizeof(ntfs_inode));
-		ino = vol->bitmap;
-		vol->bitmap = NTFS_I(inode);
-		vol->ino_flags &= ~4;
-		ntfs_free(ino);
-		ino = vol->bitmap;
-		ntfs_debug(DEBUG_OTHER, "Opening $Bitmap!\n");
-		break;
-	case FILE_LogFile ... FILE_AttrDef:
-	/* No need to log root directory accesses. */
-	case FILE_Boot ... FILE_UpCase:
-		ntfs_debug(DEBUG_OTHER, "Opening system file %i!\n",
-				inode->i_ino);
-	default:
-		ino = NTFS_I(inode);
-		ino->u.index.recordsize = 0;
-		ino->u.index.clusters_per_record = 0;
-		if (ntfs_init_inode(ino, NTFS_INO2VOL(inode), inode->i_ino)) {
-			ntfs_debug(DEBUG_OTHER, "NTFS: Error loading inode "
-					"0x%x\n", (unsigned int)inode->i_ino);
-			return;
-		}
-	}
-	/* Set uid/gid from mount options */
-	inode->i_uid = vol->uid;
-	inode->i_gid = vol->gid;
-	inode->i_nlink = 1;
-	/* Use the size of the data attribute as file size */
-	data = ntfs_find_attr(ino, vol->at_data, NULL);
-	if (!data)
-		inode->i_size = 0;
-	else
-		inode->i_size = data->size;
-	/* Get the file modification times from the standard information. */
-	si = ntfs_find_attr(ino, vol->at_standard_information, NULL);
-	if (si) {
-		char *attr = si->d.data;
-		inode->i_atime = ntfs_ntutc2unixutc(NTFS_GETU64(attr + 0x18));
-		inode->i_ctime = ntfs_ntutc2unixutc(NTFS_GETU64(attr));
-		inode->i_mtime = ntfs_ntutc2unixutc(NTFS_GETU64(attr + 8));
-	}
-	/* If it has an index root, it's a directory. */
-	if (ntfs_find_attr(ino, vol->at_index_root, "$I30")) {
-		ntfs_attribute *at;
-		at = ntfs_find_attr(ino, vol->at_index_allocation, "$I30");
-		inode->i_size = at ? at->size : 0;
-		inode->i_op = &ntfs_dir_inode_operations;
-		inode->i_fop = &ntfs_dir_operations;
-		inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
-	} else {
-		inode->i_op = &ntfs_inode_operations;
-		inode->i_fop = &ntfs_file_operations;
-		inode->i_mode = S_IFREG | S_IRUGO;
-	}
-#ifdef CONFIG_NTFS_RW
-	if (!data || !(data->flags & (ATTR_IS_COMPRESSED | ATTR_IS_ENCRYPTED)))
-		inode->i_mode |= S_IWUGO;
-#endif
-	inode->i_mode &= ~vol->umask;
-	return;
-sys_file_error:
-	ntfs_error("Critical error. Tried to call ntfs_read_inode() before we "
-		"have completed read_super() or VFS error.\n");
-	// FIXME: Should we panic() at this stage?
-}
-
-#ifdef CONFIG_NTFS_RW
-static void ntfs_write_inode(struct inode *ino, int unused)
-{
-	lock_kernel();
-	ntfs_debug(DEBUG_LINUX, "ntfs_write_inode 0x%x\n", ino->i_ino);
-	ntfs_update_inode(NTFS_I(ino));
-	unlock_kernel();
-}
-#endif
-
-static void _ntfs_clear_inode(struct inode *inode)
-{
-	ntfs_inode *ino;
-	ntfs_volume *vol;
-	
-	lock_kernel();
-	ntfs_debug(DEBUG_OTHER, "_ntfs_clear_inode 0x%x\n", inode->i_ino);
-	vol = NTFS_INO2VOL(inode);
-	if (!vol)
-		ntfs_error("_ntfs_clear_inode: vol = NTFS_INO2VOL(inode) is "
-				"NULL.\n");
-	switch (inode->i_ino) {
-	case FILE_Mft:
-		if (vol->mft_ino && ((vol->ino_flags & 1) == 0)) {
-			ino = (ntfs_inode*)ntfs_malloc(sizeof(ntfs_inode));
-			ntfs_memcpy(ino, NTFS_I(inode), sizeof(ntfs_inode));
-			vol->mft_ino = ino;
-			vol->ino_flags |= 1;
-			goto unl_out;
-		}
-		break;
-	case FILE_MftMirr:
-		if (vol->mftmirr && ((vol->ino_flags & 2) == 0)) {
-			ino = (ntfs_inode*)ntfs_malloc(sizeof(ntfs_inode));
-			ntfs_memcpy(ino, NTFS_I(inode), sizeof(ntfs_inode));
-			vol->mftmirr = ino;
-			vol->ino_flags |= 2;
-			goto unl_out;
-		}
-		break;
-	case FILE_BitMap:
-		if (vol->bitmap && ((vol->ino_flags & 4) == 0)) {
-			ino = (ntfs_inode*)ntfs_malloc(sizeof(ntfs_inode));
-			ntfs_memcpy(ino, NTFS_I(inode), sizeof(ntfs_inode));
-			vol->bitmap = ino;
-			vol->ino_flags |= 4;
-			goto unl_out;
-		}
-		break;
-	default:
-		/* Nothing. Just clear the inode and exit. */
-	}
-	ntfs_clear_inode(NTFS_I(inode));
-unl_out:
-	unlock_kernel();
-	return;
-}
-
-/* Called when umounting a filesystem by do_umount() in fs/super.c. */
-static void ntfs_put_super(struct super_block *sb)
-{
-	ntfs_volume *vol;
-
-	ntfs_debug(DEBUG_OTHER, "ntfs_put_super\n");
-	vol = NTFS_SB2VOL(sb);
-	ntfs_release_volume(vol);
-	if (vol->nls_map)
-		unload_nls(vol->nls_map);
-	ntfs_debug(DEBUG_OTHER, "ntfs_put_super: done\n");
-}
-
-/* Called by the kernel when asking for stats. */
-static int ntfs_statfs(struct super_block *sb, struct statfs *sf)
-{
-	struct inode *mft;
-	ntfs_volume *vol;
-	__s64 size;
-	int error;
-
-	ntfs_debug(DEBUG_OTHER, "ntfs_statfs\n");
-	vol = NTFS_SB2VOL(sb);
-	sf->f_type = NTFS_SUPER_MAGIC;
-	sf->f_bsize = vol->cluster_size;
-	error = ntfs_get_volumesize(NTFS_SB2VOL(sb), &size);
-	if (error)
-		return error;
-	sf->f_blocks = size;	/* Volumesize is in clusters. */
-	size = (__s64)ntfs_get_free_cluster_count(vol->bitmap);
-	/* Just say zero if the call failed. */
-	if (size < 0LL)
-		size = 0;
-	sf->f_bfree = sf->f_bavail = size;
-	ntfs_debug(DEBUG_OTHER, "ntfs_statfs: calling mft = iget(sb, "
-			"FILE_Mft)\n");
-	mft = iget(sb, FILE_Mft);
-	ntfs_debug(DEBUG_OTHER, "ntfs_statfs: iget(sb, FILE_Mft) returned "
-			"0x%x\n", mft);
-	if (!mft)
-		return -EIO;
-	sf->f_files = mft->i_size >> vol->mft_record_size_bits;
-	ntfs_debug(DEBUG_OTHER, "ntfs_statfs: calling iput(mft)\n");
-	iput(mft);
-	/* Should be read from volume. */
-	sf->f_namelen = 255;
-	return 0;
-}
-
-/* Called when remounting a filesystem by do_remount_sb() in fs/super.c. */
-static int ntfs_remount_fs(struct super_block *sb, int *flags, char *options)
-{
-	if (!parse_options(NTFS_SB2VOL(sb), options))
-		return -EINVAL;
-	return 0;
-}
-
-/* Define the super block operation that are implemented */
-
-static kmem_cache_t * ntfs_inode_cachep;
-
-static struct inode *__ntfs_alloc_inode(struct super_block *sb)
-{
-	struct ntfs_i *ei;
-	ei = (struct ntfs_i *)kmem_cache_alloc(ntfs_inode_cachep, SLAB_KERNEL);
-	if (!ei)
-		return NULL;
-	return &ei->vfs_inode;
-}
-
-static void ntfs_destroy_inode(struct inode *inode)
-{
-	kmem_cache_free(ntfs_inode_cachep, ntfs_i(inode));
-}
-
-static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
-{
-	struct ntfs_i *ei = (struct ntfs_i *) foo;
-
-	if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
-	    SLAB_CTOR_CONSTRUCTOR)
-		inode_init_once(&ei->vfs_inode);
-}
- 
-static int init_inodecache(void)
-{
-	ntfs_inode_cachep = kmem_cache_create("ntfs_inode_cache",
-					     sizeof(struct ntfs_i),
-					     0, SLAB_HWCACHE_ALIGN,
-					     init_once, NULL);
-	if (ntfs_inode_cachep == NULL)
-		return -ENOMEM;
-	return 0;
-}
-
-static void destroy_inodecache(void)
-{
-	if (kmem_cache_destroy(ntfs_inode_cachep))
-		printk(KERN_INFO "ntfs_inode_cache: not all structures were freed\n");
-}
-static struct super_operations ntfs_super_operations = {
-	alloc_inode:	__ntfs_alloc_inode,
-	destroy_inode:	ntfs_destroy_inode,
-	read_inode:	ntfs_read_inode,
-#ifdef CONFIG_NTFS_RW
-	write_inode:	ntfs_write_inode,
-#endif
-	put_super:	ntfs_put_super,
-	statfs:		ntfs_statfs,
-	remount_fs:	ntfs_remount_fs,
-	clear_inode:	_ntfs_clear_inode,
-};
-
-/**
- * is_boot_sector_ntfs - check an NTFS boot sector for validity
- * @b:		buffer containing bootsector to check
- * 
- * Check whether @b contains a valid NTFS boot sector.
- * Return 1 if @b is a valid NTFS bootsector or 0 if not.
- */
-static int is_boot_sector_ntfs(ntfs_u8 *b)
-{
-	ntfs_u32 i;
-
-	/* FIXME: We don't use checksumming yet as NT4(SP6a) doesn't either...
-	 * But we might as well have the code ready to do it. (AIA) */
-#if 0
-	/* Calculate the checksum. */
-	if (b < b + 0x50) {
-		ntfs_u32 *u;
-		ntfs_u32 *bi = (ntfs_u32 *)(b + 0x50);
-		
-		for (u = bi, i = 0; u < bi; ++u)
-			i += NTFS_GETU32(*u);
-	}
-#endif
-	/* Check magic is "NTFS    " */
-	if (b[3] != 0x4e) goto not_ntfs;
-	if (b[4] != 0x54) goto not_ntfs;
-	if (b[5] != 0x46) goto not_ntfs;
-	if (b[6] != 0x53) goto not_ntfs;
-	for (i = 7; i < 0xb; ++i)
-		if (b[i] != 0x20) goto not_ntfs;
-	/* Check bytes per sector value is between 512 and 4096. */
-	if (b[0xb] != 0) goto not_ntfs;
-	if (b[0xc] > 0x10) goto not_ntfs;
-	/* Check sectors per cluster value is valid. */
-	switch (b[0xd]) {
-	case 1: case 2: case 4: case 8: case 16:
-	case 32: case 64: case 128:
-		break;
-	default:
-		goto not_ntfs;
-	}
-	/* Check reserved sectors value and four other fields are zero. */
-	for (i = 0xe; i < 0x15; ++i) 
-		if (b[i] != 0) goto not_ntfs;
-	if (b[0x16] != 0) goto not_ntfs;
-	if (b[0x17] != 0) goto not_ntfs;
-	for (i = 0x20; i < 0x24; ++i)
-		if (b[i] != 0) goto not_ntfs;
-	/* Check clusters per file record segment value is valid. */
-	if (b[0x40] < 0xe1 || b[0x40] > 0xf7) {
-		switch (b[0x40]) {
-		case 1: case 2: case 4: case 8: case 16: case 32: case 64:
-			break;
-		default:
-			goto not_ntfs;
-		}
-	}
-	/* Check clusters per index block value is valid. */
-	if (b[0x44] < 0xe1 || b[0x44] > 0xf7) {
-		switch (b[0x44]) {
-		case 1: case 2: case 4: case 8: case 16: case 32: case 64:
-			break;
-		default:
-			goto not_ntfs;
-		}
-	}
-	return 1;
-not_ntfs:
-	return 0;
-}
-
-/* Called to mount a filesystem by read_super() in fs/super.c.
- * Return a super block, the main structure of a filesystem.
- *
- * NOTE : Don't store a pointer to an option, as the page containing the
- * options is freed after ntfs_read_super() returns.
- *
- * NOTE : A context switch can happen in kernel code only if the code blocks
- * (= calls schedule() in kernel/sched.c). */
-static int ntfs_fill_super(struct super_block *sb, void *options, int silent)
-{
-	ntfs_volume *vol;
-	struct buffer_head *bh;
-	int i, to_read, blocksize;
-
-	ntfs_debug(DEBUG_OTHER, "ntfs_read_super\n");
-	vol = NTFS_SB2VOL(sb);
-	init_ntfs_super_block(vol);
-	if (!parse_options(vol, (char*)options))
-		goto ntfs_read_super_vol;
-	blocksize = sb_min_blocksize(sb, 512);
-	if (!blocksize) {
-		ntfs_error("Unable to set blocksize.\n");
-		goto ntfs_read_super_vol;
-	}
-	/* Read the super block (boot block). */
-	if (!(bh = sb_bread(sb, 0))) {
-		ntfs_error("Reading super block failed\n");
-		goto ntfs_read_super_unl;
-	}
-	ntfs_debug(DEBUG_OTHER, "Done reading boot block\n");
-	/* Check for valid 'NTFS' boot sector. */
-	if (!is_boot_sector_ntfs(bh->b_data)) {
-		ntfs_debug(DEBUG_OTHER, "Not a NTFS volume\n");
-		bforget(bh);
-		goto ntfs_read_super_unl;
-	}
-	ntfs_debug(DEBUG_OTHER, "Going to init volume\n");
-	if (ntfs_init_volume(vol, bh->b_data) < 0) {
-		ntfs_debug(DEBUG_OTHER, "Init volume failed.\n");
-		bforget(bh);
-		goto ntfs_read_super_unl;
-	}
-	ntfs_debug(DEBUG_OTHER, "$Mft at cluster 0x%lx\n", vol->mft_lcn);
-	brelse(bh);
-	NTFS_SB(vol) = sb;
-	if (vol->cluster_size > PAGE_SIZE) {
-		ntfs_error("Partition cluster size is not supported yet (it "
-			   "is > max kernel blocksize).\n");
-		goto ntfs_read_super_unl;
-	}
-	ntfs_debug(DEBUG_OTHER, "Done to init volume\n");
-	/* Inform the kernel that a device block is a NTFS cluster. */
-	if (!sb_set_blocksize(sb, vol->cluster_size)) {
-		ntfs_error("Cluster size too small for device.\n");
-		goto ntfs_read_super_unl;
-	}
-	ntfs_debug(DEBUG_OTHER, "set_blocksize\n");
-	/* Allocate an MFT record (MFT record can be smaller than a cluster). */
-	i = vol->cluster_size;
-	if (i < vol->mft_record_size)
-		i = vol->mft_record_size;
-	if (!(vol->mft = ntfs_malloc(i)))
-		goto ntfs_read_super_unl;
-
-	/* Read at least the MFT record for $Mft. */
-	to_read = vol->mft_clusters_per_record;
-	if (to_read < 1)
-		to_read = 1;
-	for (i = 0; i < to_read; i++) {
-		if (!(bh = sb_bread(sb, vol->mft_lcn + i))) {
-			ntfs_error("Could not read $Mft record 0\n");
-			goto ntfs_read_super_mft;
-		}
-		ntfs_memcpy(vol->mft + ((__s64)i << vol->cluster_size_bits),
-						bh->b_data, vol->cluster_size);
-		brelse(bh);
-		ntfs_debug(DEBUG_OTHER, "Read cluster 0x%x\n",
-							 vol->mft_lcn + i);
-	}
-	/* Check and fixup this MFT record */
-	if (!ntfs_check_mft_record(vol, vol->mft)){
-		ntfs_error("Invalid $Mft record 0\n");
-		goto ntfs_read_super_mft;
-	}
-	/* Inform the kernel about which super operations are available. */
-	sb->s_op = &ntfs_super_operations;
-	sb->s_magic = NTFS_SUPER_MAGIC;
-	sb->s_maxbytes = MAX_LFS_FILESIZE;
-	ntfs_debug(DEBUG_OTHER, "Reading special files\n");
-	if (ntfs_load_special_files(vol)) {
-		ntfs_error("Error loading special files\n");
-		goto ntfs_read_super_mft;
-	}
-	ntfs_debug(DEBUG_OTHER, "Getting RootDir\n");
-	/* Get the root directory. */
-	if (!(sb->s_root = d_alloc_root(iget(sb, FILE_root)))) {
-		ntfs_error("Could not get root dir inode\n");
-		goto ntfs_read_super_mft;
-	}
-	return 0;
-ntfs_read_super_mft:
-	ntfs_free(vol->mft);
-ntfs_read_super_unl:
-ntfs_read_super_vol:
-	ntfs_debug(DEBUG_OTHER, "read_super: done\n");
-	return -EINVAL;
-}
-
-/* Define the filesystem */
-static struct super_block *ntfs_get_sb(struct file_system_type *fs_type,
-	int flags, char *dev_name, void *data)
-{
-	return get_sb_bdev(fs_type, flags, dev_name, data, ntfs_fill_super);
-}
-
-static struct file_system_type ntfs_fs_type = {
-	owner:		THIS_MODULE,
-	name:		"ntfs",
-	get_sb:		ntfs_get_sb,
-	kill_sb:	kill_block_super,
-	fs_flags:	FS_REQUIRES_DEV,
-};
-
-static int __init init_ntfs_fs(void)
-{
-	int err;
-	/* Comment this if you trust klogd. There are reasons not to trust it */
-#if defined(DEBUG) && !defined(MODULE)
-	console_verbose();
-#endif
-	printk(KERN_NOTICE "NTFS driver v" NTFS_VERSION " [Flags: R/"
-#ifdef CONFIG_NTFS_RW
-			"W"
-#else
-			"O"
-#endif
-#ifdef DEBUG
-			" DEBUG"
-#endif
-#ifdef MODULE
-			" MODULE"
-#endif
-			"]\n");
-	SYSCTL(1);
-	ntfs_debug(DEBUG_OTHER, "registering %s\n", ntfs_fs_type.name);
-	err = init_inodecache();
-	if (err)
-		goto out1;
-	err = register_filesystem(&ntfs_fs_type);
-	if (err)
-		goto out;
-	return 0;
-out:
-	destroy_inodecache();
-out1:
-	SYSCTL(0);
-	return err;
-}
-
-static void __exit exit_ntfs_fs(void)
-{
-	SYSCTL(0);
-	ntfs_debug(DEBUG_OTHER, "unregistering %s\n", ntfs_fs_type.name);
-	unregister_filesystem(&ntfs_fs_type);
-	destroy_inodecache();
-}
-
-EXPORT_NO_SYMBOLS;
-/*
- * Not strictly true. The driver was written originally by Martin von Löwis.
- * I am just maintaining and rewriting it.
- */
-MODULE_AUTHOR("Anton Altaparmakov <aia21@cus.cam.ac.uk>");
-MODULE_DESCRIPTION("Linux NTFS driver");
-MODULE_LICENSE("GPL");
-#ifdef DEBUG
-MODULE_PARM(ntdebug, "i");
-MODULE_PARM_DESC(ntdebug, "Debug level");
-#endif
-
-module_init(init_ntfs_fs)
-module_exit(exit_ntfs_fs)
-
diff -Nru a/fs/ntfs/inode.c b/fs/ntfs/inode.c
--- a/fs/ntfs/inode.c	Sun Apr 28 20:10:35 2002
+++ b/fs/ntfs/inode.c	Sun Apr 28 20:10:39 2002
@@ -1,2317 +1,1390 @@
-/*
- * inode.c
+/**
+ * inode.c - NTFS kernel inode handling. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2001,2002 Anton Altaparmakov.
  *
- * Copyright (C) 1995-1999 Martin von Löwis
- * Copyright (C) 1996 Albert D. Cahalan
- * Copyright (C) 1996-1997 Régis Duchesne
- * Copyright (C) 1998 Joseph Malicki
- * Copyright (C) 1999 Steve Dodd
- * Copyright (C) 2000-2001 Anton Altaparmakov (AIA)
+ * This program/include file 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/include file 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 (in the main directory of the Linux-NTFS 
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
-#include "ntfstypes.h"
-#include "ntfsendian.h"
-#include "struct.h"
-#include "inode.h"
-#include <linux/errno.h>
-#include "macros.h"
-#include "attr.h"
-#include "super.h"
-#include "dir.h"
-#include "support.h"
-#include "util.h"
-#include <linux/ntfs_fs.h>
-#include <linux/smp_lock.h>
-
-typedef struct {
-	int recno;
-	unsigned char *record;
-} ntfs_mft_record;
-
-typedef struct {
-	int size;
-	int count;
-	ntfs_mft_record *records;
-} ntfs_disk_inode;
 
-static void ntfs_fill_mft_header(ntfs_u8 *mft, int rec_size, int seq_no,
-		int links, int flags)
-{
-	int fixup_ofs = 0x2a;
-	int fixup_cnt = rec_size / NTFS_SECTOR_SIZE + 1;
-	int attr_ofs = (fixup_ofs + 2 * fixup_cnt + 7) & ~7;
-
-	NTFS_PUTU32(mft + 0x00, 0x454c4946);	/* FILE */
-	NTFS_PUTU16(mft + 0x04, fixup_ofs);	/* Offset to fixup. */
-	NTFS_PUTU16(mft + 0x06, fixup_cnt);	/* Number of fixups. */
-	NTFS_PUTU64(mft + 0x08, 0);		/* Logical sequence number. */
-	NTFS_PUTU16(mft + 0x10, seq_no);	/* Sequence number. */
-	NTFS_PUTU16(mft + 0x12, links);		/* Hard link count. */
-	NTFS_PUTU16(mft + 0x14, attr_ofs);	/* Offset to attributes. */
-	NTFS_PUTU16(mft + 0x16, flags);		/* Flags: 1 = In use,
-							  2 = Directory. */
-	NTFS_PUTU32(mft + 0x18, attr_ofs + 8);	/* Bytes in use. */
-	NTFS_PUTU32(mft + 0x1c, rec_size);	/* Total allocated size. */
-	NTFS_PUTU64(mft + 0x20, 0);		/* Base mft record. */
-	NTFS_PUTU16(mft + 0x28, 0);		/* Next attr instance. */
-	NTFS_PUTU16(mft + fixup_ofs, 1);	/* Fixup word. */
-	NTFS_PUTU32(mft + attr_ofs, (__u32)-1);	/* End of attributes marker. */
-}
+#include <linux/pagemap.h>
 
-/*
- * Search in an inode an attribute by type and name. 
- * FIXME: Check that when attributes are inserted all attribute list
- * attributes are expanded otherwise need to modify this function to deal
- * with attribute lists. (AIA)
- */
-ntfs_attribute *ntfs_find_attr(ntfs_inode *ino, int type, char *name)
-{
-	int i;
-	
-	if (!ino) {
-		ntfs_error("ntfs_find_attr: NO INODE!\n");
-		return 0;
-	}
-	for (i = 0; i < ino->attr_count; i++) {
-		if (type < ino->attrs[i].type)
-			return 0;
-		if (type == ino->attrs[i].type) {
-			if (!name) {
-				if (!ino->attrs[i].name)
-					return ino->attrs + i;
-			} else if (ino->attrs[i].name &&
-				   !ntfs_ua_strncmp(ino->attrs[i].name, name,
-						    strlen(name)))
-				return ino->attrs + i;
-		}
-	}
-	return 0;
-}
+#include "ntfs.h"
 
-/*
- * Insert all attributes from the record mftno of the MFT in the inode ino.
- * If mftno is a base mft record we abort as soon as we find the attribute
- * list, but only on the first pass. We will get called later when the attribute
- * list attribute is being parsed so we need to distinguish the two cases.
- * FIXME: We should be performing structural consistency checks. (AIA)
- * Return 0 on success or -errno on error.
- */
-static int ntfs_insert_mft_attributes(ntfs_inode* ino, char *mft, int mftno)
+struct inode *ntfs_alloc_big_inode(struct super_block *sb)
 {
-	int i, error, type, len, present = 0;
-	char *it;
+	ntfs_inode *ni;
 
-	/* Check for duplicate extension record. */
-	for(i = 0; i < ino->record_count; i++)
-		if (ino->records[i] == mftno) {
-			if (i)
-				return 0;
-			present = 1;
-			break;
-		}
-	if (!present) {
-		/* (re-)allocate space if necessary. */
-		if (ino->record_count % 8 == 0)	{
-			int *new;
-
-			new = ntfs_malloc((ino->record_count + 8) *
-								sizeof(int));
-			if (!new)
-				return -ENOMEM;
-			if (ino->records) {
-				for (i = 0; i < ino->record_count; i++)
-					new[i] = ino->records[i];
-				ntfs_free(ino->records);
-			}
-			ino->records = new;
-		}
-		ino->records[ino->record_count] = mftno;
-		ino->record_count++;
+	ntfs_debug("Entering.");
+	ni = (ntfs_inode *)kmem_cache_alloc(ntfs_big_inode_cache,
+			SLAB_NOFS);
+	if (!ni) {
+		ntfs_error(sb, "Allocation of NTFS big inode structure "
+				"failed.");
+		return NULL;
 	}
-	it = mft + NTFS_GETU16(mft + 0x14); /* mft->attrs_offset */
-	do {
-		type = NTFS_GETU32(it);
-		len = NTFS_GETU32(it + 4);
-		if (type != -1) {
-			error = ntfs_insert_attribute(ino, it);
-			if (error)
-				return error;
-		}
-		/* If we have just processed the attribute list and this is
-		 * the first time we are parsing this (base) mft record then we
-		 * are done so that the attribute list gets parsed before the
-		 * entries in the base mft record. Otherwise we run into
-		 * problems with encountering attributes out of order and when
-		 * this happens with different attribute extents we die. )-:
-		 * This way we are ok as the attribute list is always sorted
-		 * fully and correctly. (-: */
-		if (type == 0x20 && !present)
-			return 0;
-		it += len;
-	} while (type != -1); /* Attribute listing ends with type -1. */
-	return 0;
+	return VFS_I(ni);
 }
 
-/*
- * Insert a single specific attribute from the record mftno of the MFT in the
- * inode ino. We disregard the attribute list assuming we have already parsed
- * it.
- * FIXME: We should be performing structural consistency checks. (AIA)
- * Return 0 on success or -errno on error.
- */
-static int ntfs_insert_mft_attribute(ntfs_inode* ino, int mftno,
-		ntfs_u8 *attr)
+void ntfs_destroy_big_inode(struct inode *inode)
 {
-	int i, error, present = 0;
+	ntfs_inode *ni = NTFS_I(inode);
 
-	/* Check for duplicate extension record. */
-	for(i = 0; i < ino->record_count; i++)
-		if (ino->records[i] == mftno) {
-			present = 1;
-			break;
-		}
-	if (!present) {
-		/* (re-)allocate space if necessary. */
-		if (ino->record_count % 8 == 0)	{
-			int *new;
-
-			new = ntfs_malloc((ino->record_count + 8) *
-								sizeof(int));
-			if (!new)
-				return -ENOMEM;
-			if (ino->records) {
-				for (i = 0; i < ino->record_count; i++)
-					new[i] = ino->records[i];
-				ntfs_free(ino->records);
-			}
-			ino->records = new;
-		}
-		ino->records[ino->record_count] = mftno;
-		ino->record_count++;
-	}
-	if (NTFS_GETU32(attr) == -1) {
-		ntfs_debug(DEBUG_FILE3, "ntfs_insert_mft_attribute: attribute "
-				"type is -1.\n");
-		return 0;
-	}
-	error = ntfs_insert_attribute(ino, attr);
-	if (error)
-		return error;
-	return 0;
+	ntfs_debug("Entering.");
+	BUG_ON(atomic_read(&ni->mft_count) || !atomic_dec_and_test(&ni->count));
+	kmem_cache_free(ntfs_big_inode_cache, NTFS_I(inode));
 }
 
-/* Read and insert all the attributes of an 'attribute list' attribute.
- * Return the number of remaining bytes in *plen. */
-static int parse_attributes(ntfs_inode *ino, ntfs_u8 *alist, int *plen)
-{
-	ntfs_u8 *mft, *attr;
-	int mftno, l, error;
-	int last_mft = -1;
-	int len = *plen;
-	int tries = 0;
-	
-	if (!ino->attr) {
-		ntfs_error("parse_attributes: called on inode 0x%x without a "
-				"loaded base mft record.\n", ino->i_number);
-		return -EINVAL;
-	}
-	mft = ntfs_malloc(ino->vol->mft_record_size);
-	if (!mft)
-		return -ENOMEM;
-	while (len > 8)	{
-		l = NTFS_GETU16(alist + 4);
-		if (l > len)
-			break;
-	        /* Process an attribute description. */
-		mftno = NTFS_GETU32(alist + 0x10); 
-			/* FIXME: The mft reference (alist + 0x10) is __s64.
-			* - Not a problem unless we encounter a huge partition.
-			* - Should be consistency checking the sequence numbers
-			*   though! This should maybe happen in 
-			*   ntfs_read_mft_record() itself and a hotfix could
-			*   then occur there or the user notified to run
-			*   ntfsck. (AIA) */
-		if (mftno != ino->i_number && mftno != last_mft) {
-continue_after_loading_mft_data:
-			last_mft = mftno;
-			error = ntfs_read_mft_record(ino->vol, mftno, mft);
-			if (error) {
-				if (error == -EINVAL && !tries)
-					goto force_load_mft_data;
-failed_reading_mft_data:
-				ntfs_debug(DEBUG_FILE3, "parse_attributes: "
-					"ntfs_read_mft_record(mftno = 0x%x) "
-					"failed\n", mftno);
-				ntfs_free(mft);
-				return error;
-			}
-		}
-		attr = ntfs_find_attr_in_mft_rec(
-				ino->vol,		/* ntfs volume */
-				mftno == ino->i_number ?/* mft record is: */
-					ino->attr:	/*   base record */
-					mft,		/*   extension record */
-				NTFS_GETU32(alist + 0),	/* type */
-				(wchar_t*)(alist + alist[7]),	/* name */
-				alist[6], 		/* name length */
-				1,			/* ignore case */
-				NTFS_GETU16(alist + 24)	/* instance number */
-				);
-		if (!attr) {
-			ntfs_error("parse_attributes: mft records 0x%x and/or "
-				       "0x%x corrupt!\n", ino->i_number, mftno);
-			ntfs_free(mft);
-			return -EINVAL; /* FIXME: Better error code? (AIA) */
-		}
-		error = ntfs_insert_mft_attribute(ino, mftno, attr);
-		if (error) {
-			ntfs_debug(DEBUG_FILE3, "parse_attributes: "
-				"ntfs_insert_mft_attribute(mftno 0x%x, "
-				"attribute type 0x%x) failed\n", mftno,
-				NTFS_GETU32(alist + 0));
-			ntfs_free(mft);
-			return error;
-		}
-		len -= l;
-		alist += l;
-	}
-	ntfs_free(mft);
-	*plen = len;
-	return 0;
-force_load_mft_data:
+ntfs_inode *ntfs_alloc_inode(void)
 {
-	ntfs_u8 *mft2, *attr2;
-	int mftno2;
-	int last_mft2 = last_mft;
-	int len2 = len;
-	int error2;
-	int found2 = 0;
-	ntfs_u8 *alist2 = alist;
-	/*
-	 * We only get here if $DATA wasn't found in $MFT which only happens
-	 * on volume mount when $MFT has an attribute list and there are
-	 * attributes before $DATA which are inside extent mft records. So
-	 * we just skip forward to the $DATA attribute and read that. Then we
-	 * restart which is safe as an attribute will not be inserted twice.
-	 *
-	 * This still will not fix the case where the attribute list is non-
-	 * resident, larger than 1024 bytes, and the $DATA attribute list entry
-	 * is not in the first 1024 bytes. FIXME: This should be implemented
-	 * somehow! Perhaps by passing special error code up to
-	 * ntfs_load_attributes() so it keeps going trying to get to $DATA
-	 * regardless. Then it would have to restart just like we do here.
-	 */
-	mft2 = ntfs_malloc(ino->vol->mft_record_size);
-	if (!mft2) {
-		ntfs_free(mft);
-		return -ENOMEM;
-	}
-	ntfs_memcpy(mft2, mft, ino->vol->mft_record_size);
-	while (len2 > 8) {
-		l = NTFS_GETU16(alist2 + 4);
-		if (l > len2)
-			break;
-		if (NTFS_GETU32(alist2 + 0x0) < ino->vol->at_data) {
-			len2 -= l;
-			alist2 += l;
-			continue;
-		}
-		if (NTFS_GETU32(alist2 + 0x0) > ino->vol->at_data) {
-			if (found2)
-				break;
-			/* Uh-oh! It really isn't there! */
-			ntfs_error("Either the $MFT is corrupt or, equally "
-					"likely, the $MFT is too complex for "
-					"the current driver to handle. Please "
-					"email the ntfs maintainer that you "
-					"saw this message. Thank you.\n");
-			goto failed_reading_mft_data;
-		}
-	        /* Process attribute description. */
-		mftno2 = NTFS_GETU32(alist2 + 0x10); 
-		if (mftno2 != ino->i_number && mftno2 != last_mft2) {
-			last_mft2 = mftno2;
-			error2 = ntfs_read_mft_record(ino->vol, mftno2, mft2);
-			if (error2) {
-				ntfs_debug(DEBUG_FILE3, "parse_attributes: "
-					"ntfs_read_mft_record(mftno2 = 0x%x) "
-					"failed\n", mftno2);
-				ntfs_free(mft2);
-				goto failed_reading_mft_data;
-			}
-		}
-		attr2 = ntfs_find_attr_in_mft_rec(
-				ino->vol,		 /* ntfs volume */
-				mftno2 == ino->i_number ?/* mft record is: */
-					ino->attr:	 /*  base record */
-					mft2,		 /*  extension record */
-				NTFS_GETU32(alist2 + 0),	/* type */
-				(wchar_t*)(alist2 + alist2[7]),	/* name */
-				alist2[6], 		 /* name length */
-				1,			 /* ignore case */
-				NTFS_GETU16(alist2 + 24) /* instance number */
-				);
-		if (!attr2) {
-			ntfs_error("parse_attributes: mft records 0x%x and/or "
-				       "0x%x corrupt!\n", ino->i_number,
-				       mftno2);
-			ntfs_free(mft2);
-			goto failed_reading_mft_data;
-		}
-		error2 = ntfs_insert_mft_attribute(ino, mftno2, attr2);
-		if (error2) {
-			ntfs_debug(DEBUG_FILE3, "parse_attributes: "
-				"ntfs_insert_mft_attribute(mftno2 0x%x, "
-				"attribute2 type 0x%x) failed\n", mftno2,
-				NTFS_GETU32(alist2 + 0));
-			ntfs_free(mft2);
-			goto failed_reading_mft_data;
-		}
-		len2 -= l;
-		alist2 += l;
-		found2 = 1;
-	}
-	ntfs_free(mft2);
-	tries = 1;
-	goto continue_after_loading_mft_data;
-}
+	ntfs_inode *ni = (ntfs_inode *)kmem_cache_alloc(ntfs_inode_cache,
+			SLAB_NOFS);
+	ntfs_debug("Entering.");
+	if (unlikely(!ni))
+		ntfs_error(NULL, "Allocation of NTFS inode structure failed.");
+	return ni;
 }
 
-static void ntfs_load_attributes(ntfs_inode *ino)
+void ntfs_destroy_inode(ntfs_inode *ni)
 {
-	ntfs_attribute *alist;
-	int datasize;
-	int offset, len, delta;
-	char *buf;
-	ntfs_volume *vol = ino->vol;
-	
-	ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x 1\n", ino->i_number);
-	if (ntfs_insert_mft_attributes(ino, ino->attr, ino->i_number))
-		return;
-	ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x 2\n", ino->i_number);
-	alist = ntfs_find_attr(ino, vol->at_attribute_list, 0);
-	ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x 3\n", ino->i_number);
-	if (!alist)
-		return;
-	ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x 4\n", ino->i_number);
-	datasize = alist->size;
-	ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x: alist->size = 0x%x\n",
-			ino->i_number, alist->size);
-	if (alist->resident) {
-		parse_attributes(ino, alist->d.data, &datasize);
-		return;
-	}
-	ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x 5\n", ino->i_number);
-	buf = ntfs_malloc(1024);
-	if (!buf)    /* FIXME: Should be passing error code to caller. (AIA) */
-		return;
-	delta = 0;
-	for (offset = 0; datasize; datasize -= len, offset += len) {
-		ntfs_io io;
-		
-		io.fn_put = ntfs_put;
-		io.fn_get = 0;
-		io.param = buf + delta;
-		len = 1024 - delta;
-		if (len > datasize)
-			len = datasize;
-		ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x: len = %i\n",
-						ino->i_number, len);
-		ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x: delta = %i\n",
-						ino->i_number, delta);
-		io.size = len;
-		if (ntfs_read_attr(ino, vol->at_attribute_list, 0, offset,
-				   &io))
-			ntfs_error("error in load_attributes\n");
-		delta += len;
-		ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x: after += len, "
-				"delta = %i\n", ino->i_number, delta);
-		parse_attributes(ino, buf, &delta);
-		ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x: after "
-				"parse_attr, delta = %i\n", ino->i_number,
-				delta);
-		if (delta)
-			/* Move remaining bytes to buffer start. */
-			ntfs_memmove(buf, buf + len - delta, delta);
-	}
-	ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x 6\n", ino->i_number);
-	ntfs_free(buf);
+	ntfs_debug("Entering.");
+	BUG_ON(atomic_read(&ni->mft_count) || !atomic_dec_and_test(&ni->count));
+	kmem_cache_free(ntfs_inode_cache, ni);
 }
-	
-int ntfs_init_inode(ntfs_inode *ino, ntfs_volume *vol, int inum)
-{
-	char *buf;
-	int error;
 
-	ntfs_debug(DEBUG_FILE1, "Initializing inode 0x%x\n", inum);
-	ino->i_number = inum;
-	ino->vol = vol;
-	ino->attr = buf = ntfs_malloc(vol->mft_record_size);
-	if (!buf)
-		return -ENOMEM;
-	error = ntfs_read_mft_record(vol, inum, ino->attr);
-	if (error) {
-		ntfs_debug(DEBUG_OTHER, "Init inode: 0x%x failed\n", inum);
-		return error;
-	}
-	ntfs_debug(DEBUG_FILE2, "Init inode: got mft 0x%x\n", inum);
-	ino->sequence_number = NTFS_GETU16(buf + 0x10);
-	ino->attr_count = 0;
-	ino->record_count = 0;
-	ino->records = 0;
-	ino->attrs = 0;
-	ntfs_load_attributes(ino);
-	ntfs_debug(DEBUG_FILE2, "Init inode: done 0x%x\n", inum);
-	return 0;
-}
-
-void ntfs_clear_inode(ntfs_inode *ino)
-{
-	int i;
-	if (!ino->attr) {
-		ntfs_error("ntfs_clear_inode: double free\n");
-		return;
-	}
-	ntfs_free(ino->attr);
-	ino->attr = 0;
-	ntfs_free(ino->records);
-	ino->records = 0;
-	for (i = 0; i < ino->attr_count; i++) {
-		if (ino->attrs[i].name)
-			ntfs_free(ino->attrs[i].name);
-		if (ino->attrs[i].resident) {
-			if (ino->attrs[i].d.data)
-				ntfs_free(ino->attrs[i].d.data);
-		} else {
-			if (ino->attrs[i].d.r.runlist)
-				ntfs_vfree(ino->attrs[i].d.r.runlist);
-		}
-	}
-	ntfs_free(ino->attrs);
-	ino->attrs = 0;
-}
-
-/* Check and fixup a MFT record. */
-int ntfs_check_mft_record(ntfs_volume *vol, char *record)
-{
-	return ntfs_fixup_record(record, "FILE", vol->mft_record_size);
-}
-
-/* Return (in result) the value indicating the next available attribute 
- * chunk number. Works for inodes w/o extension records only. */
-int ntfs_allocate_attr_number(ntfs_inode *ino, int *result)
-{
-	if (ino->record_count != 1)
-		return -EOPNOTSUPP;
-	*result = NTFS_GETU16(ino->attr + 0x28);
-	NTFS_PUTU16(ino->attr + 0x28, (*result) + 1);
-	return 0;
-}
-
-/* Find the location of an attribute in the inode. A name of NULL indicates
- * unnamed attributes. Return pointer to attribute or NULL if not found. */
-char *ntfs_get_attr(ntfs_inode *ino, int attr, char *name)
+/**
+ * __ntfs_init_inode - initialize ntfs specific part of an inode
+ *
+ * Initialize an ntfs inode to defaults.
+ *
+ * Return zero on success and -ENOMEM on error.
+ */
+static void __ntfs_init_inode(struct super_block *sb, ntfs_inode *ni)
 {
-	/* Location of first attribute. */
-	char *it = ino->attr + NTFS_GETU16(ino->attr + 0x14);
-	int type;
-	int len;
-	
-	/* Only check for magic DWORD here, fixup should have happened before.*/
-	if (!IS_MFT_RECORD(ino->attr))
-		return 0;
-	do {
-		type = NTFS_GETU32(it);
-		len = NTFS_GETU16(it + 4);
-		/* We found the attribute type. Is the name correct, too? */
-		if (type == attr) {
-			int namelen = NTFS_GETU8(it + 9);
-			char *name_it, *n = name;
-			/* Match given name and attribute name if present.
-			   Make sure attribute name is Unicode. */
-			if (!name) {
-				goto check_namelen;
-			} else if (namelen) {
-				for (name_it = it + NTFS_GETU16(it + 10);
-				     namelen; n++, name_it += 2, namelen--)
-					if (*name_it != *n || name_it[1])
-						break;
-check_namelen:
-				if (!namelen)
-					break;
-			}
-		}
-		it += len;
-	} while (type != -1); /* List of attributes ends with type -1. */
-	if (type == -1)
-		return 0;
-	return it;
+	ntfs_debug("Entering.");
+	memset(ni, 0, sizeof(ntfs_inode));
+	atomic_set(&ni->count, 1);
+	ni->vol = NULL;
+	init_run_list(&ni->run_list);
+	init_rwsem(&ni->mrec_lock);
+	atomic_set(&ni->mft_count, 0);
+	ni->page = NULL;
+	ni->attr_list = NULL;
+	init_run_list(&ni->attr_list_rl);
+	init_run_list(&ni->_IDM(bmp_rl));
+	init_MUTEX(&ni->extent_lock);
+	ni->_INE(base_ntfs_ino) = NULL;
+	ni->vol = NTFS_SB(sb);
+	return;
+}
+
+static void ntfs_init_big_inode(struct inode *vi)
+{
+	ntfs_inode *ni = NTFS_I(vi);
+
+	ntfs_debug("Entering.");
+	__ntfs_init_inode(vi->i_sb, ni);
+	ni->mft_no = vi->i_ino;
+	return;
+}
+
+ntfs_inode *ntfs_new_inode(struct super_block *sb)
+{
+	ntfs_inode *ni = ntfs_alloc_inode();
+
+	ntfs_debug("Entering.");
+	if (ni)
+		__ntfs_init_inode(sb, ni);
+	return ni;
 }
 
-__s64 ntfs_get_attr_size(ntfs_inode *ino, int type, char *name)
-{
-	ntfs_attribute *attr = ntfs_find_attr(ino, type, name);
-	if (!attr)
-		return 0;
-	return
-		attr->size;
-}
-	
-int ntfs_attr_is_resident(ntfs_inode *ino, int type, char *name)
-{
-	ntfs_attribute *attr = ntfs_find_attr(ino, type, name);
-	if (!attr)
-		return 0;
-	return attr->resident;
-}
-	
-/*
- * A run is coded as a type indicator, an unsigned length, and a signed cluster
- * offset.
- * . To save space, length and offset are fields of variable length. The low
- *   nibble of the type indicates the width of the length :), the high nibble
- *   the width of the offset.
- * . The first offset is relative to cluster 0, later offsets are relative to
- *   the previous cluster.
+/**
+ * ntfs_is_extended_system_file - check if a file is in the $Extend directory
+ * @ctx:	initialized attribute search context
  *
- * This function decodes a run. Length is an output parameter, data and cluster
- * are in/out parameters.
+ * Search all file name attributes in the inode described by the attribute
+ * search context @ctx and check if any of the names are in the $Extend system
+ * directory.
+ * 
+ * Return values:
+ *	   1: file is in $Extend directory
+ *	   0: file is not in $Extend directory
+ *	-EIO: file is corrupt
  */
-int ntfs_decompress_run(unsigned char **data, int *length, 
-			ntfs_cluster_t *cluster, int *ctype)
+static int ntfs_is_extended_system_file(attr_search_context *ctx)
 {
-	unsigned char type = *(*data)++;
-	*ctype = 0;
-	switch (type & 0xF) {
-	case 1: 
-		*length = NTFS_GETS8(*data);
-		break;
-	case 2: 
-		*length = NTFS_GETS16(*data);
-		break;
-	case 3: 
-		*length = NTFS_GETS24(*data);
-		break;
-        case 4: 
-		*length = NTFS_GETS32(*data);
-		break;
-        	/* Note: cases 5-8 are probably pointless to code, since how
-		 * many runs > 4GB of length are there? At the most, cases 5
-		 * and 6 are probably necessary, and would also require making
-		 * length 64-bit throughout. */
-	default:
-		ntfs_error("Can't decode run type field 0x%x\n", type);
-		return -1;
-	}
-//	ntfs_debug(DEBUG_FILE3, "ntfs_decompress_run: length = 0x%x\n",*length);
-	if (*length < 0)
-	{
-		ntfs_error("Negative run length decoded\n");
-		return -1;
-	}
-	*data += (type & 0xF);
-	switch (type & 0xF0) {
-	case 0:
-		*ctype = 2;
-		break;
-	case 0x10:
-		*cluster += NTFS_GETS8(*data);
-		break;
-	case 0x20:
-		*cluster += NTFS_GETS16(*data);
-		break;
-	case 0x30:
-		*cluster += NTFS_GETS24(*data);
-		break;
-	case 0x40:
-		*cluster += NTFS_GETS32(*data);
-		break;
-#if 0 /* Keep for future, in case ntfs_cluster_t ever becomes 64bit. */
-	case 0x50: 
-		*cluster += NTFS_GETS40(*data);
-		break;
-	case 0x60: 
-		*cluster += NTFS_GETS48(*data);
-		break;
-	case 0x70: 
-		*cluster += NTFS_GETS56(*data);
-		break;
-	case 0x80: 
-		*cluster += NTFS_GETS64(*data);
-		break;
-#endif
-	default:
-		ntfs_error("Can't decode run type field 0x%x\n", type);
-		return -1;
-	}
-//	ntfs_debug(DEBUG_FILE3, "ntfs_decompress_run: cluster = 0x%x\n",
-//								*cluster);
-	*data += (type >> 4);
-	return 0;
-}
+	int nr_links;
 
-static void dump_runlist(const ntfs_runlist *rl, const int rlen);
+	/* Restart search. */
+	reinit_attr_search_ctx(ctx);
 
-/*
- * FIXME: ntfs_readwrite_attr() has the effect of writing @dest to @offset of
- * the attribute value of the attribute @attr in the in memory inode @ino.
- * If the attribute value of @attr is non-resident the value's contents at
- * @offset are actually written to disk (from @dest). The on disk mft record
- * describing the non-resident attribute value is not updated!
- * If the attribute value is resident then the value is written only in
- * memory. The on disk mft record containing the value is not written to disk.
- * A possible fix would be to call ntfs_update_inode() before returning. (AIA)
- */
-/* Reads l bytes of the attribute (attr, name) of ino starting at offset on
- * vol into buf. Returns the number of bytes read in the ntfs_io struct.
- * Returns 0 on success, errno on failure */
-int ntfs_readwrite_attr(ntfs_inode *ino, ntfs_attribute *attr, __s64 offset,
-		ntfs_io *dest)
-{
-	int rnum, s_vcn, error, clustersizebits;
-	ntfs_cluster_t cluster, s_cluster, vcn, len;
-	__s64 l, chunk, copied;
-
-	ntfs_debug(DEBUG_FILE3, __FUNCTION__ "(): %s 0x%x bytes at offset "
-			"0x%Lx %s inode 0x%x, attr type 0x%x.\n",
-			dest->do_read ? "Read" : "Write", dest->size, offset,
-			dest->do_read ? "from" : "to", ino->i_number,
-			attr->type);
-	l = dest->size;
-	if (l == 0)
-		return 0;
-	if (dest->do_read) {
-		/* If read _starts_ beyond end of stream, return nothing. */
-		if (offset >= attr->size) {
-			dest->size = 0;
-			return 0;
-		}
-		/* If read _extends_ beyond end of stream, return as much
-		 * initialised data as we have. */
-		if (offset + l >= attr->size)
-			l = dest->size = attr->size - offset;
-	} else {
+	/* Get number of hard links. */
+	nr_links = le16_to_cpu(ctx->mrec->link_count);
+
+	/* Loop through all hard links. */
+	while (lookup_attr(AT_FILE_NAME, NULL, 0, 0, 0, NULL, 0, ctx)) {
+		FILE_NAME_ATTR *file_name_attr;
+		ATTR_RECORD *attr = ctx->attr;
+		u8 *p, *p2;
+
+		nr_links--;
 		/*
-		 * If write extends beyond _allocated_ size, extend attribute,
-		 * updating attr->allocated and attr->size in the process. (AIA)
+		 * Maximum sanity checking as we are called on an inode that
+		 * we suspect might be corrupt.
 		 */
-		if ((!attr->resident && offset + l > attr->allocated) ||
-				(attr->resident && offset + l > attr->size)) {
-			error = ntfs_resize_attr(ino, attr, offset + l);
-			if (error)
-				return error;
-		}
-		if (!attr->resident) {
-			/* Has amount of data increased? */
-			if (offset + l > attr->size)
-				attr->size = offset + l;
-			/* Has amount of initialised data increased? */
-			if (offset + l > attr->initialized) {
-				/* FIXME: Clear the section between the old
-			 	 * initialised length and the write start.
-				 * (AIA) */
-				attr->initialized = offset + l;
-			}
+		p = (u8*)attr + le32_to_cpu(attr->length);
+		if (p < (u8*)ctx->mrec || (u8*)p > (u8*)ctx->mrec +
+				le32_to_cpu(ctx->mrec->bytes_in_use)) {
+err_corrupt_attr:
+			ntfs_error(ctx->ntfs_ino->vol->sb, "Corrupt file name "
+					"attribute. You should run chkdsk.");
+			return -EIO;
 		}
-	}
-	if (attr->resident) {
-		if (dest->do_read)
-			dest->fn_put(dest, (ntfs_u8*)attr->d.data + offset, l);
-		else
-			dest->fn_get((ntfs_u8*)attr->d.data + offset, dest, l);
-		dest->size = l;
-		return 0;
-	}
-	if (dest->do_read) {
-		/* Read uninitialized data. */
-		if (offset >= attr->initialized)
-			return ntfs_read_zero(dest, l);
-		if (offset + l > attr->initialized) {
-			dest->size = chunk = attr->initialized - offset;
-			error = ntfs_readwrite_attr(ino, attr, offset, dest);
-			if (error || (dest->size != chunk && (error = -EIO, 1)))
-				return error;
-			dest->size += l - chunk;
-			return ntfs_read_zero(dest, l - chunk);
+		if (attr->non_resident) {
+			ntfs_error(ctx->ntfs_ino->vol->sb, "Non-resident file "
+					"name. You should run chkdsk.");
+			return -EIO;
 		}
-		if (attr->flags & ATTR_IS_COMPRESSED)
-			return ntfs_read_compressed(ino, attr, offset, dest);
-	} else {
-		if (attr->flags & ATTR_IS_COMPRESSED)
-			return ntfs_write_compressed(ino, attr, offset, dest);
-	}
-	vcn = 0;
-	clustersizebits = ino->vol->cluster_size_bits;
-	s_vcn = offset >> clustersizebits;
-	for (rnum = 0; rnum < attr->d.r.len &&
-			vcn + attr->d.r.runlist[rnum].len <= s_vcn; rnum++)
-		vcn += attr->d.r.runlist[rnum].len;
-	if (rnum == attr->d.r.len) {
-		ntfs_debug(DEBUG_FILE3, __FUNCTION__ "(): EOPNOTSUPP: "
-			"inode = 0x%x, rnum = %i, offset = 0x%Lx, vcn = 0x%x, "
-			"s_vcn = 0x%x.\n", ino->i_number, rnum, offset, vcn,
-			s_vcn);
-		dump_runlist(attr->d.r.runlist, attr->d.r.len);
-		/*FIXME: Should extend runlist. */
-		return -EOPNOTSUPP;
-	}
-	copied = 0;
-	while (l) {
-		s_vcn = offset >> clustersizebits;
-		cluster = attr->d.r.runlist[rnum].lcn;
-		len = attr->d.r.runlist[rnum].len;
-		s_cluster = cluster + s_vcn - vcn;
-		chunk = ((__s64)(vcn + len) << clustersizebits) - offset;
-		if (chunk > l)
-			chunk = l;
-		dest->size = chunk;
-		error = ntfs_getput_clusters(ino->vol, s_cluster, offset -
-				((__s64)s_vcn << clustersizebits), dest);
-		if (error) {
-			ntfs_error("Read/write error.\n");
-			dest->size = copied;
-			return error;
-		}
-		l -= chunk;
-		copied += chunk;
-		offset += chunk;
-		if (l && offset >= ((__s64)(vcn + len) << clustersizebits)) {
-			rnum++;
-			vcn += len;
-			cluster = attr->d.r.runlist[rnum].lcn;
-			len = attr->d.r.runlist[rnum].len;
+		if (attr->flags) {
+			ntfs_error(ctx->ntfs_ino->vol->sb, "File name with "
+					"invalid flags. You should run "
+					"chkdsk.");
+			return -EIO;
+		}
+		if (!(attr->_ARA(resident_flags) & RESIDENT_ATTR_IS_INDEXED)) {
+			ntfs_error(ctx->ntfs_ino->vol->sb, "Unindexed file "
+					"name. You should run chkdsk.");
+			return -EIO;
 		}
+		file_name_attr = (FILE_NAME_ATTR*)((u8*)attr +
+				le16_to_cpu(attr->_ARA(value_offset)));
+		p2 = (u8*)attr + le32_to_cpu(attr->_ARA(value_length));
+		if (p2 < (u8*)attr || p2 > p)
+			goto err_corrupt_attr;
+		/* This attribute is ok, but is it in the $Extend directory? */
+		if (MREF_LE(file_name_attr->parent_directory) == FILE_Extend)
+			return 1;	/* YES, it's an extended system file. */
+	}
+	if (nr_links) {
+		ntfs_error(ctx->ntfs_ino->vol->sb, "Inode hard link count "
+				"doesn't match number of name attributes. You "
+				"should run chkdsk.");
+		return -EIO;
 	}
-	dest->size = copied;
-	return 0;
+	return 0;	/* NO, it is not an extended system file. */
 }
 
-int ntfs_read_attr(ntfs_inode *ino, int type, char *name, __s64 offset,
-		   ntfs_io *buf)
+/**
+ * ntfs_read_inode - read an inode from its device
+ * @vi:		inode to read
+ *
+ * ntfs_read_inode() is called from the VFS iget() function to read the inode
+ * described by @vi into memory from the device.
+ *
+ * The only fields in @vi that we need to/can look at when the function is
+ * called are i_sb, pointing to the mounted device's super block, and i_ino,
+ * the number of the inode to load.
+ *
+ * ntfs_read_inode() maps, pins and locks the mft record number i_ino for
+ * reading and sets up the necessary @vi fields as well as initializing
+ * the ntfs inode.
+ *
+ * Q: What locks are held when the function is called?
+ * A: i_state has I_LOCK set, hence the inode is locked, also
+ *    i_count is set to 1, so it is not going to go away
+ *    i_flags is set to 0 and we have no business touching it. Only an ioctl()
+ *    is allowed to write to them. We should of course be honouring them but
+ *    we need to do that using the IS_* macros defined in include/linux/fs.h.
+ *    In any case ntfs_read_inode() has nothing to do with i_flags at all.
+ */
+void ntfs_read_inode(struct inode *vi)
 {
-	ntfs_attribute *attr;
+	ntfs_volume *vol = NTFS_SB(vi->i_sb);
+	ntfs_inode *ni;
+	MFT_RECORD *m;
+	STANDARD_INFORMATION *si;
+	attr_search_context *ctx;
+	int err;
 
-	buf->do_read = 1;
-	attr = ntfs_find_attr(ino, type, name);
-	if (!attr) {
-		ntfs_debug(DEBUG_FILE3, __FUNCTION__ "(): attr 0x%x not found "
-				"in inode 0x%x\n", type, ino->i_number);
-		return -EINVAL;
-	}
-	return ntfs_readwrite_attr(ino, attr, offset, buf);
-}
+	ntfs_debug("Entering for i_ino 0x%lx.", vi->i_ino);
 
-int ntfs_write_attr(ntfs_inode *ino, int type, char *name, __s64 offset,
-		    ntfs_io *buf)
-{
-	ntfs_attribute *attr;
-	
-	buf->do_read = 0;
-	attr = ntfs_find_attr(ino, type, name);
-	if (!attr) {
-		ntfs_debug(DEBUG_FILE3, __FUNCTION__ "(): attr 0x%x not found "
-				"in inode 0x%x\n", type, ino->i_number);
-		return -EINVAL;
+	/* Setup the generic vfs inode parts now. */
+
+	/* This is the optimal IO size (for stat), not the fs block size. */
+	vi->i_blksize = PAGE_CACHE_SIZE;
+	/*
+	 * This is for checking whether an inode has changed w.r.t. a file so
+	 * that the file can be updated if necessary (compare with f_version).
+	 */
+	vi->i_version = ++event;
+	/* Set uid and gid from the mount options. */
+	vi->i_uid = vol->uid;
+	vi->i_gid = vol->gid;
+	/* Set to zero so we can use logical operations on it from here on. */
+	vi->i_mode = 0;
+
+	/*
+	 * Initialize the ntfs specific part of @vi special casing
+	 * FILE_MFT which we need to do at mount time.
+	 */
+	if (vi->i_ino != FILE_MFT)
+		ntfs_init_big_inode(vi);
+
+	ni = NTFS_I(vi);
+
+	/* Map, pin and lock the mft record for reading. */
+	m = map_mft_record(READ, ni);
+	if (IS_ERR(m)) {
+		err = PTR_ERR(m);
+		goto err_out;
 	}
-	return ntfs_readwrite_attr(ino, attr, offset, buf);
-}
 
-/* -2 = error, -1 = hole, >= 0 means real disk cluster (lcn). */
-int ntfs_vcn_to_lcn(ntfs_inode *ino, int vcn)
-{
-	int rnum;
-	ntfs_attribute *data;
-	
-	data = ntfs_find_attr(ino, ino->vol->at_data, 0);
-	if (!data || data->resident || data->flags & (ATTR_IS_COMPRESSED |
-			ATTR_IS_ENCRYPTED))
-		return -2;
-	if (data->size <= (__s64)vcn << ino->vol->cluster_size_bits)
-		return -2;
-	if (data->initialized <= (__s64)vcn << ino->vol->cluster_size_bits)
-		return -1;
-	for (rnum = 0; rnum < data->d.r.len &&
-			vcn >= data->d.r.runlist[rnum].len; rnum++)
-		vcn -= data->d.r.runlist[rnum].len;
-	if (data->d.r.runlist[rnum].lcn >= 0)
-		return data->d.r.runlist[rnum].lcn + vcn;
-	return data->d.r.runlist[rnum].lcn + vcn;
-}
+	/* Is the record in use? */
+	if (!(m->flags & MFT_RECORD_IN_USE)) {
+		ntfs_error(vi->i_sb, "Inode is not in use! You should "
+				"run chkdsk.");
+		goto unm_err_out;
+	}
 
-static int allocate_store(ntfs_volume *vol, ntfs_disk_inode *store, int count)
-{
-	int i;
-	
-	if (store->count > count)
-		return 0;
-	if (store->size < count) {
-		ntfs_mft_record *n = ntfs_malloc((count + 4) * 
-						 sizeof(ntfs_mft_record));
-		if (!n)
-			return -ENOMEM;
-		if (store->size) {
-			for (i = 0; i < store->size; i++)
-				n[i] = store->records[i];
-			ntfs_free(store->records);
-		}
-		store->size = count + 4;
-		store->records = n;
-	}
-	for (i = store->count; i < count; i++) {
-		store->records[i].record = ntfs_malloc(vol->mft_record_size);
-		if (!store->records[i].record)
-			return -ENOMEM;
-		store->count++;
+	/* Is this an extent mft record / inode? Treat same as if not in use. */
+	if (m->base_mft_record) {
+		ntfs_error(vi->i_sb, "Inode is an extent inode! iget() "
+				"not possible. You should run chkdsk.");
+		goto unm_err_out;
 	}
-	return 0;
-}
 
-static void deallocate_store(ntfs_disk_inode* store)
-{
-	int i;
-	
-	for (i = 0; i < store->count; i++)
-		ntfs_free(store->records[i].record);
-	ntfs_free(store->records);
-	store->count = store->size = 0;
-	store->records = 0;
-}
+	/* Transfer information from mft record into vfs and ntfs inodes. */
 
-/**
- * layout_runs - compress runlist into mapping pairs array
- * @attr:	attribute containing the runlist to compress
- * @rec:	destination buffer to hold the mapping pairs array
- * @offs:	current position in @rec (in/out variable)
- * @size:	size of the buffer @rec
- *
- * layout_runs walks the runlist in @attr, compresses it and writes it out the
- * resulting mapping pairs array into @rec (up to a maximum of @size bytes are
- * written). On entry @offs is the offset in @rec at which to begin writing the
- * mapping pairs array. On exit, it contains the offset in @rec of the first
- * byte after the end of the mapping pairs array.
- */
-static int layout_runs(ntfs_attribute *attr, char *rec, int *offs, int size)
-{
-	int i, len, offset, coffs;
-	/* ntfs_cluster_t MUST be signed! (AIA) */
-	ntfs_cluster_t cluster, rclus;
-	ntfs_runlist *rl = attr->d.r.runlist;
-	cluster = 0;
-	offset = *offs;
-	for (i = 0; i < attr->d.r.len; i++) {
+	/* Cache the sequence number in the ntfs inode. */
+	ni->seq_no = le16_to_cpu(m->sequence_number);
+
+	/*
+	 * FIXME: Keep in mind that link_count is two for files which have both
+	 * a long file name and a short file name as separate entries, so if
+	 * we are hiding short file names this will be too high. Either we need
+	 * to account for the short file names by subtracting them or we need
+	 * to make sure we delete files even though i_nlink is not zero which
+	 * might be tricky due to vfs interactions. Need to think about this
+	 * some more when implementing the unlink command.
+	 */
+	vi->i_nlink = le16_to_cpu(m->link_count);
+	/*
+	 * FIXME: Reparse points can have the directory bit set even though
+	 * they would be S_IFLNK. Need to deal with this further below when we
+	 * implement reparse points / symbolic links but it will do for now.
+	 * Also if not a directory, it could be something else, rather than
+	 * a regular file. But again, will do for now.
+	 */
+	if (m->flags & MFT_RECORD_IS_DIRECTORY) {
+		vi->i_mode |= S_IFDIR;
 		/*
-		 * We cheat with this check on the basis that lcn will never
-		 * be less than -1 and the lcn delta will fit in signed
-		 * 32-bits (ntfs_cluster_t). (AIA)
+		 * Linux/Unix do not support directory hard links and things
+		 * break without this kludge.
 		 */
-		if (rl[i].lcn < (ntfs_cluster_t)-1) {
-			ntfs_error("layout_runs() encountered an out of bounds "
-					"cluster delta, lcn = %i.\n",
-					rl[i].lcn);
-			return -ERANGE;
-		}
-		rclus = rl[i].lcn - cluster;
-		len = rl[i].len;
-		rec[offset] = 0;
- 		if (offset + 9 > size)
-			return -E2BIG; /* It might still fit, but this
-					* simplifies testing. */
+		if (vi->i_nlink > 1)
+			vi->i_nlink = 1;
+	} else
+		vi->i_mode |= S_IFREG;
+
+	ctx = get_attr_search_ctx(ni, m);
+	if (!ctx) {
+		err = -ENOMEM;
+		goto unm_err_out;
+	}
+
+	/*
+	 * Find the standard information attribute in the mft record. At this
+	 * stage we haven't setup the attribute list stuff yet, so this could
+	 * in fact fail if the standard information is in an extent record, but
+	 * I don't think this actually ever happens.
+	 */
+	if (!lookup_attr(AT_STANDARD_INFORMATION, NULL, 0, 0, 0, NULL, 0,
+			ctx)) {
 		/*
-		 * Run length is stored as signed number, so deal with it
-		 * properly, i.e. observe that a negative number will have all
-		 * its most significant bits set to 1 but we don't store that
-		 * in the mapping pairs array. We store the smallest type of
-		 * negative number required, thus in the first if we check
-		 * whether len fits inside a signed byte and if so we store it
-		 * as such, the next ifs check for a signed short, then a signed
-		 * 24-bit and finally the full blown signed 32-bit. Same goes
-		 * for rlus below. (AIA)
+		 * TODO: We should be performing a hot fix here (if the recover
+		 * mount option is set) by creating a new attribute.
 		 */
-		if (len >= -0x80 && len <= 0x7f) {
-			NTFS_PUTU8(rec + offset + 1, len & 0xff);
-			coffs = 1;
- 		} else if (len >= -0x8000 && len <= 0x7fff) {
-			NTFS_PUTU16(rec + offset + 1, len & 0xffff);
-			coffs = 2;
- 		} else if (len >= -0x800000 && len <= 0x7fffff) {
-			NTFS_PUTU24(rec + offset + 1, len & 0xffffff);
-			coffs = 3;
-		} else /* if (len >= -0x80000000LL && len <= 0x7fffffff */ {
-			NTFS_PUTU32(rec + offset + 1, len);
-			coffs = 4;
-		} /* else ... FIXME: When len becomes 64-bit we need to extend
-		   * 		     the else if () statements. (AIA) */
-		*(rec + offset) |= coffs++;
-		if (rl[i].lcn == (ntfs_cluster_t)-1) /* Compressed run. */
-			/* Nothing */;
-		else if (rclus >= -0x80 && rclus <= 0x7f) {
-			*(rec + offset) |= 0x10;
-			NTFS_PUTS8(rec + offset + coffs, rclus & 0xff);
-			coffs += 1;
-		} else if (rclus >= -0x8000 && rclus <= 0x7fff) {
-			*(rec + offset) |= 0x20;
-			NTFS_PUTS16(rec + offset + coffs, rclus & 0xffff);
-			coffs += 2;
-		} else if (rclus >= -0x800000 && rclus <= 0x7fffff) {
-			*(rec + offset) |= 0x30;
-			NTFS_PUTS24(rec + offset + coffs, rclus & 0xffffff);
-			coffs += 3;
-		} else /* if (rclus >= -0x80000000LL && rclus <= 0x7fffffff)*/ {
-			*(rec + offset) |= 0x40;
-			NTFS_PUTS32(rec + offset + coffs, rclus
-							/* & 0xffffffffLL */);
-			coffs += 4;
-		} /* FIXME: When rclus becomes 64-bit.
-		else if (rclus >= -0x8000000000 && rclus <= 0x7FFFFFFFFF) {
-			*(rec + offset) |= 0x50;
-			NTFS_PUTS40(rec + offset + coffs, rclus &
-							0xffffffffffLL);
-			coffs += 5;
-		} else if (rclus >= -0x800000000000 && 
-						rclus <= 0x7FFFFFFFFFFF) {
-			*(rec + offset) |= 0x60;
-			NTFS_PUTS48(rec + offset + coffs, rclus &
-							0xffffffffffffLL);
-			coffs += 6;
-		} else if (rclus >= -0x80000000000000 && 
-						rclus <= 0x7FFFFFFFFFFFFF) {
-			*(rec + offset) |= 0x70;
-			NTFS_PUTS56(rec + offset + coffs, rclus &
-							0xffffffffffffffLL);
-			coffs += 7;
-		} else {
-			*(rec + offset) |= 0x80;
-			NTFS_PUTS64(rec + offset + coffs, rclus);
-			coffs += 8;
-		} */
-		offset += coffs;
-		if (rl[i].lcn)
-			cluster = rl[i].lcn;
-	}
-	if (offset >= size)
-		return -E2BIG;
-	/* Terminating null. */
-	*(rec + offset++) = 0;
-	*offs = offset;
-	return 0;
-}
-
-static void count_runs(ntfs_attribute *attr, char *buf)
-{
-	ntfs_u32 first, count, last, i;
-	
-	first = 0;
-	for (i = 0, count = 0; i < attr->d.r.len; i++)
-		count += attr->d.r.runlist[i].len;
-	last = first + count - 1;
-	NTFS_PUTU64(buf + 0x10, first);
-	NTFS_PUTU64(buf + 0x18, last);
-} 
-
-/**
- * layout_attr - convert in memory attribute to on disk attribute record
- * @attr:	in memory attribute to convert
- * @buf:	destination buffer for on disk attribute record
- * @size:	size of the destination buffer
- * @psize:	size of converted on disk attribute record (out variable)
- *
- * layout_attr() takes the attribute @attr and converts it into the appropriate
- * on disk structure, writing it into @buf (up to @size bytes are written).
- *
- * On success we return 0 and set @*psize to the actual byte size of the on-
- * disk attribute that was written into @buf.
- */
-static int layout_attr(ntfs_attribute *attr, char *buf, int size, int *psize)
-{
-	int nameoff, hdrsize, asize;
-	
-	if (attr->resident) {
-		nameoff = 0x18;
-		hdrsize = (nameoff + 2 * attr->namelen + 7) & ~7;
-		asize = (hdrsize + attr->size + 7) & ~7;
-		if (size < asize)
-			return -E2BIG;
-		NTFS_PUTU32(buf + 0x10, attr->size);
-		NTFS_PUTU8(buf + 0x16, attr->indexed);
-		NTFS_PUTU16(buf + 0x14, hdrsize);
-		if (attr->size)
-			ntfs_memcpy(buf + hdrsize, attr->d.data, attr->size);
-	} else {
-		int error;
+		ntfs_error(vi->i_sb, "$STANDARD_INFORMATION attribute is "
+				"missing.");
+		goto put_unm_err_out;
+	}
+	/* Get the standard information attribute value. */
+	si = (STANDARD_INFORMATION*)((char*)ctx->attr +
+			le16_to_cpu(ctx->attr->_ARA(value_offset)));
 
-		if (attr->flags & ATTR_IS_COMPRESSED)
- 			nameoff = 0x48;
- 		else
- 			nameoff = 0x40;
- 		hdrsize = (nameoff + 2 * attr->namelen + 7) & ~7;
- 		if (size < hdrsize)
- 			return -E2BIG;
- 		/* Make asize point at the end of the attribute record header,
-		   i.e. at the beginning of the mapping pairs array. */
- 		asize = hdrsize;
- 		error = layout_runs(attr, buf, &asize, size);
- 		/* Now, asize points one byte beyond the end of the mapping
-		   pairs array. */
-		if (error)
- 			return error;
- 		/* The next attribute has to begin on 8-byte boundary. */
-		asize = (asize + 7) & ~7;
-		/* FIXME: fragments */
-		count_runs(attr, buf);
-		NTFS_PUTU16(buf + 0x20, hdrsize);
-		NTFS_PUTU16(buf + 0x22, attr->cengine);
-		NTFS_PUTU32(buf + 0x24, 0);
-		NTFS_PUTS64(buf + 0x28, attr->allocated);
-		NTFS_PUTS64(buf + 0x30, attr->size);
-		NTFS_PUTS64(buf + 0x38, attr->initialized);
-		if (attr->flags & ATTR_IS_COMPRESSED)
-			NTFS_PUTS64(buf + 0x40, attr->compsize);
-	}
-	NTFS_PUTU32(buf, attr->type);
-	NTFS_PUTU32(buf + 4, asize);
-	NTFS_PUTU8(buf + 8, attr->resident ? 0 : 1);
-	NTFS_PUTU8(buf + 9, attr->namelen);
-	NTFS_PUTU16(buf + 0xa, nameoff);
-	NTFS_PUTU16(buf + 0xc, attr->flags);
-	NTFS_PUTU16(buf + 0xe, attr->attrno);
-	if (attr->namelen)
-		ntfs_memcpy(buf + nameoff, attr->name, 2 * attr->namelen);
-	*psize = asize;
-	return 0;
-}
+	/* Transfer information from the standard information into vfs_ino. */
+	/*
+	 * Note: The i_?times do not quite map perfectly onto the NTFS times,
+	 * but they are close enough, and in the end it doesn't really matter
+	 * that much...
+	 */
+	/*
+	 * mtime is the last change of the data within the file. Not changed
+	 * when only metadata is changed, e.g. a rename doesn't affect mtime.
+	 */
+	vi->i_mtime = ntfs2utc(si->last_data_change_time);
+	/*
+	 * ctime is the last change of the metadata of the file. This obviously
+	 * always changes, when mtime is changed. ctime can be changed on its
+	 * own, mtime is then not changed, e.g. when a file is renamed.
+	 */
+	vi->i_ctime = ntfs2utc(si->last_mft_change_time);
+	/*
+	 * Last access to the data within the file. Not changed during a rename
+	 * for example but changed whenever the file is written to.
+	 */
+	vi->i_atime = ntfs2utc(si->last_access_time);
 
-/**
- * layout_inode - convert an in-memory inode into on disk mft record(s)
- * @ino:	in memory inode to convert
- * @store:	on disk inode, contain buffers for the on disk mft record(s)
- *
- * layout_inode takes the in memory inode @ino, converts it into a (sequence of)
- * mft record(s) and writes them to the appropriate buffers in the @store.
- *
- * Return 0 on success,
- * the required mft record count (>0) if the inode does not fit,
- * -ENOMEM if memory allocation problem, or
- * -EOPNOTSUP if beyond our capabilities.
- *
- * TODO: We at the moment do not support extension mft records. (AIA)
- */
-int layout_inode(ntfs_inode *ino, ntfs_disk_inode *store)
-{
-	int offset, i, size, psize, error, count, recno;
-	ntfs_attribute *attr;
-	unsigned char *rec;
-
-	error = allocate_store(ino->vol, store, ino->record_count);
-	if (error)
-		return error;
-	size = ino->vol->mft_record_size;
- 	count = i = 0;
- 	do {
- 		if (count < ino->record_count) {
- 			recno = ino->records[count];
- 		} else {
- 			error = allocate_store(ino->vol, store, count + 1);
- 			if (error)
- 				return error;
-	 		recno = -1;
+	/*
+	 * Find the attribute list attribute and set the corresponding bit in
+	 * ntfs_ino->state.
+	 */
+	reinit_attr_search_ctx(ctx);
+	if (lookup_attr(AT_ATTRIBUTE_LIST, NULL, 0, 0, 0, NULL, 0, ctx)) {
+		if (vi->i_ino == FILE_MFT)
+			goto skip_attr_list_load;
+		ntfs_debug("Attribute list found in inode %li (0x%lx).",
+				vi->i_ino, vi->i_ino);
+		ni->state |= 1 << NI_AttrList;
+		if (ctx->attr->flags & ATTR_IS_ENCRYPTED ||
+				ctx->attr->flags & ATTR_COMPRESSION_MASK) {
+			ntfs_error(vi->i_sb, "Attribute list attribute is "
+					"compressed/encrypted. Not allowed. "
+					"Corrupt inode. You should run "
+					"chkdsk.");
+			goto put_unm_err_out;
+		}
+		/* Now allocate memory for the attribute list. */
+		ni->attr_list_size = (u32)attribute_value_length(ctx->attr);
+		ni->attr_list = ntfs_malloc_nofs(ni->attr_list_size);
+		if (!ni->attr_list) {
+			ntfs_error(vi->i_sb, "Not enough memory to allocate "
+					"buffer for attribute list.");
+			err = -ENOMEM;
+			goto ec_put_unm_err_out;
+		}
+		if (ctx->attr->non_resident) {
+			ni->state |= 1 << NI_AttrListNonResident;
+			if (ctx->attr->_ANR(lowest_vcn)) {
+				ntfs_error(vi->i_sb, "Attribute list has non "
+						"zero lowest_vcn. Inode is "
+						"corrupt. You should run "
+						"chkdsk.");
+				goto put_unm_err_out;
+			}
+			/*
+			 * Setup the run list. No need for locking as we have
+			 * exclusive access to the inode at this time.
+			 */
+			ni->attr_list_rl.rl = decompress_mapping_pairs(vol,
+					ctx->attr, NULL);
+			if (IS_ERR(ni->attr_list_rl.rl)) {
+				err = PTR_ERR(ni->attr_list_rl.rl);
+				ni->attr_list_rl.rl = NULL;
+				ntfs_error(vi->i_sb, "Mapping pairs "
+						"decompression failed with "
+						"error code %i. Corrupt "
+						"attribute list in inode.",
+						-err);
+				goto ec_put_unm_err_out;
+			}
+			/* Now load the attribute list. */
+			if ((err = load_attribute_list(vol, &ni->attr_list_rl,
+					ni->attr_list, ni->attr_list_size,
+					sle64_to_cpu(
+					ctx->attr->_ANR(initialized_size))))) {
+				ntfs_error(vi->i_sb, "Failed to load "
+						"attribute list attribute.");
+				goto ec_put_unm_err_out;
+			}
+		} else /* if (!ctx.attr->non_resident) */ {
+			if ((u8*)ctx->attr + le16_to_cpu(
+					ctx->attr->_ARA(value_offset)) +
+					le32_to_cpu(
+					ctx->attr->_ARA(value_length)) >
+					(u8*)ctx->mrec + vol->mft_record_size) {
+				ntfs_error(vi->i_sb, "Corrupt attribute list "
+						"in inode.");
+				goto put_unm_err_out;
+			}
+			/* Now copy the attribute list. */
+			memcpy(ni->attr_list, (u8*)ctx->attr + le16_to_cpu(
+					ctx->attr->_ARA(value_offset)),
+					le32_to_cpu(
+					ctx->attr->_ARA(value_length)));
+		}
+	}
+skip_attr_list_load:
+	/*
+	 * If an attribute list is present we now have the attribute list value
+	 * in ntfs_ino->attr_list and it is ntfs_ino->attr_list_size bytes.
+	 */
+	if (S_ISDIR(vi->i_mode)) {
+		INDEX_ROOT *ir;
+		char *ir_end, *index_end;
+
+		/* It is a directory, find index root attribute. */
+		reinit_attr_search_ctx(ctx);
+		if (!lookup_attr(AT_INDEX_ROOT, I30, 4, CASE_SENSITIVE, 0,
+				NULL, 0, ctx)) {
+			// FIXME: File is corrupt! Hot-fix with empty index
+			// root attribute if recovery option is set.
+			ntfs_error(vi->i_sb, "$INDEX_ROOT attribute is "
+					"missing.");
+			goto put_unm_err_out;
+		}
+		/* Set up the state. */
+		if (ctx->attr->non_resident) {
+			ntfs_error(vi->i_sb, "$INDEX_ROOT attribute is "
+					"not resident. Not allowed.");
+			goto put_unm_err_out;
 		}
 		/*
-		 * FIXME: We need to support extension records properly.
-		 * At the moment they wouldn't work. Probably would "just" get
-		 * corrupted if we write to them... (AIA)
+		 * Compressed/encrypted index root just means that the newly
+		 * created files in that directory should be created compressed/
+		 * encrypted. However index root cannot be both compressed and
+		 * encrypted.
 		 */
-	 	store->records[count].recno = recno;
- 		rec = store->records[count].record;
-	 	count++;
- 		/* Copy mft record header. */
-	 	offset = NTFS_GETU16(ino->attr + 0x14); /* attrs_offset */
-		ntfs_memcpy(rec, ino->attr, offset);
-	 	/* Copy attributes. */
- 		while (i < ino->attr_count) {
- 			attr = ino->attrs + i;
-	 		error = layout_attr(attr, rec + offset,
-					size - offset - 8, &psize);
-	 		if (error == -E2BIG && offset != NTFS_GETU16(ino->attr
-					+ 0x14))
- 				break;
- 			if (error)
- 				return error;
- 			offset += psize;
- 			i++;
- 		}
- 		/* Terminating attribute. */
-		NTFS_PUTU32(rec + offset, 0xFFFFFFFF);
-		offset += 4;
-		NTFS_PUTU32(rec + offset, 0);
-		offset += 4;
-		NTFS_PUTU32(rec + 0x18, offset);
-	} while (i < ino->attr_count || count < ino->record_count);
-	return count - ino->record_count;
-}
-
-/*
- * FIXME: ntfs_update_inode() calls layout_inode() to create the mft record on
- * disk structure corresponding to the inode @ino. After that, ntfs_write_attr()
- * is called to write out the created mft record to disk.
- * We shouldn't need to re-layout every single time we are updating an mft
- * record. No wonder the ntfs driver is slow like hell. (AIA)
- */
-int ntfs_update_inode(ntfs_inode *ino)
-{
-	int error, i;
-	ntfs_disk_inode store;
-	ntfs_io io;
-
-	ntfs_bzero(&store, sizeof(store));
-	error = layout_inode(ino, &store);
-	if (error == -E2BIG) {
-		i = ntfs_split_indexroot(ino);
-		if (i != -ENOTDIR) {
-			if (!i)
-				i = layout_inode(ino, &store);
-			error = i;
-		}
-	}
-	if (error == -E2BIG) {
-		error = ntfs_attr_allnonresident(ino);
-		if (!error)
-			error = layout_inode(ino, &store);
-	}
-	if (error > 0) {
-		/* FIXME: Introduce extension records. */
-		error = -E2BIG;
-	}
-	if (error) {
-		if (error == -E2BIG)
-			ntfs_error("Cannot handle saving inode 0x%x.\n",
-				   ino->i_number);
-		deallocate_store(&store);
-		return error;
-	}
-	io.fn_get = ntfs_get;
-	io.fn_put = 0;
-	for (i = 0; i < store.count; i++) {
-		error = ntfs_insert_fixups(store.records[i].record,
-				ino->vol->mft_record_size);
-		if (error) {
-			printk(KERN_ALERT "NTFS: ntfs_update_inode() caught "
-					"corrupt %s mtf record ntfs record "
-					"header. Refusing to write corrupt "
-					"data to disk. Unmount and run chkdsk "
-					"immediately!\n", i ? "extension":
-					"base");
-			deallocate_store(&store);
-			return -EIO;
+		if (ctx->attr->flags & ATTR_COMPRESSION_MASK)
+			ni->state |= 1 << NI_Compressed;
+		if (ctx->attr->flags & ATTR_IS_ENCRYPTED) {
+			if (ctx->attr->flags & ATTR_COMPRESSION_MASK) {
+				ntfs_error(vi->i_sb, "Found encrypted and "
+						"compressed attribute. Not "
+						"allowed.");
+				goto put_unm_err_out;
+			}
+			ni->state |= 1 << NI_Encrypted;
 		}
-		io.param = store.records[i].record;
-		io.size = ino->vol->mft_record_size;
-		error = ntfs_write_attr(ino->vol->mft_ino, ino->vol->at_data,
-				0, (__s64)store.records[i].recno <<
-				ino->vol->mft_record_size_bits, &io);
-		if (error || io.size != ino->vol->mft_record_size) {
-			/* Big trouble, partially written file. */
-			ntfs_error("Please unmount: Write error in inode "
-					"0x%x\n", ino->i_number);
-			deallocate_store(&store);
-			return error ? error : -EIO;
+		ir = (INDEX_ROOT*)((char*)ctx->attr +
+				le16_to_cpu(ctx->attr->_ARA(value_offset)));
+		ir_end = (char*)ir + le32_to_cpu(ctx->attr->_ARA(value_length));
+		if (ir_end > (char*)ctx->mrec + vol->mft_record_size) {
+			ntfs_error(vi->i_sb, "$INDEX_ROOT attribute is "
+					"corrupt.");
+			goto put_unm_err_out;
+		}
+		index_end = (char*)&ir->index +
+				le32_to_cpu(ir->index.index_length);
+		if (index_end > ir_end) {
+			ntfs_error(vi->i_sb, "Directory index is corrupt.");
+			goto put_unm_err_out;
+		}
+		if (ir->type != AT_FILE_NAME) {
+			ntfs_error(vi->i_sb, __FUNCTION__ "(): Indexed "
+					"attribute is not $FILE_NAME. Not "
+					"allowed.");
+			goto put_unm_err_out;
+		}
+		if (ir->collation_rule != COLLATION_FILE_NAME) {
+			ntfs_error(vi->i_sb, "Index collation rule is not "
+					"COLLATION_FILE_NAME. Not allowed.");
+			goto put_unm_err_out;
+		}
+		ni->_IDM(index_block_size) = le32_to_cpu(ir->index_block_size);
+		if (ni->_IDM(index_block_size) &
+				(ni->_IDM(index_block_size) - 1)) {
+			ntfs_error(vi->i_sb, "Index block size (%u) is not a "
+					"power of two.",
+					ni->_IDM(index_block_size));
+			goto put_unm_err_out;
+		}
+		if (ni->_IDM(index_block_size) > PAGE_CACHE_SIZE) {
+			ntfs_error(vi->i_sb, "Index block size (%u) > "
+					"PAGE_CACHE_SIZE (%ld) is not "
+					"supported. Sorry.",
+					ni->_IDM(index_block_size),
+					PAGE_CACHE_SIZE);
+			err = -EOPNOTSUPP;
+			goto ec_put_unm_err_out;
+		}
+		if (ni->_IDM(index_block_size) < NTFS_BLOCK_SIZE) {
+			ntfs_error(vi->i_sb, "Index block size (%u) < "
+					"NTFS_BLOCK_SIZE (%i) is not "
+					"supported. Sorry.",
+					ni->_IDM(index_block_size),
+					NTFS_BLOCK_SIZE);
+			err = -EOPNOTSUPP;
+			goto ec_put_unm_err_out;
+		}
+		ni->_IDM(index_block_size_bits) =
+				ffs(ni->_IDM(index_block_size)) - 1;
+		/* Determine the size of a vcn in the directory index. */
+		if (vol->cluster_size <= ni->_IDM(index_block_size)) {
+			ni->_IDM(index_vcn_size) = vol->cluster_size;
+			ni->_IDM(index_vcn_size_bits) = vol->cluster_size_bits;
+		} else {
+			ni->_IDM(index_vcn_size) = vol->sector_size;
+			ni->_IDM(index_vcn_size_bits) = vol->sector_size_bits;
 		}
-	}
-	deallocate_store(&store);
-	return 0;
-}	
-
-void ntfs_decompress(unsigned char *dest, unsigned char *src, ntfs_size_t l)
-{
-	int head, comp;
-	int copied = 0;
-	unsigned char *stop;
-	int bits;
-	int tag = 0;
-	int clear_pos;
-	
-	while (1) {
-		head = NTFS_GETU16(src) & 0xFFF;
-		/* High bit indicates that compression was performed. */
-		comp = NTFS_GETU16(src) & 0x8000;
-		src += 2;
-		stop = src + head;
-		bits = 0;
-		clear_pos = 0;
-		if (head == 0)
-			/* Block is not used. */
-			return;/* FIXME: copied */
-		if (!comp) { /* uncompressible */
-			ntfs_memcpy(dest, src, 0x1000);
-			dest += 0x1000;
-			copied += 0x1000;
-			src += 0x1000;
-			if (l == copied)
-				return;
-			continue;
-		}
-		while (src <= stop) {
-			if (clear_pos > 4096) {
-				ntfs_error("Error 1 in decompress\n");
-				return;
+		if (!(ir->index.flags & LARGE_INDEX)) {
+			/* No index allocation. */
+			vi->i_size = ni->initialized_size = 0;
+			goto skip_large_dir_stuff;
+		} /* LARGE_INDEX: Index allocation present. Setup state. */
+		ni->state |= 1 << NI_NonResident;
+		/* Find index allocation attribute. */
+		reinit_attr_search_ctx(ctx);
+		if (!lookup_attr(AT_INDEX_ALLOCATION, I30, 4, CASE_SENSITIVE,
+				0, NULL, 0, ctx)) {
+			ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute "
+					"is not present but $INDEX_ROOT "
+					"indicated it is.");
+			goto put_unm_err_out;
+		}
+		if (!ctx->attr->non_resident) {
+			ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute "
+					"is resident.");
+			goto put_unm_err_out;
+		}
+		if (ctx->attr->flags & ATTR_IS_ENCRYPTED) {
+			ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute "
+					"is encrypted.");
+			goto put_unm_err_out;
+		}
+		if (ctx->attr->flags & ATTR_COMPRESSION_MASK) {
+			ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute "
+					"is compressed.");
+			goto put_unm_err_out;
+		}
+		if (ctx->attr->_ANR(lowest_vcn)) {
+			ntfs_error(vi->i_sb, "First extent of "
+					"$INDEX_ALLOCATION attribute has non "
+					"zero lowest_vcn. Inode is corrupt. "
+					"You should run chkdsk.");
+			goto put_unm_err_out;
+		}
+		vi->i_size = sle64_to_cpu(ctx->attr->_ANR(data_size));
+		ni->initialized_size = sle64_to_cpu(
+				ctx->attr->_ANR(initialized_size));
+		ni->allocated_size = sle64_to_cpu(
+				ctx->attr->_ANR(allocated_size));
+		/* Find bitmap attribute. */
+		reinit_attr_search_ctx(ctx);
+		if (!lookup_attr(AT_BITMAP, I30, 4, CASE_SENSITIVE, 0, NULL, 0,
+				ctx)) {
+			ntfs_error(vi->i_sb, "$BITMAP attribute is not "
+					"present but it must be.");
+			goto put_unm_err_out;
+		}
+		if (ctx->attr->flags & (ATTR_COMPRESSION_MASK |
+				ATTR_IS_ENCRYPTED)) {
+			ntfs_error(vi->i_sb, "$BITMAP attribute is compressed "
+					"and/or encrypted.");
+			goto put_unm_err_out;
+		}
+		if (ctx->attr->non_resident) {
+			ni->state |= 1 << NI_BmpNonResident;
+			if (ctx->attr->_ANR(lowest_vcn)) {
+				ntfs_error(vi->i_sb, "First extent of $BITMAP "
+						"attribute has non zero "
+						"lowest_vcn. Inode is corrupt. "
+						"You should run chkdsk.");
+				goto put_unm_err_out;
 			}
-			if (!bits) {
-				tag = NTFS_GETU8(src);
-				bits = 8;
-				src++;
-				if (src > stop)
-					break;
+			ni->_IDM(bmp_size) = sle64_to_cpu(
+					ctx->attr->_ANR(data_size));
+			ni->_IDM(bmp_initialized_size) = sle64_to_cpu(
+					ctx->attr->_ANR(initialized_size));
+			ni->_IDM(bmp_allocated_size) = sle64_to_cpu(
+					ctx->attr->_ANR(allocated_size));
+			/*
+			 * Setup the run list. No need for locking as we have
+			 * exclusive access to the inode at this time.
+			 */
+			ni->_IDM(bmp_rl).rl = decompress_mapping_pairs(vol,
+					ctx->attr, NULL);
+			if (IS_ERR(ni->_IDM(bmp_rl).rl)) {
+				err = PTR_ERR(ni->_IDM(bmp_rl).rl);
+				ni->_IDM(bmp_rl).rl = NULL;
+				ntfs_error(vi->i_sb, "Mapping pairs "
+						"decompression failed with "
+						"error code %i.", -err);
+				goto ec_put_unm_err_out;
 			}
-			if (tag & 1) {
-				int i, len, delta, code, lmask, dshift;
-				code = NTFS_GETU16(src);
-				src += 2;
-				if (!clear_pos) {
-					ntfs_error("Error 2 in decompress\n");
-					return;
+		} else
+			ni->_IDM(bmp_size) = ni->_IDM(bmp_initialized_size) =
+					ni->_IDM(bmp_allocated_size) =
+					le32_to_cpu(
+					ctx->attr->_ARA(value_length));
+		/* Consistency check bitmap size vs. index allocation size. */
+		if (ni->_IDM(bmp_size) << 3 < vi->i_size >>
+				ni->_IDM(index_block_size_bits)) {
+			ntfs_error(vi->i_sb, "$I30 bitmap too small (0x%Lx) "
+					"for index allocation (0x%Lx).",
+					(long long)ni->_IDM(bmp_size) << 3,
+					vi->i_size);
+			goto put_unm_err_out;
+		}
+skip_large_dir_stuff:
+		/* Everyone gets read and scan permissions. */
+		vi->i_mode |= S_IRUGO | S_IXUGO;
+		/* If not read-only, set write permissions. */
+		if (!IS_RDONLY(vi))
+			vi->i_mode |= S_IWUGO;
+		/*
+		 * Apply the directory permissions mask set in the mount
+		 * options.
+		 */
+		vi->i_mode &= ~vol->dmask;
+		/* Setup the operations for this inode. */
+		vi->i_op = &ntfs_dir_inode_ops;
+		vi->i_fop = &ntfs_dir_ops;
+		vi->i_mapping->a_ops = &ntfs_dir_aops;
+	} else {
+		/* It is a file: find first extent of unnamed data attribute. */
+		reinit_attr_search_ctx(ctx);
+		if (!lookup_attr(AT_DATA, NULL, 0, 0, 0, NULL, 0, ctx)) {
+			vi->i_size = ni->initialized_size =
+					ni->allocated_size = 0LL;
+			/*
+			 * FILE_Secure does not have an unnamed $DATA
+			 * attribute, so we special case it here.
+			 */
+			if (vi->i_ino == FILE_Secure)
+				goto no_data_attr_special_case;
+			/*
+			 * Most if not all the system files in the $Extend
+			 * system directory do not have unnamed data
+			 * attributes so we need to check if the parent
+			 * directory of the file is FILE_Extend and if it is
+			 * ignore this error. To do this we need to get the
+			 * name of this inode from the mft record as the name
+			 * contains the back reference to the parent directory.
+			 */
+			if (ntfs_is_extended_system_file(ctx) > 0)
+				goto no_data_attr_special_case;
+			// FIXME: File is corrupt! Hot-fix with empty data
+			// attribute if recovery option is set.
+			ntfs_error(vi->i_sb, "$DATA attribute is "
+					"missing.");
+			goto put_unm_err_out;
+		}
+		/* Setup the state. */
+		if (ctx->attr->non_resident) {
+			ni->state |= 1 << NI_NonResident;
+			if (ctx->attr->flags & ATTR_COMPRESSION_MASK) {
+				ni->state |= 1 << NI_Compressed;
+				if (vol->cluster_size > 4096) {
+					ntfs_error(vi->i_sb, "Found "
+						"compressed data but "
+						"compression is disabled due "
+						"to cluster size (%i) > 4kiB.",
+						vol->cluster_size);
+					goto put_unm_err_out;
 				}
-				for (i = clear_pos - 1, lmask = 0xFFF,
-				     dshift = 12; i >= 0x10; i >>= 1) {
-					lmask >>= 1;
-					dshift--;
+				if ((ctx->attr->flags & ATTR_COMPRESSION_MASK)
+						!= ATTR_IS_COMPRESSED) {
+					ntfs_error(vi->i_sb, "Found "
+						"unknown compression method or "
+						"corrupt file.");
+					goto put_unm_err_out;
 				}
-				delta = code >> dshift;
-				len = (code & lmask) + 3;
-				for (i = 0; i < len; i++) {
-					dest[clear_pos] = dest[clear_pos - 
-								    delta - 1];
-					clear_pos++;
-					copied++;
-					if (copied==l)
-						return;
+				ni->_ICF(compression_block_clusters) = 1U <<
+					ctx->attr->_ANR(compression_unit);
+				if (ctx->attr->_ANR(compression_unit) != 4) {
+					ntfs_error(vi->i_sb, "Found "
+						"nonstandard compression unit "
+						"(%u instead of 4). Cannot "
+						"handle this. This might "
+						"indicate corruption so you "
+						"should run chkdsk.",
+					     ctx->attr->_ANR(compression_unit));
+					err = -EOPNOTSUPP;
+					goto ec_put_unm_err_out;
 				}
-			} else {
-				dest[clear_pos++] = NTFS_GETU8(src);
-				src++;
-				copied++;
-				if (copied==l)
-					return;
+				ni->_ICF(compression_block_size) = 1U << (
+					       ctx->attr->_ANR(compression_unit)
+						+ vol->cluster_size_bits);
+				ni->_ICF(compression_block_size_bits) = ffs(
+					ni->_ICF(compression_block_size)) - 1;
+			}
+			if (ctx->attr->flags & ATTR_IS_ENCRYPTED) {
+				if (ctx->attr->flags & ATTR_COMPRESSION_MASK) {
+					ntfs_error(vi->i_sb, "Found encrypted "
+							"and compressed data.");
+					goto put_unm_err_out;
+				}
+				ni->state |= 1 << NI_Encrypted;
+			}
+			if (ctx->attr->_ANR(lowest_vcn)) {
+				ntfs_error(vi->i_sb, "First extent of $DATA "
+						"attribute has non zero "
+						"lowest_vcn. Inode is corrupt. "
+						"You should run chkdsk.");
+				goto put_unm_err_out;
+			}
+			/* Setup all the sizes. */
+			vi->i_size = sle64_to_cpu(ctx->attr->_ANR(data_size));
+			ni->initialized_size = sle64_to_cpu(
+					ctx->attr->_ANR(initialized_size));
+			ni->allocated_size = sle64_to_cpu(
+					ctx->attr->_ANR(allocated_size));
+			if (NInoCompressed(ni)) {
+				ni->_ICF(compressed_size) = sle64_to_cpu(
+					ctx->attr->_ANR(compressed_size));
+				if (vi->i_size != ni->initialized_size)
+					ntfs_warning(vi->i_sb, "Compressed "
+							"file with data_size "
+							"unequal to "
+							"initialized size "
+							"found. This will "
+							"probably cause "
+							"problems when trying "
+							"to access the file. "
+							"Please notify "
+							"linux-ntfs-dev@"
+							"lists.sf.net that you"
+							"saw this message."
+							"Thanks!");
 			}
-			tag >>= 1;
-			bits--;
+		} else { /* Resident attribute. */
+			/*
+			 * Make all sizes equal for simplicity in read code
+			 * paths. FIXME: Need to keep this in mind when
+			 * converting to non-resident attribute in write code
+			 * path. (Probably only affects truncate().)
+			 */
+			vi->i_size = ni->initialized_size = ni->allocated_size =
+				le32_to_cpu(ctx->attr->_ARA(value_length));
 		}
-		dest += clear_pos;
+no_data_attr_special_case:
+		/* Everyone gets all permissions. */
+		vi->i_mode |= S_IRWXUGO;
+		/* If read-only, noone gets write permissions. */
+		if (IS_RDONLY(vi))
+			vi->i_mode &= ~S_IWUGO;
+		/* Apply the file permissions mask set in the mount options. */
+		vi->i_mode &= ~vol->fmask;
+		/* Setup the operations for this inode. */
+		vi->i_op = &ntfs_file_inode_ops;
+		vi->i_fop = &ntfs_file_ops;
+		vi->i_mapping->a_ops = &ntfs_file_aops;
 	}
+	/*
+	 * The number of 512-byte blocks used on disk (for stat). This is in so
+	 * far inaccurate as it doesn't account for any named streams or other
+	 * special non-resident attributes, but that is how Windows works, too,
+	 * so we are at least consistent with Windows, if not entirely
+	 * consistent with the Linux Way. Doing it the Linux Way would cause a
+	 * significant slowdown as it would involve iterating over all
+	 * attributes in the mft record and adding the allocated/compressed
+	 * sizes of all non-resident attributes present to give us the Linux
+	 * correct size that should go into i_blocks (after division by 512).
+	 */
+	if (!NInoCompressed(ni))
+		vi->i_blocks = ni->allocated_size >> 9;
+	else
+		vi->i_blocks = ni->_ICF(compressed_size) >> 9;
+	/* Done. */
+	put_attr_search_ctx(ctx);
+	unmap_mft_record(READ, ni);
+	ntfs_debug("Done.");
+	return;
+ec_put_unm_err_out:
+	put_attr_search_ctx(ctx);
+	goto ec_unm_err_out;
+put_unm_err_out:
+	put_attr_search_ctx(ctx);
+unm_err_out:
+	err = -EIO;
+ec_unm_err_out:
+	unmap_mft_record(READ, ni);
+err_out:
+	ntfs_error(vi->i_sb, "Failed with error code %i. Marking inode "
+			"%li (0x%lx) as bad.", -err, vi->i_ino, vi->i_ino);
+	make_bad_inode(vi);
+	return;
 }
 
-/*
- * NOTE: Neither of the ntfs_*_bit functions are atomic! But we don't need
- * them atomic at present as we never operate on shared/cached bitmaps.
+/**
+ * ntfs_read_inode_mount - special read_inode for mount time use only
+ * @vi:		inode to read
+ *
+ * Read inode FILE_MFT at mount time, only called with super_block lock
+ * held from within the read_super() code path.
+ *
+ * This function exists because when it is called the page cache for $MFT/$DATA
+ * is not initialized and hence we cannot get at the contents of mft records
+ * by calling map_mft_record*().
+ *
+ * Further it needs to cope with the circular references problem, i.e. can't
+ * load any attributes other than $ATTRIBUTE_LIST until $DATA is loaded, because
+ * we don't know where the other extent mft records are yet and again, because
+ * we cannot call map_mft_record*() yet. Obviously this applies only when an
+ * attribute list is actually present in $MFT inode.
+ *
+ * We solve these problems by starting with the $DATA attribute before anything
+ * else and iterating using lookup_attr($DATA) over all extents. As each extent
+ * is found, we decompress_mapping_pairs() including the implied
+ * merge_run_lists(). Each step of the iteration necessarily provides
+ * sufficient information for the next step to complete.
+ *
+ * This should work but there are two possible pit falls (see inline comments
+ * below), but only time will tell if they are real pits or just smoke...
  */
-static __inline__ int ntfs_test_bit(unsigned char *byte, const int bit)
-{
-	return byte[bit >> 3] & (1 << (bit & 7)) ? 1 : 0;
-}
-
-static __inline__ void ntfs_set_bit(unsigned char *byte, const int bit)
-{
-	byte[bit >> 3] |= 1 << (bit & 7);
-}
-
-static __inline__ void ntfs_clear_bit(unsigned char *byte, const int bit)
+void ntfs_read_inode_mount(struct inode *vi)
 {
-	byte[bit >> 3] &= ~(1 << (bit & 7));
-}
-
-static __inline__ int ntfs_test_and_clear_bit(unsigned char *byte,
-		const int bit)
-{
-	unsigned char *ptr = byte + (bit >> 3);
-	int b = 1 << (bit & 7);
-	int oldbit = *ptr & b ? 1 : 0;
-	*ptr &= ~b;
-	return oldbit;
-}
+	VCN next_vcn, last_vcn, highest_vcn;
+	s64 block;
+	struct super_block *sb = vi->i_sb;
+	ntfs_volume *vol = NTFS_SB(sb);
+	struct buffer_head *bh;
+	ntfs_inode *ni;
+	MFT_RECORD *m = NULL;
+	ATTR_RECORD *attr;
+	attr_search_context *ctx;
+	unsigned int i, nr_blocks;
+	int err;
 
-static void dump_runlist(const ntfs_runlist *rl, const int rlen)
-{
-#ifdef DEBUG
-	int i;
-	ntfs_cluster_t ct;
+	ntfs_debug("Entering.");
 
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): rlen = %i.\n", rlen);
-	ntfs_debug(DEBUG_OTHER, "VCN        LCN        Run length\n");
-	for (i = 0, ct = 0; i < rlen; ct += rl[i++].len) {
-		if (rl[i].lcn == (ntfs_cluster_t)-1)
-			ntfs_debug(DEBUG_OTHER, "0x%-8x LCN_HOLE   0x%-8x "
-					"(%s)\n", ct, rl[i].len, rl[i].len ?
-					"sparse run" : "run list end");
-		else
-			ntfs_debug(DEBUG_OTHER, "0x%-8x 0x%-8x 0x%-8x%s\n", ct,
-					rl[i].lcn, rl[i].len, rl[i].len &&
-					i + 1 < rlen ? "" : " (run list end)");
-		if (!rl[i].len)
-			break;
+	/* Initialize the ntfs specific part of @vi. */
+	ntfs_init_big_inode(vi);
+	ni = NTFS_I(vi);
+	if (vi->i_ino != FILE_MFT) {
+		ntfs_error(sb, "Called for inode %ld but only inode %d "
+				"allowed.", vi->i_ino, FILE_MFT);
+		goto err_out;
 	}
-#endif
-}
 
-/**
- * splice_runlists - splice two run lists into one
- * @rl1:	pointer to address of first run list
- * @r1len:	number of elementfs in first run list
- * @rl2:	pointer to second run list
- * @r2len:	number of elements in second run list
- *
- * Append the run list @rl2 to the run list *@rl1 and return the result in
- * *@rl1 and *@r1len.
- *
- * Return 0 on success or -errno on error, in which case *@rl1 and *@r1len are
- * left untouched.
- *
- * The only possible error code at the moment is -ENOMEM and only happens if
- * there is insufficient memory to allocate the new run list (only happens
- * when size of (rl1 + rl2) > allocated size of rl1).
- */
-int splice_runlists(ntfs_runlist **rl1, int *r1len, const ntfs_runlist *rl2,
-		int r2len)
-{
-	ntfs_runlist *rl;
-	int rlen, rl_size, rl2_pos;
+	/*
+	 * This sets up our little cheat allowing us to reuse the async io
+	 * completion handler for directories.
+	 */
+	ni->_IDM(index_block_size) = vol->mft_record_size;
+	ni->_IDM(index_block_size_bits) = vol->mft_record_size_bits;
 
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Entering with *r1len = %i, "
-			"r2len = %i.\n", *r1len, r2len);
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Dumping 1st runlist.\n");
-	if (*rl1)
-		dump_runlist(*rl1, *r1len);
-	else
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Not present.\n");
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Dumping 2nd runlist.\n");
-	dump_runlist(rl2, r2len);
-	rlen = *r1len + r2len + 1;
-	rl_size = (rlen * sizeof(ntfs_runlist) + PAGE_SIZE - 1) &
-			PAGE_MASK;
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): rlen = %i, rl_size = %i.\n",
-			rlen, rl_size);
-	/* Do we have enough space? */
-	if (rl_size <= ((*r1len * sizeof(ntfs_runlist) + PAGE_SIZE - 1) &
-			PAGE_MASK)) {
-		/* Have enough space already. */
-		rl = *rl1;
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Have enough space "
-				"already.\n");
-	} else {
-		/* Need more space. Reallocate. */
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Need more space.\n");
-		rl = ntfs_vmalloc(rlen << sizeof(ntfs_runlist));
-		if (!rl)
-			return -ENOMEM;
-		/* Copy over rl1. */
-		ntfs_memcpy(rl, *rl1, *r1len * sizeof(ntfs_runlist));
-		ntfs_vfree(*rl1);
-		*rl1 = rl;
-	}
-	/* Reuse rl_size as the current position index into rl. */
-	rl_size = *r1len - 1;
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): rl_size = %i.\n");
-	/* Coalesce neighbouring elements, if present. */
-	rl2_pos = 0;
-	if (rl[rl_size].lcn + rl[rl_size].len == rl2[rl2_pos].lcn) {
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Coalescing adjacent "
-				"runs.\n");
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Before: "
-				"rl[rl_size].len = %i.\n", rl[rl_size].len);
-		rl[rl_size].len += rl2[rl2_pos].len;
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After: "
-				"rl[rl_size].len = %i.\n", rl[rl_size].len);
-		rl2_pos++;
-		r2len--;
-		rlen--;
-	}
-	rl_size++;
-	/* Copy over rl2. */
-	ntfs_memcpy(rl + rl_size, rl2 + rl2_pos, r2len * sizeof(ntfs_runlist));
-	rlen--;
-	rl[rlen].lcn = (ntfs_cluster_t)-1;
-	rl[rlen].len = (ntfs_cluster_t)0;
-	*r1len = rlen;
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Dumping result runlist.\n");
-	dump_runlist(*rl1, *r1len);
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Returning with *r1len = "
-			"%i.\n", rlen);
-	return 0;
-}
+	/* Very important! Needed to be able to call map_mft_record*(). */
+	vol->mft_ino = vi;
 
-/**
- * ntfs_alloc_mft_record - allocate an mft record
- * @vol:	volume to allocate an mft record on
- * @result:	the mft record number allocated
- *
- * Allocate a new mft record on disk. Return 0 on success or -ERRNO on error.
- * On success, *@result contains the allocated mft record number. On error,
- * *@result is -1UL.
- *
- * Note, this function doesn't actually set the mft record to be in use. This
- * is done by the caller, which at the moment is only ntfs_alloc_inode().
- *
- * To find a free mft record, we scan the mft bitmap for a zero bit. To
- * optimize this we start scanning at the place where we last stopped and we
- * perform wrap around when we reach the end. Note, we do not try to allocate
- * mft records below number 24 because numbers 0 to 15 are the defined system
- * files anyway and 16 to 24 are special in that they are used for storing
- * extension mft records for $MFT's $DATA attribute. This is required to avoid
- * the possibility of creating a run list with a circular dependence which once
- * written to disk can never be read in again. Windows will only use records
- * 16 to 24 for normal files if the volume is completely out of space. We never
- * use them which means that when the volume is really out of space we cannot
- * create any more files while Windows can still create up to 8 small files. We
- * can start doing this at some later time, doesn't matter much for now.
- *
- * When scanning the mft bitmap, we only search up to the last allocated mft
- * record. If there are no free records left in the range 24 to number of
- * allocated mft records, then we extend the mft data in order to create free
- * mft records. We extend the allocated size of $MFT/$DATA by 16 records at a
- * time or one cluster, if cluster size is above 16kiB. If there isn't
- * sufficient space to do this, we try to extend by a single mft record or one
- * cluster, if cluster size is above mft record size, but we only do this if
- * there is enough free space, which we know from the values returned by the
- * failed cluster allocation function when we tried to do the first allocation.
- *
- * No matter how many mft records we allocate, we initialize only the first
- * allocated mft record (incrementing mft data size and initialized size) and
- * return its number to the caller in @*result, unless there are less than 24
- * mft records, in which case we allocate and initialize mft records until we
- * reach record 24 which we consider as the first free mft record for use by
- * normal files.
- *
- * If during any stage we overflow the initialized data in the mft bitmap, we
- * extend the initialized size (and data size) by 8 bytes, allocating another
- * cluster if required. The bitmap data size has to be at least equal to the
- * number of mft records in the mft, but it can be bigger, in which case the
- * superflous bits are padded with zeroes.
- *
- * Thus, when we return successfully (return value 0), we will have:
- *	- initialized / extended the mft bitmap if necessary,
- *	- initialized / extended the mft data if necessary,
- *	- set the bit corresponding to the mft record being allocated in the
- *	  mft bitmap, and we will
- *	- return the mft record number in @*result.
- *
- * On error (return value below zero), nothing will have changed. If we had
- * changed anything before the error occured, we will have reverted back to
- * the starting state before returning to the caller. Thus, except for bugs,
- * we should always leave the volume in a consitents state when returning from
- * this function. NOTE: Small exception to this is that we set the bit in the
- * mft bitmap but we do not mark the mft record in use, which is inconsistent.
- * However, the caller will immediately add the wanted attributes to the mft
- * record, set it in use and write it out to disk, so there should be no
- * problem.
- *
- * Note, this function cannot make use of most of the normal functions, like
- * for example for attribute resizing, etc, because when the run list overflows
- * the base mft record and an attribute list is used, it is very important
- * that the extension mft records used to store the $DATA attribute of $MFT
- * can be reached without having to read the information contained inside
- * them, as this would make it impossible to find them in the first place
- * after the volume is dismounted. $MFT/$BITMAP probably doesn't need to
- * follow this rule because the bitmap is not essential for finding the mft
- * records, but on the other hand, handling the bitmap in this special way
- * would make life easier because otherwise there might be circular invocations
- * of functions when reading the bitmap but if we are careful, we should be
- * able to avoid all problems.
- *
- * FIXME: Don't forget $MftMirr, though this probably belongs in
- *	  ntfs_update_inode() (or even deeper). (AIA)
- *
- * FIXME: Want finer grained locking. (AIA)
- */
-static int ntfs_alloc_mft_record(ntfs_volume *vol, unsigned long *result)
-{
-	unsigned long nr_mft_records, buf_size, buf_pos, pass_start, pass_end;
-	unsigned long last_read_pos, mft_rec_size, bit, l;
-	ntfs_attribute *data, *bmp;
-	__u8 *buf, *byte, pass, b, have_allocated_mftbmp = 0;
-	int rlen, rl_size = 0, r2len, rl2_size, old_data_rlen, err = 0;
-	ntfs_runlist *rl, *rl2;
-	ntfs_cluster_t lcn = 0, old_data_len;
-	ntfs_io io;
-	__s64 ll, old_data_allocated, old_data_initialized, old_data_size;
-
-	*result = -1UL;
-	/* Allocate a buffer and setup the io structure. */
-	buf = (__u8*)__get_free_page(GFP_NOFS);
-	if (!buf)
-		return -ENOMEM;
-	lock_kernel();
-	/* Get the $DATA and $BITMAP attributes of $MFT. */
-	data = ntfs_find_attr(vol->mft_ino, vol->at_data, 0);
-	bmp = ntfs_find_attr(vol->mft_ino, vol->at_bitmap, 0);
-	if (!data || !bmp) {
-		err = -EINVAL;
-		goto err_ret;
-	}
-	/* Determine the number of allocated mft records in the mft. */
-	pass_end = nr_mft_records = data->allocated >>
-			vol->mft_record_size_bits;
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): nr_mft_records = %lu.\n",
-			nr_mft_records);
-	/* Make sure we don't overflow the bitmap. */
-	l = bmp->initialized << 3;
-	if (l < nr_mft_records)
-		// FIXME: It might be a good idea to extend the bitmap instead.
-		pass_end = l;
-	pass = 1;
-	buf_pos = vol->mft_data_pos;
-	if (buf_pos >= pass_end) {
-		buf_pos = 24UL;
-		pass = 2;
-	}
-	pass_start = buf_pos;
-	rl = bmp->d.r.runlist;
-	rlen = bmp->d.r.len - 1;
-	lcn = rl[rlen].lcn + rl[rlen].len;
-	io.fn_put = ntfs_put;
-	io.fn_get = ntfs_get;
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Starting bitmap search.\n");
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): pass = %i, pass_start = %lu, "
-			"pass_end = %lu.\n", pass, pass_start, pass_end);
-	byte = NULL; // FIXME: For debugging only.
-	/* Loop until a free mft record is found. */
-	io.size = (nr_mft_records >> 3) & ~PAGE_MASK;
-	for (;; io.size = PAGE_SIZE) {
-		io.param = buf;
-		io.do_read = 1;
-		last_read_pos = buf_pos >> 3;
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Before: "
-				"bmp->allocated = 0x%Lx, bmp->size = 0x%Lx, "
-				"bmp->initialized = 0x%Lx.\n", bmp->allocated,
-				bmp->size, bmp->initialized);
-		err = ntfs_readwrite_attr(vol->mft_ino, bmp, last_read_pos,
-				&io);
-		if (err)
-			goto err_ret;
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Read %lu bytes.\n",
-				(unsigned long)io.size);
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After: "
-				"bmp->allocated = 0x%Lx, bmp->size = 0x%Lx, "
-				"bmp->initialized = 0x%Lx.\n", bmp->allocated,
-				bmp->size, bmp->initialized);
-		if (!io.size)
-			goto pass_done;
-		buf_size = io.size << 3;
-		bit = buf_pos & 7UL;
-		buf_pos &= ~7UL;
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Before loop: "
-				"buf_size = %lu, buf_pos = %lu, bit = %lu, "
-				"*byte = 0x%x, b = %u.\n",
-				buf_size, buf_pos, bit, byte ? *byte : -1, b);
-		for (; bit < buf_size && bit + buf_pos < pass_end;
-				bit &= ~7UL, bit += 8UL) {
-			byte = buf + (bit >> 3);
-			if (*byte == 0xff)
+	/* Allocate enough memory to read the first mft record. */
+	if (vol->mft_record_size > 64 * 1024) {
+		ntfs_error(sb, "Unsupported mft record size %i (max 64kiB).",
+				vol->mft_record_size);
+		goto err_out;
+	}
+	i = vol->mft_record_size;
+	if (i < sb->s_blocksize)
+		i = sb->s_blocksize;
+	m = (MFT_RECORD*)ntfs_malloc_nofs(i);
+	if (!m) {
+		ntfs_error(sb, "Failed to allocate buffer for $MFT record 0.");
+		goto err_out;
+	}
+
+	/* Determine the first block of the $MFT/$DATA attribute. */
+	block = vol->mft_lcn << vol->cluster_size_bits >>
+			sb->s_blocksize_bits;
+	nr_blocks = vol->mft_record_size >> sb->s_blocksize_bits;
+	if (!nr_blocks)
+		nr_blocks = 1;
+
+	/* Load $MFT/$DATA's first mft record. */
+	for (i = 0; i < nr_blocks; i++) {
+		bh = sb_bread(sb, block++);
+		if (!bh) {
+			ntfs_error(sb, "Device read failed.");
+			goto err_out;
+		}
+		memcpy((char*)m + (i << sb->s_blocksize_bits), bh->b_data,
+				sb->s_blocksize);
+		brelse(bh);
+	}
+
+	/* Apply the mst fixups. */
+	if (post_read_mst_fixup((NTFS_RECORD*)m, vol->mft_record_size)) {
+		/* FIXME: Try to use the $MFTMirr now. */
+		ntfs_error(sb, "MST fixup failed. $MFT is corrupt.");
+		goto err_out;
+	}
+
+	/* Need this to sanity check attribute list references to $MFT. */
+	ni->seq_no = le16_to_cpu(m->sequence_number);
+
+	/* Provides readpage() and sync_page() for map_mft_record(READ). */
+	vi->i_mapping->a_ops = &ntfs_mft_aops;
+
+	ctx = get_attr_search_ctx(ni, m);
+	if (!ctx) {
+		err = -ENOMEM;
+		goto err_out;
+	}
+
+	/* Find the attribute list attribute if present. */
+	if (lookup_attr(AT_ATTRIBUTE_LIST, NULL, 0, 0, 0, NULL, 0, ctx)) {
+		ATTR_LIST_ENTRY *al_entry, *next_al_entry;
+		u8 *al_end;
+
+		ntfs_debug("Attribute list attribute found in $MFT.");
+		ni->state |= 1 << NI_AttrList;
+		if (ctx->attr->flags & ATTR_IS_ENCRYPTED ||
+				ctx->attr->flags & ATTR_COMPRESSION_MASK) {
+			ntfs_error(sb, "Attribute list attribute is "
+					"compressed/encrypted. Not allowed. "
+					"$MFT is corrupt. You should run "
+					"chkdsk.");
+			goto put_err_out;
+		}
+		/* Now allocate memory for the attribute list. */
+		ni->attr_list_size = (u32)attribute_value_length(ctx->attr);
+		ni->attr_list = ntfs_malloc_nofs(ni->attr_list_size);
+		if (!ni->attr_list) {
+			ntfs_error(sb, "Not enough memory to allocate buffer "
+					"for attribute list.");
+			goto put_err_out;
+		}
+		if (ctx->attr->non_resident) {
+			ni->state |= 1 << NI_AttrListNonResident;
+			if (ctx->attr->_ANR(lowest_vcn)) {
+				ntfs_error(sb, "Attribute list has non zero "
+						"lowest_vcn. $MFT is corrupt. "
+						"You should run chkdsk.");
+				goto put_err_out;
+			}
+			/* Setup the run list. */
+			ni->attr_list_rl.rl = decompress_mapping_pairs(vol,
+					ctx->attr, NULL);
+			if (IS_ERR(ni->attr_list_rl.rl)) {
+				err = PTR_ERR(ni->attr_list_rl.rl);
+				ni->attr_list_rl.rl = NULL;
+				ntfs_error(sb, "Mapping pairs decompression "
+						"failed with error code %i.",
+						-err);
+				goto put_err_out;
+			}
+			/* Now load the attribute list. */
+			if ((err = load_attribute_list(vol, &ni->attr_list_rl,
+					ni->attr_list, ni->attr_list_size,
+					sle64_to_cpu(
+					ctx->attr->_ANR(initialized_size))))) {
+				ntfs_error(sb, "Failed to load attribute list "
+						"attribute with error code %i.",
+						-err);
+				goto put_err_out;
+			}
+		} else /* if (!ctx.attr->non_resident) */ {
+			if ((u8*)ctx->attr + le16_to_cpu(
+					ctx->attr->_ARA(value_offset)) +
+					le32_to_cpu(
+					ctx->attr->_ARA(value_length)) >
+					(u8*)ctx->mrec + vol->mft_record_size) {
+				ntfs_error(sb, "Corrupt attribute list "
+						"attribute.");
+				goto put_err_out;
+			}
+			/* Now copy the attribute list. */
+			memcpy(ni->attr_list, (u8*)ctx->attr + le16_to_cpu(
+					ctx->attr->_ARA(value_offset)),
+					le32_to_cpu(
+					ctx->attr->_ARA(value_length)));
+		}
+		/* The attribute list is now setup in memory. */
+		/*
+		 * FIXME: I don't know if this case is actually possible.
+		 * According to logic it is not possible but I have seen too
+		 * many weird things in MS software to rely on logic... Thus we
+		 * perform a manual search and make sure the first $MFT/$DATA
+		 * extent is in the base inode. If it is not we abort with an
+		 * error and if we ever see a report of this error we will need
+		 * to do some magic in order to have the necessary mft record
+		 * loaded and in the right place in the page cache. But
+		 * hopefully logic will prevail and this never happens...
+		 */
+		al_entry = (ATTR_LIST_ENTRY*)ni->attr_list;
+		al_end = (u8*)al_entry + ni->attr_list_size;
+		for (;; al_entry = next_al_entry) {
+			/* Out of bounds check. */
+			if ((u8*)al_entry < ni->attr_list ||
+					(u8*)al_entry > al_end)
+				goto em_put_err_out;
+			/* Catch the end of the attribute list. */
+			if ((u8*)al_entry == al_end)
+				goto em_put_err_out;
+			if (!al_entry->length)
+				goto em_put_err_out;
+			if ((u8*)al_entry + 6 > al_end || (u8*)al_entry +
+					le16_to_cpu(al_entry->length) > al_end)
+				goto em_put_err_out;
+			next_al_entry = (ATTR_LIST_ENTRY*)((u8*)al_entry +
+					le16_to_cpu(al_entry->length));
+			if (le32_to_cpu(al_entry->type) >
+					const_le32_to_cpu(AT_DATA))
+				goto em_put_err_out;
+			if (AT_DATA != al_entry->type)
 				continue;
-			b = ffz((unsigned long)*byte);
-			if (b < (__u8)8 && b >= (bit & 7UL)) {
-				bit = b + (bit & ~7UL) + buf_pos;
-				ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): "
-						"Found free rec in for loop. "
-						"bit = %lu\n", bit);
-				goto found_free_rec;
+			/* We want an unnamed attribute. */
+			if (al_entry->name_length)
+				goto em_put_err_out;
+			/* Want the first entry, i.e. lowest_vcn == 0. */
+			if (al_entry->lowest_vcn)
+				goto em_put_err_out;
+			/* First entry has to be in the base mft record. */
+			if (MREF_LE(al_entry->mft_reference) != ni->mft_no) {
+				/* MFT references do not match, logic fails. */
+				ntfs_error(sb, "BUG: The first $DATA extent "
+						"of $MFT is not in the base "
+						"mft record. Please report "
+						"you saw this message to "
+						"linux-ntfs-dev@lists.sf.net");
+				goto put_err_out;
+			} else {
+				/* Sequence numbers must match. */
+				if (MSEQNO_LE(al_entry->mft_reference) !=
+						ni->seq_no)
+					goto em_put_err_out;
+				/* Got it. All is ok. We can stop now. */
+				break;
 			}
 		}
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After loop: "
-				"buf_size = %lu, buf_pos = %lu, bit = %lu, "
-				"*byte = 0x%x, b = %u.\n",
-				buf_size, buf_pos, bit, byte ? *byte : -1, b);
-		buf_pos += buf_size;
-		if (buf_pos < pass_end)
-			continue;
-pass_done:	/* Finished with the current pass. */
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At pass_done.\n");
-		if (pass == 1) {
+	}
+
+	reinit_attr_search_ctx(ctx);
+
+	/* Now load all attribute extents. */
+	attr = NULL;
+	next_vcn = last_vcn = highest_vcn = 0;
+	while (lookup_attr(AT_DATA, NULL, 0, 0, next_vcn, NULL, 0, ctx)) {
+		run_list_element *nrl;
+
+		/* Cache the current attribute. */
+		attr = ctx->attr;
+		/* $MFT must be non-resident. */
+		if (!attr->non_resident) {
+			ntfs_error(sb, "$MFT must be non-resident but a "
+					"resident extent was found. $MFT is "
+					"corrupt. Run chkdsk.");
+			goto put_err_out;
+		}
+		/* $MFT must be uncompressed and unencrypted. */
+		if (attr->flags & ATTR_COMPRESSION_MASK ||
+				attr->flags & ATTR_IS_ENCRYPTED) {
+			ntfs_error(sb, "$MFT must be uncompressed and "
+					"unencrypted but a compressed/"
+					"encrypted extent was found. "
+					"$MFT is corrupt. Run chkdsk.");
+			goto put_err_out;
+		}
+		/*
+		 * Decompress the mapping pairs array of this extent and merge
+		 * the result into the existing run list. No need for locking
+		 * as we have exclusive access to the inode at this time and we
+		 * are a mount in progress task, too.
+		 */
+		nrl = decompress_mapping_pairs(vol, attr, ni->run_list.rl);
+		if (IS_ERR(nrl)) {
+			ntfs_error(sb, "decompress_mapping_pairs() failed with "
+					"error code %ld. $MFT is corrupt.",
+					PTR_ERR(nrl));
+			goto put_err_out;
+		}
+		ni->run_list.rl = nrl;
+
+		/* Are we in the first extent? */
+		if (!next_vcn) {
+			if (attr->_ANR(lowest_vcn)) {
+				ntfs_error(sb, "First extent of $DATA "
+						"attribute has non zero "
+						"lowest_vcn. $MFT is corrupt. "
+						"You should run chkdsk.");
+				goto put_err_out;
+			}
+			/* Get the last vcn in the $DATA attribute. */
+			last_vcn = sle64_to_cpu(attr->_ANR(allocated_size)) >>
+					vol->cluster_size_bits;
+			/* Fill in the inode size. */
+			vi->i_size = sle64_to_cpu(attr->_ANR(data_size));
+			ni->initialized_size = sle64_to_cpu(
+					attr->_ANR(initialized_size));
+			ni->allocated_size = sle64_to_cpu(
+					attr->_ANR(allocated_size));
+			/* Set the number of mft records. */
+			vol->_VMM(nr_mft_records) = vi->i_size >>
+					vol->mft_record_size_bits;
 			/*
-			 * Now do pass 2, scanning the first part of the zone
-			 * we omitted in pass 1.
+			 * We have got the first extent of the run_list for
+			 * $MFT which means it is now relatively safe to call
+			 * the normal ntfs_read_inode() function. Thus, take
+			 * us out of the calling chain. Also we need to do this
+			 * now because we need ntfs_read_inode() in place to
+			 * get at subsequent extents.
 			 */
-			ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Done pass "
-					"1.\n");
-			ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Pass = 2.\n");
-			pass = 2;
-			pass_end = pass_start;
-			buf_pos = pass_start = 24UL;
-			ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): pass = %i, "
-					"pass_start = %lu, pass_end = %lu.\n",
-					pass, pass_start, pass_end);
-			continue;
-		} /* pass == 2 */
-		/* No free records left. */
-		if (bmp->initialized << 3 > nr_mft_records &&
-				bmp->initialized > 3) {
+			sb->s_op = &ntfs_sops;
 			/*
-			 * The mft bitmap is already bigger but the space is
-			 * not covered by mft records, this implies that the
-			 * next records are all free, so we already have found
-			 * a free record.
+			 * Complete reading the inode, this will actually
+			 * re-read the mft record for $MFT, this time entering
+			 * it into the page cache with which we complete the
+			 * kick start of the volume. It should be safe to do
+			 * this now as the first extent of $MFT/$DATA is
+			 * already known and we would hope that we don't need
+			 * further extents in order to find the other
+			 * attributes belonging to $MFT. Only time will tell if
+			 * this is really the case. If not we will have to play
+			 * magic at this point, possibly duplicating a lot of
+			 * ntfs_read_inode() at this point. We will need to
+			 * ensure we do enough of its work to be able to call
+			 * ntfs_read_inode() on extents of $MFT/$DATA. But lets
+			 * hope this never happens...
 			 */
-			bit = nr_mft_records;
-			if (bit < 24UL)
-				bit = 24UL;
-			ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Found free "
-					"record bit (#1) = 0x%lx.\n", bit);
-			goto found_free_rec;
-		}
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Done pass 2.\n");
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Before: "
-				"bmp->allocated = 0x%Lx, bmp->size = 0x%Lx, "
-				"bmp->initialized = 0x%Lx.\n", bmp->allocated,
-				bmp->size, bmp->initialized);
-		/* Need to extend the mft bitmap. */
-		if (bmp->initialized + 8LL > bmp->allocated) {
-			ntfs_io io2;
-
-			ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Initialized "
-					"> allocated.\n");
-			/* Need to extend bitmap by one more cluster. */
-			rl = bmp->d.r.runlist;
-			rlen = bmp->d.r.len - 1;
-			lcn = rl[rlen].lcn + rl[rlen].len;
-			io2.fn_put = ntfs_put;
-			io2.fn_get = ntfs_get;
-			io2.param = &b;
-			io2.size = 1;
-			io2.do_read = 1;
-			err = ntfs_readwrite_attr(vol->bitmap, data, lcn >> 3,
-					&io2);
-			if (err)
-				goto err_ret;
-			ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Read %lu "
-					"bytes.\n", (unsigned long)io2.size);
-			if (io2.size == 1 && b != 0xff) {
-				__u8 tb = 1 << (lcn & (ntfs_cluster_t)7);
-				if (!(b & tb)) {
-					/* Next cluster is free. Allocate it. */
-					b |= tb;
-					io2.param = &b;
-					io2.do_read = 0;
-					err = ntfs_readwrite_attr(vol->bitmap,
-							data, lcn >> 3, &io2);
-					if (err || io.size != 1) {
-						if (!err)
-							err = -EIO;
-						goto err_ret;
-					}
-append_mftbmp_simple:			rl[rlen].len++;
-					have_allocated_mftbmp |= 1;
-					ntfs_debug(DEBUG_OTHER, __FUNCTION__
-							"(): Appending one "
-							"cluster to mftbmp.\n");
-				}
-			}
-			if (!have_allocated_mftbmp) {
-				/* Allocate a cluster from the DATA_ZONE. */
-				ntfs_cluster_t lcn2 = lcn;
-				ntfs_cluster_t count = 1;
-				err = ntfs_allocate_clusters(vol, &lcn2,
-						&count, &rl2, &r2len,
-						DATA_ZONE);
-				if (err)
-					goto err_ret;
-				if (count != 1 || lcn2 <= 0) {
-					if (count > 0) {
-rl2_dealloc_err_out:				if (ntfs_deallocate_clusters(
-							vol, rl2, r2len))
-							ntfs_error(__FUNCTION__
-							"(): Cluster "
-							"deallocation in error "
-							"code path failed! You "
-							"should run chkdsk.\n");
-					}
-					ntfs_vfree(rl2);
-					if (!err)
-						err = -EINVAL;
-					goto err_ret;
-				}
-				if (lcn2 == lcn) {
-					ntfs_vfree(rl2);
-					goto append_mftbmp_simple;
-				}
-				/* We need to append a new run. */
-				rl_size = (rlen * sizeof(ntfs_runlist) +
-						PAGE_SIZE - 1) & PAGE_MASK;
-				/* Reallocate memory if necessary. */
-				if ((rlen + 2) * sizeof(ntfs_runlist) >=
-						rl_size) {
-					ntfs_runlist *rlt;
-
-					rl_size += PAGE_SIZE;
-					rlt = ntfs_vmalloc(rl_size);
-					if (!rlt) {
-						err = -ENOMEM;
-						goto rl2_dealloc_err_out;
-					}
-					ntfs_memcpy(rlt, rl, rl_size -
-							PAGE_SIZE);
-					ntfs_vfree(rl);
-					bmp->d.r.runlist = rl = rlt;
-				}
-				ntfs_vfree(rl2);
-				rl[rlen].lcn = lcn = lcn2;
-				rl[rlen].len = count;
-				bmp->d.r.len = ++rlen;
-				have_allocated_mftbmp |= 2;
-				ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): "
-						"Adding run to mftbmp. "
-						"LCN = %i, len = %i\n", lcn,
-						count);
+			ntfs_read_inode(vi);
+			if (is_bad_inode(vi)) {
+				ntfs_error(sb, "ntfs_read_inode() of $MFT "
+						"failed. BUG or corrupt $MFT. "
+						"Run chkdsk and if no errors "
+						"are found, please report you "
+						"saw this message to "
+						"linux-ntfs-dev@lists.sf.net");
+				put_attr_search_ctx(ctx);
+				/* Revert to the safe super operations. */
+				sb->s_op = &ntfs_mount_sops;
+				goto out_now;
 			}
 			/*
-			 * We now have extended the mft bitmap allocated size
-			 * by one cluster. Reflect this in the attribute.
+			 * Re-initialize some specifics about $MFT's inode as
+			 * ntfs_read_inode() will have set up the default ones.
 			 */
-			bmp->allocated += (__s64)vol->cluster_size;
-		}
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After: "
-				"bmp->allocated = 0x%Lx, bmp->size = 0x%Lx, "
-				"bmp->initialized = 0x%Lx.\n", bmp->allocated,
-				bmp->size, bmp->initialized);
-		/* We now have sufficient allocated space. */
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Now have sufficient "
-				"allocated space in mftbmp.\n");
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Before: "
-				"bmp->allocated = 0x%Lx, bmp->size = 0x%Lx, "
-				"bmp->initialized = 0x%Lx.\n", bmp->allocated,
-				bmp->size, bmp->initialized);
-		buf_pos = bmp->initialized;
-		bmp->initialized += 8LL;
-		if (bmp->initialized > bmp->size)
-			bmp->size = bmp->initialized;
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After: "
-				"bmp->allocated = 0x%Lx, bmp->size = 0x%Lx, "
-				"bmp->initialized = 0x%Lx.\n", bmp->allocated,
-				bmp->size, bmp->initialized);
-		have_allocated_mftbmp |= 4;
-		/* Update the mft bitmap attribute value. */
-		memset(buf, 0, 8);
-		io.param = buf;
-		io.size = 8;
-		io.do_read = 0;
-		err = ntfs_readwrite_attr(vol->mft_ino, bmp, buf_pos, &io);
-		if (err || io.size != 8) {
-			if (!err)
-				err = -EIO;
-			goto shrink_mftbmp_err_ret;
-		}
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Wrote extended "
-				"mftbmp bytes %lu.\n", (unsigned long)io.size);
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After write: "
-				"bmp->allocated = 0x%Lx, bmp->size = 0x%Lx, "
-				"bmp->initialized = 0x%Lx.\n", bmp->allocated,
-				bmp->size, bmp->initialized);
-		bit = buf_pos << 3;
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Found free record "
-				"bit (#2) = 0x%lx.\n", bit);
-		goto found_free_rec;
-	}
-found_free_rec:
-	/* bit is the found free mft record. Allocate it in the mft bitmap. */
-	vol->mft_data_pos = bit;
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At found_free_rec.\n");
-	io.param = buf;
-	io.size = 1;
-	io.do_read = 1;
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Before update: "
-			"bmp->allocated = 0x%Lx, bmp->size = 0x%Lx, "
-			"bmp->initialized = 0x%Lx.\n", bmp->allocated,
-			bmp->size, bmp->initialized);
-	err = ntfs_readwrite_attr(vol->mft_ino, bmp, bit >> 3, &io);
-	if (err || io.size != 1) {
-		if (!err)
-			err = -EIO;
-		goto shrink_mftbmp_err_ret;
-	}
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Read %lu bytes.\n",
-			(unsigned long)io.size);
-#ifdef DEBUG
-	/* Check our bit is really zero! */
-	if (*buf & (1 << (bit & 7)))
-		BUG();
-#endif
-	*buf |= 1 << (bit & 7);
-	io.param = buf;
-	io.do_read = 0;
-	err = ntfs_readwrite_attr(vol->mft_ino, bmp, bit >> 3, &io);
-	if (err || io.size != 1) {
-		if (!err)
-			err = -EIO;
-		goto shrink_mftbmp_err_ret;
-	}
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Wrote %lu bytes.\n",
-			(unsigned long)io.size);
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After update: "
-			"bmp->allocated = 0x%Lx, bmp->size = 0x%Lx, "
-			"bmp->initialized = 0x%Lx.\n", bmp->allocated,
-			bmp->size, bmp->initialized);
-	/* The mft bitmap is now uptodate. Deal with mft data attribute now. */
-	ll = (__s64)(bit + 1) << vol->mft_record_size_bits;
-	if (ll <= data->initialized) {
-		/* The allocated record is already initialized. We are done! */
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Allocated mft record "
-				"already initialized!\n");
-		goto done_ret;
-	}
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Allocated mft record needs "
-			"to be initialized.\n");
-	/* The mft record is outside the initialized data. */
-	mft_rec_size = (unsigned long)vol->mft_record_size;
-	/* Preserve old values for undo purposes. */
-	old_data_allocated = data->allocated;
-	old_data_rlen = data->d.r.len - 1;
-	old_data_len = data->d.r.runlist[old_data_rlen].len;
-	/*
-	 * If necessary, extend the mft until it covers the allocated record.
-	 * The loop is only actually used when a freshly formatted volume is
-	 * first written to. But it optimizes away nicely in the common case.
-	 */
-	while (ll > data->allocated) {
-		ntfs_cluster_t lcn2, nr_lcn2, nr, min_nr;
+			/* Set uid and gid to root. */
+			vi->i_uid = vi->i_gid = 0;
+			/* Regular file. No access for anyone. */
+			vi->i_mode = S_IFREG;
+			/* No VFS initiated operations allowed for $MFT. */
+			vi->i_op = &ntfs_empty_inode_ops;
+			vi->i_fop = &ntfs_empty_file_ops;
+			/* Put back our special address space operations. */
+			vi->i_mapping->a_ops = &ntfs_mft_aops;
+		}
+
+		/* Get the lowest vcn for the next extent. */
+		highest_vcn = sle64_to_cpu(attr->_ANR(highest_vcn));
+		next_vcn = highest_vcn + 1;
 
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Extending mft "
-				"data allocation, data->allocated = 0x%Lx, "
-				"data->size = 0x%Lx, data->initialized = "
-				"0x%Lx.\n", data->allocated, data->size,
-				data->initialized);
-		/* Minimum allocation is one mft record worth of clusters. */
-		if (mft_rec_size <= vol->cluster_size)
-			min_nr = (ntfs_cluster_t)1;
-		else
-			min_nr = mft_rec_size >> vol->cluster_size_bits;
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): min_nr = %i.\n",
-				min_nr);
-		/* Allocate 16 mft records worth of clusters. */
-		nr = mft_rec_size << 4 >> vol->cluster_size_bits;
-		if (!nr)
-			nr = (ntfs_cluster_t)1;
-		/* Determine the preferred allocation location. */
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): nr = %i.\n", nr);
-		rl2 = data->d.r.runlist;
-		r2len = data->d.r.len;
-		lcn2 = rl2[r2len - 1].lcn + rl2[r2len - 1].len;
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): rl2[r2len - 1].lcn "
-				"= %i, .len = %i.\n", rl2[r2len - 1].lcn,
-				rl2[r2len - 1].len);
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): lcn2 = %i, r2len = "
-				"%i.\n", lcn2, r2len);
-retry_mft_data_allocation:
-		nr_lcn2 = nr;
-		err = ntfs_allocate_clusters(vol, &lcn2, &nr_lcn2, &rl2,
-				&r2len, MFT_ZONE);
-#ifdef DEBUG
-		if (!err && nr_lcn2 < min_nr)
-			/* Allocated less than minimum needed. Weird! */
-			BUG();
-#endif
-		if (err) {
-			/*
-			 * If there isn't enough space to do the wanted
-			 * allocation, but there is enough space to do a
-			 * minimal allocation, then try that, unless the wanted
-			 * allocation was already the minimal allocation.
-			 */
-			if (err == -ENOSPC && nr > min_nr &&
-					nr_lcn2 >= min_nr) {
-				nr = min_nr;
-				ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): "
-						"Retrying mft data "
-						"allocation, nr = min_nr = %i"
-						".\n", nr);
-				goto retry_mft_data_allocation;
-			}
-			goto undo_mftbmp_alloc_err_ret;
-		}
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Allocated %i "
-				"clusters starting at LCN %i.\n", nr_lcn2,
-				lcn2);
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Allocated "
-				"runlist:\n");
-		dump_runlist(rl2, r2len);
-		/* Append rl2 to the mft data attribute's run list. */
-		err = splice_runlists(&data->d.r.runlist, (int*)&data->d.r.len,
-				rl2, r2len);
-		if (err) {
-			ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): "
-					"splice_runlists failed with error "
-					"code %i.\n", -err);
-			goto undo_partial_data_alloc_err_ret;
-		}
-		/* Reflect the allocated clusters in the mft allocated data. */
-		data->allocated += nr_lcn2 << vol->cluster_size_bits;
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After extending mft "
-				"data allocation, data->allocated = 0x%Lx, "
-				"data->size = 0x%Lx, data->initialized = "
-				"0x%Lx.\n", data->allocated, data->size,
-				data->initialized);
-	}
-	/* Prepare a formatted (empty) mft record. */
-	memset(buf, 0, mft_rec_size);
-	ntfs_fill_mft_header(buf, mft_rec_size, 0, 0, 0);
-	err = ntfs_insert_fixups(buf, mft_rec_size);
-	if (err)
-		goto undo_data_alloc_err_ret;
-	/*
-	 * Extend mft data initialized size to reach the allocated mft record
-	 * and write the formatted mft record buffer to each mft record being
-	 * initialized. Note, that ntfs_readwrite_attr extends both
-	 * data->initialized and data->size, so no need for us to touch them.
-	 */
-	old_data_initialized = data->initialized;
-	old_data_size = data->size;
-	while (ll > data->initialized) {
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Initializing mft "
-				"record 0x%Lx.\n",
-				data->initialized >> vol->mft_record_size_bits);
-		io.param = buf;
-		io.size = mft_rec_size;
-		io.do_read = 0;
-		err = ntfs_readwrite_attr(vol->mft_ino, data,
-				data->initialized, &io);
-		if (err || io.size != mft_rec_size) {
-			if (!err)
-				err = -EIO;
-			goto undo_data_init_err_ret;
-		}
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Wrote %i bytes to "
-				"mft data.\n", io.size);
-	}
-	/* Update the VFS inode size as well. */
-	VFS_I(vol->mft_ino)->i_size = data->size;
-#ifdef DEBUG
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After mft record "
-			"initialization: data->allocated = 0x%Lx, data->size "
-			"= 0x%Lx, data->initialized = 0x%Lx.\n",
-			data->allocated, data->size, data->initialized);
-	/* Sanity checks. */
-	if (data->size > data->allocated || data->size < data->initialized ||
-			data->initialized > data->allocated)
-		BUG();
-#endif
-done_ret:
-	/* Return the number of the allocated mft record. */
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At done_ret. *result = bit = "
-			"0x%lx.\n", bit);
-	*result = bit;
-	vol->mft_data_pos = bit + 1;
-err_ret:
-	unlock_kernel();
-	free_page((unsigned long)buf);
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Syncing inode $MFT.\n");
-	if (ntfs_update_inode(vol->mft_ino))
-		ntfs_error(__FUNCTION__ "(): Failed to sync inode $MFT. "
-				"Continuing anyway.\n");
-	if (!err) {
-		ntfs_debug(DEBUG_FILE3, __FUNCTION__ "(): Done. Allocated mft "
-				"record number *result = 0x%lx.\n", *result);
-		return 0;
-	}
-	if (err != -ENOSPC)
-		ntfs_error(__FUNCTION__ "(): Failed to allocate an mft "
-				"record. Returning error code %i.\n", -err);
-	else
-		ntfs_debug(DEBUG_FILE3, __FUNCTION__ "(): Failed to allocate "
-				"an mft record due to lack of free space.\n");
-	return err;
-undo_data_init_err_ret:
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At "
-			"undo_data_init_err_ret.\n");
-	data->initialized = old_data_initialized;
-	data->size = old_data_size;
-undo_data_alloc_err_ret:
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At undo_data_alloc_err_ret."
-			"\n");
-	data->allocated = old_data_allocated;
-undo_partial_data_alloc_err_ret:
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At "
-			"undo_partial_data_alloc_err_ret.\n");
-	/* Deallocate the clusters. */
-	if (ntfs_deallocate_clusters(vol, rl2, r2len))
-		ntfs_error(__FUNCTION__ "(): Error deallocating clusters in "
-				"error code path. You should run chkdsk.\n");
-	ntfs_vfree(rl2);
-	/* Revert the run list back to what it was before. */
-	r2len = data->d.r.len;
-	rl2 = data->d.r.runlist;
-	rl2[old_data_rlen++].len = old_data_len;
-	rl2[old_data_rlen].lcn = (ntfs_cluster_t)-1;
-	rl2[old_data_rlen].len = (ntfs_cluster_t)0;
-	data->d.r.len = old_data_rlen;
-	rl2_size = ((old_data_rlen + 1) * sizeof(ntfs_runlist) + PAGE_SIZE -
-			1) & PAGE_MASK;
-	/* Reallocate memory freeing any extra memory allocated. */
-	if (rl2_size < ((r2len * sizeof(ntfs_runlist) + PAGE_SIZE - 1) &
-			PAGE_MASK)) {
-		rl2 = ntfs_vmalloc(rl2_size);
-		if (rl2) {
-			ntfs_memcpy(rl2, data->d.r.runlist, rl2_size);
-			ntfs_vfree(data->d.r.runlist);
-			data->d.r.runlist = rl2;
-		} else
-			ntfs_error(__FUNCTION__ "(): Error reallocating "
-					"memory in error code path. This "
-					"should be harmless.\n");
-	}	
-undo_mftbmp_alloc_err_ret:
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At "
-			"undo_mftbmp_alloc_err_ret.\n");
-	/* Deallocate the allocated bit in the mft bitmap. */
-	io.param = buf;
-	io.size = 1;
-	io.do_read = 1;
-	err = ntfs_readwrite_attr(vol->mft_ino, bmp, bit >> 3, &io);
-	if (!err && io.size == 1) {
-		*buf &= ~(1 << (bit & 7));
-		io.param = buf;
-		io.do_read = 0;
-		err = ntfs_readwrite_attr(vol->mft_ino, bmp, bit >> 3, &io);
-	}
-	if (err || io.size != 1) {
-		if (!err)
-			err = -EIO;
-		ntfs_error(__FUNCTION__ "(): Error deallocating mft record in "
-				"error code path. You should run chkdsk.\n");
-	}
-shrink_mftbmp_err_ret:
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At shrink_mftbmp_err_ret.\n");
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): have_allocated_mftbmp = "
-			"%i.\n", have_allocated_mftbmp);
-	if (!have_allocated_mftbmp)
-		goto err_ret;
-	/* Shrink the mftbmp back to previous size. */
-	if (bmp->size == bmp->initialized)
-		bmp->size -= 8LL;
-	bmp->initialized -= 8LL;
-	have_allocated_mftbmp &= ~4;
-	/* If no allocation occured then we are done. */
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): have_allocated_mftbmp = "
-			"%i.\n", have_allocated_mftbmp);
-	if (!have_allocated_mftbmp)
-		goto err_ret;
-	/* Deallocate the allocated cluster. */
-	bmp->allocated -= (__s64)vol->cluster_size;
-	if (ntfs_deallocate_cluster_run(vol, lcn, (ntfs_cluster_t)1))
-		ntfs_error(__FUNCTION__ "(): Error deallocating cluster in "
-				"error code path. You should run chkdsk.\n");
-	switch (have_allocated_mftbmp & 3) {
-	case 1:
-		/* Delete the last lcn from the last run of mftbmp. */
-		rl[rlen - 1].len--;
-		break;
-	case 2:
-		/* Delete the last run of mftbmp. */
-		bmp->d.r.len = --rlen;
-		/* Reallocate memory if necessary. */
-		if ((rlen + 1) * sizeof(ntfs_runlist) <= rl_size - PAGE_SIZE) {
-			ntfs_runlist *rlt;
-
-			rl_size -= PAGE_SIZE;
-			rlt = ntfs_vmalloc(rl_size);
-			if (rlt) {
-				ntfs_memcpy(rlt, rl, rl_size);
-				ntfs_vfree(rl);
-				bmp->d.r.runlist = rl = rlt;
-			} else
-				ntfs_error(__FUNCTION__ "(): Error "
-						"reallocating memory in error "
-						"code path. This should be "
-						"harmless.\n");
+		/* Only one extent or error, which we catch below. */
+		if (next_vcn <= 0)
+			break;
+
+		/* Avoid endless loops due to corruption. */
+		if (next_vcn < sle64_to_cpu(attr->_ANR(lowest_vcn))) {
+			ntfs_error(sb, "$MFT has corrupt attribute list "
+					"attribute. Run chkdsk.");
+			goto put_err_out;
 		}
-		bmp->d.r.runlist[bmp->d.r.len].lcn = (ntfs_cluster_t)-1;
-		bmp->d.r.runlist[bmp->d.r.len].len = (ntfs_cluster_t)0;
-		break;
-	default:
-		BUG();
 	}
-	goto err_ret;
+	if (!attr) {
+		ntfs_error(sb, "$MFT/$DATA attribute not found. $MFT is "
+				"corrupt. Run chkdsk.");
+		goto put_err_out;
+	}
+	if (highest_vcn && highest_vcn != last_vcn - 1) {
+		ntfs_error(sb, "Failed to load the complete run list "
+				"for $MFT/$DATA. Driver bug or "
+				"corrupt $MFT. Run chkdsk.");
+		ntfs_debug("highest_vcn = 0x%Lx, last_vcn - 1 = 0x%Lx",
+				(long long)highest_vcn, (long long)last_vcn - 1);
+		goto put_err_out;
+	}
+	put_attr_search_ctx(ctx);
+	ntfs_debug("Done.");
+out_now:
+	ntfs_free(m);
+	return;
+em_put_err_out:
+	ntfs_error(sb, "Couldn't find first extent of $DATA attribute in "
+			"attribute list. $MFT is corrupt. Run chkdsk.");
+put_err_out:
+	put_attr_search_ctx(ctx);
+err_out:
+	/* Make sure we revert to the safe super operations. */
+	sb->s_op = &ntfs_mount_sops;
+	ntfs_error(sb, "Failed. Marking inode as bad.");
+	make_bad_inode(vi);
+	goto out_now;
 }
 
-/* We need 0x48 bytes in total. */
-static int add_standard_information(ntfs_inode *ino)
+/**
+ * ntfs_dirty_inode - mark the inode's metadata dirty
+ * @vi:		inode to mark dirty
+ *
+ * This is called from fs/inode.c::__mark_inode_dirty(), when the inode itself
+ * is being marked dirty. An example is when UPDATE_ATIME() is invoked.
+ *
+ * We mark the inode dirty by setting both the page in which the mft record
+ * resides and the buffer heads in that page which correspond to the mft record
+ * dirty. This ensures that the changes will eventually be propagated to disk
+ * when the inode is set dirty.
+ *
+ * FIXME: Can we do that with the buffer heads? I am not too sure. Because if we
+ * do that we need to make sure that the kernel will not write out those buffer
+ * heads or we are screwed as it will write corrupt data to disk. The only way
+ * a mft record can be written correctly is by mst protecting it, writting it
+ * synchronously and fast mst deprotecting it. During this period, obviously,
+ * the mft record must be marked as not uptodate, be locked for writing or
+ * whatever, so that nobody attempts anything stupid.
+ *
+ * FIXME: Do we need to check that the fs is not mounted read only? And what
+ * about the inode? Anything else?
+ *
+ * FIXME: As we are only a read only driver it is safe to just return here for
+ * the moment.
+ */
+void ntfs_dirty_inode(struct inode *vi)
 {
-	ntfs_time64_t now;
-	char data[0x30];
-	char *position = data;
-	ntfs_attribute *si;
-
-	now = ntfs_now();
-	NTFS_PUTU64(position + 0x00, now);		/* File creation */
-	NTFS_PUTU64(position + 0x08, now);		/* Last modification */
-	NTFS_PUTU64(position + 0x10, now);		/* Last mod for MFT */
-	NTFS_PUTU64(position + 0x18, now);		/* Last access */
-	NTFS_PUTU64(position + 0x20, 0);		/* MSDOS file perms */
-	NTFS_PUTU64(position + 0x28, 0);		/* unknown */
-	return ntfs_create_attr(ino, ino->vol->at_standard_information, 0,
-			data, sizeof(data), &si);
+	ntfs_debug("Entering for inode 0x%lx.", vi->i_ino);
+	NInoSetDirty(NTFS_I(vi));
+	return;
 }
 
-static int add_filename(ntfs_inode *ino, ntfs_inode *dir, 
-		const unsigned char *filename, int length, ntfs_u32 flags)
+/**
+ * ntfs_commit_inode - write out a dirty inode
+ * @ni:		inode to write out
+ *
+ */
+int ntfs_commit_inode(ntfs_inode *ni)
 {
-	unsigned char *position;
-	unsigned int size;
-	ntfs_time64_t now;
-	int count, error;
-	unsigned char* data;
-	ntfs_attribute *fn;
-
-	/* Work out the size. */
-	size = 0x42 + 2 * length;
-	data = ntfs_malloc(size);
-	if (!data)
-		return -ENOMEM;
-	/* Search for a position. */
-	position = data;
-	NTFS_PUTINUM(position, dir);			/* Inode num of dir */
-	now = ntfs_now();
-	NTFS_PUTU64(position + 0x08, now);		/* File creation */
-	NTFS_PUTU64(position + 0x10, now);		/* Last modification */
-	NTFS_PUTU64(position + 0x18, now);		/* Last mod for MFT */
-	NTFS_PUTU64(position + 0x20, now);		/* Last access */
-	/* FIXME: Get the following two sizes by finding the data attribute
-	 * in ino->attr and copying the corresponding fields from there.
-	 * If no data present then set to zero. In current implementation
-	 * add_data is called after add_filename so zero is correct on
-	 * creation. Need to change when we have hard links / support different
-	 * filename namespaces. (AIA) */
-	NTFS_PUTS64(position + 0x28, 0);		/* Allocated size */
-	NTFS_PUTS64(position + 0x30, 0);		/* Data size */
-	NTFS_PUTU32(position + 0x38, flags);		/* File flags */
-	NTFS_PUTU32(position + 0x3c, 0);		/* We don't use these
-							 * features yet. */
-	NTFS_PUTU8(position + 0x40, length);		/* Filename length */
-	NTFS_PUTU8(position + 0x41, 0);			/* Only long name */
-		/* FIXME: This is madness. We are defining the POSIX namespace
-		 * for the filename here which can mean that the file will be
-		 * invisible when in Windows NT/2k! )-: (AIA) */
-	position += 0x42;
-	for (count = 0; count < length; count++) {
-		NTFS_PUTU16(position + 2 * count, filename[count]);
-	}
-	error = ntfs_create_attr(ino, ino->vol->at_file_name, 0, data, size,
-				 &fn);
-	if (!error)
-		error = ntfs_dir_add(dir, ino, fn);
-	ntfs_free(data);
-	return error;
+	ntfs_debug("Entering for inode 0x%Lx.",
+			(unsigned long long)ni->mft_no);
+	NInoClearDirty(ni);
+	return 0;
 }
 
-int add_security(ntfs_inode* ino, ntfs_inode* dir)
+void __ntfs_clear_inode(ntfs_inode *ni)
 {
-	int error;
-	char *buf;
-	int size;
-	ntfs_attribute* attr;
-	ntfs_io io;
-	ntfs_attribute *se;
-
-	attr = ntfs_find_attr(dir, ino->vol->at_security_descriptor, 0);
-	if (!attr)
-		return -EOPNOTSUPP; /* Need security in directory. */
-	size = attr->size;
-	if (size > 512)
-		return -EOPNOTSUPP;
-	buf = ntfs_malloc(size);
-	if (!buf)
-		return -ENOMEM;
-	io.fn_get = ntfs_get;
-	io.fn_put = ntfs_put;
-	io.param = buf;
-	io.size = size;
-	error = ntfs_read_attr(dir, ino->vol->at_security_descriptor, 0, 0,&io);
-	if (!error && io.size != size)
-		ntfs_error("wrong size in add_security\n");
-	if (error) {
-		ntfs_free(buf);
-		return error;
-	}
-	/* FIXME: Consider ACL inheritance. */
-	error = ntfs_create_attr(ino, ino->vol->at_security_descriptor,
-				 0, buf, size, &se);
-	ntfs_free(buf);
-	return error;
+	int err;
+
+	ntfs_debug("Entering for inode 0x%Lx.",
+			(unsigned long long)ni->mft_no);
+	if (NInoDirty(ni)) {
+		err = ntfs_commit_inode(ni);
+		if (err) {
+			ntfs_error(ni->vol->sb, "Failed to commit dirty "
+					"inode synchronously.");
+			// FIXME: Do something!!!
+		}
+	}
+	/* Synchronize with ntfs_commit_inode(). */
+	down_write(&ni->mrec_lock);
+	up_write(&ni->mrec_lock);
+	if (NInoDirty(ni)) {
+		ntfs_error(ni->vol->sb, "Failed to commit dirty inode "
+				"asynchronously.");
+		// FIXME: Do something!!!
+	}
+	/* No need to lock at this stage as no one else has a reference. */
+	if (ni->nr_extents > 0) {
+		int i;
+
+		// FIXME: Handle dirty case for each extent inode!
+		for (i = 0; i < ni->nr_extents; i++)
+			ntfs_destroy_inode(ni->_INE(extent_ntfs_inos)[i]);
+		kfree(ni->_INE(extent_ntfs_inos));
+	}
+	/* Free all alocated memory. */
+	down_write(&ni->run_list.lock);
+	ntfs_free(ni->run_list.rl);
+	ni->run_list.rl = NULL;
+	up_write(&ni->run_list.lock);
+
+	ntfs_free(ni->attr_list);
+
+	down_write(&ni->attr_list_rl.lock);
+	ntfs_free(ni->attr_list_rl.rl);
+	ni->attr_list_rl.rl = NULL;
+	up_write(&ni->attr_list_rl.lock);
 }
 
-static int add_data(ntfs_inode* ino, unsigned char *data, int length)
+void ntfs_clear_inode(ntfs_inode *ni)
 {
-	ntfs_attribute *da;
-	
-	return ntfs_create_attr(ino, ino->vol->at_data, 0, data, length, &da);
+	__ntfs_clear_inode(ni);
+
+	/* Bye, bye... */
+	ntfs_destroy_inode(ni);
 }
 
-/*
- * We _could_ use 'dir' to help optimise inode allocation.
+/**
+ * ntfs_clear_big_inode - clean up the ntfs specific part of an inode
+ * @vi:		vfs inode pending annihilation
+ *
+ * When the VFS is going to remove an inode from memory, ntfs_clear_big_inode()
+ * is called, which deallocates all memory belonging to the NTFS specific part
+ * of the inode and returns.
  *
- * FIXME: Need to undo what we do in ntfs_alloc_mft_record if we get an error
- * further on in ntfs_alloc_inode. Either fold the two functions to allow
- * proper undo or just deallocate the record from the mft bitmap. (AIA)
+ * If the MFT record is dirty, we commit it before doing anything else.
  */
-int ntfs_alloc_inode(ntfs_inode *dir, ntfs_inode *result, const char *filename,
-		int namelen, ntfs_u32 flags)
+void ntfs_clear_big_inode(struct inode *vi)
 {
-	ntfs_volume *vol = dir->vol;
-	int err;
-	ntfs_u8 buffer[2];
-	ntfs_io io;
+	ntfs_inode *ni = NTFS_I(vi);
+
+	__ntfs_clear_inode(ni);
 
-	err = ntfs_alloc_mft_record(vol, &(result->i_number));
-	if (err) {
-		if (err == -ENOSPC)
-			ntfs_error(__FUNCTION__ "(): No free inodes.\n");
-		return err;
-	}
-	/* Get the sequence number. */
-	io.fn_put = ntfs_put;
-	io.fn_get = ntfs_get;
-	io.param = buffer;
-	io.size = 2;
-	err = ntfs_read_attr(vol->mft_ino, vol->at_data, 0, 
-			((__s64)result->i_number << vol->mft_record_size_bits)
-			+ 0x10, &io);
-	// FIXME: We are leaving the MFT in inconsistent state! (AIA)
-	if (err)
-		return err;
-	/* Increment the sequence number skipping zero. */
-	result->sequence_number = (NTFS_GETU16(buffer) + 1) & 0xffff;
-	if (!result->sequence_number)
-		result->sequence_number++;
-	result->vol = vol;
-	result->attr_count = 0;
-	result->attrs = 0;
-	result->record_count = 1;
-	result->records = ntfs_calloc(8 * sizeof(int));
-	if (!result->records)
-		goto mem_err_out;
-	result->records[0] = result->i_number;
-	result->attr = ntfs_calloc(vol->mft_record_size);
-	if (!result->attr) {
-		ntfs_free(result->records);
-		result->records = NULL;
-		goto mem_err_out;
-	}
-	ntfs_fill_mft_header(result->attr, vol->mft_record_size,
-			result->sequence_number, 1, 1);
-	err = add_standard_information(result);
-	if (!err)
-		err = add_filename(result, dir, filename, namelen, flags);
-	if (!err)
-		err = add_security(result, dir);
-	// FIXME: We are leaving the MFT in inconsistent state on error! (AIA)
-	return err;
-mem_err_out:
-	// FIXME: We are leaving the MFT in inconsistent state! (AIA)
-	result->record_count = 0;
-	result->attr = NULL;
-	return -ENOMEM;
+	if (S_ISDIR(vi->i_mode)) {
+		down_write(&ni->_IDM(bmp_rl).lock);
+		ntfs_free(ni->_IDM(bmp_rl).rl);
+		up_write(&ni->_IDM(bmp_rl).lock);
+	}
+	return;
 }
 
-int ntfs_alloc_file(ntfs_inode *dir, ntfs_inode *result, char *filename,
-		int namelen)
+static const option_t si_readdir_opts_arr[] = {
+	{ SHOW_SYSTEM,	"system" },
+	{ SHOW_WIN32,	"win32" },
+	{ SHOW_DOS,	"dos" },
+	{ 0,		NULL }
+};
+
+/**
+ * ntfs_show_options - show mount options in /proc/mounts
+ * @sf:		seq_file in which to write our mount options
+ * @mnt:	vfs mount whose mount options to display
+ *
+ * Called by the VFS once for each mounted ntfs volume when someone reads
+ * /proc/mounts in order to display the NTFS specific mount options of each
+ * mount. The mount options of the vfs mount @mnt are written to the seq file
+ * @sf and success is returned.
+ */
+int ntfs_show_options(struct seq_file *sf, struct vfsmount *mnt)
 {
-	int err;
+	ntfs_volume *vol = NTFS_SB(mnt->mnt_sb);
+	int i;
 
-	err = ntfs_alloc_inode(dir, result, filename, namelen, 0);
-	if (!err)
-		err = add_data(result, 0, 0);
-	return err;
+	seq_printf(sf, ",uid=%i", vol->uid);
+	seq_printf(sf, ",gid=%i", vol->gid);
+	if (vol->fmask == vol->dmask)
+		seq_printf(sf, ",umask=0%o", vol->fmask);
+	else {
+		seq_printf(sf, ",fmask=0%o", vol->fmask);
+		seq_printf(sf, ",dmask=0%o", vol->dmask);
+	}
+	seq_printf(sf, ",nls=%s", vol->nls_map->charset);
+	switch (vol->readdir_opts) {
+	case SHOW_ALL:
+		seq_printf(sf, ",show_inodes=all");
+		break;
+	case SHOW_POSIX:
+		seq_printf(sf, ",show_inodes=posix");
+		break;
+	default:
+		for (i = 0; si_readdir_opts_arr[i].val; i++) {
+			if (si_readdir_opts_arr[i].val & vol->readdir_opts)
+				seq_printf(sf, ",show_inodes=%s",
+						si_readdir_opts_arr[i].str);
+		}
+	}
+	for (i = 0; on_errors_arr[i].val; i++) {
+		if (on_errors_arr[i].val & vol->on_errors)
+			seq_printf(sf, ",errors=%s", on_errors_arr[i].str);
+	}
+	seq_printf(sf, ",mft_zone_multiplier=%i", vol->mft_zone_multiplier);
+	return 0;
 }
 
diff -Nru a/fs/ntfs/inode.h b/fs/ntfs/inode.h
--- a/fs/ntfs/inode.h	Sun Apr 28 20:10:30 2002
+++ b/fs/ntfs/inode.h	Sun Apr 28 20:10:39 2002
@@ -1,58 +1,154 @@
 /*
- * inode.h -  Header file for inode.c
+ * inode.h - Defines for inode structures NTFS Linux kernel driver. Part of
+ *	     the Linux-NTFS project.
  *
- * Copyright (C) 1997 Régis Duchesne
- * Copyright (C) 1998 Martin von Löwis
- * Copyright (c) 2001 Anton Altparmakov (AIA)
+ * Copyright (c) 2001,2002 Anton Altaparmakov.
+ * Copyright (C) 2002 Richard Russon.
+ *
+ * This program/include file 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/include file 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 (in the main directory of the Linux-NTFS 
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
-ntfs_attribute *ntfs_find_attr(ntfs_inode *ino, int type, char *name);
-
-int ntfs_read_attr(ntfs_inode *ino, int type, char *name, __s64 offset,
-		ntfs_io *buf);
-
-int ntfs_write_attr(ntfs_inode *ino, int type, char *name, __s64 offset,
-		ntfs_io *buf);
+#ifndef _LINUX_NTFS_INODE_H
+#define _LINUX_NTFS_INODE_H
 
-int ntfs_init_inode(ntfs_inode *ino, ntfs_volume *vol, int inum);
+#include <linux/seq_file.h>
 
-void ntfs_clear_inode(ntfs_inode *ino);
+#include "volume.h"
 
-int ntfs_check_mft_record(ntfs_volume *vol, char *record);
+typedef struct _ntfs_inode ntfs_inode;
 
-int ntfs_alloc_inode(ntfs_inode *dir, ntfs_inode *result, const char *filename,
-		int namelen, ntfs_u32);
-
-int ntfs_alloc_file(ntfs_inode *dir, ntfs_inode *result, char *filename,
-		int namelen);
-
-int ntfs_update_inode(ntfs_inode *ino);
+/*
+ * The NTFS in-memory inode structure. It is just used as an extension to the
+ * fields already provided in the VFS inode.
+ */
+struct _ntfs_inode {
+	s64 initialized_size;	/* Copy from $DATA/$INDEX_ALLOCATION. */
+	s64 allocated_size;	/* Copy from $DATA/$INDEX_ALLOCATION. */
+	unsigned long state;	/* NTFS specific flags describing this inode.
+				   See fs/ntfs/ntfs.h:ntfs_inode_state_bits. */
+	u64 mft_no;		/* Mft record number (inode number). */
+	u16 seq_no;		/* Sequence number of the mft record. */
+	atomic_t count;		/* Inode reference count for book keeping. */
+	ntfs_volume *vol;	/* Pointer to the ntfs volume of this inode. */
+	run_list run_list;	/* If state has the NI_NonResident bit set,
+				   the run list of the unnamed data attribute
+				   (if a file) or of the index allocation
+				   attribute (directory). If run_list.rl is
+				   NULL, the run list has not been read in or
+				   has been unmapped. If NI_NonResident is
+				   clear, the unnamed data attribute is
+				   resident (file) or there is no $I30 index
+				   allocation attribute (directory). In that
+				   case run_list.rl is always NULL.*/
+	struct rw_semaphore mrec_lock;	/* Lock for serializing access to the
+				   mft record belonging to this inode. */
+	atomic_t mft_count;	/* Mapping reference count for book keeping. */
+	struct page *page;	/* The page containing the mft record of the
+				   inode. This should only be touched by the
+				   (un)map_mft_record*() functions. */
+	int page_ofs;		/* Offset into the page at which the mft record
+				   begins. This should only be touched by the
+				   (un)map_mft_record*() functions. */
+	/*
+	 * Attribute list support (only for use by the attribute lookup
+	 * functions). Setup during read_inode for all inodes with attribute
+	 * lists. Only valid if NI_AttrList is set in state, and attr_list_rl is
+	 * further only valid if NI_AttrListNonResident is set.
+	 */
+	u32 attr_list_size;	/* Length of attribute list value in bytes. */
+	u8 *attr_list;		/* Attribute list value itself. */
+	run_list attr_list_rl;	/* Run list for the attribute list value. */
+	union {
+		struct { /* It is a directory or $MFT. */
+			u32 index_block_size;	/* Size of an index block. */
+			u8 index_block_size_bits; /* Log2 of the above. */
+			u32 index_vcn_size;	/* Size of a vcn in this
+						   directory index. */
+			u8 index_vcn_size_bits;	/* Log2 of the above. */
+			s64 bmp_size;		/* Size of the $I30 bitmap. */
+			s64 bmp_initialized_size; /* Copy from $I30 bitmap. */
+			s64 bmp_allocated_size;	/* Copy from $I30 bitmap. */
+			run_list bmp_rl;	/* Run list for the $I30 bitmap
+						   if it is non-resident. */
+		} SN(idm);
+		struct { /* It is a compressed file. */
+			u32 compression_block_size;     /* Size of a compression
+						           block (cb). */
+			u8 compression_block_size_bits; /* Log2 of the size of
+							   a cb. */
+			u8 compression_block_clusters;  /* Number of clusters
+							   per compression
+							   block. */
+			s64 compressed_size;		/* Copy from $DATA. */
+		} SN(icf);
+	} SN(idc);
+	struct semaphore extent_lock;	/* Lock for accessing/modifying the
+					   below . */
+	s32 nr_extents;	/* For a base mft record, the number of attached extent
+			   inodes (0 if none), for extent records this is -1. */
+	union {		/* This union is only used if nr_extents != 0. */
+		ntfs_inode **extent_ntfs_inos;	/* For nr_extents > 0, array of
+						   the ntfs inodes of the extent
+						   mft records belonging to
+						   this base inode which have
+						   been loaded. */
+		ntfs_inode *base_ntfs_ino;	/* For nr_extents == -1, the
+						   ntfs inode of the base mft
+						   record. */
+	} SN(ine);
+};
+
+#define _IDM(X)  SC(idc.idm,X)
+#define _ICF(X)  SC(idc.icf,X)
+#define _INE(X)  SC(ine,X)
+
+typedef struct {
+	ntfs_inode ntfs_inode;
+	struct inode vfs_inode;		/* The vfs inode structure. */
+} big_ntfs_inode;
+
+/**
+ * NTFS_I - return the ntfs inode given a vfs inode
+ * @inode:	VFS inode
+ *
+ * NTFS_I() returns the ntfs inode associated with the VFS @inode.
+ */
+static inline ntfs_inode *NTFS_I(struct inode *inode)
+{
+	return (ntfs_inode *)list_entry(inode, big_ntfs_inode, vfs_inode);
+}
 
-int ntfs_vcn_to_lcn(ntfs_inode *ino, int vcn);
+static inline struct inode *VFS_I(ntfs_inode *ni)
+{
+	return &((big_ntfs_inode*)ni)->vfs_inode;
+}
 
-int ntfs_readwrite_attr(ntfs_inode *ino, ntfs_attribute *attr, __s64 offset,
-		ntfs_io *dest);
+extern struct inode *ntfs_alloc_big_inode(struct super_block *sb);
+extern void ntfs_destroy_big_inode(struct inode *inode);
+extern void ntfs_clear_big_inode(struct inode *vi);
 
-int ntfs_allocate_attr_number(ntfs_inode *ino, int *result);
+extern ntfs_inode *ntfs_new_inode(struct super_block *sb);
+extern void ntfs_clear_inode(ntfs_inode *ni);
 
-int ntfs_decompress_run(unsigned char **data, int *length,
-		ntfs_cluster_t *cluster, int *ctype);
+extern void ntfs_read_inode(struct inode *vi);
+extern void ntfs_read_inode_mount(struct inode *vi);
 
-void ntfs_decompress(unsigned char *dest, unsigned char *src, ntfs_size_t l);
+extern void ntfs_dirty_inode(struct inode *vi);
 
-int splice_runlists(ntfs_runlist **rl1, int *r1len, const ntfs_runlist *rl2,
-		int r2len);
+extern int ntfs_show_options(struct seq_file *sf, struct vfsmount *mnt);
 
-/*
- * NOTE: Neither of the ntfs_*_bit functions are atomic! But we don't need
- * them atomic at present as we never operate on shared/cached bitmaps.
- */
-static __inline__ int ntfs_test_and_set_bit(unsigned char *byte, const int bit)
-{
-	unsigned char *ptr = byte + (bit >> 3);
-	int b = 1 << (bit & 7);
-	int oldbit = *ptr & b ? 1 : 0;
-	*ptr |= b;
-	return oldbit;
-}
+#endif /* _LINUX_NTFS_FS_INODE_H */
 
diff -Nru a/fs/ntfs/layout.h b/fs/ntfs/layout.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/fs/ntfs/layout.h	Sun Apr 28 20:10:39 2002
@@ -0,0 +1,2229 @@
+/*
+ * layout.h - All NTFS associated on-disk structures. Part of the Linux-NTFS
+ *	      project.
+ *
+ * Copyright (c) 2001,2002 Anton Altaparmakov.
+ * Copyright (C) 2002 Richard Russon.
+ *
+ * This program/include file 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/include file 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 (in the main directory of the Linux-NTFS 
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _LINUX_NTFS_LAYOUT_H
+#define _LINUX_NTFS_LAYOUT_H
+
+#include <linux/types.h>
+#include <linux/bitops.h>
+#include <linux/list.h>
+#include <asm/byteorder.h>
+
+#include "volume.h"
+
+/*
+ * Constant endianness conversion defines.
+ */
+#define const_le16_to_cpu(x)	__constant_le16_to_cpu(x)
+#define const_le32_to_cpu(x)	__constant_le32_to_cpu(x)
+#define const_le64_to_cpu(x)	__constant_le64_to_cpu(x)
+
+#define const_cpu_to_le16(x)	__constant_cpu_to_le16(x)
+#define const_cpu_to_le32(x)	__constant_cpu_to_le32(x)
+#define const_cpu_to_le64(x)	__constant_cpu_to_le64(x)
+
+/* The NTFS oem_id */
+#define magicNTFS	const_cpu_to_le64(0x202020205346544e) /* "NTFS    " */
+
+/*
+ * Location of bootsector on partition:
+ * 	The standard NTFS_BOOT_SECTOR is on sector 0 of the partition.
+ * 	On NT4 and above there is one backup copy of the boot sector to
+ * 	be found on the last sector of the partition (not normally accessible
+ * 	from within Windows as the bootsector contained number of sectors
+ *	value is one less than the actual value!).
+ * 	On versions of NT 3.51 and earlier, the backup copy was located at 
+ * 	number of sectors/2 (integer divide), i.e. in the middle of the volume.
+ */
+
+/*
+ * BIOS parameter block (bpb) structure.
+ */
+typedef struct {
+	u16 bytes_per_sector;		/* Size of a sector in bytes. */
+	u8  sectors_per_cluster;	/* Size of a cluster in sectors. */
+	u16 reserved_sectors;		/* zero */
+	u8  fats;			/* zero */
+	u16 root_entries;		/* zero */
+	u16 sectors;			/* zero */
+	u8  media_type;			/* 0xf8 = hard disk */
+	u16 sectors_per_fat;		/* zero */
+	u16 sectors_per_track;		/* irrelevant */
+	u16 heads;			/* irrelevant */
+	u32 hidden_sectors;		/* zero */
+	u32 large_sectors;		/* zero */
+} __attribute__ ((__packed__)) BIOS_PARAMETER_BLOCK;
+
+/*
+ * NTFS boot sector structure.
+ */
+typedef struct {
+	u8  jump[3];			/* Irrelevant (jump to boot up code).*/
+	u64 oem_id;			/* Magic "NTFS    ". */
+	BIOS_PARAMETER_BLOCK bpb;	/* See BIOS_PARAMETER_BLOCK. */
+	u8  unused[4];			/* zero, NTFS diskedit.exe states that
+					   this is actually:
+						__u8 physical_drive;	// 0x80
+						__u8 current_head;	// zero
+						__u8 extended_boot_signature;
+									// 0x80
+						__u8 unused;		// zero
+					 */
+/*0x28*/s64 number_of_sectors;		/* Number of sectors in volume. Gives
+					   maximum volume size of 2^63 sectors.
+					   Assuming standard sector size of 512
+					   bytes, the maximum byte size is
+					   approx. 4.7x10^21 bytes. (-; */
+	s64 mft_lcn;			/* Cluster location of mft data. */
+	s64 mftmirr_lcn;		/* Cluster location of copy of mft. */
+	s8  clusters_per_mft_record;	/* Mft record size in clusters. */
+	u8  reserved0[3];		/* zero */
+	s8  clusters_per_index_record;	/* Index block size in clusters. */
+	u8  reserved1[3];		/* zero */
+	u64 volume_serial_number;	/* Irrelevant (serial number). */
+	u32 checksum;			/* Boot sector checksum. */
+/*0x54*/u8  bootstrap[426];		/* Irrelevant (boot up code). */
+	u16 end_of_sector_marker;	/* End of bootsector magic. Always is
+					   0xaa55 in little endian. */
+/* sizeof() = 512 (0x200) bytes */
+} __attribute__ ((__packed__)) NTFS_BOOT_SECTOR;
+
+/*
+ * Magic identifiers present at the beginning of all ntfs record containing
+ * records (like mft records for example).
+ */
+typedef enum {
+	magic_BAAD = const_cpu_to_le32(0x44414142), /* BAAD == corrupt record */
+	magic_CHKD = const_cpu_to_le32(0x424b4843), /* CHKD == chkdsk ??? */
+        magic_FILE = const_cpu_to_le32(0x454c4946), /* FILE == mft entry */
+	magic_HOLE = const_cpu_to_le32(0x454c4f48), /* HOLE == ? (NTFS 3.0+?) */
+        magic_INDX = const_cpu_to_le32(0x58444e49), /* INDX == index buffer */
+} NTFS_RECORD_TYPES;
+
+/*
+ * Generic magic comparison macros. Finally found a use for the ## preprocessor
+ * operator! (-8
+ */
+#define is_magic(x, m)		(   (u32)(x) == magic_##m )
+#define is_magicp(p, m) 	( *(u32*)(p) == magic_##m )
+
+/*
+ * Specialised magic comparison macros.
+ */
+#define is_baad_record(x)	( is_magic (x, BAAD) )
+#define is_baad_recordp(p)	( is_magicp(p, BAAD) )
+#define is_chkd_record(x)       ( is_magic (x, CHKD) )
+#define is_chkd_recordp(p)      ( is_magicp(p, CHKD) )
+#define is_file_record(x)	( is_magic (x, FILE) )
+#define is_file_recordp(p)	( is_magicp(p, FILE) )
+#define is_hole_record(x)       ( is_magic (x, HOLE) )
+#define is_hole_recordp(p)      ( is_magicp(p, HOLE) )
+#define is_indx_record(x)       ( is_magic (x, INDX) )
+#define is_indx_recordp(p)      ( is_magicp(p, INDX) )
+
+#define is_mft_record(x)	( is_file_record(x) )
+#define is_mft_recordp(p)	( is_file_recordp(p) )
+
+/*
+ * The Update Sequence Array (usa) is an array of the u16 values which belong
+ * to the end of each sector protected by the update sequence record in which
+ * this array is contained. Note that the first entry is the Update Sequence
+ * Number (usn), a cyclic counter of how many times the protected record has
+ * been written to disk. The values 0 and -1 (ie. 0xffff) are not used. All
+ * last u16's of each sector have to be equal to the usn (during reading) or
+ * are set to it (during writing). If they are not, an incomplete multi sector
+ * transfer has occured when the data was written.
+ * The maximum size for the update sequence array is fixed to:
+ * 	maximum size = usa_ofs + (usa_count * 2) = 510 bytes
+ * The 510 bytes comes from the fact that the last u16 in the array has to
+ * (obviously) finish before the last u16 of the first 512-byte sector.
+ * This formula can be used as a consistency check in that usa_ofs +
+ * (usa_count * 2) has to be less than or equal to 510.
+ */
+typedef struct {
+	NTFS_RECORD_TYPES magic;	/* A four-byte magic identifying the
+					   record type and/or status. */
+	u16 usa_ofs;		/* Offset to the Update Sequence Array (usa)
+				   from the start of the ntfs record. */
+	u16 usa_count;		/* Number of u16 sized entries in the usa
+				   including the Update Sequence Number (usn),
+				   thus the number of fixups is the usa_count
+				   minus 1. */
+} __attribute__ ((__packed__)) NTFS_RECORD;
+
+/*
+ * System files mft record numbers. All these files are always marked as used
+ * in the bitmap attribute of the mft; presumably in order to avoid accidental
+ * allocation for random other mft records. Also, the sequence number for each
+ * of the system files is always equal to their mft record number and it is
+ * never modified.
+ */
+typedef enum {
+	FILE_MFT       = 0,	/* Master file table (mft). Data attribute
+				   contains the entries and bitmap attribute
+				   records which ones are in use (bit==1). */
+	FILE_MFTMirr   = 1,	/* Mft mirror: copy of first four mft records
+				   in data attribute. If cluster size > 4kiB,
+				   copy of first N mft records, with
+					N = cluster_size / mft_record_size. */
+	FILE_LogFile   = 2,	/* Journalling log in data attribute. */
+	FILE_Volume    = 3,	/* Volume name attribute and volume information
+				   attribute (flags and ntfs version). Windows
+				   refers to this file as volume DASD (Direct
+				   Access Storage Device). */
+	FILE_AttrDef   = 4,	/* Array of attribute definitions in data
+				   attribute. */
+	FILE_root      = 5,	/* Root directory. */
+	FILE_Bitmap    = 6,	/* Allocation bitmap of all clusters (lcns) in
+				   data attribute. */
+	FILE_Boot      = 7,	/* Boot sector (always at cluster 0) in data
+				   attribute. */
+	FILE_BadClus   = 8,	/* Contains all bad clusters in the non-resident
+				   data attribute. */
+	FILE_Secure    = 9,	/* Shared security descriptors in data attribute
+				   and two indexes into the descriptors.
+				   Appeared in Windows 2000. Before that, this
+				   file was named $Quota but was unused. */
+	FILE_UpCase    = 10,	/* Uppercase equivalents of all 65536 Unicode
+				   characters in data attribute. */
+	FILE_Extend    = 11,	/* Directory containing other system files (eg.
+				   $ObjId, $Quota, $Reparse and $UsnJrnl). This
+				   is new to NTFS3.0. */
+	FILE_reserved12 = 12,	/* Reserved for future use (records 12-15). */
+	FILE_reserved13 = 13,
+	FILE_reserved14 = 14,
+	FILE_reserved15 = 15,
+	FILE_first_user = 16,	/* First user file, used as test limit for
+				   whether to allow opening a file or not. */
+} NTFS_SYSTEM_FILES;
+
+/*
+ * These are the so far known MFT_RECORD_* flags (16-bit) which contain 
+ * information about the mft record in which they are present.
+ */
+typedef enum {
+	MFT_RECORD_IN_USE	= const_cpu_to_le16(0x0001),
+	MFT_RECORD_IS_DIRECTORY	= const_cpu_to_le16(0x0002),
+	MFT_REC_SPACE_FILLER	= 0xffff	/* Just to make flags 16-bit. */
+} __attribute__ ((__packed__)) MFT_RECORD_FLAGS;
+
+/*
+ * mft references (aka file references or file record segment references) are
+ * used whenever a structure needs to refer to a record in the mft.
+ * 
+ * A reference consists of a 48-bit index into the mft and a 16-bit sequence
+ * number used to detect stale references.
+ *
+ * For error reporting purposes we treat the 48-bit index as a signed quantity.
+ *
+ * The sequence number is a circular counter (skipping 0) describing how many
+ * times the referenced mft record has been (re)used. This has to match the
+ * sequence number of the mft record being referenced, otherwise the reference
+ * is considered stale and removed (FIXME: only ntfsck or the driver itself?).
+ *
+ * If the sequence number is zero it is assumed that no sequence number
+ * consistency checking should be performed.
+ *
+ * FIXME: Since inodes are 32-bit as of now, the driver needs to always check
+ * for high_part being 0 and if not either BUG(), cause a panic() or handle
+ * the situation in some other way. This shouldn't be a problem as a volume has
+ * to become HUGE in order to need more than 32-bits worth of mft records.
+ * Assuming the standard mft record size of 1kb only the records (never mind
+ * the non-resident attributes, etc.) would require 4Tb of space on their own
+ * for the first 32 bits worth of records. This is only if some strange person
+ * doesn't decide to foul play and make the mft sparse which would be a really
+ * horrible thing to do as it would trash our current driver implementation. )-:
+ * Do I hear screams "we want 64-bit inodes!" ?!? (-;
+ *
+ * FIXME: The mft zone is defined as the first 12% of the volume. This space is
+ * reserved so that the mft can grow contiguously and hence doesn't become 
+ * fragmented. Volume free space includes the empty part of the mft zone and
+ * when the volume's free 88% are used up, the mft zone is shrunk by a factor
+ * of 2, thus making more space available for more files/data. This process is
+ * repeated everytime there is no more free space except for the mft zone until
+ * there really is no more free space.
+ */
+
+/*
+ * Typedef the MFT_REF as a 64-bit value for easier handling.
+ * Also define two unpacking macros to get to the reference (MREF) and
+ * sequence number (MSEQNO) respectively.
+ * The _LE versions are to be applied on little endian MFT_REFs.
+ * Note: The _LE versions will return a CPU endian formatted value!
+ */
+typedef enum {
+	MFT_REF_MASK_CPU	= 0x0000ffffffffffffULL,
+	MFT_REF_MASK_LE		= const_cpu_to_le64(0x0000ffffffffffffULL),
+} MFT_REF_CONSTS;
+
+typedef u64 MFT_REF;
+
+#define MREF(x)		((u64)((x) & MFT_REF_MASK_CPU))
+#define MSEQNO(x)	((u16)(((x) >> 48) & 0xffff))
+#define MREF_LE(x)	((u64)(le64_to_cpu(x) & MFT_REF_MASK_CPU))
+#define MSEQNO_LE(x)	((u16)((le64_to_cpu(x) >> 48) & 0xffff))
+
+#define IS_ERR_MREF(x)	(((x) & 0x0000800000000000ULL) ? 1 : 0)
+#define ERR_MREF(x)	((u64)((s64)(x)))
+#define MREF_ERR(x)	((int)((s64)(x)))
+
+/*
+ * The mft record header present at the beginning of every record in the mft.
+ * This is followed by a sequence of variable length attribute records which
+ * is terminated by an attribute of type AT_END which is a truncated attribute
+ * in that it only consists of the attribute type code AT_END and none of the
+ * other members of the attribute structure are present.
+ */
+typedef struct {
+/*Ofs*/
+/*  0*/	NTFS_RECORD SN(mnr);	/* Usually the magic is "FILE". */
+/*  8*/	u64 lsn;		/* $LogFile sequence number for this record.
+				   Changed every time the record is modified. */
+/* 16*/	u16 sequence_number;	/* Number of times this mft record has been
+		   		   reused. (See description for MFT_REF
+				   above.) NOTE: The increment (skipping zero)
+				   is done when the file is deleted. NOTE: If
+				   this is zero it is left zero. */
+/* 18*/	u16 link_count; 	/* Number of hard links, i.e. the number of 
+				   directory entries referencing this record.
+				   NOTE: Only used in mft base records.
+				   NOTE: When deleting a directory entry we
+				   check the link_count and if it is 1 we
+				   delete the file. Otherwise we delete the
+				   FILE_NAME_ATTR being referenced by the
+				   directory entry from the mft record and
+				   decrement the link_count.
+				   FIXME: Careful with Win32 + DOS names! */
+/* 20*/	u16 attrs_offset;	/* Byte offset to the first attribute in this
+				   mft record from the start of the mft record.
+				   NOTE: Must be aligned to 8-byte boundary. */
+/* 22*/	MFT_RECORD_FLAGS flags;	/* Bit array of MFT_RECORD_FLAGS. When a file
+				   is deleted, the MFT_RECORD_IN_USE flag is
+				   set to zero. */
+/* 24*/	u32 bytes_in_use;	/* Number of bytes used in this mft record.
+				   NOTE: Must be aligned to 8-byte boundary. */
+/* 28*/	u32 bytes_allocated;	/* Number of bytes allocated for this mft
+				   record. This should be equal to the mft
+				   record size. */
+/* 32*/	MFT_REF base_mft_record; /* This is zero for base mft records.
+				   When it is not zero it is a mft reference
+				   pointing to the base mft record to which
+				   this record belongs (this is then used to
+				   locate the attribute list attribute present
+				   in the base record which describes this
+				   extension record and hence might need
+				   modification when the extension record
+				   itself is modified, also locating the
+				   attribute list also means finding the other
+				   potential extents, belonging to the non-base
+				   mft record). */
+/* 40*/	u16 next_attr_instance;	/* The instance number that will be
+				   assigned to the next attribute added to this
+				   mft record. NOTE: Incremented each time
+				   after it is used. NOTE: Every time the mft
+				   record is reused this number is set to zero.
+				   NOTE: The first instance number is always 0.
+				 */
+/* sizeof() = 42 bytes */
+/* NTFS 3.1+ (Windows XP and above) introduce the following additions. */
+/* 42*/ //u16 reserved;		/* Reserved/alignment. */
+/* 44*/ //u32 mft_record_number;/* Number of this mft record. */
+/* sizeof() = 48 bytes */
+/*
+ * When (re)using the mft record, we place the update sequence array at this
+ * offset, i.e. before we start with the attributes. This also makes sense,
+ * otherwise we could run into problems with the update sequence array
+ * containing in itself the last two bytes of a sector which would mean that
+ * multi sector transfer protection wouldn't work. As you can't protect data
+ * by overwriting it since you then can't get it back...
+ * When reading we obviously use the data from the ntfs record header.
+ */
+} __attribute__ ((__packed__)) MFT_RECORD;
+
+#define _MNR(X)  SC(mnr,X)
+
+/*
+ * System defined attributes (32-bit). Each attribute type has a corresponding
+ * attribute name (Unicode string of maximum 64 character length) as described
+ * by the attribute definitions present in the data attribute of the $AttrDef
+ * system file. On NTFS 3.0 volumes the names are just as the types are named
+ * in the below enum exchanging AT_ for the dollar sign ($). If that isn't a
+ * revealing choice of symbol... (-;
+ */
+typedef enum {
+	AT_UNUSED			= const_cpu_to_le32(         0),
+	AT_STANDARD_INFORMATION		= const_cpu_to_le32(      0x10),
+	AT_ATTRIBUTE_LIST		= const_cpu_to_le32(      0x20),
+	AT_FILE_NAME			= const_cpu_to_le32(      0x30),
+	AT_OBJECT_ID			= const_cpu_to_le32(      0x40),
+	AT_SECURITY_DESCRIPTOR		= const_cpu_to_le32(      0x50),
+	AT_VOLUME_NAME			= const_cpu_to_le32(      0x60),
+	AT_VOLUME_INFORMATION		= const_cpu_to_le32(      0x70),
+	AT_DATA				= const_cpu_to_le32(      0x80),
+	AT_INDEX_ROOT			= const_cpu_to_le32(      0x90),
+	AT_INDEX_ALLOCATION		= const_cpu_to_le32(      0xa0),
+	AT_BITMAP			= const_cpu_to_le32(      0xb0),
+	AT_REPARSE_POINT		= const_cpu_to_le32(      0xc0),
+	AT_EA_INFORMATION		= const_cpu_to_le32(      0xd0),
+	AT_EA				= const_cpu_to_le32(      0xe0),
+	AT_PROPERTY_SET			= const_cpu_to_le32(      0xf0),
+	AT_LOGGED_UTILITY_STREAM	= const_cpu_to_le32(     0x100),
+	AT_FIRST_USER_DEFINED_ATTRIBUTE	= const_cpu_to_le32(    0x1000),
+	AT_END				= const_cpu_to_le32(0xffffffff),
+} ATTR_TYPES;
+
+/*
+ * The collation rules for sorting views/indexes/etc (32-bit).
+ *
+ * COLLATION_UNICODE_STRING - Collate Unicode strings by comparing their binary
+ *	Unicode values, except that when a character can be uppercased, the
+ *	upper case value collates before the lower case one.
+ * COLLATION_FILE_NAME - Collate file names as Unicode strings. The collation
+ *	is done very much like COLLATION_UNICODE_STRING. In fact I have no idea
+ *	what the difference is. Perhaps the difference is that file names
+ *	would treat some special characters in an odd way (see
+ *	unistr.c::ntfs_collate_names() and unistr.c::legal_ansi_char_array[]
+ *	for what I mean but COLLATION_UNICODE_STRING would not give any special
+ *	treatment to any characters at all, but this is speculation.
+ * COLLATION_NTOFS_ULONG - Sorting is done according to ascending u32 key
+ * 	values. E.g. used for $SII index in FILE_Secure, which sorts by
+ * 	security_id (u32).
+ * COLLATION_NTOFS_SID - Sorting is done according to ascending SID values.
+ * 	E.g. used for $O index in FILE_Extend/$Quota.
+ * COLLATION_NTOFS_SECURITY_HASH - Sorting is done first by ascending hash
+ * 	values and second by ascending security_id values. E.g. used for $SDH
+ * 	index in FILE_Secure.
+ * COLLATION_NTOFS_ULONGS - Sorting is done according to a sequence of ascending
+ *	u32 key values. E.g. used for $O index in FILE_Extend/$ObjId, which
+ *	sorts by object_id (16-byte), by splitting up the object_id in four
+ *	u32 values and using them as individual keys. E.g. take the following
+ *	two security_ids, stored as follows on disk:
+ *		1st: a1 61 65 b7 65 7b d4 11 9e 3d 00 e0 81 10 42 59
+ *		2nd: 38 14 37 d2 d2 f3 d4 11 a5 21 c8 6b 79 b1 97 45
+ *	To compare them, they are split into four u32 values each, like so:
+ *		1st: 0xb76561a1 0x11d47b65 0xe0003d9e 0x59421081
+ *		2nd: 0xd2371438 0x11d4f3d2 0x6bc821a5 0x4597b179
+ *	Now, it is apparent why the 2nd object_id collates after the 1st: the
+ *	first u32 value of the 1st object_id is less than the first u32 of
+ *	the 2nd object_id. If the first u32 values of both object_ids were
+ *	equal then the second u32 values would be compared, etc.
+ */
+typedef enum {
+	COLLATION_BINARY	 = const_cpu_to_le32(0), /* Collate by binary
+					compare where the first byte is most
+					significant. */
+	COLLATION_FILE_NAME	 = const_cpu_to_le32(1), /* Collate file names
+					as Unicode strings. */
+	COLLATION_UNICODE_STRING = const_cpu_to_le32(2), /* Collate Unicode
+					strings by comparing their binary
+					Unicode values, except that when a
+					character can be uppercased, the upper
+					case value collates before the lower
+					case one. */
+	COLLATION_NTOFS_ULONG		= const_cpu_to_le32(16),
+	COLLATION_NTOFS_SID		= const_cpu_to_le32(17),
+	COLLATION_NTOFS_SECURITY_HASH	= const_cpu_to_le32(18),
+	COLLATION_NTOFS_ULONGS		= const_cpu_to_le32(19),
+} COLLATION_RULES;
+
+/*
+ * The flags (32-bit) describing attribute properties in the attribute
+ * definition structure. FIXME: This information is from Regis's information
+ * and, according to him, it is not certain and probably incomplete.
+ * The INDEXABLE flag is fairly certainly correct as only the file name
+ * attribute has this flag set and this is the only attribute indexed in NT4.
+ */
+typedef enum {
+	INDEXABLE	    = const_cpu_to_le32(0x02),	/* Attribute can be
+							   indexed. */
+	NEED_TO_REGENERATE  = const_cpu_to_le32(0x40),	/* Need to regenerate
+							   during regeneration
+							   phase. */
+	CAN_BE_NON_RESIDENT = const_cpu_to_le32(0x80),	/* Attribute can be
+							   non-resident. */
+} ATTR_DEF_FLAGS;
+
+/*
+ * The data attribute of FILE_AttrDef contains a sequence of attribute
+ * definitions for the NTFS volume. With this, it is supposed to be safe for an
+ * older NTFS driver to mount a volume containing a newer NTFS version without
+ * damaging it (that's the theory. In practice it's: not damaging it too much).
+ * Entries are sorted by attribute type. The flags describe whether the
+ * attribute can be resident/non-resident and possibly other things, but the
+ * actual bits are unknown.
+ */
+typedef struct {
+/*hex ofs*/
+/*  0*/	uchar_t name[0x40];		/* Unicode name of the attribute. Zero
+					   terminated. */
+/* 80*/	ATTR_TYPES type;		/* Type of the attribute. */
+/* 84*/	u32 display_rule;		/* Default display rule.
+					   FIXME: What does it mean? (AIA) */
+/* 88*/ COLLATION_RULES collation_rule;	/* Default collation rule. */
+/* 8c*/	ATTR_DEF_FLAGS flags;		/* Flags describing the attribute. */
+/* 90*/	u64 min_size;			/* Optional minimum attribute size. */
+/* 98*/	u64 max_size;			/* Maximum size of attribute. */
+/* sizeof() = 0xa0 or 160 bytes */
+} __attribute__ ((__packed__)) ATTR_DEF;
+
+/*
+ * Attribute flags (16-bit). 
+ */
+typedef enum {
+	ATTR_IS_COMPRESSED	= const_cpu_to_le16(0x0001),
+	ATTR_COMPRESSION_MASK	= const_cpu_to_le16(0x00ff),  /* Compression
+						method mask. Also, first
+						illegal value. */
+	ATTR_IS_ENCRYPTED	= const_cpu_to_le16(0x4000),
+	ATTR_IS_SPARSE		= const_cpu_to_le16(0x8000),
+} __attribute__ ((__packed__)) ATTR_FLAGS;
+
+/*
+ * Attribute compression.
+ *
+ * Only the data attribute is ever compressed in the current ntfs driver in
+ * Windows. Further, compression is only applied when the data attribute is
+ * non-resident. Finally, to use compression, the maximum allowed cluster size
+ * on a volume is 4kib.
+ *
+ * The compression method is based on independently compressing blocks of X
+ * clusters, where X is determined from the compression_unit value found in the
+ * non-resident attribute record header (more precisely: X = 2^compression_unit
+ * clusters). On Windows NT/2k, X always is 16 clusters (compression_unit = 4).
+ *
+ * There are three different cases of how a compression block of X clusters
+ * can be stored:
+ *
+ *   1) The data in the block is all zero (a sparse block):
+ *	  This is stored as a sparse block in the run list, i.e. the run list
+ *	  entry has length = X and lcn = -1. The mapping pairs array actually
+ *	  uses a delta_lcn value length of 0, i.e. delta_lcn is not present at
+ *	  all, which is then interpreted by the driver as lcn = -1.
+ *	  NOTE: Even uncompressed files can be sparse on NTFS 3.0 volumes, then
+ *	  the same principles apply as above, except that the length is not
+ *	  restricted to being any particular value.
+ *
+ *   2) The data in the block is not compressed:
+ *	  This happens when compression doesn't reduce the size of the block
+ *	  in clusters. I.e. if compression has a small effect so that the
+ *	  compressed data still occupies X clusters, then the uncompressed data
+ *	  is stored in the block.
+ *	  This case is recognised by the fact that the run list entry has
+ *	  length = X and lcn >= 0. The mapping pairs array stores this as
+ *	  normal with a run length of X and some specific delta_lcn, i.e.
+ *	  delta_lcn has to be present.
+ *
+ *   3) The data in the block is compressed:
+ *	  The common case. This case is recognised by the fact that the run
+ *	  list entry has length L < X and lcn >= 0. The mapping pairs array
+ *	  stores this as normal with a run length of X and some specific
+ *	  delta_lcn, i.e. delta_lcn has to be present. This run list entry is
+ *	  immediately followed by a sparse entry with length = X - L and
+ *	  lcn = -1. The latter entry is to make up the vcn counting to the
+ *	  full compression block size X.
+ *
+ * In fact, life is more complicated because adjacent entries of the same type
+ * can be coalesced. This means that one has to keep track of the number of
+ * clusters handled and work on a basis of X clusters at a time being one
+ * block. An example: if length L > X this means that this particular run list
+ * entry contains a block of length X and part of one or more blocks of length
+ * L - X. Another example: if length L < X, this does not necessarily mean that
+ * the block is compressed as it might be that the lcn changes inside the block
+ * and hence the following run list entry describes the continuation of the
+ * potentially compressed block. The block would be compressed if the
+ * following run list entry describes at least X - L sparse clusters, thus
+ * making up the compression block length as described in point 3 above. (Of
+ * course, there can be several run list entries with small lengths so that the
+ * sparse entry does not follow the first data containing entry with
+ * length < X.)
+ *
+ * NOTE: At the end of the compressed attribute value, there most likely is not
+ * just the right amount of data to make up a compression block, thus this data
+ * is not even attempted to be compressed. It is just stored as is, unless
+ * the number of clusters it occupies is reduced when compressed in which case
+ * it is stored as a compressed compression block, complete with sparse
+ * clusters at the end.
+ */
+
+/*
+ * Flags of resident attributes (8-bit).
+ */
+typedef enum {
+	RESIDENT_ATTR_IS_INDEXED = 0x01, /* Attribute is referenced in an index
+					    (has implications for deleting and
+					    modifying the attribute). */
+} __attribute__ ((__packed__)) RESIDENT_ATTR_FLAGS;
+
+/*
+ * Attribute record header. Always aligned to 8-byte boundary.
+ */
+typedef struct {
+/*Ofs*/
+/*  0*/	ATTR_TYPES type;	/* The (32-bit) type of the attribute. */
+/*  4*/	u32 length;		/* Byte size of the resident part of the
+				   attribute (aligned to 8-byte boundary).
+				   Used to get to the next attribute. */
+/*  8*/	u8 non_resident;	/* If 0, attribute is resident.
+				   If 1, attribute is non-resident. */
+/*  9*/	u8 name_length;		/* Unicode character size of name of attribute.
+				   0 if unnamed. */
+/* 10*/	u16 name_offset;	/* If name_length != 0, the byte offset to the
+				   beginning of the name from the attribute
+				   record. Note that the name is stored as a
+				   Unicode string. When creating, place offset
+				   just at the end of the record header. Then,
+				   follow with attribute value or mapping pairs
+				   array, resident and non-resident attributes
+				   respectively, aligning to an 8-byte
+				   boundary. */
+/* 12*/	ATTR_FLAGS flags;	/* Flags describing the attribute. */
+/* 14*/	u16 instance;		/* The instance of this attribute record. This
+				   number is unique within this mft record (see 
+				   MFT_RECORD/next_attribute_instance notes in
+				   in mft.h for more details). */
+/* 16*/	union {
+		/* Resident attributes. */
+		struct {
+/* 16 */		u32 value_length; /* Byte size of attribute value. */
+/* 20 */		u16 value_offset; /* Byte offset of the attribute
+					     value from the start of the
+					     attribute record. When creating,
+					     align to 8-byte boundary if we 
+					     have a name present as this might
+					     not have a length of a multiple
+					     of 8-bytes. */
+/* 22 */		RESIDENT_ATTR_FLAGS resident_flags; /* See above. */
+/* 23 */		s8 reservedR;	  /* Reserved/alignment to 8-byte
+					     boundary. */
+		} SN(ara) __attribute__ ((__packed__));
+		/* Non-resident attributes. */
+		struct {
+/* 16*/			VCN lowest_vcn;	/* Lowest valid virtual cluster number
+				for this portion of the attribute value or
+				0 if this is the only extent (usually the
+				case). - Only when an attribute list is used
+				does lowest_vcn != 0 ever occur. */
+/* 24*/			VCN highest_vcn; /* Highest valid vcn of this extent of
+				the attribute value. - Usually there is only one
+				portion, so this usually equals the attribute
+				value size in clusters minus 1. Can be -1 for
+				zero length files. Can be 0 for "single extent"
+				attributes. */
+/* 32*/			u16 mapping_pairs_offset; /* Byte offset from the
+				beginning of the structure to the mapping pairs
+				array which contains the mappings between the
+				vcns and the logical cluster numbers (lcns).
+				When creating, place this at the end of this
+				record header aligned to 8-byte boundary. */
+/* 34*/			u8 compression_unit; /* The compression unit expressed
+				as the log to the base 2 of the number of
+				clusters in a compression unit. 0 means not
+				compressed. (This effectively limits the
+				compression unit size to be a power of two
+				clusters.) WinNT4 only uses a value of 4. */
+/* 35*/			u8 reserved1[5];	/* Align to 8-byte boundary. */
+/* The sizes below are only used when lowest_vcn is zero, as otherwise it would
+   be difficult to keep them up-to-date.*/
+/* 40*/			s64 allocated_size;	/* Byte size of disk space
+				allocated to hold the attribute value. Always
+				is a multiple of the cluster size. When a file
+				is compressed, this field is a multiple of the
+				compression block size (2^compression_unit) and
+				it represents the logically allocated space
+				rather than the actual on disk usage. For this
+				use the compressed_size (see below). */
+/* 48*/			s64 data_size;	/* Byte size of the attribute
+				value. Can be larger than allocated_size if
+				attribute value is compressed or sparse. */
+/* 56*/			s64 initialized_size;	/* Byte size of initialized
+				portion of the attribute value. Usually equals
+				data_size. */
+/* sizeof(uncompressed attr) = 64*/
+/* 64*/			s64 compressed_size;	/* Byte size of the attribute
+				value after compression. Only present when
+				compressed. Always is a multiple of the
+				cluster size. Represents the actual amount of
+				disk space being used on the disk. */
+/* sizeof(compressed attr) = 72*/
+		} SN(anr) __attribute__ ((__packed__));
+	} SN(aua) __attribute__ ((__packed__));
+} __attribute__ ((__packed__)) ATTR_RECORD;
+
+#define _ARA(X)  SC(aua.ara,X)
+#define _ANR(X)  SC(aua.anr,X)
+
+typedef ATTR_RECORD ATTR_REC;
+
+/*
+ * File attribute flags (32-bit).
+ */
+typedef enum {
+	/*
+	 * These flags are only presnt in the STANDARD_INFORMATION attribute
+	 * (in the field file_attributes).
+	 */
+	FILE_ATTR_READONLY		= const_cpu_to_le32(0x00000001),
+	FILE_ATTR_HIDDEN		= const_cpu_to_le32(0x00000002),
+	FILE_ATTR_SYSTEM		= const_cpu_to_le32(0x00000004),
+	/* Old DOS volid. Unused in NT.	= cpu_to_le32(0x00000008), */
+
+	FILE_ATTR_DIRECTORY		= const_cpu_to_le32(0x00000010),
+	/* FILE_ATTR_DIRECTORY is not considered valid in NT. It is reserved
+	   for the DOS SUBDIRECTORY flag. */
+	FILE_ATTR_ARCHIVE		= const_cpu_to_le32(0x00000020),
+	FILE_ATTR_DEVICE		= const_cpu_to_le32(0x00000040),
+	FILE_ATTR_NORMAL		= const_cpu_to_le32(0x00000080),
+
+	FILE_ATTR_TEMPORARY		= const_cpu_to_le32(0x00000100),
+	FILE_ATTR_SPARSE_FILE		= const_cpu_to_le32(0x00000200),
+	FILE_ATTR_REPARSE_POINT		= const_cpu_to_le32(0x00000400),
+	FILE_ATTR_COMPRESSED		= const_cpu_to_le32(0x00000800),
+
+	FILE_ATTR_OFFLINE		= const_cpu_to_le32(0x00001000),
+	FILE_ATTR_NOT_CONTENT_INDEXED	= const_cpu_to_le32(0x00002000),
+	FILE_ATTR_ENCRYPTED		= const_cpu_to_le32(0x00004000),
+
+	FILE_ATTR_VALID_FLAGS		= const_cpu_to_le32(0x00007fb7),
+	/* FILE_ATTR_VALID_FLAGS masks out the old DOS VolId and the 
+	   FILE_ATTR_DEVICE and preserves everything else. This mask
+	   is used to obtain all flags that are valid for reading. */
+	FILE_ATTR_VALID_SET_FLAGS	= const_cpu_to_le32(0x000031a7),
+	/* FILE_ATTR_VALID_SET_FLAGS masks out the old DOS VolId, the
+	   F_A_DEVICE, F_A_DIRECTORY, F_A_SPARSE_FILE, F_A_REPARSE_POINT,
+	   F_A_COMPRESSED and F_A_ENCRYPTED and preserves the rest. This mask
+	   is used to to obtain all flags that are valid for setting. */
+
+	/*
+	 * These flags are only present in the FILE_NAME attribute (in the
+	 * field file_attributes).
+	 */
+	FILE_ATTR_DUP_FILE_NAME_INDEX_PRESENT	= const_cpu_to_le32(0x10000000),
+	/* This is a copy of the corresponding bit from the mft record, telling
+	   us whether this is a directory or not, i.e. whether it has an
+	   index root attribute or not. */
+	FILE_ATTR_DUP_VIEW_INDEX_PRESENT	= const_cpu_to_le32(0x20000000),
+	/* This is a copy of the corresponding bit from the mft record, telling
+	   us whether this file has a view index present (eg. object id index,
+	   quota index, one of the security indexes or the encrypting file
+	   system related indexes). */
+} FILE_ATTR_FLAGS;
+
+/*
+ * NOTE on times in NTFS: All times are in MS standard time format, i.e. they
+ * are the number of 100-nanosecond intervals since 1st January 1601, 00:00:00
+ * universal coordinated time (UTC). (In Linux time starts 1st January 1970,
+ * 00:00:00 UTC and is stored as the number of 1-second intervals since then.)
+ */
+
+/*
+ * Attribute: Standard information (0x10).
+ *
+ * NOTE: Always resident.
+ * NOTE: Present in all base file records on a volume.
+ * NOTE: There is conflicting information about the meaning of each of the time
+ * 	 fields but the meaning as defined below has been verified to be
+ * 	 correct by practical experimentation on Windows NT4 SP6a and is hence
+ * 	 assumed to be the one and only correct interpretation.
+ */
+typedef struct {
+/*Ofs*/
+/*  0*/	s64 creation_time;		/* Time file was created. Updated when
+					   a filename is changed(?). */
+/*  8*/	s64 last_data_change_time;	/* Time the data attribute was last
+					   modified. */
+/* 16*/	s64 last_mft_change_time;	/* Time this mft record was last
+					   modified. */
+/* 24*/	s64 last_access_time;		/* Approximate time when the file was
+					   last accessed (obviously this is not
+					   updated on read-only volumes). In
+					   Windows this is only updated when
+					   accessed if some time delta has
+					   passed since the last update. Also,
+					   last access times updates can be
+					   disabled altogether for speed. */
+/* 32*/	FILE_ATTR_FLAGS file_attributes; /* Flags describing the file. */
+/* 36*/	union {
+		/* NTFS 1.2 (and previous, presumably) */
+/* 36 */	u8 reserved12[12];	/* Reserved/alignment to 8-byte
+					   boundary. */
+/* sizeof() = 48 bytes */
+		/* NTFS 3.0 */
+		struct {
+/*
+ * If a volume has been upgraded from a previous NTFS version, then these
+ * fields are present only if the file has been accessed since the upgrade.
+ * Recognize the difference by comparing the length of the resident attribute
+ * value. If it is 48, then the following fields are missing. If it is 72 then
+ * the fields are present. Maybe just check like this:
+ * 	if (resident.ValueLength < sizeof(STANDARD_INFORMATION)) {
+ * 		Assume NTFS 1.2- format.
+ * 		If (volume version is 3.0+)
+ * 			Upgrade attribute to NTFS 3.0 format.
+ * 		else
+ * 			Use NTFS 1.2- format for access.
+ * 	} else
+ * 		Use NTFS 3.0 format for access.
+ * Only problem is that it might be legal to set the length of the value to
+ * arbitrarily large values thus spoiling this check. - But chkdsk probably
+ * views that as a corruption, assuming that it behaves like this for all
+ * attributes.
+ */
+		/* 36*/	u32 maximum_versions;	/* Maximum allowed versions for
+				file. Zero if version numbering is disabled. */
+		/* 40*/	u32 version_number;	/* This file's version (if any).
+				Set to zero if maximum_versions is zero. */
+		/* 44*/	u32 class_id;		/* Class id from bidirectional
+				class id index (?). */
+		/* 48*/	u32 owner_id;		/* Owner_id of the user owning
+				the file. Translate via $Q index in FILE_Extend
+				/$Quota to the quota control entry for the user
+				owning the file. Zero if quotas are disabled. */
+		/* 52*/	u32 security_id;	/* Security_id for the file.
+				Translate via $SII index and $SDS data stream
+				in FILE_Secure to the security descriptor. */
+		/* 56*/	u64 quota_charged;	/* Byte size of the charge to
+				the quota for all streams of the file. Note: Is
+				zero if quotas are disabled. */
+		/* 64*/	u64 usn;		/* Last update sequence number
+				of the file. This is a direct index into the
+				change (aka usn) journal file. It is zero if
+				the usn journal is disabled.
+				NOTE: To disable the journal need to delete
+				the journal file itself and to then walk the
+				whole mft and set all Usn entries in all mft
+				records to zero! (This can take a while!)
+				The journal is FILE_Extend/$UsnJrnl. Win2k
+				will recreate the journal and initiate
+				logging if necessary when mounting the
+				partition. This, in contrast to disabling the
+				journal is a very fast process, so the user
+				won't even notice it. */
+		} SN(svs);
+	} SN(sei);
+/* sizeof() = 72 bytes (NTFS 3.0) */
+} __attribute__ ((__packed__)) STANDARD_INFORMATION;
+
+#define _SVS(X)  SC(sei.svs,X)
+
+/*
+ * Attribute: Attribute list (0x20).
+ *
+ * - Can be either resident or non-resident.
+ * - Value consists of a sequence of variable length, 8-byte aligned,
+ * ATTR_LIST_ENTRY records.
+ * - The list is not terminated by anything at all! The only way to know when
+ * the end is reached is to keep track of the current offset and compare it to
+ * the attribute value size.
+ * - The attribute list attribute contains one entry for each attribute of
+ * the file in which the list is located, except for the list attribute
+ * itself. The list is sorted: first by attribute type, second by attribute
+ * name (if present), third by instance number. The extents of one
+ * non-resident attribute (if present) immediately follow after the initial
+ * extent. They are ordered by lowest_vcn and have their instace set to zero. 
+ * It is not allowed to have two attributes with all sorting keys equal.
+ * - Further restrictions: 
+ * 	- If not resident, the vcn to lcn mapping array has to fit inside the
+ * 	  base mft record.
+ * 	- The attribute list attribute value has a maximum size of 256kb. This
+ * 	  is imposed by the Windows cache manager.
+ * - Attribute lists are only used when the attributes of mft record do not
+ * fit inside the mft record despite all attributes (that can be made
+ * non-resident) having been made non-resident. This can happen e.g. when:
+ *  	- File has a large number of hard links (lots of file name
+ *  	  attributes present).
+ *  	- The mapping pairs array of some non-resident attribute becomes so
+ *	  large due to fragmentation that it overflows the mft record.
+ *  	- The security descriptor is very complex (not applicable to
+ *  	  NTFS 3.0 volumes).
+ *  	- There are many named streams.
+ */
+typedef struct {
+/*Ofs*/
+/*  0*/	ATTR_TYPES type;	/* Type of referenced attribute. */
+/*  4*/	u16 length;		/* Byte size of this entry (8-byte aligned). */
+/*  6*/	u8 name_length;		/* Size in Unicode chars of the name of the
+				   attribute or 0 if unnamed. */
+/*  7*/	u8 name_offset;		/* Byte offset to beginning of attribute name
+				   (always set this to where the name would
+				   start even if unnamed). */
+/*  8*/	VCN lowest_vcn;		/* Lowest virtual cluster number of this portion
+				   of the attribute value. This is usually 0. It
+				   is non-zero for the case where one attribute
+				   does not fit into one mft record and thus
+				   several mft records are allocated to hold
+				   this attribute. In the latter case, each mft
+				   record holds one extent of the attribute and
+				   there is one attribute list entry for each
+				   extent. NOTE: This is DEFINITELY a signed
+				   value! The windows driver uses cmp, followed
+				   by jg when comparing this, thus it treats it
+				   as signed. */
+/* 16*/	MFT_REF mft_reference;	/* The reference of the mft record holding
+				   the ATTR_RECORD for this portion of the
+				   attribute value. */
+/* 24*/	u16 instance;		/* If lowest_vcn = 0, the instance of the
+				   attribute being referenced; otherwise 0. */
+/* 26*/	uchar_t name[0];	/* Use when creating only. When reading use
+				   name_offset to determine the location of the
+				   name. */
+/* sizeof() = 26 + (attribute_name_length * 2) bytes */
+} __attribute__ ((__packed__)) ATTR_LIST_ENTRY;
+
+/*
+ * The maximum allowed length for a file name.
+ */
+#define MAXIMUM_FILE_NAME_LENGTH	255
+
+/*
+ * Possible namespaces for filenames in ntfs (8-bit).
+ */
+typedef enum {
+	FILE_NAME_POSIX			= 0x00,
+		/* This is the largest namespace. It is case sensitive and 
+		   allows all Unicode characters except for: '\0' and '/'.
+		   Beware that in WinNT/2k files which eg have the same name
+		   except for their case will not be distinguished by the
+		   standard utilities and thus a "del filename" will delete
+		   both "filename" and "fileName" without warning. */
+	FILE_NAME_WIN32			= 0x01,
+		/* The standard WinNT/2k NTFS long filenames. Case insensitive.
+		   All Unicode chars except: '\0', '"', '*', '/', ':', '<', 
+		   '>', '?', '\' and '|'. Further, names cannot end with a '.'
+		   or a space. */
+	FILE_NAME_DOS			= 0x02,
+		/* The standard DOS filenames (8.3 format). Uppercase only.
+		   All 8-bit characters greater space, except: '"', '*', '+',
+		   ',', '/', ':', ';', '<', '=', '>', '?' and '\'. */
+	FILE_NAME_WIN32_AND_DOS		= 0x03, 
+		/* 3 means that both the Win32 and the DOS filenames are
+		   identical and hence have been saved in this single filename
+		   record. */
+} __attribute__ ((__packed__)) FILE_NAME_TYPE_FLAGS;
+
+/*
+ * Attribute: Filename (0x30).
+ *
+ * NOTE: Always resident.
+ * NOTE: All fields, except the parent_directory, are only updated when the
+ *	 filename is changed. Until then, they just become out of sync with
+ *	 reality and the more up to date values are present in the standard
+ *	 information attribute.
+ * NOTE: There is conflicting information about the meaning of each of the time
+ * 	 fields but the meaning as defined below has been verified to be
+ * 	 correct by practical experimentation on Windows NT4 SP6a and is hence
+ * 	 assumed to be the one and only correct interpretation.
+ */
+typedef struct {
+/*hex ofs*/
+/*  0*/	MFT_REF parent_directory;	/* Directory this filename is
+					   referenced from. */
+/*  8*/	s64 creation_time;		/* Time file was created. */
+/* 10*/	s64 last_data_change_time;	/* Time the data attribute was last
+					   modified. */
+/* 18*/	s64 last_mft_change_time;	/* Time this mft record was last
+					   modified. */
+/* 20*/	s64 last_access_time;		/* Last time this mft record was
+					   accessed. */
+/* 28*/	s64 allocated_size;		/* Byte size of allocated space for the
+					   data attribute. NOTE: Is a multiple
+					   of the cluster size. */
+/* 30*/	s64 data_size;			/* Byte size of actual data in data
+					   attribute. NOTE: Only present when
+					   lowest_vcn is 0. */
+/* 38*/	FILE_ATTR_FLAGS file_attributes;	/* Flags describing the file. */
+/* 3c*/	union {
+	/* 3c*/	struct {
+		/* 3c*/	u16 packed_ea_size;	/* Size of the buffer needed to
+						   pack the extended attributes
+						   (EAs), if such are present.*/
+		/* 3e*/	u16 reserved;		/* Reserved for alignment. */
+		} SN(fea) __attribute__ ((__packed__));
+	/* 3c*/	u32 reparse_point_tag;		/* Type of reparse point,
+						   present only in reparse
+						   points and only if there are
+						   no EAs. */
+	} SN(fer) __attribute__ ((__packed__));
+/* 40*/	u8 file_name_length;			/* Length of file name in
+						   (Unicode) characters. */
+/* 41*/	FILE_NAME_TYPE_FLAGS file_name_type;	/* Namespace of the file name.*/
+/* 42*/	uchar_t file_name[0];			/* File name in Unicode. */
+} __attribute__ ((__packed__)) FILE_NAME_ATTR;
+
+#define _FEA(X)  SC(fer.fea,X)
+#define _FER(X)  SC(fer,X)
+
+/*
+ * GUID structures store globally unique identifiers (GUID). A GUID is a 
+ * 128-bit value consisting of one group of eight hexadecimal digits, followed
+ * by three groups of four hexadecimal digits each, followed by one group of
+ * twelve hexadecimal digits. GUIDs are Microsoft's implementation of the
+ * distributed computing environment (DCE) universally unique identifier (UUID).
+ * Example of a GUID:
+ * 	1F010768-5A73-BC91-0010A52216A7
+ */
+typedef struct {
+	u32 data1;	/* The first eight hexadecimal digits of the GUID. */
+	u16 data2;	/* The first group of four hexadecimal digits. */
+	u16 data3;	/* The second group of four hexadecimal digits. */
+	u8 data4[8];	/* The first two bytes are the third group of four
+			   hexadecimal digits. The remaining six bytes are the
+			   final 12 hexadecimal digits. */
+} __attribute__ ((__packed__)) GUID;
+
+/*
+ * FILE_Extend/$ObjId contains an index named $O. This index contains all
+ * object_ids present on the volume as the index keys and the corresponding
+ * mft_record numbers as the index entry data parts. The data part (defined
+ * below) also contains three other object_ids:
+ *	birth_volume_id - object_id of FILE_Volume on which the file was first
+ *			  created. Optional (i.e. can be zero).
+ *	birth_object_id - object_id of file when it was first created. Usually
+ *			  equals the object_id. Optional (i.e. can be zero).
+ *	domain_id	- Reserved (always zero).
+ */
+typedef struct {
+	MFT_REF mft_reference;	/* Mft record containing the object_id in
+				   the index entry key. */
+	union {
+		struct {
+			GUID birth_volume_id;
+			GUID birth_object_id;
+			GUID domain_id;
+		} SN(obv) __attribute__ ((__packed__));
+		u8 extended_info[48];
+	} SN(oei) __attribute__ ((__packed__));
+} __attribute__ ((__packed__)) OBJ_ID_INDEX_DATA;
+
+/*
+ * Attribute: Object id (NTFS 3.0+) (0x40).
+ *
+ * NOTE: Always resident.
+ */
+typedef struct {
+	GUID object_id;				/* Unique id assigned to the
+						   file.*/
+	/* The following fields are optional. The attribute value size is 16
+	   bytes, i.e. sizeof(GUID), if these are not present at all. Note,
+	   the entries can be present but one or more (or all) can be zero
+	   meaning that that particular value(s) is(are) not defined. */
+	union {
+		struct {
+			GUID birth_volume_id;	/* Unique id of volume on which
+						   the file was first created.*/
+			GUID birth_object_id;	/* Unique id of file when it was
+						   first created. */
+			GUID domain_id;		/* Reserved, zero. */
+		} SN(obv) __attribute__ ((__packed__));
+		u8 extended_info[48];
+	} SN(oei) __attribute__ ((__packed__));
+} __attribute__ ((__packed__)) OBJECT_ID_ATTR;
+
+#define _OBV(X)  SC(oei.obv,X)
+
+/*
+ * The pre-defined IDENTIFIER_AUTHORITIES used as SID_IDENTIFIER_AUTHORITY in
+ * the SID structure (see below).
+ */
+//typedef enum {					/* SID string prefix. */
+//	SECURITY_NULL_SID_AUTHORITY	= {0, 0, 0, 0, 0, 0},	/* S-1-0 */
+//	SECURITY_WORLD_SID_AUTHORITY	= {0, 0, 0, 0, 0, 1},	/* S-1-1 */
+//	SECURITY_LOCAL_SID_AUTHORITY 	= {0, 0, 0, 0, 0, 2},	/* S-1-2 */
+//	SECURITY_CREATOR_SID_AUTHORITY	= {0, 0, 0, 0, 0, 3},	/* S-1-3 */
+//	SECURITY_NON_UNIQUE_AUTHORITY	= {0, 0, 0, 0, 0, 4},	/* S-1-4 */
+//	SECURITY_NT_SID_AUTHORITY	= {0, 0, 0, 0, 0, 5},	/* S-1-5 */
+//} IDENTIFIER_AUTHORITIES;
+
+/*
+ * These relative identifiers (RIDs) are used with the above identifier
+ * authorities to make up universal well-known SIDs.
+ * 	
+ * Note: The relative identifier (RID) refers to the portion of a SID, which
+ * identifies a user or group in relation to the authority that issued the SID.
+ * For example, the universal well-known SID Creator Owner ID (S-1-3-0) is
+ * made up of the identifier authority SECURITY_CREATOR_SID_AUTHORITY (3) and
+ * the relative identifier SECURITY_CREATOR_OWNER_RID (0).
+ */
+typedef enum {					/* Identifier authority. */
+	SECURITY_NULL_RID		  = 0,	/* S-1-0 */
+	SECURITY_WORLD_RID		  = 0,	/* S-1-1 */
+	SECURITY_LOCAL_RID		  = 0,	/* S-1-2 */
+	
+	SECURITY_CREATOR_OWNER_RID	  = 0,	/* S-1-3 */
+	SECURITY_CREATOR_GROUP_RID	  = 1,	/* S-1-3 */
+
+	SECURITY_CREATOR_OWNER_SERVER_RID = 2,	/* S-1-3 */
+	SECURITY_CREATOR_GROUP_SERVER_RID = 3,	/* S-1-3 */
+
+	SECURITY_DIALUP_RID		  = 1,
+	SECURITY_NETWORK_RID		  = 2,
+	SECURITY_BATCH_RID		  = 3,
+	SECURITY_INTERACTIVE_RID	  = 4,
+	SECURITY_SERVICE_RID		  = 6,
+	SECURITY_ANONYMOUS_LOGON_RID	  = 7,
+	SECURITY_PROXY_RID		  = 8,
+	SECURITY_ENTERPRISE_CONTROLLERS_RID=9,
+	SECURITY_SERVER_LOGON_RID	  = 9,
+	SECURITY_PRINCIPAL_SELF_RID	  = 0xa,
+	SECURITY_AUTHENTICATED_USER_RID	  = 0xb,
+	SECURITY_RESTRICTED_CODE_RID	  = 0xc,
+	SECURITY_TERMINAL_SERVER_RID	  = 0xd,
+	
+	SECURITY_LOGON_IDS_RID		  = 5,
+	SECURITY_LOGON_IDS_RID_COUNT	  = 3,
+	
+	SECURITY_LOCAL_SYSTEM_RID	  = 0x12,
+
+	SECURITY_NT_NON_UNIQUE		  = 0x15,
+
+	SECURITY_BUILTIN_DOMAIN_RID	  = 0x20,
+
+	/*
+	 * Well-known domain relative sub-authority values (RIDs).
+	 */
+	
+	/* Users. */
+	DOMAIN_USER_RID_ADMIN		  = 0x1f4,
+	DOMAIN_USER_RID_GUEST		  = 0x1f5,
+	DOMAIN_USER_RID_KRBTGT		  = 0x1f6,
+	
+	/* Groups. */
+	DOMAIN_GROUP_RID_ADMINS		  = 0x200,
+	DOMAIN_GROUP_RID_USERS		  = 0x201,
+	DOMAIN_GROUP_RID_GUESTS		  = 0x202,
+	DOMAIN_GROUP_RID_COMPUTERS	  = 0x203,
+	DOMAIN_GROUP_RID_CONTROLLERS	  = 0x204,
+	DOMAIN_GROUP_RID_CERT_ADMINS	  = 0x205,
+	DOMAIN_GROUP_RID_SCHEMA_ADMINS	  = 0x206,
+	DOMAIN_GROUP_RID_ENTERPRISE_ADMINS= 0x207,
+	DOMAIN_GROUP_RID_POLICY_ADMINS	  = 0x208,
+
+	/* Aliases. */
+	DOMAIN_ALIAS_RID_ADMINS		  = 0x220,
+	DOMAIN_ALIAS_RID_USERS		  = 0x221,
+	DOMAIN_ALIAS_RID_GUESTS		  = 0x222,
+	DOMAIN_ALIAS_RID_POWER_USERS	  = 0x223,
+	
+	DOMAIN_ALIAS_RID_ACCOUNT_OPS	  = 0x224,
+	DOMAIN_ALIAS_RID_SYSTEM_OPS	  = 0x225,
+	DOMAIN_ALIAS_RID_PRINT_OPS	  = 0x226,
+	DOMAIN_ALIAS_RID_BACKUP_OPS	  = 0x227,
+	
+	DOMAIN_ALIAS_RID_REPLICATOR	  = 0x228,
+	DOMAIN_ALIAS_RID_RAS_SERVERS	  = 0x229,
+	DOMAIN_ALIAS_RID_PREW2KCOMPACCESS = 0x22a,
+} RELATIVE_IDENTIFIERS;
+
+/*
+ * The universal well-known SIDs:
+ *
+ * 	NULL_SID			S-1-0-0
+ * 	WORLD_SID			S-1-1-0
+ * 	LOCAL_SID			S-1-2-0
+ * 	CREATOR_OWNER_SID		S-1-3-0
+ * 	CREATOR_GROUP_SID		S-1-3-1
+ * 	CREATOR_OWNER_SERVER_SID	S-1-3-2
+ * 	CREATOR_GROUP_SERVER_SID	S-1-3-3
+ *
+ * 	(Non-unique IDs)		S-1-4
+ *
+ * NT well-known SIDs:
+ * 
+ * 	NT_AUTHORITY_SID	S-1-5
+ * 	DIALUP_SID		S-1-5-1
+ *
+ * 	NETWORD_SID		S-1-5-2
+ * 	BATCH_SID		S-1-5-3
+ * 	INTERACTIVE_SID		S-1-5-4
+ * 	SERVICE_SID		S-1-5-6
+ * 	ANONYMOUS_LOGON_SID	S-1-5-7		(aka null logon session)
+ * 	PROXY_SID		S-1-5-8
+ * 	SERVER_LOGON_SID	S-1-5-9		(aka domain controller account)
+ * 	SELF_SID		S-1-5-10	(self RID)
+ * 	AUTHENTICATED_USER_SID	S-1-5-11
+ * 	RESTRICTED_CODE_SID	S-1-5-12	(running restricted code)
+ * 	TERMINAL_SERVER_SID	S-1-5-13	(running on terminal server)
+ *
+ * 	(Logon IDs)		S-1-5-5-X-Y
+ *
+ * 	(NT non-unique IDs)	S-1-5-0x15-...
+ *
+ * 	(Built-in domain)	S-1-5-0x20
+ */
+
+/*
+ * The SID_IDENTIFIER_AUTHORITY is a 48-bit value used in the SID structure.
+ */
+typedef union {
+	struct {
+		u32 low_part;         /* Low 32-bits. */
+		u16 high_part;        /* High 16-bits. */
+	} SN(sia) __attribute__ ((__packed__));
+	u8 value[6];			/* Value as individual bytes. */
+} __attribute__ ((__packed__)) SID_IDENTIFIER_AUTHORITY;
+
+#define _SIA(X)  SC(sia,X)
+
+/*
+ * The SID structure is a variable-length structure used to uniquely identify
+ * users or groups. SID stands for security identifier.
+ * 
+ * The standard textual representation of the SID is of the form:
+ * 	S-R-I-S-S...
+ * Where:
+ *    - The first "S" is the literal character 'S' identifying the following
+ * 	digits as a SID.
+ *    - R is the revision level of the SID expressed as a sequence of digits
+ *	either in decimal or hexadecimal (if the later, prefixed by "0x").
+ *    - I is the 48-bit identifier_authority, expressed as digits as R above.
+ *    - S... is one or more sub_authority values, expressed as digits as above.
+ *    
+ * Example SID; the domain-relative SID of the local Administrators group on
+ * Windows NT/2k:
+ * 	S-1-5-32-544
+ * This translates to a SID with:
+ * 	revision = 1,
+ * 	sub_authority_count = 2,
+ * 	identifier_authority = {0,0,0,0,0,5},	// SECURITY_NT_AUTHORITY
+ * 	sub_authority[0] = 32,			// SECURITY_BUILTIN_DOMAIN_RID
+ * 	sub_authority[1] = 544			// DOMAIN_ALIAS_RID_ADMINS
+ */
+typedef struct {
+	u8 revision;
+	u8 sub_authority_count;
+	SID_IDENTIFIER_AUTHORITY identifier_authority;
+	u32 sub_authority[1];		/* At least one sub_authority. */
+} __attribute__ ((__packed__)) SID;
+
+/*
+ * Current constants for SIDs.
+ */
+typedef enum {
+	SID_REVISION			=  1,	/* Current revision level. */
+	SID_MAX_SUB_AUTHORITIES		= 15,	/* Maximum number of those. */
+	SID_RECOMMENDED_SUB_AUTHORITIES	=  1,	/* Will change to around 6 in
+						   a future revision. */
+} SID_CONSTANTS;
+
+/*
+ * The predefined ACE types (8-bit, see below).
+ */
+typedef enum {
+	ACCESS_MIN_MS_ACE_TYPE		= 0,
+	ACCESS_ALLOWED_ACE_TYPE		= 0,
+	ACCESS_DENIED_ACE_TYPE		= 1,
+	SYSTEM_AUDIT_ACE_TYPE		= 2,
+	SYSTEM_ALARM_ACE_TYPE		= 3, /* Not implemented as of Win2k. */
+	ACCESS_MAX_MS_V2_ACE_TYPE	= 3,
+
+	ACCESS_ALLOWED_COMPOUND_ACE_TYPE= 4,
+	ACCESS_MAX_MS_V3_ACE_TYPE	= 4,
+	
+	/* The following are Win2k only. */
+	ACCESS_MIN_MS_OBJECT_ACE_TYPE	= 5,
+	ACCESS_ALLOWED_OBJECT_ACE_TYPE	= 5,
+	ACCESS_DENIED_OBJECT_ACE_TYPE	= 6,
+	SYSTEM_AUDIT_OBJECT_ACE_TYPE	= 7,
+	SYSTEM_ALARM_OBJECT_ACE_TYPE	= 8,
+	ACCESS_MAX_MS_OBJECT_ACE_TYPE	= 8,
+
+	ACCESS_MAX_MS_V4_ACE_TYPE	= 8,
+
+	/* This one is for WinNT&2k. */
+	ACCESS_MAX_MS_ACE_TYPE		= 8,
+} __attribute__ ((__packed__)) ACE_TYPES;
+
+/*
+ * The ACE flags (8-bit) for audit and inheritance (see below).
+ *
+ * SUCCESSFUL_ACCESS_ACE_FLAG is only used with system audit and alarm ACE
+ * types to indicate that a message is generated (in Windows!) for successful
+ * accesses.
+ *
+ * FAILED_ACCESS_ACE_FLAG is only used with system audit and alarm ACE types
+ * to indicate that a message is generated (in Windows!) for failed accesses.
+ */
+typedef enum {
+	/* The inheritance flags. */
+	OBJECT_INHERIT_ACE		= 0x01,
+	CONTAINER_INHERIT_ACE		= 0x02,
+	NO_PROPAGATE_INHERIT_ACE	= 0x04,
+	INHERIT_ONLY_ACE		= 0x08,
+	INHERITED_ACE			= 0x10,	/* Win2k only. */
+	VALID_INHERIT_FLAGS		= 0x1f,
+
+	/* The audit flags. */
+	SUCCESSFUL_ACCESS_ACE_FLAG	= 0x40,
+	FAILED_ACCESS_ACE_FLAG		= 0x80,
+} __attribute__ ((__packed__)) ACE_FLAGS;
+
+/*
+ * An ACE is an access-control entry in an access-control list (ACL).
+ * An ACE defines access to an object for a specific user or group or defines
+ * the types of access that generate system-administration messages or alarms
+ * for a specific user or group. The user or group is identified by a security
+ * identifier (SID).
+ *
+ * Each ACE starts with an ACE_HEADER structure (aligned on 4-byte boundary),
+ * which specifies the type and size of the ACE. The format of the subsequent
+ * data depends on the ACE type.
+ */
+typedef struct {
+	ACE_TYPES type;		/* Type of the ACE. */
+	ACE_FLAGS flags;	/* Flags describing the ACE. */
+	u16 size;		/* Size in bytes of the ACE. */
+} __attribute__ ((__packed__)) ACE_HEADER;
+
+/*
+ * The access mask (32-bit). Defines the access rights.
+ */
+typedef enum {
+	/*
+	 * The specific rights (bits 0 to 15). Depend on the type of the
+	 * object being secured by the ACE.
+	 */
+
+	/* Specific rights for files and directories are as follows: */
+	
+	/* Right to read data from the file. (FILE) */
+	FILE_READ_DATA			= const_cpu_to_le32(0x00000001),
+	/* Right to list contents of a directory. (DIRECTORY) */
+	FILE_LIST_DIRECTORY		= const_cpu_to_le32(0x00000001),
+	
+	/* Right to write data to the file. (FILE) */
+	FILE_WRITE_DATA			= const_cpu_to_le32(0x00000002),
+	/* Right to create a file in the directory. (DIRECTORY) */
+	FILE_ADD_FILE			= const_cpu_to_le32(0x00000002),
+	
+	/* Right to append data to the file. (FILE) */
+	FILE_APPEND_DATA		= const_cpu_to_le32(0x00000004),
+	/* Right to create a subdirectory. (DIRECTORY) */
+	FILE_ADD_SUBDIRECTORY		= const_cpu_to_le32(0x00000004),
+	
+	/* Right to read extended attributes. (FILE/DIRECTORY) */
+	FILE_READ_EA			= const_cpu_to_le32(0x00000008),
+	
+	/* Right to write extended attributes. (FILE/DIRECTORY) */
+	FILE_WRITE_EA			= const_cpu_to_le32(0x00000010),
+	
+	/* Right to execute a file. (FILE) */
+	FILE_EXECUTE			= const_cpu_to_le32(0x00000020),
+	/* Right to traverse the directory. (DIRECTORY) */
+	FILE_TRAVERSE			= const_cpu_to_le32(0x00000020),
+	
+	/*
+	 * Right to delete a directory and all the files it contains (its
+	 * children), even if the files are read-only. (DIRECTORY)
+	 */
+	FILE_DELETE_CHILD		= const_cpu_to_le32(0x00000040),
+
+	/* Right to read file attributes. (FILE/DIRECTORY) */
+	FILE_READ_ATTRIBUTES		= const_cpu_to_le32(0x00000080),
+	
+	/* Right to change file attributes. (FILE/DIRECTORY) */
+	FILE_WRITE_ATTRIBUTES		= const_cpu_to_le32(0x00000100),
+	
+	/*
+	 * The standard rights (bits 16 to 23). Are independent of the type of
+	 * object being secured.
+	 */
+
+	/* Right to delete the object. */
+	DELETE				= const_cpu_to_le32(0x00010000),
+
+	/*
+	 * Right to read the information in the object's security descriptor,
+	 * not including the information in the SACL. I.e. right to read the
+	 * security descriptor and owner.
+	 */
+	READ_CONTROL			= const_cpu_to_le32(0x00020000),
+
+	/* Right to modify the DACL in the object's security descriptor. */
+	WRITE_DAC			= const_cpu_to_le32(0x00040000),
+
+	/* Right to change the owner in the object's security descriptor. */
+	WRITE_OWNER			= const_cpu_to_le32(0x00080000),
+
+	/*
+	 * Right to use the object for synchronization. Enables a process to
+	 * wait until the object is in the signalled state. Some object types
+	 * do not support this access right.
+	 */
+	SYNCHRONIZE			= const_cpu_to_le32(0x00100000),
+
+	/*
+	 * The following STANDARD_RIGHTS_* are combinations of the above for
+	 * convenience and are defined by the Win32 API.
+	 */
+	
+	/* These are currently defined to READ_CONTROL. */
+	STANDARD_RIGHTS_READ		= const_cpu_to_le32(0x00020000),
+	STANDARD_RIGHTS_WRITE		= const_cpu_to_le32(0x00020000),
+	STANDARD_RIGHTS_EXECUTE		= const_cpu_to_le32(0x00020000),
+	
+	/* Combines DELETE, READ_CONTROL, WRITE_DAC, and WRITE_OWNER access. */
+	STANDARD_RIGHTS_REQUIRED	= const_cpu_to_le32(0x000f0000),
+	
+	/*
+	 * Combines DELETE, READ_CONTROL, WRITE_DAC, WRITE_OWNER, and
+	 * SYNCHRONIZE access.
+	 */
+	STANDARD_RIGHTS_ALL		= const_cpu_to_le32(0x001f0000),
+
+	/*
+	 * The access system ACL and maximum allowed access types (bits 24 to
+	 * 25, bits 26 to 27 are reserved).
+	 */
+	ACCESS_SYSTEM_SECURITY		= const_cpu_to_le32(0x01000000),
+	MAXIMUM_ALLOWED			= const_cpu_to_le32(0x02000000),
+
+	/*
+	 * The generic rights (bits 28 to 31). These map onto the standard and
+	 * specific rights.
+	 */
+
+	/* Read, write, and execute access. */
+	GENERIC_ALL			= const_cpu_to_le32(0x10000000),
+
+	/* Execute access. */
+	GENERIC_EXECUTE			= const_cpu_to_le32(0x20000000),
+
+	/*
+	 * Write access. For files, this maps onto:
+	 *	FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_DATA |
+	 *	FILE_WRITE_EA | STANDARD_RIGHTS_WRITE | SYNCHRONIZE
+	 * For directories, the mapping has the same numberical value. See
+	 * above for the descriptions of the rights granted.
+	 */
+	GENERIC_WRITE			= const_cpu_to_le32(0x40000000),
+
+	/*
+	 * Read access. For files, this maps onto:
+	 *	FILE_READ_ATTRIBUTES | FILE_READ_DATA | FILE_READ_EA |
+	 *	STANDARD_RIGHTS_READ | SYNCHRONIZE
+	 * For directories, the mapping has the same numberical value. See
+	 * above for the descriptions of the rights granted.
+	 */
+	GENERIC_READ			= const_cpu_to_le32(0x80000000),
+} ACCESS_MASK;
+
+/*
+ * The generic mapping array. Used to denote the mapping of each generic
+ * access right to a specific access mask.
+ * 
+ * FIXME: What exactly is this and what is it for? (AIA)
+ */
+typedef struct {
+	ACCESS_MASK generic_read;
+	ACCESS_MASK generic_write;
+	ACCESS_MASK generic_execute;
+	ACCESS_MASK generic_all;
+} __attribute__ ((__packed__)) GENERIC_MAPPING;
+
+/*
+ * The predefined ACE type structures are as defined below.
+ */
+
+/*
+ * ACCESS_ALLOWED_ACE, ACCESS_DENIED_ACE, SYSTEM_AUDIT_ACE, SYSTEM_ALARM_ACE
+ */
+typedef struct {
+	ACE_HEADER SN(aah);		/* The ACE header. */
+	ACCESS_MASK mask;	/* Access mask associated with the ACE. */
+	SID sid;		/* The SID associated with the ACE. */
+} __attribute__ ((__packed__)) ACCESS_ALLOWED_ACE, ACCESS_DENIED_ACE,
+			       SYSTEM_AUDIT_ACE, SYSTEM_ALARM_ACE;
+#define _AAH(X)  SC(aah,X)
+
+/*
+ * The object ACE flags (32-bit).
+ */
+typedef enum {
+	ACE_OBJECT_TYPE_PRESENT			= const_cpu_to_le32(1),
+	ACE_INHERITED_OBJECT_TYPE_PRESENT	= const_cpu_to_le32(2),
+} OBJECT_ACE_FLAGS;
+
+typedef struct {
+	ACE_HEADER SN(aah);	/* The ACE_HEADER. */
+	ACCESS_MASK mask;	/* Access mask associated with the ACE. */
+	OBJECT_ACE_FLAGS flags;	/* Flags describing the object ACE. */
+	GUID object_type;
+	GUID inherited_object_type;
+	SID sid;		/* The SID associated with the ACE. */
+} __attribute__ ((__packed__)) ACCESS_ALLOWED_OBJECT_ACE,
+			       ACCESS_DENIED_OBJECT_ACE,
+			       SYSTEM_AUDIT_OBJECT_ACE,
+			       SYSTEM_ALARM_OBJECT_ACE;
+
+/*
+ * An ACL is an access-control list (ACL).
+ * An ACL starts with an ACL header structure, which specifies the size of
+ * the ACL and the number of ACEs it contains. The ACL header is followed by
+ * zero or more access control entries (ACEs). The ACL as well as each ACE
+ * are aligned on 4-byte boundaries.
+ */
+typedef struct {
+	u8 revision;	/* Revision of this ACL. */
+	u8 alignment1;
+	u16 size;	/* Allocated space in bytes for ACL. Includes this
+			   header, the ACEs and the remaining free space. */
+	u16 ace_count;	/* Number of ACEs in the ACL. */
+	u16 alignment2;
+/* sizeof() = 8 bytes */
+} __attribute__ ((__packed__)) ACL;
+
+/*
+ * Current constants for ACLs.
+ */
+typedef enum {
+	/* Current revision. */
+	ACL_REVISION		= 2,
+	ACL_REVISION_DS		= 4,
+
+	/* History of revisions. */
+	ACL_REVISION1		= 1,
+	MIN_ACL_REVISION	= 2,
+	ACL_REVISION2		= 2,
+	ACL_REVISION3		= 3,
+	ACL_REVISION4		= 4,
+	MAX_ACL_REVISION	= 4,
+} ACL_CONSTANTS;
+
+/*
+ * The security descriptor control flags (16-bit).
+ *
+ * SE_OWNER_DEFAULTED - This boolean flag, when set, indicates that the
+ *          SID pointed to by the Owner field was provided by a
+ *          defaulting mechanism rather than explicitly provided by the
+ *          original provider of the security descriptor.  This may
+ *          affect the treatment of the SID with respect to inheritence
+ *          of an owner.
+ *
+ * SE_GROUP_DEFAULTED - This boolean flag, when set, indicates that the
+ *          SID in the Group field was provided by a defaulting mechanism
+ *          rather than explicitly provided by the original provider of
+ *          the security descriptor.  This may affect the treatment of
+ *          the SID with respect to inheritence of a primary group.
+ *
+ * SE_DACL_PRESENT - This boolean flag, when set, indicates that the
+ *          security descriptor contains a discretionary ACL.  If this
+ *          flag is set and the Dacl field of the SECURITY_DESCRIPTOR is
+ *          null, then a null ACL is explicitly being specified.
+ *
+ * SE_DACL_DEFAULTED - This boolean flag, when set, indicates that the
+ *          ACL pointed to by the Dacl field was provided by a defaulting
+ *          mechanism rather than explicitly provided by the original
+ *          provider of the security descriptor.  This may affect the
+ *          treatment of the ACL with respect to inheritence of an ACL.
+ *          This flag is ignored if the DaclPresent flag is not set.
+ *
+ * SE_SACL_PRESENT - This boolean flag, when set,  indicates that the
+ *          security descriptor contains a system ACL pointed to by the
+ *          Sacl field.  If this flag is set and the Sacl field of the
+ *          SECURITY_DESCRIPTOR is null, then an empty (but present)
+ *          ACL is being specified.
+ *
+ * SE_SACL_DEFAULTED - This boolean flag, when set, indicates that the
+ *          ACL pointed to by the Sacl field was provided by a defaulting
+ *          mechanism rather than explicitly provided by the original
+ *          provider of the security descriptor.  This may affect the
+ *          treatment of the ACL with respect to inheritence of an ACL.
+ *          This flag is ignored if the SaclPresent flag is not set.
+ *
+ * SE_SELF_RELATIVE - This boolean flag, when set, indicates that the
+ *          security descriptor is in self-relative form.  In this form,
+ *          all fields of the security descriptor are contiguous in memory
+ *          and all pointer fields are expressed as offsets from the
+ *          beginning of the security descriptor.
+ */
+typedef enum {
+	SE_OWNER_DEFAULTED		= const_cpu_to_le16(0x0001),
+	SE_GROUP_DEFAULTED		= const_cpu_to_le16(0x0002),
+	SE_DACL_PRESENT			= const_cpu_to_le16(0x0004),
+	SE_DACL_DEFAULTED		= const_cpu_to_le16(0x0008),
+	SE_SACL_PRESENT			= const_cpu_to_le16(0x0010),
+	SE_SACL_DEFAULTED		= const_cpu_to_le16(0x0020),
+	SE_DACL_AUTO_INHERIT_REQ	= const_cpu_to_le16(0x0100),
+	SE_SACL_AUTO_INHERIT_REQ	= const_cpu_to_le16(0x0200),
+	SE_DACL_AUTO_INHERITED		= const_cpu_to_le16(0x0400),
+	SE_SACL_AUTO_INHERITED		= const_cpu_to_le16(0x0800),
+	SE_DACL_PROTECTED		= const_cpu_to_le16(0x1000),
+	SE_SACL_PROTECTED		= const_cpu_to_le16(0x2000),
+	SE_RM_CONTROL_VALID		= const_cpu_to_le16(0x4000),
+	SE_SELF_RELATIVE		= const_cpu_to_le16(0x8000),
+} __attribute__ ((__packed__)) SECURITY_DESCRIPTOR_CONTROL;
+
+/*
+ * Self-relative security descriptor. Contains the owner and group SIDs as well
+ * as the sacl and dacl ACLs inside the security descriptor itself.
+ */
+typedef struct {
+	u8 revision;	/* Revision level of the security descriptor. */
+	u8 alignment;
+	SECURITY_DESCRIPTOR_CONTROL control; /* Flags qualifying the type of
+			   the descriptor as well as the following fields. */
+	u32 owner;	/* Byte offset to a SID representing an object's
+			   owner. If this is NULL, no owner SID is present in
+			   the descriptor. */
+	u32 group;	/* Byte offset to a SID representing an object's
+			   primary group. If this is NULL, no primary group
+			   SID is present in the descriptor. */
+	u32 sacl;	/* Byte offset to a system ACL. Only valid, if
+			   SE_SACL_PRESENT is set in the control field. If
+			   SE_SACL_PRESENT is set but sacl is NULL, a NULL ACL
+			   is specified. */
+	u32 dacl;	/* Byte offset to a discretionary ACL. Only valid, if 
+			   SE_DACL_PRESENT is set in the control field. If
+			   SE_DACL_PRESENT is set but dacl is NULL, a NULL ACL
+			   (unconditionally granting access) is specified. */
+/* sizeof() = 0x14 bytes */
+} __attribute__ ((__packed__)) SECURITY_DESCRIPTOR_RELATIVE;
+
+/*
+ * Absolute security descriptor. Does not contain the owner and group SIDs, nor
+ * the sacl and dacl ACLs inside the security descriptor. Instead, it contains
+ * pointers to these structures in memory. Obviously, absolute security
+ * descriptors are only useful for in memory representations of security
+ * descriptors. On disk, a self-relative security descriptor is used.
+ */
+typedef struct {
+	u8 revision;	/* Revision level of the security descriptor. */
+	u8 alignment;
+	SECURITY_DESCRIPTOR_CONTROL control;	/* Flags qualifying the type of 
+			   the descriptor as well as the following fields. */
+	SID *owner;	/* Points to a SID representing an object's owner. If
+			   this is NULL, no owner SID is present in the
+			   descriptor. */
+	SID *group;	/* Points to a SID representing an object's primary
+			   group. If this is NULL, no primary group SID is
+			   present in the descriptor. */
+	ACL *sacl;	/* Points to a system ACL. Only valid, if
+			   SE_SACL_PRESENT is set in the control field. If
+			   SE_SACL_PRESENT is set but sacl is NULL, a NULL ACL
+			   is specified. */
+	ACL *dacl;	/* Points to a discretionary ACL. Only valid, if 
+			   SE_DACL_PRESENT is set in the control field. If
+			   SE_DACL_PRESENT is set but dacl is NULL, a NULL ACL
+			   (unconditionally granting access) is specified. */
+} __attribute__ ((__packed__)) SECURITY_DESCRIPTOR;
+
+/*
+ * Current constants for security descriptors.
+ */
+typedef enum {
+	/* Current revision. */
+	SECURITY_DESCRIPTOR_REVISION	= 1,
+	SECURITY_DESCRIPTOR_REVISION1	= 1,
+
+	/* The sizes of both the absolute and relative security descriptors is
+	   the same as pointers, at least on ia32 architecture are 32-bit. */
+	SECURITY_DESCRIPTOR_MIN_LENGTH	= sizeof(SECURITY_DESCRIPTOR),
+} SECURITY_DESCRIPTOR_CONSTANTS;
+
+/*
+ * Attribute: Security descriptor (0x50). A standard self-relative security
+ * descriptor.
+ *
+ * NOTE: Can be resident or non-resident.
+ * NOTE: Not used in NTFS 3.0+, as security descriptors are stored centrally
+ * in FILE_Secure and the correct descriptor is found using the security_id
+ * from the standard information attribute.
+ */
+typedef SECURITY_DESCRIPTOR_RELATIVE SECURITY_DESCRIPTOR_ATTR;
+
+/*
+ * On NTFS 3.0+, all security descriptors are stored in FILE_Secure. Only one
+ * referenced instance of each unique security descriptor is stored.
+ * 
+ * FILE_Secure contains no unnamed data attribute, i.e. it has zero length. It
+ * does, however, contain two indexes ($SDH and $SII) as well as a named data
+ * stream ($SDS).
+ * 
+ * Every unique security descriptor is assigned a unique security identifier
+ * (security_id, not to be confused with a SID). The security_id is unique for
+ * the NTFS volume and is used as an index into the $SII index, which maps
+ * security_ids to the security descriptor's storage location within the $SDS
+ * data attribute. The $SII index is sorted by ascending security_id.
+ *
+ * A simple hash is computed from each security descriptor. This hash is used
+ * as an index into the $SDH index, which maps security descriptor hashes to
+ * the security descriptor's storage location within the $SDS data attribute.
+ * The $SDH index is sorted by security descriptor hash and is stored in a B+
+ * tree. When searching $SDH (with the intent of determining whether or not a
+ * new security descriptor is already present in the $SDS data stream), if a
+ * matching hash is found, but the security descriptors do not match, the
+ * search in the $SDH index is continued, searching for a next matching hash.
+ * 
+ * When a precise match is found, the security_id coresponding to the security
+ * descriptor in the $SDS attribute is read from the found $SDH index entry and
+ * is stored in the $STANDARD_INFORMATION attribute of the file/directory to
+ * which the security descriptor is being applied. The $STANDARD_INFORMATION
+ * attribute is present in all base mft records (i.e. in all files and 
+ * directories).
+ *
+ * If a match is not found, the security descriptor is assigned a new unique
+ * security_id and is added to the $SDS data attribute. Then, entries
+ * referencing the this security descriptor in the $SDS data attribute are
+ * added to the $SDH and $SII indexes.
+ *
+ * Note: Entries are never deleted from FILE_Secure, even if nothing
+ * references an entry any more.
+ */
+ 
+/*
+ * This header precedes each security descriptor in the $SDS data stream.
+ * This is also the index entry data part of both the $SII and $SDH indexes.
+ */
+typedef struct {
+	u32 hash;	   /* Hash of the security descriptor. */
+	u32 security_id;   /* The security_id assigned to the descriptor. */
+	u64 offset;	   /* Byte offset of this entry in the $SDS stream. */
+	u32 length;	   /* Size in bytes of this entry in $SDS stream. */
+} __attribute__ ((__packed__)) SECURITY_DESCRIPTOR_HEADER;
+
+/*
+ * The $SDS data stream contains the security descriptors, aligned on 16-byte
+ * boundaries, sorted by security_id in a B+ tree. Security descriptors cannot
+ * cross 256kib boundaries (this restriction is imposed by the Windows cache
+ * manager). Each security descriptor is contained in a SDS_ENTRY structure.
+ * Also, each security descriptor is stored twice in the $SDS stream with a
+ * fixed offset of 0x40000 bytes (256kib, the Windows cache manager's max size)
+ * between them; i.e. if a SDS_ENTRY specifies an offset of 0x51d0, then the
+ * the first copy of the security descriptor will be at offset 0x51d0 in the
+ * $SDS data stream and the second copy will be at offset 0x451d0.
+ */
+typedef struct {
+	SECURITY_DESCRIPTOR_HEADER SN(sdh);	  /* The security descriptor header. */
+	SECURITY_DESCRIPTOR_RELATIVE sid; /* The self-relative security
+					     descriptor. */
+} __attribute__ ((__packed__)) SDS_ENTRY;
+
+#define _SDH(X)  SC(sdh,X)
+
+/*
+ * The index entry key used in the $SII index. The collation type is
+ * COLLATION_NTOFS_ULONG. 
+ */
+typedef struct {
+	u32 security_id; /* The security_id assigned to the descriptor. */
+} __attribute__ ((__packed__)) SII_INDEX_KEY;
+
+/*
+ * The index entry key used in the $SDH index. The keys are sorted first by
+ * hash and then by security_id. The collation rule is
+ * COLLATION_NTOFS_SECURITY_HASH.
+ */
+typedef struct {
+	u32 hash;	   /* Hash of the security descriptor. */
+	u32 security_id;   /* The security_id assigned to the descriptor. */
+} __attribute__ ((__packed__)) SDH_INDEX_KEY;
+
+/*
+ * Attribute: Volume name (0x60).
+ * 
+ * NOTE: Always resident.
+ * NOTE: Present only in FILE_Volume.
+ */
+typedef struct {
+	uchar_t name[0];		/* The name of the volume in Unicode. */
+} __attribute__ ((__packed__)) VOLUME_NAME;
+
+/*
+ * Possible flags for the volume (16-bit).
+ */
+typedef enum {
+	VOLUME_IS_DIRTY			= const_cpu_to_le16(0x0001),
+	VOLUME_RESIZE_LOG_FILE		= const_cpu_to_le16(0x0002),
+	VOLUME_UPGRADE_ON_MOUNT 	= const_cpu_to_le16(0x0004),
+	VOLUME_MOUNTED_ON_NT4		= const_cpu_to_le16(0x0008),
+	VOLUME_DELETE_USN_UNDERWAY	= const_cpu_to_le16(0x0010),
+	VOLUME_REPAIR_OBJECT_ID		= const_cpu_to_le16(0x0020),
+	VOLUME_MODIFIED_BY_CHKDSK	= const_cpu_to_le16(0x8000),
+	VOLUME_FLAGS_MASK		= const_cpu_to_le16(0x803f),
+} __attribute__ ((__packed__)) VOLUME_FLAGS;
+
+/*
+ * Attribute: Volume information (0x70).
+ *
+ * NOTE: Always resident.
+ * NOTE: Present only in FILE_Volume.
+ * NOTE: Windows 2000 uses NTFS 3.0 while Windows NT4 service pack 6a uses
+ * 	 NTFS 1.2. I haven't personally seen other values yet.
+ */
+typedef struct {
+	u64 reserved;		/* Not used (yet?). */
+	u8 major_ver;		/* Major version of the ntfs format. */
+	u8 minor_ver;		/* Minor version of the ntfs format. */
+	VOLUME_FLAGS flags;	/* Bit array of VOLUME_* flags. */
+} __attribute__ ((__packed__)) VOLUME_INFORMATION;
+
+/*
+ * Attribute: Data attribute (0x80).
+ *
+ * NOTE: Can be resident or non-resident.
+ *
+ * Data contents of a file (i.e. the unnamed stream) or of a named stream.
+ */
+typedef struct {
+	u8 data[0];		/* The file's data contents. */
+} __attribute__ ((__packed__)) DATA_ATTR;
+
+/*
+ * Index header flags (8-bit).
+ */
+typedef enum {
+	/* When index header is in an index root attribute: */
+	SMALL_INDEX	= 0, /* The index is small enough to fit inside the
+				index root attribute and there is no index
+				allocation attribute present. */
+	LARGE_INDEX	= 1, /* The index is too large to fit in the index
+				root attribute and/or an index allocation
+				attribute is present. */
+	/*
+	 * When index header is in an index block, i.e. is part of index
+	 * allocation attribute:
+	 */
+	LEAF_NODE	= 0, /* This is a leaf node, i.e. there are no more
+			        nodes branching off it. */
+	INDEX_NODE	= 1, /* This node indexes other nodes, i.e. is not a
+				leaf node. */
+	NODE_MASK	= 1, /* Mask for accessing the *_NODE bits. */
+} __attribute__ ((__packed__)) INDEX_HEADER_FLAGS;
+
+/*
+ * This is the header for indexes, describing the INDEX_ENTRY records, which
+ * follow the INDEX_HEADER. Together the index header and the index entries
+ * make up a complete index.
+ *
+ * IMPORTANT NOTE: The offset, length and size structure members are counted
+ * relative to the start of the index header structure and not relative to the
+ * start of the index root or index allocation structures themselves.
+ */
+typedef struct {
+	u32 entries_offset;		/* Byte offset to first INDEX_ENTRY
+					   aligned to 8-byte boundary. */
+	u32 index_length;		/* Data size of the index in bytes,
+					   i.e. bytes used from allocated
+					   size, aligned to 8-byte boundary. */
+	u32 allocated_size;		/* Byte size of this index (block),
+					   multiple of 8 bytes. */
+	/* NOTE: For the index root attribute, the above two numbers are always
+	   equal, as the attribute is resident and it is resized as needed. In
+	   the case of the index allocation attribute the attribute is not
+	   resident and hence the allocated_size is a fixed value and must
+	   equal the index_block_size specified by the INDEX_ROOT attribute
+	   corresponding to the INDEX_ALLOCATION attribute this INDEX_BLOCK
+	   belongs to. */
+	INDEX_HEADER_FLAGS flags;	/* Bit field of INDEX_HEADER_FLAGS. */
+	u8 reserved[3];			/* Reserved/align to 8-byte boundary. */
+} __attribute__ ((__packed__)) INDEX_HEADER;
+
+/*
+ * Attribute: Index root (0x90).
+ *
+ * NOTE: Always resident.
+ *
+ * This is followed by a sequence of index entries (INDEX_ENTRY structures)
+ * as described by the index header.
+ *
+ * When a directory is small enough to fit inside the index root then this
+ * is the only attribute describing the directory. When the directory is too
+ * large to fit in the index root, on the other hand, two aditional attributes
+ * are present: an index allocation attribute, containing sub-nodes of the B+
+ * directory tree (see below), and a bitmap attribute, describing which virtual
+ * cluster numbers (vcns) in the index allocation attribute are in use by an
+ * index block.
+ *
+ * NOTE: The root directory (FILE_root) contains an entry for itself. Other
+ * dircetories do not contain entries for themselves, though.
+ */
+typedef struct {
+	ATTR_TYPES type;		/* Type of the indexed attribute. Is
+					   $FILE_NAME for directories, zero
+					   for view indexes. No other values
+					   allowed. */
+	COLLATION_RULES collation_rule;	/* Collation rule used to sort the
+					   index entries. If type is $FILE_NAME,
+					   this must be COLLATION_FILE_NAME. */
+	u32 index_block_size;		/* Size of each index block in bytes (in
+					   the index allocation attribute). */
+	u8 clusters_per_index_block;	/* Cluster size of each index block (in
+					   the index allocation attribute), when
+					   an index block is >= than a cluster,
+					   otherwise this will be the log of
+					   the size (like how the encoding of 
+					   the mft record size and the index
+					   record size found in the boot sector
+					   work). Has to be a power of 2. */
+	u8 reserved[3];			/* Reserved/align to 8-byte boundary. */
+	INDEX_HEADER index;		/* Index header describing the
+					   following index entries. */
+} __attribute__ ((__packed__)) INDEX_ROOT;
+
+/*
+ * Attribute: Index allocation (0xa0).
+ *
+ * NOTE: Always non-resident (doesn't make sense to be resident anyway!).
+ * 
+ * This is an array of index blocks. Each index block starts with an
+ * INDEX_BLOCK structure containing an index header, followed by a sequence of
+ * index entries (INDEX_ENTRY structures), as described by the INDEX_HEADER.
+ */
+typedef struct {
+/*  0*/	NTFS_RECORD SN(inr);	/* Magic is "INDX". */
+/*  8*/	s64 lsn;		/* $LogFile sequence number of the last
+				   modification of this index block. */
+/* 16*/	VCN index_block_vcn;	/* Virtual cluster number of the index block.
+				   If the cluster_size on the volume is <= the
+				   index_block_size of the directory,
+				   index_block_vcn counts in units of clusters,
+				   and in units of sectors otherwise. */
+/* 24*/	INDEX_HEADER index;	/* Describes the following index entries. */
+/* sizeof()= 40 (0x28) bytes */
+/*
+ * When creating the index block, we place the update sequence array at this
+ * offset, i.e. before we start with the index entries. This also makes sense,
+ * otherwise we could run into problems with the update sequence array
+ * containing in itself the last two bytes of a sector which would mean that
+ * multi sector transfer protection wouldn't work. As you can't protect data
+ * by overwriting it since you then can't get it back...
+ * When reading use the data from the ntfs record header.
+ */
+} __attribute__ ((__packed__)) INDEX_BLOCK;
+
+#define _INR(X)  SC(inr,X)
+
+typedef INDEX_BLOCK INDEX_ALLOCATION;
+
+/*
+ * The system file FILE_Extend/$Reparse contains an index named $R listing
+ * all reparse points on the volume. The index entry keys are as defined
+ * below. Note, that there is no index data associated with the index entries.
+ *
+ * The index entries are sorted by the index key file_id. The collation rule is
+ * COLLATION_NTOFS_ULONGS. FIXME: Verify whether the reparse_tag is not the
+ * primary key / is not a key at all. (AIA)
+ */
+typedef struct {
+	u32 reparse_tag;	/* Reparse point type (inc. flags). */
+	MFT_REF file_id;	/* Mft record of the file containing the
+				   reparse point attribute. */
+} __attribute__ ((__packed__)) REPARSE_INDEX_KEY;
+
+/*
+ * Quota flags (32-bit).
+ */
+typedef enum {
+	/* The user quota flags. Names explain meaning. */
+	QUOTA_FLAG_DEFAULT_LIMITS	= const_cpu_to_le32(0x00000001),
+	QUOTA_FLAG_LIMIT_REACHED	= const_cpu_to_le32(0x00000002),
+	QUOTA_FLAG_ID_DELETED		= const_cpu_to_le32(0x00000004),
+
+	QUOTA_FLAG_USER_MASK		= const_cpu_to_le32(0x00000007),
+		/* Bit mask for user quota flags. */
+	
+	/* These flags are only present in the quota defaults index entry,
+	   i.e. in the entry where owner_id = QUOTA_DEFAULTS_ID. */
+	QUOTA_FLAG_TRACKING_ENABLED	= const_cpu_to_le32(0x00000010),
+	QUOTA_FLAG_ENFORCEMENT_ENABLED	= const_cpu_to_le32(0x00000020),
+	QUOTA_FLAG_TRACKING_REQUESTED	= const_cpu_to_le32(0x00000040),
+	QUOTA_FLAG_LOG_THRESHOLD	= const_cpu_to_le32(0x00000080),
+	QUOTA_FLAG_LOG_LIMIT		= const_cpu_to_le32(0x00000100),
+	QUOTA_FLAG_OUT_OF_DATE		= const_cpu_to_le32(0x00000200),
+	QUOTA_FLAG_CORRUPT		= const_cpu_to_le32(0x00000400),
+	QUOTA_FLAG_PENDING_DELETES	= const_cpu_to_le32(0x00000800),
+} QUOTA_FLAGS;
+
+/*
+ * The system file FILE_Extend/$Quota contains two indexes $O and $Q. Quotas
+ * are on a per volume and per user basis.
+ *
+ * The $Q index contains one entry for each existing user_id on the volume. The
+ * index key is the user_id of the user/group owning this quota control entry,
+ * i.e. the key is the owner_id. The user_id of the owner of a file, i.e. the 
+ * owner_id, is found in the standard information attribute. The collation rule
+ * for $Q is COLLATION_NTOFS_ULONG.
+ *
+ * The $O index contains one entry for each user/group who has been assigned
+ * a quota on that volume. The index key holds the SID of the user_id the
+ * entry belongs to, i.e. the owner_id. The collation rule for $O is
+ * COLLATION_NTOFS_SID.
+ *
+ * The $O index entry data is the user_id of the user corresponding to the SID.
+ * This user_id is used as an index into $Q to find the quota control entry
+ * associated with the SID.
+ *
+ * The $Q index entry data is the quota control entry and is defined below.
+ */
+typedef struct {
+	u32 version;		/* Currently equals 2. */
+	QUOTA_FLAGS flags;	/* Flags describing this quota entry. */
+	u64 bytes_used;		/* How many bytes of the quota are in use. */
+	s64 change_time;	/* Last time this quota entry was changed. */
+	s64 threshold;		/* Soft quota (-1 if not limited). */
+	s64 limit;		/* Hard quota (-1 if not limited). */
+	s64 exceeded_time;	/* How long the soft quota has been exceeded. */
+	SID sid;		/* The SID of the user/object associated with
+				   this quota entry. Equals zero for the quota
+				   defaults entry. */
+} __attribute__ ((__packed__)) QUOTA_CONTROL_ENTRY;
+
+/*
+ * Predefined owner_id values (32-bit).
+ */
+typedef enum {
+	QUOTA_INVALID_ID	= const_cpu_to_le32(0x00000000),
+	QUOTA_DEFAULTS_ID	= const_cpu_to_le32(0x00000001),
+	QUOTA_FIRST_USER_ID	= const_cpu_to_le32(0x00000100),
+} PREDEFINED_OWNER_IDS;
+
+/*
+ * Index entry flags (16-bit).
+ */
+typedef enum {
+	INDEX_ENTRY_NODE = const_cpu_to_le16(1), /* This entry contains a sub-node,
+					      i.e. a reference to an index
+					      block in form of a virtual
+					      cluster number (see below). */
+	INDEX_ENTRY_END  = const_cpu_to_le16(2), /* This signifies the last entry in
+					      an index block. The index entry
+					      does not represent a file but it
+					      can point to a sub-node. */
+	INDEX_ENTRY_SPACE_FILLER = 0xffff, /* Just to force 16-bit width. */
+} __attribute__ ((__packed__)) INDEX_ENTRY_FLAGS;
+
+/*
+ * This the index entry header (see below).
+ */
+typedef struct {
+/*  0*/	union {		/* Only valid when INDEX_ENTRY_END is not set. */
+		MFT_REF indexed_file;		/* The mft reference of the file
+						   described by this index
+						   entry. Used for directory
+						   indexes. */
+		struct { /* Used for views/indexes to find the entry's data. */
+			u16 data_offset;	/* Data byte offset from this
+						   INDEX_ENTRY. Follows the
+						   index key. */
+			u16 data_length;	/* Data length in bytes. */
+			u32 reservedV;		/* Reserved (zero). */
+		} SN(iev) __attribute__ ((__packed__));
+	} SN(iif) __attribute__ ((__packed__));
+/*  8*/	u16 length;		 /* Byte size of this index entry, multiple of
+				    8-bytes. */
+/* 10*/	u16 key_length;		 /* Byte size of the key value, which is in the
+				    index entry. It follows field reserved. Not
+				    multiple of 8-bytes. */
+/* 12*/	INDEX_ENTRY_FLAGS flags; /* Bit field of INDEX_ENTRY_* flags. */
+/* 14*/	u16 reserved;		 /* Reserved/align to 8-byte boundary. */
+/* sizeof() = 16 bytes */
+} __attribute__ ((__packed__)) INDEX_ENTRY_HEADER;
+
+#define _IIF(X)  SC(ieh.iif,X)
+#define _IEV(X)  SC(iif.iev,X)
+
+/*
+ * This is an index entry. A sequence of such entries follows each INDEX_HEADER
+ * structure. Together they make up a complete index. The index follows either
+ * an index root attribute or an index allocation attribute.
+ *
+ * NOTE: Before NTFS 3.0 only filename attributes were indexed.
+ */
+typedef struct {
+/*  0*/ INDEX_ENTRY_HEADER SN(ieh);	/* The index entry header (see above). */
+/* 16*/	union {		/* The key of the indexed attribute. NOTE: Only present
+			   if INDEX_ENTRY_END bit in flags is not set. NOTE: On
+			   NTFS versions before 3.0 the only valid key is the
+			   FILE_NAME_ATTR. On NTFS 3.0+ the following
+			   additional index keys are defined: */
+		FILE_NAME_ATTR file_name;/* $I30 index in directories. */
+		SII_INDEX_KEY sii;	/* $SII index in $Secure. */
+		SDH_INDEX_KEY sdh;	/* $SDH index in $Secure. */
+		GUID object_id;		/* $O index in FILE_Extend/$ObjId: The
+					   object_id of the mft record found in
+					   the data part of the index. */
+		REPARSE_INDEX_KEY SN(iri);	/* $R index in FILE_Extend/$Reparse. */
+		SID sid;		/* $O index in FILE_Extend/$Quota:
+					   SID of the owner of the user_id. */
+		u32 owner_id;		/* $Q index in FILE_Extend/$Quota:
+					   user_id of the owner of the quota
+					   control entry in the data part of
+					   the index. */
+	} __attribute__ ((__packed__)) key;
+	/* The (optional) index data is inserted here when creating. */
+	// VCN vcn;	/* If INDEX_ENTRY_NODE bit in flags is set, the last
+	//		   eight bytes of this index entry contain the virtual
+	//		   cluster number of the index block that holds the
+	//		   entries immediately preceding the current entry (the
+	//		   vcn references the corresponding cluster in the data
+	//		   of the non-resident index allocation attribute). If
+	//		   the key_length is zero, then the vcn immediately
+	//		   follows the INDEX_ENTRY_HEADER. Regardless of
+	//		   key_length, the address of the 8-byte boundary
+	//		   alligned vcn of INDEX_ENTRY{_HEADER} *ie is given by
+	//		   (char*)ie + le16_to_cpu(ie*)->length) - sizeof(VCN),
+	//		   where sizeof(VCN) can be hardcoded as 8 if wanted. */
+} __attribute__ ((__packed__)) INDEX_ENTRY;
+
+#define _IEH(X)  SC(ieh,X)
+#define _IRI(X)  SC(key.iri,X)
+
+/*
+ * Attribute: Bitmap (0xb0).
+ *
+ * Contains an array of bits (aka a bitfield).
+ *
+ * When used in conjunction with the index allocation attribute, each bit
+ * corresponds to one index block within the index allocation attribute. Thus
+ * the number of bits in the bitmap * index block size / cluster size is the
+ * number of clusters in the index allocation attribute.
+ */
+typedef struct {
+	u8 bitmap[0];			/* Array of bits. */
+} __attribute__ ((__packed__)) BITMAP_ATTR;
+
+/*
+ * The reparse point tag defines the type of the reparse point. It also
+ * includes several flags, which further describe the reparse point.
+ *
+ * The reparse point tag is an unsigned 32-bit value divided in three parts:
+ *
+ * 1. The least significant 16 bits (i.e. bits 0 to 15) specifiy the type of
+ *    the reparse point.
+ * 2. The 13 bits after this (i.e. bits 16 to 28) are reserved for future use.
+ * 3. The most significant three bits are flags describing the reparse point.
+ *    They are defined as follows:
+ *	bit 29: Name surrogate bit. If set, the filename is an alias for
+ *		another object in the system.
+ * 	bit 30: High-latency bit. If set, accessing the first byte of data will
+ *		be slow. (E.g. the data is stored on a tape drive.)
+ * 	bit 31: Microsoft bit. If set, the tag is owned by Microsoft. User
+ *		defined tags have to use zero here.
+ */
+typedef enum {
+	IO_REPARSE_TAG_IS_ALIAS		= const_cpu_to_le32(0x20000000),
+	IO_REPARSE_TAG_IS_HIGH_LATENCY	= const_cpu_to_le32(0x40000000),
+	IO_REPARSE_TAG_IS_MICROSOFT	= const_cpu_to_le32(0x80000000),
+	
+	IO_REPARSE_TAG_RESERVED_ZERO	= const_cpu_to_le32(0x00000000),
+	IO_REPARSE_TAG_RESERVED_ONE	= const_cpu_to_le32(0x00000001),
+	IO_REPARSE_TAG_RESERVED_RANGE	= const_cpu_to_le32(0x00000001),
+
+	IO_REPARSE_TAG_NSS		= const_cpu_to_le32(0x68000005),
+	IO_REPARSE_TAG_NSS_RECOVER	= const_cpu_to_le32(0x68000006),
+	IO_REPARSE_TAG_SIS		= const_cpu_to_le32(0x68000007),
+	IO_REPARSE_TAG_DFS		= const_cpu_to_le32(0x68000008),
+
+	IO_REPARSE_TAG_MOUNT_POINT	= const_cpu_to_le32(0x88000003),
+
+	IO_REPARSE_TAG_HSM		= const_cpu_to_le32(0xa8000004),
+
+	IO_REPARSE_TAG_SYMBOLIC_LINK	= const_cpu_to_le32(0xe8000000),
+
+	IO_REPARSE_TAG_VALID_VALUES	= const_cpu_to_le32(0xe000ffff),
+} PREDEFINED_REPARSE_TAGS;
+
+/*
+ * Attribute: Reparse point (0xc0).
+ *
+ * NOTE: Can be resident or non-resident.
+ */
+typedef struct {
+	u32 reparse_tag;		/* Reparse point type (inc. flags). */
+	u16 reparse_data_length;	/* Byte size of reparse data. */
+	u16 reserved;			/* Align to 8-byte boundary. */
+	u8 reparse_data[0];		/* Meaning depends on reparse_tag. */
+} __attribute__ ((__packed__)) REPARSE_POINT;
+
+/*
+ * Attribute: Extended attribute (EA) information (0xd0).
+ *
+ * NOTE: Always resident. (Is this true???)
+ */
+typedef struct {
+	u16 ea_length;		/* Byte size of the packed extended
+				   attributes. */
+	u16 need_ea_count;	/* The number of extended attributes which have
+				   the NEED_EA bit set. */
+	u32 ea_query_length;	/* Byte size of the buffer required to query
+				   the extended attributes when calling
+				   ZwQueryEaFile() in Windows NT/2k. I.e. the
+				   byte size of the unpacked extended
+				   attributes. */
+} __attribute__ ((__packed__)) EA_INFORMATION;
+
+/*
+ * Extended attribute flags (8-bit).
+ */
+typedef enum {
+	NEED_EA	= 0x80,
+} __attribute__ ((__packed__)) EA_FLAGS;
+
+/*
+ * Attribute: Extended attribute (EA) (0xe0).
+ *
+ * NOTE: Always non-resident. (Is this true?)
+ * 
+ * Like the attribute list and the index buffer list, the EA attribute value is 
+ * a sequence of EA_ATTR variable length records.
+ *
+ * FIXME: It appears weird that the EA name is not unicode. Is it true?
+ */
+typedef struct {
+	u32 next_entry_offset;	/* Offset to the next EA_ATTR. */
+	EA_FLAGS flags;		/* Flags describing the EA. */
+	u8 ea_name_length;	/* Length of the name of the EA in bytes. */
+	u16 ea_value_length;	/* Byte size of the EA's value. */
+	u8 ea_name[0];		/* Name of the EA. */
+	u8 ea_value[0];		/* The value of the EA. Immediately follows
+				   the name. */
+} __attribute__ ((__packed__)) EA_ATTR;
+
+/*
+ * Attribute: Property set (0xf0).
+ *
+ * Intended to support Native Structure Storage (NSS) - a feature removed from
+ * NTFS 3.0 during beta testing.
+ */
+typedef struct {
+	/* Irrelevant as feature unused. */
+} __attribute__ ((__packed__)) PROPERTY_SET;
+
+/*
+ * Attribute: Logged utility stream (0x100).
+ *
+ * NOTE: Can be resident or non-resident.
+ *
+ * Operations on this attribute are logged to the journal ($LogFile) like
+ * normal metadata changes.
+ *
+ * Used by the Encrypting File System (EFS). All encrypted files have this
+ * attribute with the name $EFS.
+ */
+typedef struct {
+	/* Can be anything the creator chooses. */
+	/* EFS uses it as follows: */
+	// FIXME: Type this info, verifying it along the way. (AIA)
+} __attribute__ ((__packed__)) LOGGED_UTILITY_STREAM, EFS_ATTR;
+
+#endif /* _LINUX_NTFS_LAYOUT_H */
+
diff -Nru a/fs/ntfs/macros.h b/fs/ntfs/macros.h
--- a/fs/ntfs/macros.h	Sun Apr 28 20:10:37 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,40 +0,0 @@
-/*
- * macros.h
- *
- * Copyright (C) 1995 Martin von Löwis
- * Copyright (C) 1996 Régis Duchesne
- * Copyright (c) 2001 Anton Altaparmakov
- */
-#include <linux/ntfs_fs_i.h>
-#include <linux/fs.h>
-#include <asm/page.h>
-
-#define NTFS_FD(vol)		((vol)->u.fd)
-
-#define NTFS_SB(vol)		((struct super_block*)(vol)->sb)
-#define NTFS_SB2VOL(sb)         (&(sb)->u.ntfs_sb)
-#define NTFS_INO2VOL(ino)	(&((ino)->i_sb->u.ntfs_sb))
-static inline struct ntfs_i *ntfs_i(struct inode *inode)
-{
-	return list_entry(inode, struct ntfs_i, vfs_inode);
-}
-#define NTFS_I(ino)     (&ntfs_i(ino)->n)
-static inline struct inode *VFS_I(struct ntfs_inode_info *ntfs_ino)
-{
-	return &list_entry(ntfs_ino, struct ntfs_i, n)->vfs_inode;
-}
-
-#define IS_MAGIC(a,b)		(*(int*)(a) == *(int*)(b))
-#define IS_MFT_RECORD(a)	IS_MAGIC((a),"FILE")
-#define IS_INDEX_RECORD(a)	IS_MAGIC((a),"INDX")
-
-/* 'NTFS' in little endian */
-#define NTFS_SUPER_MAGIC	0x5346544E
-
-#define NTFS_AFLAG_RO           1
-#define NTFS_AFLAG_HIDDEN       2
-#define NTFS_AFLAG_SYSTEM       4
-#define NTFS_AFLAG_ARCHIVE      20
-#define NTFS_AFLAG_COMPRESSED   0x800
-#define NTFS_AFLAG_DIR          0x10000000
-
diff -Nru a/fs/ntfs/malloc.h b/fs/ntfs/malloc.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/fs/ntfs/malloc.h	Sun Apr 28 20:10:39 2002
@@ -0,0 +1,77 @@
+/*
+ * malloc.h - NTFS kernel memory handling. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2001,2002 Anton Altaparmakov.
+ *
+ * This program/include file 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/include file 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 (in the main directory of the Linux-NTFS 
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _LINUX_NTFS_MALLOC_H
+#define _LINUX_NTFS_MALLOC_H
+
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+
+/**
+ * vmalloc_nofs - allocate any pages but don't allow calls into fs layer
+ * @size:	number of bytes to allocate
+ *
+ * Allocate any pages but don't allow calls into fs layer. Return allocated
+ * memory or NULL if insufficient memory.
+ */
+static inline void *vmalloc_nofs(unsigned long size)
+{
+	if (likely(size >> PAGE_SHIFT < num_physpages))
+		return __vmalloc(size, GFP_NOFS | __GFP_HIGHMEM, PAGE_KERNEL);
+	return NULL;
+}
+
+/**
+ * ntfs_malloc_nofs - allocate memory in multiples of pages
+ * @size	number of bytes to allocate
+ *
+ * Allocates @size bytes of memory, rounded up to multiples of PAGE_SIZE and
+ * returns a pointer to the allocated memory.
+ *
+ * If there was insufficient memory to complete the request, return NULL.
+ */
+static inline void *ntfs_malloc_nofs(unsigned long size)
+{
+	if (likely(size <= PAGE_SIZE)) {
+		if (likely(size)) {
+			/* kmalloc() has per-CPU caches so if faster for now. */
+			return kmalloc(PAGE_SIZE, GFP_NOFS);
+			/* return (void *)__get_free_page(GFP_NOFS |
+					__GFP_HIGHMEM); */
+		}
+		BUG();
+	}
+	if (likely(size >> PAGE_SHIFT < num_physpages))
+		return __vmalloc(size, GFP_NOFS | __GFP_HIGHMEM, PAGE_KERNEL);
+	return NULL;
+}
+
+static inline void ntfs_free(void *addr)
+{
+	if (likely((unsigned long)addr < VMALLOC_START)) {
+		return kfree(addr);
+		/* return free_page((unsigned long)addr); */
+	}
+	vfree(addr);
+}
+
+#endif /* _LINUX_NTFS_MALLOC_H */
+
diff -Nru a/fs/ntfs/mft.c b/fs/ntfs/mft.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/fs/ntfs/mft.c	Sun Apr 28 20:10:39 2002
@@ -0,0 +1,499 @@
+/**
+ * mft.c - NTFS kernel mft record operations. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2001,2002 Anton Altaparmakov.
+ * Copyright (C) 2002 Richard Russon.
+ *
+ * This program/include file 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/include file 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 (in the main directory of the Linux-NTFS 
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/locks.h>
+#include <linux/swap.h>
+
+#include "ntfs.h"
+
+#define MAX_BUF_PER_PAGE (PAGE_CACHE_SIZE / 512)
+
+/**
+ * __format_mft_record - initialize an empty mft record
+ * @m:		mapped, pinned and locked for writing mft record
+ * @size:	size of the mft record
+ * @rec_no:	mft record number / inode number
+ *
+ * Private function to initialize an empty mft record. Use one of the two
+ * provided format_mft_record() functions instead.
+ */
+static void __format_mft_record(MFT_RECORD *m, const int size,
+		const unsigned long rec_no)
+{
+	ATTR_RECORD *a;
+
+	memset(m, 0, size);
+	m->_MNR(magic) = magic_FILE;
+	/* Aligned to 2-byte boundary. */
+	m->_MNR(usa_ofs) = cpu_to_le16((sizeof(MFT_RECORD) + 1) & ~1);
+	m->_MNR(usa_count) = cpu_to_le16(size / NTFS_BLOCK_SIZE + 1);
+	/* Set the update sequence number to 1. */
+	*(u16*)((char*)m + ((sizeof(MFT_RECORD) + 1) & ~1)) = cpu_to_le16(1);
+	m->lsn = cpu_to_le64(0LL);
+	m->sequence_number = cpu_to_le16(1);
+	m->link_count = cpu_to_le16(0);
+	/* Aligned to 8-byte boundary. */
+	m->attrs_offset = cpu_to_le16((le16_to_cpu(m->_MNR(usa_ofs)) +
+			(le16_to_cpu(m->_MNR(usa_count)) << 1) + 7) & ~7);
+	m->flags = cpu_to_le16(0);
+	/*
+	 * Using attrs_offset plus eight bytes (for the termination attribute),
+	 * aligned to 8-byte boundary.
+	 */
+	m->bytes_in_use = cpu_to_le32((le16_to_cpu(m->attrs_offset) + 8 + 7) &
+			~7);
+	m->bytes_allocated = cpu_to_le32(size);
+	m->base_mft_record = cpu_to_le64((MFT_REF)0);
+	m->next_attr_instance = cpu_to_le16(0);
+	a = (ATTR_RECORD*)((char*)m + le16_to_cpu(m->attrs_offset));
+	a->type = AT_END;
+	a->length = cpu_to_le32(0);
+}
+
+/**
+ * format_mft_record - initialize an empty mft record
+ * @ni:		ntfs inode of mft record
+ * @mft_rec:	mapped, pinned and locked mft record (optional)
+ *
+ * Initialize an empty mft record. This is used when extending the MFT.
+ *
+ * If @mft_rec is NULL, we call map_mft_record() to obtain the
+ * record and we unmap it again when finished.
+ *
+ * We return 0 on success or -errno on error.
+ */
+int format_mft_record(ntfs_inode *ni, MFT_RECORD *mft_rec)
+{
+	MFT_RECORD *m;
+
+	if (mft_rec)
+		m = mft_rec;
+	else {
+		m = map_mft_record(WRITE, ni);
+		if (IS_ERR(m))
+			return PTR_ERR(m);
+	}
+	__format_mft_record(m, ni->vol->mft_record_size, ni->mft_no);
+	if (!mft_rec)
+		unmap_mft_record(WRITE, ni);
+	return 0;
+}
+
+/* From fs/ntfs/aops.c */
+extern int ntfs_mst_readpage(struct file *, struct page *);
+
+/**
+ * ntfs_mft_aops - address space operations for access to $MFT
+ *
+ * Address space operations for access to $MFT. This allows us to simply use
+ * read_cache_page() in map_mft_record().
+ */
+struct address_space_operations ntfs_mft_aops = {
+	writepage:	NULL,			/* Write dirty page to disk. */
+	readpage:	ntfs_mst_readpage,	/* Fill page with data. */
+	sync_page:	block_sync_page,	/* Currently, just unplugs the
+						   disk request queue. */
+	prepare_write:	NULL,			/* . */
+	commit_write:	NULL,			/* . */
+	bmap:		NULL,			/* Needed for FIBMAP.
+						   Don't use it. */
+	flushpage:	NULL,			/* . */
+	releasepage:	NULL,			/* . */
+#ifdef KERNEL_HAS_O_DIRECT
+	direct_IO:	NULL,			/* . */
+#endif
+};
+
+/**
+ * map_mft_record_page - map the page in which a specific mft record resides
+ * @ni:		ntfs inode whose mft record page to map
+ *
+ * This maps the page in which the mft record of the ntfs inode @ni is situated
+ * and returns a pointer to the mft record within the mapped page.
+ *
+ * Return value needs to be checked with IS_ERR() and if that is true PTR_ERR()
+ * contains the negative error code returned.
+ */
+static inline MFT_RECORD *map_mft_record_page(ntfs_inode *ni)
+{
+	ntfs_volume *vol = ni->vol;
+	struct inode *mft_vi = vol->mft_ino;
+	struct page *page;
+	unsigned long index, ofs, end_index;
+
+	BUG_ON(atomic_read(&ni->mft_count) || ni->page);
+	/*
+	 * The index into the page cache and the offset within the page cache
+	 * page of the wanted mft record. FIXME: We need to check for
+	 * overflowing the unsigned long, but I don't think we would ever get
+	 * here if the volume was that big...
+	 */
+	index = ni->mft_no << vol->mft_record_size_bits >> PAGE_CACHE_SHIFT;
+	ofs = (ni->mft_no << vol->mft_record_size_bits) & ~PAGE_CACHE_MASK;
+
+	/* The maximum valid index into the page cache for $MFT's data. */
+	end_index = mft_vi->i_size >> PAGE_CACHE_SHIFT;
+
+	/* If the wanted index is out of bounds the mft record doesn't exist. */
+	if (index >= end_index) {
+		if (index > end_index || (mft_vi->i_size & ~PAGE_CACHE_MASK) <
+				ofs + vol->mft_record_size) {
+			page = ERR_PTR(-ENOENT);
+			goto up_err_out;
+		}
+	}
+	/* Read, map, and pin the page. */
+	page = ntfs_map_page(mft_vi->i_mapping, index);
+	if (!IS_ERR(page)) {
+		/* Pin the mft record mapping in the ntfs_inode. */
+		atomic_inc(&ni->mft_count);
+
+		/* Setup the references in the ntfs_inode. */
+		ni->page = page;
+		ni->page_ofs = ofs;
+
+		return page_address(page) + ofs;
+	}
+up_err_out:
+	/* Just in case... */
+	ni->page = NULL;
+	ni->page_ofs = 0;
+
+	ntfs_error(vol->sb, "Failed with error code %lu.", -PTR_ERR(page));
+	return (void*)page;
+}
+
+/**
+ * unmap_mft_record_page - unmap the page in which a specific mft record resides
+ * @ni:		ntfs inode whose mft record page to unmap
+ *
+ * This unmaps the page in which the mft record of the ntfs inode @ni is
+ * situated and returns. This is a NOOP if highmem is not configured.
+ *
+ * The unmap happens via ntfs_unmap_page() which in turn decrements the use
+ * count on the page thus releasing it from the pinned state.
+ *
+ * We do not actually unmap the page from memory of course, as that will be
+ * done by the page cache code itself when memory pressure increases or
+ * whatever.
+ */
+static inline void unmap_mft_record_page(ntfs_inode *ni)
+{
+	BUG_ON(atomic_read(&ni->mft_count) || !ni->page);
+	// TODO: If dirty, blah...
+	ntfs_unmap_page(ni->page);
+	ni->page = NULL;
+	ni->page_ofs = 0;
+	return;
+}
+
+/**
+ * map_mft_record - map, pin and lock an mft record
+ * @rw:		map for read (rw = READ) or write (rw = WRITE)
+ * @ni:		ntfs inode whose MFT record to map
+ *
+ * First, take the mrec_lock semaphore for reading or writing, depending on
+ * the value or @rw. We might now be sleeping, while waiting for the semaphore
+ * if it was already locked by someone else.
+ *
+ * Then increment the map reference count and return the mft. If this is the
+ * first invocation, the page of the record is first mapped using
+ * map_mft_record_page().
+ *
+ * This in turn uses ntfs_map_page() to get the page containing the wanted mft
+ * record (it in turn calls read_cache_page() which reads it in from disk if
+ * necessary, increments the use count on the page so that it cannot disappear
+ * under us and returns a reference to the page cache page).
+ *
+ * If read_cache_page() invokes ntfs_mst_readpage() to load the page from disk,
+ * it sets PG_locked and clears PG_uptodate on the page. Once I/O has
+ * completed and the post-read mst fixups on each mft record in the page have
+ * been performed, the page gets PG_uptodate set and PG_locked cleared (this is
+ * done in our asynchronous I/O completion handler end_buffer_read_mft_async()).
+ * ntfs_map_page() waits for PG_locked to become clear and checks if
+ * PG_uptodate is set and returns an error code if not. This provides
+ * sufficient protection against races when reading/using the page.
+ *
+ * However there is the write mapping to think about. Doing the above described
+ * checking here will be fine, because when initiating the write we will set
+ * PG_locked and clear PG_uptodate making sure nobody is touching the page
+ * contents. Doing the locking this way means that the commit to disk code in
+ * the page cache code paths is automatically sufficiently locked with us as
+ * we will not touch a page that has been locked or is not uptodate. The only
+ * locking problem then is them locking the page while we are accessing it.
+ *
+ * So that code will end up having to own the mrec_lock of all mft
+ * records/inodes present in the page before I/O can proceed. Grr. In that
+ * case we wouldn't need need to bother with PG_locked and PG_uptodate as
+ * nobody will be accessing anything without owning the mrec_lock semaphore.
+ * But we do need to use them because of the read_cache_page() invokation and
+ * the code becomes so much simpler this way that it is well worth it.
+ *
+ * The mft record is now ours and we return a pointer to it. You need to check
+ * the returned pointer with IS_ERR() and if that is true, PTR_ERR() will return
+ * the error code. The following error codes are defined:
+ * 	TODO: Fill in the possible error codes.
+ *
+ * NOTE: Caller is responsible for setting the mft record dirty before calling
+ * unmap_mft_record(). This is obviously only necessary if the caller really
+ * modified the mft record...
+ * Q: Do we want to recycle one of the VFS inode state bits instead?
+ * A: No, the inode ones mean we want to change the mft record, not we want to
+ * write it out.
+ */
+MFT_RECORD *map_mft_record(const int rw, ntfs_inode *ni)
+{
+	MFT_RECORD *m;
+
+	ntfs_debug("Entering for i_ino 0x%Lx, mapping for %s.",
+			(unsigned long long)ni->mft_no,
+			rw == READ ? "READ" : "WRITE");
+
+	/* Make sure the ntfs inode doesn't go away. */
+	atomic_inc(&ni->count);
+
+	/* Serialize access to this mft record. */
+	if (rw == READ)
+		down_read(&ni->mrec_lock);
+	else
+		down_write(&ni->mrec_lock);
+
+	/* If already mapped, bump reference count and return the mft record. */
+	if (atomic_read(&ni->mft_count)) {
+		BUG_ON(!ni->page);
+		atomic_inc(&ni->mft_count);
+		return page_address(ni->page) + ni->page_ofs;
+	}
+
+	/* Wasn't mapped. Map it now and return it if all was ok. */
+	m = map_mft_record_page(ni);
+	if (!IS_ERR(m))
+		return m;
+
+	/* Mapping failed. Release the mft record lock. */
+	if (rw == READ)
+		up_read(&ni->mrec_lock);
+	else
+		up_write(&ni->mrec_lock);
+
+	ntfs_error(ni->vol->sb, "Failed with error code %lu.", -PTR_ERR(m));
+
+	/* Release the ntfs inode and return the error code. */
+	atomic_dec(&ni->count);
+	return m;
+}
+
+/**
+ * unmap_mft_record - release a mapped mft record
+ * @rw:		unmap from read (@rw = READ) or write (@rw = WRITE)
+ * @ni:		ntfs inode whose MFT record to unmap
+ *
+ * First, decrement the mapping count and when it reaches zero unmap the mft
+ * record.
+ *
+ * Second, release the mrec_lock semaphore.
+ *
+ * The mft record is now released for others to get hold of.
+ *
+ * Finally, release the ntfs inode by decreasing the ntfs inode reference count.
+ *
+ * NOTE: If caller had the mft record mapped for write and has modified it, it
+ * is imperative to set the mft record dirty BEFORE calling unmap_mft_record().
+ *
+ * NOTE: This has to be done both for 'normal' mft records, and for extent mft
+ * records.
+ */
+void unmap_mft_record(const int rw, ntfs_inode *ni)
+{
+	struct page *page = ni->page;
+
+	BUG_ON(!atomic_read(&ni->mft_count) || !page);
+
+	ntfs_debug("Entering for mft_no 0x%Lx, unmapping from %s.",
+			(unsigned long long)ni->mft_no,
+			rw == READ ? "READ" : "WRITE");
+
+	/* Only release the actual page mapping if this is the last one. */
+	if (atomic_dec_and_test(&ni->mft_count))
+		unmap_mft_record_page(ni);
+
+	/* Release the semaphore. */
+	if (rw == READ)
+		up_read(&ni->mrec_lock);
+	else
+		up_write(&ni->mrec_lock);
+
+	/* Release the ntfs inode. */
+	atomic_dec(&ni->count);
+
+	/*
+	 * If pure ntfs_inode, i.e. no vfs inode attached, we leave it to
+	 * ntfs_clear_inode() in the extent inode case, and to the caller in
+	 * the non-extent, yet pure ntfs inode case, to do the actual tear
+	 * down of all structures and freeing of all allocated memory.
+	 */
+	return;
+}
+
+/**
+ * map_extent_mft_record - load an extent inode and attach it to its base
+ * @base_ni:	base ntfs inode
+ * @mref:	mft reference of the extent inode to load (in little endian)
+ * @ntfs_ino:	on successful return, pointer to the ntfs_inode structure
+ *
+ * Load the extent mft record @mref and attach it to its base inode @base_ni.
+ * Return the mapped extent mft record if IS_ERR(result) is false. Otherwise
+ * PTR_ERR(result) gives the negative error code.
+ *
+ * On successful return, @ntfs_ino contains a pointer to the ntfs_inode
+ * structure of the mapped extent inode.
+ *
+ * Note, we always map for READ. We consider this lock as irrelevant because
+ * the base inode will be write locked in all cases when we want to write to
+ * an extent inode which already gurantees that there is no-one else accessing
+ * the extent inode.
+ */
+MFT_RECORD *map_extent_mft_record(ntfs_inode *base_ni, MFT_REF mref,
+		ntfs_inode **ntfs_ino)
+{
+	MFT_RECORD *m;
+	ntfs_inode *ni = NULL;
+	ntfs_inode **extent_nis = NULL;
+	int i;
+	u64 mft_no = MREF_LE(mref);
+	u16 seq_no = MSEQNO_LE(mref);
+	BOOL destroy_ni = FALSE;
+
+	ntfs_debug("Mapping extent mft record 0x%Lx (base mft record 0x%Lx).",
+			(unsigned long long)mft_no,
+			(unsigned long long)base_ni->mft_no);
+	/* Make sure the base ntfs inode doesn't go away. */
+	atomic_inc(&base_ni->count);
+	/*
+	 * Check if this extent inode has already been added to the base inode,
+	 * in which case just return it. If not found, add it to the base
+	 * inode before returning it.
+	 */
+	down(&base_ni->extent_lock);
+	if (base_ni->nr_extents > 0) {
+		extent_nis = base_ni->_INE(extent_ntfs_inos);
+		for (i = 0; i < base_ni->nr_extents; i++) {
+			if (mft_no != extent_nis[i]->mft_no)
+				continue;
+			ni = extent_nis[i];
+			/* Make sure the ntfs inode doesn't go away. */
+			atomic_inc(&ni->count);
+			break;
+		}
+	}
+	if (ni) {
+		up(&base_ni->extent_lock);
+		atomic_dec(&base_ni->count);
+		/* We found the record; just have to map and return it. */
+		m = map_mft_record(READ, ni);
+		/* Map mft record increments this on success. */
+		atomic_dec(&ni->count);
+		if (!IS_ERR(m)) {
+			/* Verify the sequence number. */
+			if (le16_to_cpu(m->sequence_number) == seq_no) {
+				ntfs_debug("Done 1.");
+				*ntfs_ino = ni;
+				return m;
+			}
+			unmap_mft_record(READ, ni);
+			ntfs_error(base_ni->vol->sb, "Found stale extent mft "
+					"reference! Corrupt file system. "
+					"Run chkdsk.");
+			return ERR_PTR(-EIO);
+		}
+map_err_out:
+		ntfs_error(base_ni->vol->sb, "Failed to map extent "
+				"mft record, error code %ld.", -PTR_ERR(m));
+		return m;
+	}
+	/* Record wasn't there. Get a new ntfs inode and initialize it. */
+	ni = ntfs_new_inode(base_ni->vol->sb);
+	if (!ni) {
+		up(&base_ni->extent_lock);
+		atomic_dec(&base_ni->count);
+		return ERR_PTR(-ENOMEM);
+	}
+	ni->vol = base_ni->vol;
+	ni->mft_no = mft_no;
+	ni->seq_no = seq_no;
+	ni->nr_extents = -1;
+	ni->_INE(base_ntfs_ino) = base_ni;
+	/* Now map the record. */
+	m = map_mft_record(READ, ni);
+	if (IS_ERR(m)) {
+		up(&base_ni->extent_lock);
+		atomic_dec(&base_ni->count);
+		ntfs_clear_inode(ni);
+		goto map_err_out;
+	}
+	/* Verify the sequence number. */
+	if (le16_to_cpu(m->sequence_number) != seq_no) {
+		ntfs_error(base_ni->vol->sb, "Found stale extent mft "
+				"reference! Corrupt file system. Run chkdsk.");
+		destroy_ni = TRUE;
+		m = ERR_PTR(-EIO);
+		goto unm_err_out;
+	}
+	/* Attach extent inode to base inode, reallocating memory if needed. */
+	if (!(base_ni->nr_extents & ~3)) {
+		ntfs_inode **tmp;
+		int new_size = (base_ni->nr_extents + 4) * sizeof(ntfs_inode *);
+
+		tmp = (ntfs_inode **)kmalloc(new_size, GFP_NOFS);
+		if (!tmp) {
+			ntfs_error(base_ni->vol->sb, "Failed to allocate "
+					"internal buffer.");
+			destroy_ni = TRUE;
+			m = ERR_PTR(-ENOMEM);
+			goto unm_err_out;
+		}
+		if (base_ni->_INE(extent_ntfs_inos)) {
+			memcpy(tmp, base_ni->_INE(extent_ntfs_inos), new_size -
+					4 * sizeof(ntfs_inode *));
+			kfree(base_ni->_INE(extent_ntfs_inos));
+		}
+		base_ni->_INE(extent_ntfs_inos) = tmp;
+	}
+	base_ni->_INE(extent_ntfs_inos)[base_ni->nr_extents++] = ni;
+	up(&base_ni->extent_lock);
+	atomic_dec(&base_ni->count);
+	ntfs_debug("Done 2.");
+	*ntfs_ino = ni;
+	return m;
+unm_err_out:
+	unmap_mft_record(READ, ni);
+	up(&base_ni->extent_lock);
+	atomic_dec(&base_ni->count);
+	/*
+	 * If the extent inode was not attached to the base inode we need to
+	 * release it or we will leak memory.
+	 */
+	if (destroy_ni)
+		ntfs_clear_inode(ni);
+	return m;
+}
+
diff -Nru a/fs/ntfs/mft.h b/fs/ntfs/mft.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/fs/ntfs/mft.h	Sun Apr 28 20:10:39 2002
@@ -0,0 +1,47 @@
+/*
+ * mft.h - Defines for mft record handling in NTFS Linux kernel driver.
+ *	   Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2001,2002 Anton Altaparmakov.
+ *
+ * This program/include file 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/include file 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 (in the main directory of the Linux-NTFS 
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _LINUX_NTFS_MFT_H
+#define _LINUX_NTFS_MFT_H
+
+#include <linux/fs.h>
+
+#include "inode.h"
+
+extern int format_mft_record(ntfs_inode *ni, MFT_RECORD *m);
+//extern int format_mft_record2(struct super_block *vfs_sb,
+//		const unsigned long inum, MFT_RECORD *m);
+
+extern MFT_RECORD *map_mft_record(const int rw, ntfs_inode *ni);
+extern void unmap_mft_record(const int rw, ntfs_inode *ni);
+
+extern MFT_RECORD *map_extent_mft_record(ntfs_inode *base_ni, MFT_REF mref,
+		ntfs_inode **ntfs_ino);
+
+static inline void unmap_extent_mft_record(ntfs_inode *ni)
+{
+	unmap_mft_record(READ, ni);
+	return;
+}
+
+#endif /* _LINUX_NTFS_MFT_H */
+
diff -Nru a/fs/ntfs/mst.c b/fs/ntfs/mst.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/fs/ntfs/mst.c	Sun Apr 28 20:10:39 2002
@@ -0,0 +1,202 @@
+/*
+ * mst.c - NTFS multi sector transfer protection handling code. Part of the
+ * 	   Linux-NTFS project.
+ *
+ * Copyright (c) 2001 Anton Altaparmakov.
+ *
+ * This program/include file 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/include file 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 (in the main directory of the Linux-NTFS 
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "ntfs.h"
+
+/**
+ * post_read_mst_fixup - deprotect multi sector transfer protected data
+ * @b:		pointer to the data to deprotect
+ * @size:	size in bytes of @b
+ * 
+ * Perform the necessary post read multi sector transfer fixup and detect the
+ * presence of incomplete multi sector transfers. - In that case, overwrite the
+ * magic of the ntfs record header being processed with "BAAD" (in memory only!)
+ * and abort processing.
+ *
+ * Return 0 on success and -EINVAL on error ("BAAD" magic will be present).
+ *
+ * NOTE: We consider the absence / invalidity of an update sequence array to
+ * mean that the structure is not protected at all and hence doesn't need to
+ * be fixed up. Thus, we return success and not failure in this case. This is
+ * in contrast to pre_write_mst_fixup(), see below.
+ */
+int post_read_mst_fixup(NTFS_RECORD *b, const u32 size)
+{
+	u16 usa_ofs, usa_count, usn;
+	u16 *usa_pos, *data_pos;
+
+	/* Setup the variables. */
+	usa_ofs = le16_to_cpu(b->usa_ofs);
+	/* Decrement usa_count to get number of fixups. */
+	usa_count = le16_to_cpu(b->usa_count) - 1;
+	/* Size and alignement checks. */
+	if ( size & (NTFS_BLOCK_SIZE - 1)	||
+	     usa_ofs & 1			||
+	     usa_ofs + (usa_count * 2) > size	||
+	     (size >> NTFS_BLOCK_SIZE_BITS) != usa_count)
+		return 0;
+	/* Position of usn in update sequence array. */  
+	usa_pos = (u16*)b + usa_ofs/sizeof(u16);
+	/* 
+	 * The update sequence number which has to be equal to each of the
+	 * u16 values before they are fixed up. Note no need to care for
+	 * endianness since we are comparing and moving data for on disk
+	 * structures which means the data is consistent. - If it is 
+	 * consistenty the wrong endianness it doesn't make any difference.
+	 */
+	usn = *usa_pos;
+	/*
+	 * Position in protected data of first u16 that needs fixing up.
+	 */
+	data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1;
+        /*
+	 * Check for incomplete multi sector transfer(s).
+	 */
+	while (usa_count--) {
+                if (*data_pos != usn) {
+			/*
+			 * Incomplete multi sector transfer detected! )-:
+			 * Set the magic to "BAAD" and return failure.
+			 * Note that magic_BAAD is already converted to le32.
+			 */
+			b->magic = magic_BAAD;
+	                return -EINVAL;
+		}
+		data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
+	}
+	/* Re-setup the variables. */
+	usa_count = le16_to_cpu(b->usa_count) - 1;
+	data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1;
+	/* Fixup all sectors. */
+	while (usa_count--) {
+		/*
+		 * Increment position in usa and restore original data from
+		 * the usa into the data buffer.
+		 */
+		*data_pos = *(++usa_pos);
+                /* Increment position in data as well. */
+		data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
+        }
+	return 0;
+}
+
+/**
+ * pre_write_mst_fixup - apply multi sector transfer protection
+ * @b:		pointer to the data to protect
+ * @size:	size in bytes of @b
+ * 
+ * Perform the necessary pre write multi sector transfer fixup on the data
+ * pointer to by @b of @size.
+ *
+ * Return 0 if fixup applied (success) or -EINVAL if no fixup was performed
+ * (assumed not needed). This is in contrast to post_read_mst_fixup() above.
+ *
+ * NOTE: We consider the absence / invalidity of an update sequence array to
+ * mean that the structure is not subject to protection and hence doesn't need
+ * to be fixed up. This means that you have to create a valid update sequence
+ * array header in the ntfs record before calling this function, otherwise it
+ * will fail (the header needs to contain the position of the update seqeuence
+ * array together with the number of elements in the array). You also need to
+ * initialise the update sequence number before calling this function
+ * otherwise a random word will be used (whatever was in the record at that
+ * position at that time).
+ */
+int pre_write_mst_fixup(NTFS_RECORD *b, const u32 size)
+{
+	u16 usa_ofs, usa_count, usn;
+	u16 *usa_pos, *data_pos;
+
+	/* Sanity check + only fixup if it makes sense. */
+	if (!b || is_baad_record(b->magic) || is_hole_record(b->magic))
+		return -EINVAL;
+	/* Setup the variables. */
+	usa_ofs = le16_to_cpu(b->usa_ofs);
+	/* Decrement usa_count to get number of fixups. */
+	usa_count = le16_to_cpu(b->usa_count) - 1;
+	/* Size and alignement checks. */
+	if ( size & (NTFS_BLOCK_SIZE - 1)	||
+	     usa_ofs & 1			||
+	     usa_ofs + (usa_count * 2) > size	||
+	     (size >> NTFS_BLOCK_SIZE_BITS) != usa_count)
+		return -EINVAL;
+	/* Position of usn in update sequence array. */  
+	usa_pos = (u16*)((u8*)b + usa_ofs);
+	/*
+	 * Cyclically increment the update sequence number 
+	 * (skipping 0 and -1, i.e. 0xffff).
+	 */
+	usn = le16_to_cpup(usa_pos) + 1;
+	if (usn == 0xffff || !usn)
+		usn = 1;
+	usn = cpu_to_le16(usn);
+	*usa_pos = usn;
+	/* Position in data of first u16 that needs fixing up. */
+	data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1;
+        /* Fixup all sectors. */
+        while (usa_count--) {
+		/*
+		 * Increment the position in the usa and save the 
+		 * original data from the data buffer into the usa.
+		 */
+		*(++usa_pos) = *data_pos;
+		/* Apply fixup to data. */
+		*data_pos = usn;
+		/* Increment position in data as well. */
+		data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
+        }
+	return 0;
+}
+
+/**
+ * post_write_mst_fixup - fast deprotect multi sector transfer protected data
+ * @b:		pointer to the data to deprotect
+ * 
+ * Perform the necessary post write multi sector transfer fixup, not checking
+ * for any errors, because we assume we have just used pre_write_mst_fixup(),
+ * thus the data will be fine or we would never have gotten here.
+ */
+void post_write_mst_fixup(NTFS_RECORD *b)
+{
+	u16 *usa_pos, *data_pos;
+
+	u16 usa_ofs = le16_to_cpu(b->usa_ofs);
+	u16 usa_count = le16_to_cpu(b->usa_count) - 1;
+
+	/* Position of usn in update sequence array. */  
+	usa_pos = (u16*)b + usa_ofs/sizeof(u16);
+
+	/* Position in protected data of first u16 that needs fixing up. */
+	data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1;
+
+        /* Fixup all sectors. */
+	while (usa_count--) {
+		/*
+		 * Increment position in usa and restore original data from
+		 * the usa into the data buffer.
+		 */
+		*data_pos = *(++usa_pos);
+
+                /* Increment position in data as well. */
+		data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
+        }
+}
+
diff -Nru a/fs/ntfs/namei.c b/fs/ntfs/namei.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/fs/ntfs/namei.c	Sun Apr 28 20:10:39 2002
@@ -0,0 +1,145 @@
+/*
+ * namei.c - NTFS kernel directory inode operations. Part of the Linux-NTFS
+ * 	     project.
+ *
+ * Copyright (c) 2001,2002 Anton Altaparmakov.
+ *
+ * This program/include file 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/include file 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 (in the main directory of the Linux-NTFS 
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "ntfs.h"
+
+/**
+ * ntfs_lookup - find the inode represented by a dentry in a directory inode
+ * @dir_ino:	directory inode in which to look for the inode
+ * @dent:	dentry representing the inode to look for
+ *
+ * In short, ntfs_lookup() looks for the inode represented by the dentry @dent
+ * in the directory inode @dir_ino and if found attaches the inode to the
+ * dentry @dent.
+ *
+ * In more detail, the dentry @dent specifies which inode to look for by
+ * supplying the name of the inode in @dent->d_name.name. ntfs_lookup()
+ * converts the name to Unicode and walks the contents of the directory inode
+ * @dir_ino looking for the converted Unicode name. If the name is found in the
+ * directory, the corresponding inode is loaded by calling iget() on its inode
+ * number and the inode is associated with the dentry @dent via a call to
+ * d_add().
+ *
+ * If the name is not found in the directory, a NULL inode is inserted into the
+ * dentry @dent. The dentry is then termed a negative dentry.
+ *
+ * Only if an actual error occurs, do we return an error via ERR_PTR().
+ */
+static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent)
+{
+	ntfs_volume *vol = NTFS_SB(dir_ino->i_sb);
+	struct inode *dent_inode;
+	u64 mref;
+	unsigned long dent_ino;
+	uchar_t *uname;
+	int uname_len;
+
+	ntfs_debug("Looking up %s in directory inode 0x%lx.",
+			dent->d_name.name, dir_ino->i_ino);
+	/* Convert the name of the dentry to Unicode. */
+	uname_len = ntfs_nlstoucs(vol, dent->d_name.name, dent->d_name.len,
+			&uname);
+	if (uname_len < 0) {
+		ntfs_error(vol->sb, "Failed to convert name to Unicode.");
+		return ERR_PTR(uname_len);
+	}
+	mref = ntfs_lookup_inode_by_name(NTFS_I(dir_ino), uname, uname_len);
+	kmem_cache_free(ntfs_name_cache, uname);
+	if (!IS_ERR_MREF(mref)) {
+		dent_ino = (unsigned long)MREF(mref);
+		ntfs_debug("Found inode 0x%lx. Calling iget.", dent_ino);
+		dent_inode = iget(vol->sb, dent_ino);
+		if (dent_inode) {
+			/* Consistency check. */
+			if (MSEQNO(mref) == NTFS_I(dent_inode)->seq_no ||
+					dent_ino == FILE_MFT) {
+				d_add(dent, dent_inode);
+				ntfs_debug("Done.");
+				return NULL;
+			}
+			ntfs_error(vol->sb, "Found stale reference to inode "
+					"0x%Lx (reference sequence number = "
+					"0x%x, inode sequence number = 0x%x, "
+					"returning -EACCES. Run chkdsk.",
+					(unsigned long long)MREF(mref),
+					MSEQNO(mref),
+					NTFS_I(dent_inode)->seq_no);
+			iput(dent_inode);
+		} else
+			ntfs_error(vol->sb, "iget(0x%Lx) failed, returning "
+					"-EACCES.",
+					(unsigned long long)MREF(mref));
+		return ERR_PTR(-EACCES);
+	}
+	if (MREF_ERR(mref) == -ENOENT) {
+		ntfs_debug("Entry was not found, adding negative dentry.");
+		/* The dcache will handle negative entries. */
+		d_add(dent, NULL);
+		ntfs_debug("Done.");
+		return NULL;
+	}
+	ntfs_error(vol->sb, "ntfs_lookup_ino_by_name() failed with error "
+			"code %i.", -MREF_ERR(mref));
+	return ERR_PTR(MREF_ERR(mref));
+}
+
+struct inode_operations ntfs_dir_inode_ops = {
+	create:		NULL,		/* . */
+	lookup:		ntfs_lookup,	/* lookup directory. */
+	link:		NULL,		/* . */
+	unlink:		NULL,		/* . */
+	symlink:	NULL,		/* . */
+	mkdir:		NULL,		/* . */
+	rmdir:		NULL,		/* . */
+	mknod:		NULL,		/* . */
+	rename:		NULL,		/* . */
+	readlink:	NULL,		/* . */
+	follow_link:	NULL,		/* . */
+	truncate:	NULL,		/* . */
+	permission:	NULL,		/* . */
+	revalidate:	NULL,		/* . */
+	setattr:	NULL,		/* . */
+	getattr:	NULL,		/* . */
+};
+
+#if 0
+struct inode_operations {
+	int (*create) (struct inode *,struct dentry *,int);
+	struct dentry * (*lookup) (struct inode *,struct dentry *);
+	int (*link) (struct dentry *,struct inode *,struct dentry *);
+	int (*unlink) (struct inode *,struct dentry *);
+	int (*symlink) (struct inode *,struct dentry *,const char *);
+	int (*mkdir) (struct inode *,struct dentry *,int);
+	int (*rmdir) (struct inode *,struct dentry *);
+	int (*mknod) (struct inode *,struct dentry *,int,int);
+	int (*rename) (struct inode *, struct dentry *,
+			struct inode *, struct dentry *);
+	int (*readlink) (struct dentry *, char *,int);
+	int (*follow_link) (struct dentry *, struct nameidata *);
+	void (*truncate) (struct inode *);
+	int (*permission) (struct inode *, int);
+	int (*revalidate) (struct dentry *);
+	int (*setattr) (struct dentry *, struct iattr *);
+	int (*getattr) (struct dentry *, struct iattr *);
+};
+#endif
+
diff -Nru a/fs/ntfs/ntfs.h b/fs/ntfs/ntfs.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/fs/ntfs/ntfs.h	Sun Apr 28 20:10:39 2002
@@ -0,0 +1,266 @@
+/*
+ * ntfs.h - Defines for NTFS Linux kernel driver. Part of the Linux-NTFS
+ *	    project.
+ *
+ * Copyright (c) 2001,2002 Anton Altaparmakov.
+ * Copyright (C) 2002 Richard Russon.
+ *
+ * This program/include file 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/include file 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 (in the main directory of the Linux-NTFS 
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _LINUX_NTFS_H
+#define _LINUX_NTFS_H
+
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,5)
+#	error The NTFS driver requires at least kernel 2.5.5.
+#endif
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/compiler.h>
+#include <linux/fs.h>
+#include <linux/nls.h>
+#include <linux/pagemap.h>
+#include <linux/smp.h>
+#include <asm/atomic.h>
+
+#include "types.h"
+#include "debug.h"
+#include "malloc.h"
+#include "endian.h"
+#include "volume.h"
+#include "inode.h"
+#include "layout.h"
+#include "attrib.h"
+#include "mft.h"
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+typedef long sector_t;
+#endif
+
+typedef enum {
+	NTFS_BLOCK_SIZE		= 512,
+	NTFS_BLOCK_SIZE_BITS	= 9,
+	NTFS_SB_MAGIC		= 0x5346544e,	/* 'NTFS' */
+	NTFS_MAX_NAME_LEN	= 255,
+} NTFS_CONSTANTS;
+
+/*
+ * Defined bits for the state field in the ntfs_inode structure.
+ * (f) = files only, (d) = directories only
+ */
+typedef enum {
+	NI_Dirty,		/* 1: Mft record needs to be written to disk. */
+	NI_AttrList,		/* 1: Mft record contains an attribute list. */
+	NI_AttrListNonResident,	/* 1: Attribute list is non-resident. Implies
+				      NI_AttrList is set. */
+	NI_NonResident,		/* 1: Unnamed data attr is non-resident (f).
+				   1: $I30 index alloc attr is present (d). */
+	NI_Compressed,		/* 1: Unnamed data attr is compressed (f).
+				   1: Create compressed files by default (d). */
+	NI_Encrypted,		/* 1: Unnamed data attr is encrypted (f).
+				   1: Create encrypted files by default (d). */
+	NI_BmpNonResident,	/* 1: $I30 bitmap attr is non resident (d). */
+} ntfs_inode_state_bits;
+
+/*
+ * NOTE: We should be adding dirty mft records to a list somewhere and they
+ * should be independent of the (ntfs/vfs) inode structure so that an inode can
+ * be removed but the record can be left dirty for syncing later.
+ */
+
+#define NInoDirty(n_ino)	  test_bit(NI_Dirty, &(n_ino)->state)
+#define NInoSetDirty(n_ino)	  set_bit(NI_Dirty, &(n_ino)->state)
+#define NInoClearDirty(n_ino)	  clear_bit(NI_Dirty, &(n_ino)->state)
+
+#define NInoAttrList(n_ino)	  test_bit(NI_AttrList, &(n_ino)->state)
+#define NInoNonResident(n_ino)	  test_bit(NI_NonResident, &(n_ino)->state)
+#define NInoIndexAllocPresent(n_ino) test_bit(NI_NonResident, &(n_ino)->state)
+#define NInoCompressed(n_ino)	  test_bit(NI_Compressed, &(n_ino)->state)
+#define NInoEncrypted(n_ino)	  test_bit(NI_Encrypted, &(n_ino)->state)
+#define NInoBmpNonResident(n_ino) test_bit(NI_BmpNonResident, &(n_ino)->state)
+
+/* Global variables. */
+
+/* Slab caches (from super.c). */
+extern kmem_cache_t *ntfs_name_cache;
+extern kmem_cache_t *ntfs_inode_cache;
+extern kmem_cache_t *ntfs_big_inode_cache;
+extern kmem_cache_t *ntfs_attr_ctx_cache;
+
+/* The little endian Unicode string $I30 as a global constant. */
+extern const uchar_t I30[5];
+
+/* The various operations structs defined throughout the driver files. */
+extern struct super_operations ntfs_mount_sops;
+extern struct super_operations ntfs_sops;
+extern struct file_operations ntfs_file_ops;
+extern struct inode_operations ntfs_file_inode_ops;
+extern struct address_space_operations ntfs_file_aops;
+extern struct file_operations ntfs_dir_ops;
+extern struct inode_operations ntfs_dir_inode_ops;
+extern struct address_space_operations ntfs_dir_aops;
+extern struct file_operations ntfs_empty_file_ops;
+extern struct inode_operations ntfs_empty_inode_ops;
+extern struct address_space_operations ntfs_mft_aops;
+extern struct address_space_operations ntfs_mftbmp_aops;
+
+/* Generic macro to convert pointers to values for comparison purposes. */
+#ifndef p2n
+#define p2n(p)          ((ptrdiff_t)((ptrdiff_t*)(p)))
+#endif
+
+/**
+ * NTFS_SB - return the ntfs volume given a vfs super block
+ * @sb:		VFS super block
+ *
+ * NTFS_SB() returns the ntfs volume associated with the VFS super block @sb.
+ */
+static inline ntfs_volume *NTFS_SB(struct super_block *sb)
+{
+	return sb->u.generic_sbp;
+}
+
+/**
+ * ntfs_unmap_page - release a page that was mapped using ntfs_map_page()
+ * @page:	the page to release
+ *
+ * Unpin, unmap and release a page that was obtained from ntfs_map_page().
+ */
+static inline void ntfs_unmap_page(struct page *page)
+{
+	kunmap(page);
+	page_cache_release(page);
+}
+
+/**
+ * ntfs_map_page - map a page into accessible memory, reading it if necessary
+ * @mapping:	address space for which to obtain the page
+ * @index:	index into the page cache for @mapping of the page to map
+ *
+ * Read a page from the page cache of the address space @mapping at position
+ * @index, where @index is in units of PAGE_CACHE_SIZE, and not in bytes.
+ *
+ * If the page is not in memory it is loaded from disk first using the readpage
+ * method defined in the address space operations of @mapping and the page is
+ * added to the page cache of @mapping in the process.
+ *
+ * If the page is in high memory it is mapped into memory directly addressible
+ * by the kernel.
+ *
+ * Finally the page count is incremented, thus pinning the page into place.
+ *
+ * The above means that page_address(page) can be used on all pages obtained
+ * with ntfs_map_page() to get the kernel virtual address of the page.
+ *
+ * When finished with the page, the caller has to call ntfs_unmap_page() to
+ * unpin, unmap and release the page.
+ *
+ * Note this does not grant exclusive access. If such is desired, the caller
+ * must provide it independently of the ntfs_{un}map_page() calls by using
+ * a {rw_}semaphore or other means of serialization. A spin lock cannot be
+ * used as ntfs_map_page() can block.
+ *
+ * The unlocked and uptodate page is returned on success or an encoded error
+ * on failure. Caller has to test for error using the IS_ERR() macro on the
+ * return value. If that evaluates to TRUE, the negative error code can be
+ * obtained using PTR_ERR() on the return value of ntfs_map_page().
+ */
+static inline struct page *ntfs_map_page(struct address_space *mapping,
+		unsigned long index)
+{
+	struct page *page = read_cache_page(mapping, index,
+			(filler_t*)mapping->a_ops->readpage, NULL);
+
+	if (!IS_ERR(page)) {
+		wait_on_page(page);
+		kmap(page);
+		if (Page_Uptodate(page) && !PageError(page))
+			return page;
+		ntfs_unmap_page(page);
+		return ERR_PTR(-EIO);
+	}
+	return page;
+}
+
+/* Declarations of functions and global variables. */
+
+/* From fs/ntfs/compress.c */
+extern int ntfs_file_read_compressed_block(struct page *page);
+
+/* From fs/ntfs/super.c */
+#define default_upcase_len 0x10000
+extern wchar_t *default_upcase;
+extern unsigned long ntfs_nr_upcase_users;
+extern unsigned long ntfs_nr_mounts;
+extern struct semaphore ntfs_lock;
+
+typedef struct {
+	int val;
+	char *str;
+} option_t;
+extern const option_t on_errors_arr[];
+
+/* From fs/ntfs/compress.c */
+extern int allocate_compression_buffers(void);
+extern void free_compression_buffers(void);
+
+/* From fs/ntfs/mst.c */
+extern int post_read_mst_fixup(NTFS_RECORD *b, const u32 size);
+extern int pre_write_mst_fixup(NTFS_RECORD *b, const u32 size);
+extern void post_write_mst_fixup(NTFS_RECORD *b);
+
+/* From fs/ntfs/time.c */
+extern inline s64 utc2ntfs(const time_t time);
+extern inline s64 get_current_ntfs_time(void);
+extern inline time_t ntfs2utc(const s64 time);
+
+/* From fs/ntfs/dir.c */
+extern u64 ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const uchar_t *uname,
+		const int uname_len);
+
+/* From fs/ntfs/unistr.c */
+extern BOOL ntfs_are_names_equal(const uchar_t *s1, size_t s1_len,
+		const uchar_t *s2, size_t s2_len,
+		const IGNORE_CASE_BOOL ic,
+		const uchar_t *upcase, const u32 upcase_size);
+extern int ntfs_collate_names(const uchar_t *name1, const u32 name1_len,
+		const uchar_t *name2, const u32 name2_len,
+		const int err_val, const IGNORE_CASE_BOOL ic,
+		const uchar_t *upcase, const u32 upcase_len);
+extern int ntfs_ucsncmp(const uchar_t *s1, const uchar_t *s2, size_t n);
+extern int ntfs_ucsncasecmp(const uchar_t *s1, const uchar_t *s2, size_t n,
+		const uchar_t *upcase, const u32 upcase_size);
+extern void ntfs_upcase_name(uchar_t *name, u32 name_len,
+		const uchar_t *upcase, const u32 upcase_len);
+extern void ntfs_file_upcase_value(FILE_NAME_ATTR *file_name_attr,
+		const uchar_t *upcase, const u32 upcase_len);
+extern int ntfs_file_compare_values(FILE_NAME_ATTR *file_name_attr1,
+		FILE_NAME_ATTR *file_name_attr2,
+		const int err_val, const IGNORE_CASE_BOOL ic,
+		const uchar_t *upcase, const u32 upcase_len);
+extern int ntfs_nlstoucs(const ntfs_volume *vol, const char *ins,
+		const int ins_len, uchar_t **outs);
+extern int ntfs_ucstonls(const ntfs_volume *vol, const uchar_t *ins,
+		const int ins_len, unsigned char **outs, int outs_len);
+
+/* From fs/ntfs/upcase.c */
+extern uchar_t *generate_default_upcase(void);
+
+#endif /* _LINUX_NTFS_H */
+
diff -Nru a/fs/ntfs/ntfsendian.h b/fs/ntfs/ntfsendian.h
--- a/fs/ntfs/ntfsendian.h	Sun Apr 28 20:10:36 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,60 +0,0 @@
-/*
- * ntfsendian.h
- *
- * Copyright (C) 1998, 1999 Martin von Löwis
- * Copyright (C) 1998 Joseph Malicki
- * Copyright (C) 1999 Werner Seiler
- * Copyright (C) 2001 Anton Altaparmakov (AIA)
- */
-#include <asm/byteorder.h>
-
-#define CPU_TO_LE16(a) __cpu_to_le16(a)
-#define CPU_TO_LE32(a) __cpu_to_le32(a)
-#define CPU_TO_LE64(a) __cpu_to_le64(a)
-
-#define LE16_TO_CPU(a) __cpu_to_le16(a)
-#define LE32_TO_CPU(a) __cpu_to_le32(a)
-#define LE64_TO_CPU(a) __cpu_to_le64(a)
-
-#define NTFS_GETU8(p)      (*(ntfs_u8*)(p))
-#define NTFS_GETU16(p)     ((ntfs_u16)LE16_TO_CPU(*(ntfs_u16*)(p)))
-#define NTFS_GETU24(p)     ((ntfs_u32)NTFS_GETU16(p) | \
-			   ((ntfs_u32)NTFS_GETU8(((char*)(p)) + 2) << 16))
-#define NTFS_GETU32(p)     ((ntfs_u32)LE32_TO_CPU(*(ntfs_u32*)(p)))
-#define NTFS_GETU40(p)     ((ntfs_u64)NTFS_GETU32(p) | \
-			   (((ntfs_u64)NTFS_GETU8(((char*)(p)) + 4)) << 32))
-#define NTFS_GETU48(p)     ((ntfs_u64)NTFS_GETU32(p) | \
-			   (((ntfs_u64)NTFS_GETU16(((char*)(p)) + 4)) << 32))
-#define NTFS_GETU56(p)     ((ntfs_u64)NTFS_GETU32(p) | \
-			   (((ntfs_u64)NTFS_GETU24(((char*)(p)) + 4)) << 32))
-#define NTFS_GETU64(p)     ((ntfs_u64)LE64_TO_CPU(*(ntfs_u64*)(p)))
- 
- /* Macros writing unsigned integers */
-#define NTFS_PUTU8(p,v)      ((*(ntfs_u8*)(p)) = (v))
-#define NTFS_PUTU16(p,v)     ((*(ntfs_u16*)(p)) = CPU_TO_LE16(v))
-#define NTFS_PUTU24(p,v)     NTFS_PUTU16(p, (v) & 0xFFFF);\
-                             NTFS_PUTU8(((char*)(p)) + 2, (v) >> 16)
-#define NTFS_PUTU32(p,v)     ((*(ntfs_u32*)(p)) = CPU_TO_LE32(v))
-#define NTFS_PUTU64(p,v)     ((*(ntfs_u64*)(p)) = CPU_TO_LE64(v))
- 
- /* Macros reading signed integers */
-#define NTFS_GETS8(p)        ((*(ntfs_s8*)(p)))
-#define NTFS_GETS16(p)       ((ntfs_s16)LE16_TO_CPU(*(short*)(p)))
-#define NTFS_GETS24(p)       (NTFS_GETU24(p) < 0x800000 ? \
-					(int)NTFS_GETU24(p) : \
-					(int)(NTFS_GETU24(p) - 0x1000000))
-#define NTFS_GETS32(p)       ((ntfs_s32)LE32_TO_CPU(*(int*)(p)))
-#define NTFS_GETS40(p)       (((ntfs_s64)NTFS_GETU32(p)) | \
-			     (((ntfs_s64)NTFS_GETS8(((char*)(p)) + 4)) << 32))
-#define NTFS_GETS48(p)       (((ntfs_s64)NTFS_GETU32(p)) | \
-			     (((ntfs_s64)NTFS_GETS16(((char*)(p)) + 4)) << 32))
-#define NTFS_GETS56(p)       (((ntfs_s64)NTFS_GETU32(p)) | \
-			     (((ntfs_s64)NTFS_GETS24(((char*)(p)) + 4)) << 32))
-#define NTFS_GETS64(p)	     ((ntfs_s64)NTFS_GETU64(p))
- 
-#define NTFS_PUTS8(p,v)      NTFS_PUTU8(p,v)
-#define NTFS_PUTS16(p,v)     NTFS_PUTU16(p,v)
-#define NTFS_PUTS24(p,v)     NTFS_PUTU24(p,v)
-#define NTFS_PUTS32(p,v)     NTFS_PUTU32(p,v)
-#define NTFS_PUTS64(p,v)     NTFS_PUTU64(p,v)
-
diff -Nru a/fs/ntfs/ntfstypes.h b/fs/ntfs/ntfstypes.h
--- a/fs/ntfs/ntfstypes.h	Sun Apr 28 20:10:37 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,84 +0,0 @@
-/*
- * ntfstypes.h - This file defines four things:
- * - Generic platform independent fixed-size types (e.g. ntfs_u32).
- * - Specific fixed-size types (e.g. ntfs_offset_t).
- * - Macros that read and write those types from and to byte arrays.
- * - Types derived from OS specific ones.
- *
- * Copyright (C) 1996, 1998, 1999 Martin von Löwis
- * Copyright (C) 2001 Anton Altaparmakov (AIA)
- */
-#include <linux/fs.h>
-#include "ntfsendian.h"
-#include <asm/types.h>
-
-/* Integral types */
-#ifndef NTFS_INTEGRAL_TYPES
-#define NTFS_INTEGRAL_TYPES
-typedef u8  ntfs_u8;
-typedef u16 ntfs_u16;
-typedef u32 ntfs_u32;
-typedef u64 ntfs_u64;
-typedef s8  ntfs_s8;
-typedef s16 ntfs_s16;
-typedef s32 ntfs_s32;
-typedef s64 ntfs_s64;
-#endif
-
-/* Unicode character type */
-#ifndef NTFS_WCHAR_T
-#define NTFS_WCHAR_T
-typedef u16 ntfs_wchar_t;
-#endif
-/* File offset */
-#ifndef NTFS_OFFSET_T
-#define NTFS_OFFSET_T
-typedef s64 ntfs_offset_t;
-#endif
-/* UTC */
-#ifndef NTFS_TIME64_T
-#define NTFS_TIME64_T
-typedef u64 ntfs_time64_t;
-#endif
-/*
- * This is really signed long long. So we support only volumes up to 2Tb. This
- * is ok as Win2k also only uses 32-bits to store clusters.
- * Whatever you do keep this a SIGNED value or a lot of NTFS users with
- * corrupted filesystems will lynch you! It causes massive fs corruption when
- * unsigned due to the nature of many checks relying on being performed on
- * signed quantities. (AIA)
- */
-#ifndef NTFS_CLUSTER_T
-#define NTFS_CLUSTER_T
-typedef s32 ntfs_cluster_t;
-#endif
-
-/* Architecture independent macros. */
-
-/* PUTU32 would not clear all bytes. */
-#define NTFS_PUTINUM(p,i)    NTFS_PUTU64(p, i->i_number); \
-                             NTFS_PUTU16(((char*)p) + 6, i->sequence_number)
-
-/* System dependent types. */
-#include <asm/posix_types.h>
-#ifndef NTMODE_T
-#define NTMODE_T
-typedef __kernel_mode_t ntmode_t;
-#endif
-#ifndef NTFS_UID_T
-#define NTFS_UID_T
-typedef uid_t ntfs_uid_t;
-#endif
-#ifndef NTFS_GID_T
-#define NTFS_GID_T
-typedef gid_t ntfs_gid_t;
-#endif
-#ifndef NTFS_SIZE_T
-#define NTFS_SIZE_T
-typedef __kernel_size_t ntfs_size_t;
-#endif
-#ifndef NTFS_TIME_T
-#define NTFS_TIME_T
-typedef __kernel_time_t ntfs_time_t;
-#endif
-
diff -Nru a/fs/ntfs/struct.h b/fs/ntfs/struct.h
--- a/fs/ntfs/struct.h	Sun Apr 28 20:10:35 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,69 +0,0 @@
-/*
- * struct.h - Structure definitions
- *
- * Copyright (C) 1997 Régis Duchesne
- * Copyright (C) 2000-2001 Anton Altaparmakov (AIA)
- */
-#include <linux/ntfs_fs.h>
-
-/* Necessary forward definition. */
-struct ntfs_inode;
-
-/* Which files should be returned from a director listing. */
-#define ngt_dos   1	/* only short names, no system files */
-#define ngt_nt    2	/* only long names, all-uppercase becomes 
-			 * all-lowercase, no system files */
-#define ngt_posix 3	/* all names except system files */
-#define ngt_full  4	/* all entries */
-
-typedef struct ntfs_sb_info ntfs_volume;
-
-typedef struct {
-	ntfs_cluster_t lcn;
-	ntfs_cluster_t len;
-} ntfs_runlist;
-
-typedef struct ntfs_attribute {
-	int type;
-	ntfs_u16 *name;
-	int namelen;
-	int attrno;
-	__s64 size, allocated, initialized, compsize;
-	ATTR_FLAGS flags;
-	__u8 resident, indexed;
-	int cengine;
-	union {
-		void *data;             /* if resident */
-		struct {
-			ntfs_runlist *runlist;
-			unsigned long len;
-		} r;
-	} d;
-} ntfs_attribute;
-
-typedef struct ntfs_inode_info ntfs_inode;
-
-/* Structure to define IO to user buffer. do_read means that the destination
- * has to be written using fn_put, do_write means that the destination has to
- * read using fn_get. So, do_read is from a user's point of view, while put and
- * get are from the driver's point of view. The first argument is always the
- * destination of the IO. */
-typedef struct ntfs_io{
-	int do_read;
-	void (*fn_put)(struct ntfs_io *dest, void *buf, ntfs_size_t);
-	void (*fn_get)(void *buf, struct ntfs_io *src, ntfs_size_t len);
-	void *param;
-	unsigned long size;
-} ntfs_io;
-
-#if 0
-typedef struct {
-	ntfs_volume *vol;
-	ntfs_inode *ino;
-	int type;
-	char *name;
-	int mftno;
-	int start_vcn;
-} ntfs_attrlist_item;
-#endif
-
diff -Nru a/fs/ntfs/super.c b/fs/ntfs/super.c
--- a/fs/ntfs/super.c	Sun Apr 28 20:10:37 2002
+++ b/fs/ntfs/super.c	Sun Apr 28 20:10:39 2002
@@ -1,1413 +1,1949 @@
 /*
- * super.c
+ * super.c - NTFS kernel super block handling. Part of the Linux-NTFS project.
  *
- * Copyright (C) 1995-1997, 1999 Martin von Löwis
- * Copyright (C) 1996-1997 Régis Duchesne
- * Copyright (C) 1999 Steve Dodd
- * Copyright (C) 2000-2001 Anton Altparmakov (AIA)
+ * Copyright (c) 2001,2002 Anton Altaparmakov.
+ * Copyright (C) 2001,2002 Richard Russon.
+ *
+ * This program/include file 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/include file 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 (in the main directory of the Linux-NTFS 
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
-#include <linux/ntfs_fs.h>
-#include <linux/errno.h>
-#include <linux/bitops.h>
-#include <linux/module.h>
-#include "ntfstypes.h"
-#include "struct.h"
-#include "super.h"
-#include "macros.h"
-#include "inode.h"
-#include "support.h"
-#include "util.h"
-#include <linux/smp_lock.h>
-
-/* All important structures in NTFS use 2 consistency checks:
- * . a magic structure identifier (FILE, INDX, RSTR, RCRD...)
- * . a fixup technique : the last word of each sector (called a fixup) of a
- *   structure's record should end with the word at offset <n> of the first
- *   sector, and if it is the case, must be replaced with the words following
- *   <n>. The value of <n> and the number of fixups is taken from the fields
- *   at the offsets 4 and 6. Note that the sector size is defined as
- *   NTFS_SECTOR_SIZE and not as the hardware sector size (this is concordant
- *   with what the Windows NTFS driver does).
- *
- * This function performs these 2 checks, and _fails_ if:
- * . the input size is invalid
- * . the fixup header is invalid
- * . the size does not match the number of sectors
- * . the magic identifier is wrong
- * . a fixup is invalid
+#include <linux/stddef.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/spinlock.h>
+#include <linux/blkdev.h>	/* For bdev_hardsect_size(). */
+
+#include "ntfs.h"
+#include "sysctl.h"
+
+/* Number of mounted file systems which have compression enabled. */
+static unsigned long ntfs_nr_compression_users = 0;
+
+/* Error constants/strings used in inode.c::ntfs_show_options(). */
+typedef enum {
+	/* One of these must be present, default is ON_ERRORS_CONTINUE. */
+	ON_ERRORS_PANIC			= 0x01,
+	ON_ERRORS_REMOUNT_RO		= 0x02,
+	ON_ERRORS_CONTINUE		= 0x04,
+	/* Optional, can be combined with any of the above. */
+	ON_ERRORS_RECOVER		= 0x10,
+} ON_ERRORS_ACTIONS;
+
+const option_t on_errors_arr[] = {
+	{ ON_ERRORS_PANIC,	"panic" },
+	{ ON_ERRORS_REMOUNT_RO,	"remount-ro", },
+	{ ON_ERRORS_CONTINUE,	"continue", },
+	{ ON_ERRORS_RECOVER,	"recover" },
+	{ 0,			NULL }
+};
+
+static const option_t readdir_opts_arr[] = {
+	{ SHOW_SYSTEM,	"system" },
+	{ SHOW_WIN32,	"win32" },
+	{ SHOW_WIN32,	"long" },
+	{ SHOW_DOS,	"dos" },
+	{ SHOW_DOS,	"short" },
+	{ SHOW_POSIX,	"posix" },
+	{ SHOW_ALL,	"all" },
+	{ 0,		NULL }
+};
+
+/**
+ * simple_getbool -
+ *
+ * Copied from old ntfs driver (which copied from vfat driver).
  */
-int ntfs_fixup_record(char *record, char *magic, int size)
+static int simple_getbool(char *s, BOOL *setval)
 {
-	int start, count, offset;
-	ntfs_u16 fixup;
+	if (s) {
+		if (!strcmp(s, "1") || !strcmp(s, "yes") || !strcmp(s, "true"))
+			*setval = TRUE;
+		else if (!strcmp(s, "0") || !strcmp(s, "no") ||
+							!strcmp(s, "false"))
+			*setval = FALSE;
+		else
+			return 0;
+	} else
+		*setval = TRUE;
+	return 1;
+}
 
-	if (!IS_MAGIC(record, magic))
-		return 0;
-	start = NTFS_GETU16(record + 4);
-	count = NTFS_GETU16(record + 6) - 1;
-	if (size & (NTFS_SECTOR_SIZE - 1) || start & 1 ||
-			start + count * 2 > size || size >> 9 != count) {
-		if (size <= 0)
-			printk(KERN_ERR "NTFS: BUG: ntfs_fixup_record() got "
-					"zero size! Please report this to "
-					"linux-ntfs-dev@lists.sf.net\n");
-		return 0;
+/**
+ * parse_options - parse the (re)mount options
+ * @vol:	ntfs volume
+ * @opt:	string containing the (re)mount options
+ *
+ * Parse the recognized options in @opt for the ntfs volume described by @vol.
+ */
+static BOOL parse_options(ntfs_volume *vol, char *opt)
+{
+	char *p, *v, *ov;
+	static char *utf8 = "utf8";
+	int errors = 0, sloppy = 0;
+	uid_t uid = (uid_t)-1;
+	gid_t gid = (gid_t)-1;
+	mode_t fmask = (mode_t)-1, dmask = (mode_t)-1;
+	int mft_zone_multiplier = -1, on_errors = -1, readdir_opts = -1;
+	struct nls_table *nls_map = NULL, *old_nls;
+
+	/* I am lazy... (-8 */
+#define NTFS_GETOPT_WITH_DEFAULT(option, variable, default_value)	\
+	if (!strcmp(p, option)) {					\
+		if (!v || !*v)						\
+			variable = default_value;			\
+		else {							\
+			variable = simple_strtoul(ov = v, &v, 0);	\
+			if (*v)						\
+				goto needs_val;				\
+		}							\
+	} 
+#define NTFS_GETOPT(option, variable)					\
+	if (!strcmp(p, option)) {					\
+		if (!v || !*v)						\
+			goto needs_arg;					\
+		variable = simple_strtoul(ov = v, &v, 0);		\
+		if (*v)							\
+			goto needs_val;					\
+	} 
+#define NTFS_GETOPT_OPTIONS_ARRAY(option, variable, opt_array)		\
+	if (!strcmp(p, option)) {					\
+		int _i;							\
+		if (!v || !*v)						\
+			goto needs_arg;					\
+		ov = v;							\
+		if (variable == -1)					\
+			variable = 0;					\
+		for (_i = 0; opt_array[_i].str && *opt_array[_i].str; _i++) \
+			if (!strcmp(opt_array[_i].str, v)) {		\
+				variable |= opt_array[_i].val;		\
+				break;					\
+			}						\
+		if (!opt_array[_i].str || !*opt_array[_i].str)		\
+			goto needs_val;					\
+	}
+	if (!opt || !*opt)
+		goto no_mount_options;
+	while ((p = strsep(&opt, ","))) {
+		if ((v = strchr(p, '=')))
+			*v++ = '\0';
+		NTFS_GETOPT("uid", uid)
+		else NTFS_GETOPT("gid", gid)
+		else NTFS_GETOPT("umask", fmask = dmask)
+		else NTFS_GETOPT("fmask", fmask)
+		else NTFS_GETOPT("dmask", dmask)
+		else NTFS_GETOPT_WITH_DEFAULT("sloppy", sloppy, TRUE)
+		else NTFS_GETOPT("mft_zone_multiplier", mft_zone_multiplier)
+		else NTFS_GETOPT_OPTIONS_ARRAY("errors", on_errors,
+				on_errors_arr)
+		else NTFS_GETOPT_OPTIONS_ARRAY("show_inodes", readdir_opts,
+				readdir_opts_arr)
+		else if (!strcmp(p, "show_sys_files")) {
+			BOOL val = FALSE;
+			ntfs_warning(vol->sb, "Option show_sys_files is "
+				   "deprecated. Please use option "
+				   "show_inodes=system in the future.");
+			if (!v || !*v)
+				val = TRUE;
+			else if (!simple_getbool(v, &val))
+				goto needs_bool;
+			if (val) {
+				if (readdir_opts == -1)
+					readdir_opts = 0;
+				readdir_opts |= SHOW_SYSTEM;
+			}
+		} else if (!strcmp(p, "posix")) {
+			BOOL val = FALSE;
+			ntfs_warning(vol->sb, "Option posix is deprecated. "
+				   "Please use option show_inodes=posix "
+				   "instead. Be aware that some userspace "
+				   "applications may be confused by this, "
+				   "since the short and long names of "
+				   "directory inodes will have the same inode "
+				   "numbers, yet each will only have a link "
+				   "count of 1 due to Linux not supporting "
+				   "directory hard links.");
+			if (!v || !*v)
+				goto needs_arg;
+			else if (!simple_getbool(v, &val))
+				goto needs_bool;
+			if (val) {
+				if (readdir_opts == -1)
+					readdir_opts = 0;
+				readdir_opts |= SHOW_POSIX;
+			}
+		} else if (!strcmp(p, "nls") || !strcmp(p, "iocharset")) {
+			if (!strcmp(p, "iocharset"))
+				ntfs_warning(vol->sb, "Option iocharset is "
+						"deprecated. Please use "
+						"option nls=<charsetname> in "
+						"the future.");
+			if (!v || !*v)
+				goto needs_arg;
+use_utf8:
+			old_nls = nls_map;
+			nls_map = load_nls(v);
+			if (!nls_map) {
+				if (!old_nls) {
+					ntfs_error(vol->sb, "NLS character set "
+							"%s not found.", v);
+					return FALSE;
+				}
+				ntfs_error(vol->sb, "NLS character set %s not "
+						"found. Using previous one %s.",
+						v, old_nls->charset);
+				nls_map = old_nls;
+			} else /* nls_map */ {
+				if (old_nls)
+					unload_nls(old_nls);
+			}
+		} else if (!strcmp(p, "utf8")) {
+			BOOL val = FALSE;
+			ntfs_warning(vol->sb, "Option utf8 is no longer "
+				   "supported, using option nls=utf8. Please "
+				   "use option nls=utf8 in the future and "
+				   "make sure utf8 is compiled either as a "
+				   "module or into the kernel.");
+			if (!v || !*v)
+				val = TRUE;
+			else if (!simple_getbool(v, &val))
+				goto needs_bool;
+			if (val) {
+				v = utf8;
+				goto use_utf8;
+			}
+		} else {
+			ntfs_error(vol->sb, "Unrecognized mount option %s.", p);
+			if (errors < INT_MAX)
+				errors++;
+		}
+#undef NTFS_GETOPT_OPTIONS_ARRAY
+#undef NTFS_GETOPT
+#undef NTFS_GETOPT_WITH_DEFAULT
+	}
+no_mount_options:
+	if (errors && !sloppy)
+		return FALSE;
+	if (sloppy)
+		ntfs_warning(vol->sb, "Sloppy option given. Ignoring "
+				"unrecognized mount option(s) and continuing.");
+	/* Keep this first! */
+	if (on_errors != -1) {
+		if (!on_errors) {
+			ntfs_error(vol->sb, "Invalid errors option argument "
+					"or bug in options parser.");
+			return FALSE;
+		}
 	}
-	fixup = NTFS_GETU16(record + start);
-	start += 2;
-	offset = NTFS_SECTOR_SIZE - 2;
-	while (count--) {
-		if (NTFS_GETU16(record + offset) != fixup)
-			return 0;
-		NTFS_PUTU16(record + offset, NTFS_GETU16(record + start));
-		start += 2;
-		offset += NTFS_SECTOR_SIZE;
+	if (nls_map) {
+		if (vol->nls_map) {
+			ntfs_error(vol->sb, "Cannot change NLS character set "
+					"on remount.");
+			return FALSE;
+		} /* else (!vol->nls_map) */
+		ntfs_debug("Using NLS character set %s.", nls_map->charset);
+		vol->nls_map = nls_map;
+	} else /* (!nls_map) */ {
+		if (!vol->nls_map) {
+			vol->nls_map = load_nls_default();
+			if (!vol->nls_map) {
+				ntfs_error(vol->sb, "Failed to load default "
+						"NLS character set.");
+				return FALSE;
+			}
+			ntfs_debug("Using default NLS character set (%s).",
+					vol->nls_map->charset);
+		}
 	}
-	return 1;
+	if (mft_zone_multiplier != -1) {
+		if (vol->mft_zone_multiplier && vol->mft_zone_multiplier !=
+				mft_zone_multiplier) {
+			ntfs_error(vol->sb, "Cannot change mft_zone_multiplier "
+					"on remount.");
+			return FALSE;
+		}
+		if (mft_zone_multiplier < 1 || mft_zone_multiplier > 4) {
+			ntfs_error(vol->sb, "Invalid mft_zone_multiplier. "
+					"Using default value, i.e. 1.");
+			mft_zone_multiplier = 1;
+		}
+		vol->mft_zone_multiplier = mft_zone_multiplier;
+	} if (!vol->mft_zone_multiplier)
+		/* Not specified and it is the first mount, so set default. */
+		vol->mft_zone_multiplier = 1;
+	if (on_errors != -1)
+		vol->on_errors = on_errors;
+	if (!vol->on_errors || vol->on_errors == ON_ERRORS_RECOVER)
+		vol->on_errors |= ON_ERRORS_CONTINUE;
+	if (uid != (uid_t)-1)
+		vol->uid = uid;
+	if (gid != (gid_t)-1)
+		vol->gid = gid;
+	if (fmask != (mode_t)-1)
+		vol->fmask = fmask;
+	if (dmask != (mode_t)-1)
+		vol->dmask = dmask;
+	if (readdir_opts != -1)
+		vol->readdir_opts = readdir_opts;
+	return TRUE;
+needs_arg:
+	ntfs_error(vol->sb, "The %s option requires an argument.", p);
+	return FALSE;
+needs_bool:
+	ntfs_error(vol->sb, "The %s option requires a boolean argument.", p);
+	return FALSE;
+needs_val:
+	ntfs_error(vol->sb, "Invalid %s option argument: %s", p, ov);
+	return FALSE;
 }
 
-/*
- * Get vital informations about the ntfs partition from the boot sector.
- * Return 0 on success or -1 on error.
+/**
+ * ntfs_remount - change the mount options of a mounted ntfs filesystem
+ * @sb:		superblock of mounted ntfs filesystem
+ * @flags:	remount flags
+ * @opt:	remount options string
+ *
+ * Change the mount options of an already mounted ntfs filesystem.
+ *
+ * NOTE: The VFS set the @sb->s_flags remount flags to @flags after
+ * ntfs_remount() returns successfully (i.e. returns 0). Otherwise,
+ * @sb->s_flags are not changed.
+ */
+static int ntfs_remount(struct super_block *sb, int *flags, char *opt)
+{
+	ntfs_volume *vol = NTFS_SB(sb);
+
+	ntfs_debug("Entering.");
+
+	// FIXME/TODO: If left like this we will have problems with rw->ro and
+	// ro->rw, as well as with sync->async and vice versa remounts.
+	// Note: The VFS already checks that there are no pending deletes and
+	// no open files for writing. So we only need to worry about dirty
+	// inode pages and dirty system files (which include dirty inodes).
+	// Either handle by flushing the whole volume NOW or by having the
+	// write routines work on MS_RDONLY fs and guarantee we don't mark
+	// anything as dirty if MS_RDONLY is set. That way the dirty data
+	// would get flushed but no new dirty data would appear. This is
+	// probably best but we need to be careful not to mark anything dirty
+	// or the MS_RDONLY will be leaking writes.
+
+	// TODO: Deal with *flags.
+
+	if (!parse_options(vol, opt))
+		return -EINVAL;
+	return 0;
+}
+
+/**
+ * is_boot_sector_ntfs - check whether a boot sector is a valid NTFS boot sector
+ * @sb:		Super block of the device to which @b belongs.
+ * @b:		Boot sector of device @sb to check.
+ * @silent:	If TRUE, all output will be silenced.
+ *
+ * is_boot_sector_ntfs() checks whether the boot sector @b is a valid NTFS boot
+ * sector. Returns TRUE if it is valid and FALSE if not.
+ *
+ * @sb is only needed for warning/error output, i.e. it can be NULL when silent
+ * is TRUE.
+ */
+static BOOL is_boot_sector_ntfs(const struct super_block *sb,
+		const NTFS_BOOT_SECTOR *b, const BOOL silent)
+{
+	/*
+	 * Check that checksum == sum of u32 values from b to the checksum
+	 * field. If checksum is zero, no checking is done.
+	 */
+	if ((void*)b < (void*)&b->checksum && b->checksum) {
+		u32 i, *u;
+		for (i = 0, u = (u32*)b; u < (u32*)(&b->checksum); ++u)
+			i += le32_to_cpup(u);
+		if (le32_to_cpu(b->checksum) != i)
+			goto not_ntfs;
+	}
+	/* Check OEMidentifier is "NTFS    " */
+	if (b->oem_id != magicNTFS)
+		goto not_ntfs;
+	/* Check bytes per sector value is between 256 and 4096. */
+	if (le16_to_cpu(b->bpb.bytes_per_sector) <  0x100 ||
+			le16_to_cpu(b->bpb.bytes_per_sector) > 0x1000)
+		goto not_ntfs;
+	/* Check sectors per cluster value is valid. */
+	switch (b->bpb.sectors_per_cluster) {
+	case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128:
+		break;
+	default:
+		goto not_ntfs;
+	}
+	/* Check the cluster size is not above 65536 bytes. */
+	if ((u32)le16_to_cpu(b->bpb.bytes_per_sector) *
+			b->bpb.sectors_per_cluster > 0x10000)
+		goto not_ntfs;
+	/* Check reserved/unused fields are really zero. */
+	if (le16_to_cpu(b->bpb.reserved_sectors) ||
+			le16_to_cpu(b->bpb.root_entries) ||
+			le16_to_cpu(b->bpb.sectors) ||
+			le16_to_cpu(b->bpb.sectors_per_fat) ||
+			le32_to_cpu(b->bpb.large_sectors) || b->bpb.fats)
+		goto not_ntfs;
+	/* Check clusters per file mft record value is valid. */
+	if ((u8)b->clusters_per_mft_record < 0xe1 || 
+			(u8)b->clusters_per_mft_record > 0xf7)
+		switch (b->clusters_per_mft_record) {
+		case 1: case 2: case 4: case 8: case 16: case 32: case 64:
+			break;
+		default:
+			goto not_ntfs;
+		}
+	/* Check clusters per index block value is valid. */
+	if ((u8)b->clusters_per_index_record < 0xe1 || 
+			(u8)b->clusters_per_index_record > 0xf7)
+		switch (b->clusters_per_index_record) {
+		case 1: case 2: case 4: case 8: case 16: case 32: case 64:
+			break;
+		default:
+			goto not_ntfs;
+		}
+	/*
+	 * Check for valid end of sector marker. We will work without it, but
+	 * many BIOSes will refuse to boot from a bootsector if the magic is
+	 * incorrect, so we emit a warning.
+	 */
+	if (!silent && b->end_of_sector_marker != cpu_to_le16(0xaa55))
+		ntfs_warning(sb, "Invalid end of sector marker.");
+	return TRUE;
+not_ntfs:
+	return FALSE;
+}
+
+/**
+ * read_boot_sector - read the NTFS boot sector of a device
+ * @sb:		super block of device to read the boot sector from
+ * @silent:	if true, suppress all output
+ *
+ * Reads the boot sector from the device and validates it. If that fails, tries
+ * to read the backup boot sector, first from the end of the device a-la NT4 and
+ * later and then from the middle of the device a-la NT3.51 and before.
+ *
+ * If a valid boot sector is found but it is not the primary boot sector, we
+ * repair the primary boot sector silently (unless the device is read-only or
+ * the primary boot sector is not accessible).
+ *
+ * NOTE: To call this function, @sb must have the fields s_dev, the ntfs super
+ * block (u.ntfs_sb), nr_blocks and the device flags (s_flags) initialized
+ * to their respective values.
+ *
+ * Return the unlocked buffer head containing the boot sector or NULL on error.
+ */
+static struct buffer_head *read_ntfs_boot_sector(struct super_block *sb,
+		const int silent)
+{
+	const char *read_err_str = "Unable to read %s boot sector.";
+	struct buffer_head *bh_primary, *bh_backup;
+	long nr_blocks = NTFS_SB(sb)->nr_blocks;
+
+	/* Try to read primary boot sector. */
+	if ((bh_primary = sb_bread(sb, 0))) {
+		if (is_boot_sector_ntfs(sb, (NTFS_BOOT_SECTOR*)
+				bh_primary->b_data, silent))
+			return bh_primary;
+		if (!silent)
+			ntfs_error(sb, "Primary boot sector is invalid.");
+	} else if (!silent)
+		ntfs_error(sb, read_err_str, "primary");
+	if (NTFS_SB(sb)->on_errors & ~ON_ERRORS_RECOVER) {
+		if (bh_primary)
+			brelse(bh_primary);
+		if (!silent)
+			ntfs_error(sb, "Mount option errors=recover not used. "
+					"Aborting without trying to recover.");
+		return NULL;
+	}
+	/* Try to read NT4+ backup boot sector. */
+	if ((bh_backup = sb_bread(sb, nr_blocks - 1))) {
+		if (is_boot_sector_ntfs(sb, (NTFS_BOOT_SECTOR*)
+				bh_backup->b_data, silent))
+			goto hotfix_primary_boot_sector;
+		brelse(bh_backup);
+	} else if (!silent)
+		ntfs_error(sb, read_err_str, "backup");
+	/* Try to read NT3.51- backup boot sector. */
+	if ((bh_backup = sb_bread(sb, nr_blocks >> 1))) {
+		if (is_boot_sector_ntfs(sb, (NTFS_BOOT_SECTOR*)
+				bh_backup->b_data, silent))
+			goto hotfix_primary_boot_sector;
+		if (!silent)
+			ntfs_error(sb, "Could not find a valid backup boot "
+					"sector.");
+		brelse(bh_backup);
+	} else if (!silent)
+		ntfs_error(sb, read_err_str, "backup");
+	/* We failed. Cleanup and return. */
+	if (bh_primary)
+		brelse(bh_primary);
+	return NULL;
+hotfix_primary_boot_sector:
+	if (bh_primary) {
+		/*
+		 * If we managed to read sector zero and the volume is not
+		 * read-only, copy the found, valid backup boot sector to the
+		 * primary boot sector.
+		 */
+		if (!(sb->s_flags & MS_RDONLY)) {
+			ntfs_warning(sb, "Hot-fix: Recovering invalid primary "
+					"boot sector from backup copy.");
+			memcpy(bh_primary->b_data, bh_backup->b_data,
+					sb->s_blocksize);
+			mark_buffer_dirty(bh_primary);
+			ll_rw_block(WRITE, 1, &bh_primary);
+			wait_on_buffer(bh_primary);
+			if (buffer_uptodate(bh_primary)) {
+				brelse(bh_backup);
+				return bh_primary;
+			}
+			ntfs_error(sb, "Hot-fix: Device write error while "
+					"recovering primary boot sector.");
+		} else {
+			ntfs_warning(sb, "Hot-fix: Recovery of primary boot "
+					"sector failed: Read-only mount.");
+		}
+		brelse(bh_primary);
+	}
+	ntfs_warning(sb, "Using backup boot sector.");
+	return bh_backup;
+}
+
+/**
+ * parse_ntfs_boot_sector - parse the boot sector and store the data in @vol
+ * @vol:	volume structure to initialise with data from boot sector
+ * @b:		boot sector to parse
+ * 
+ * Parse the ntfs boot sector @b and store all imporant information therein in
+ * the ntfs super block @vol. Return TRUE on success and FALSE on error.
  */
-int ntfs_init_volume(ntfs_volume *vol, char *boot)
+static BOOL parse_ntfs_boot_sector(ntfs_volume *vol, const NTFS_BOOT_SECTOR *b)
 {
-	int sectors_per_cluster_bits;
-	__s64 ll;
-	ntfs_cluster_t mft_zone_size, tc;
-
-	/* System defined default values, in case we don't load $AttrDef. */
-	vol->at_standard_information = 0x10;
-	vol->at_attribute_list = 0x20;
-	vol->at_file_name = 0x30;
-	vol->at_volume_version = 0x40;
-	vol->at_security_descriptor = 0x50;
-	vol->at_volume_name = 0x60;
-	vol->at_volume_information = 0x70;
-	vol->at_data = 0x80;
-	vol->at_index_root = 0x90;
-	vol->at_index_allocation = 0xA0;
-	vol->at_bitmap = 0xB0;
-	vol->at_symlink = 0xC0;
-	/* Sector size. */
-	vol->sector_size = NTFS_GETU16(boot + 0xB);
-	ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->sector_size = 0x%x\n",
-				vol->sector_size);
-	ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: sectors_per_cluster = "
-				"0x%x\n", NTFS_GETU8(boot + 0xD));
-	sectors_per_cluster_bits = ffs(NTFS_GETU8(boot + 0xD)) - 1;
-	ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: sectors_per_cluster_bits "
-				"= 0x%x\n", sectors_per_cluster_bits); 
-	vol->mft_clusters_per_record = NTFS_GETS8(boot + 0x40);
-	ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->mft_clusters_per_record"
-				" = 0x%x\n", vol->mft_clusters_per_record); 
-	vol->index_clusters_per_record = NTFS_GETS8(boot + 0x44);
-	ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: "
-				"vol->index_clusters_per_record = 0x%x\n",
-				vol->index_clusters_per_record); 
+	unsigned int sectors_per_cluster_bits, nr_hidden_sects;
+	int clusters_per_mft_record, clusters_per_index_record;
+	s64 ll;
+
+	vol->sector_size = le16_to_cpu(b->bpb.bytes_per_sector);
+	vol->sector_size_bits = ffs(vol->sector_size) - 1;
+	ntfs_debug("vol->sector_size = %i (0x%x)", vol->sector_size,
+			vol->sector_size);
+	ntfs_debug("vol->sector_size_bits = %i (0x%x)", vol->sector_size_bits,
+			vol->sector_size_bits);
+	if (vol->sector_size != vol->sb->s_blocksize)
+		ntfs_warning(vol->sb, "The boot sector indicates a sector size "
+				"different from the device sector size.");
+	ntfs_debug("sectors_per_cluster = 0x%x", b->bpb.sectors_per_cluster);
+	sectors_per_cluster_bits = ffs(b->bpb.sectors_per_cluster) - 1;
+	ntfs_debug("sectors_per_cluster_bits = 0x%x",
+			sectors_per_cluster_bits);
+	nr_hidden_sects = le32_to_cpu(b->bpb.hidden_sectors);
+	ntfs_debug("number of hidden sectors = 0x%x", nr_hidden_sects);
 	vol->cluster_size = vol->sector_size << sectors_per_cluster_bits;
+	vol->cluster_size_mask = vol->cluster_size - 1;
 	vol->cluster_size_bits = ffs(vol->cluster_size) - 1;
-	ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->cluster_size = 0x%x\n",
-				vol->cluster_size); 
-	ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->cluster_size_bits = "
-				"0x%x\n", vol->cluster_size_bits); 
-	if (vol->mft_clusters_per_record > 0)
+	ntfs_debug("vol->cluster_size = %i (0x%x)", vol->cluster_size,
+			vol->cluster_size);
+	ntfs_debug("vol->cluster_size_mask = 0x%x", vol->cluster_size_mask);
+	ntfs_debug("vol->cluster_size_bits = %i (0x%x)",
+			vol->cluster_size_bits, vol->cluster_size_bits);
+	if (vol->sector_size > vol->cluster_size) {
+		ntfs_error(vol->sb, "Sector sizes above the cluster size are "
+				"not supported. Sorry.");
+		return FALSE;
+	}
+	if (vol->sb->s_blocksize > vol->cluster_size) {
+		ntfs_error(vol->sb, "Cluster sizes smaller than the device "
+				"sector size are not supported. Sorry.");
+		return FALSE;
+	}
+	clusters_per_mft_record = b->clusters_per_mft_record;
+	ntfs_debug("clusters_per_mft_record = %i (0x%x)",
+			clusters_per_mft_record, clusters_per_mft_record);
+	if (clusters_per_mft_record > 0)
 		vol->mft_record_size = vol->cluster_size <<
-				(ffs(vol->mft_clusters_per_record) - 1);
+				(ffs(clusters_per_mft_record) - 1);
 	else
 		/*
-		 * When mft_record_size < cluster_size, mft_clusters_per_record
+		 * When mft_record_size < cluster_size, clusters_per_mft_record
 		 * = -log2(mft_record_size) bytes. mft_record_size normaly is
 		 * 1024 bytes, which is encoded as 0xF6 (-10 in decimal).
 		 */
-		vol->mft_record_size = 1 << -vol->mft_clusters_per_record;
+		vol->mft_record_size = 1 << -clusters_per_mft_record;
+	vol->mft_record_size_mask = vol->mft_record_size - 1;
 	vol->mft_record_size_bits = ffs(vol->mft_record_size) - 1;
-	ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->mft_record_size = 0x%x"
-				"\n", vol->mft_record_size); 
-	ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->mft_record_size_bits = "
-				"0x%x\n", vol->mft_record_size_bits); 
-	if (vol->index_clusters_per_record > 0)
+	ntfs_debug("vol->mft_record_size = %i (0x%x)", vol->mft_record_size,
+			vol->mft_record_size);
+	ntfs_debug("vol->mft_record_size_mask = 0x%x",
+			vol->mft_record_size_mask);
+	ntfs_debug("vol->mft_record_size_bits = %i (0x%x)",
+			vol->mft_record_size_bits, vol->mft_record_size_bits); 
+	clusters_per_index_record = b->clusters_per_index_record;
+	ntfs_debug("clusters_per_index_record = %i (0x%x)",
+			clusters_per_index_record, clusters_per_index_record); 
+	if (clusters_per_index_record > 0)
 		vol->index_record_size = vol->cluster_size <<
-				(ffs(vol->index_clusters_per_record) - 1);
+				(ffs(clusters_per_index_record) - 1);
 	else
 		/*
 		 * When index_record_size < cluster_size,
-		 * index_clusters_per_record = -log2(index_record_size) bytes.
+		 * clusters_per_index_record = -log2(index_record_size) bytes.
 		 * index_record_size normaly equals 4096 bytes, which is
 		 * encoded as 0xF4 (-12 in decimal).
 		 */
-		vol->index_record_size = 1 << -vol->index_clusters_per_record;
+		vol->index_record_size = 1 << -clusters_per_index_record;
+	vol->index_record_size_mask = vol->index_record_size - 1;
 	vol->index_record_size_bits = ffs(vol->index_record_size) - 1;
-	ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->index_record_size = "
-				"0x%x\n", vol->index_record_size);
-	ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->index_record_size_bits "
-				"= 0x%x\n", vol->index_record_size_bits);
-	/*
-	 * Get the size of the volume in clusters (ofs 0x28 is nr_sectors) and
-	 * check for 64-bit-ness. Windows currently only uses 32 bits to save
-	 * the clusters so we do the same as it is much faster on 32-bit CPUs.
-	 */
-	ll = NTFS_GETS64(boot + 0x28) >> sectors_per_cluster_bits;
-	if (ll >= (__s64)1 << 31) {
-		ntfs_error("Cannot handle 64-bit clusters. Please inform "
-				"linux-ntfs-dev@lists.sf.net that you got this "
-				"error.\n");
-		return -1;
-	}
-	vol->nr_clusters = (ntfs_cluster_t)ll;
-	ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->nr_clusters = 0x%x\n",
-			vol->nr_clusters);
-	vol->mft_lcn = (ntfs_cluster_t)NTFS_GETS64(boot + 0x30);
-	vol->mft_mirr_lcn = (ntfs_cluster_t)NTFS_GETS64(boot + 0x38);
-	/* Determine MFT zone size. */
-	mft_zone_size = vol->nr_clusters;
+	ntfs_debug("vol->index_record_size = %i (0x%x)",
+			vol->index_record_size, vol->index_record_size); 
+	ntfs_debug("vol->index_record_size_mask = 0x%x",
+			vol->index_record_size_mask);
+	ntfs_debug("vol->index_record_size_bits = %i (0x%x)",
+			vol->index_record_size_bits,
+			vol->index_record_size_bits);
+	/*
+	 * Get the size of the volume in clusters and check for 64-bit-ness.
+	 * Windows currently only uses 32 bits to save the clusters so we do
+	 * the same as it is much faster on 32-bit CPUs.
+	 */
+	ll = sle64_to_cpu(b->number_of_sectors) >> sectors_per_cluster_bits;
+	if ((u64)ll >= 1ULL << (sizeof(unsigned long) * 8)) {
+		ntfs_error(vol->sb, "Cannot handle %i-bit clusters. Sorry.",
+				sizeof(unsigned long) * 4);
+		return FALSE;
+	}
+	vol->_VCL(nr_clusters) = ll;
+	ntfs_debug("vol->nr_clusters = 0x%Lx", (long long)vol->_VCL(nr_clusters));
+	ll = sle64_to_cpu(b->mft_lcn);
+	if (ll >= vol->_VCL(nr_clusters)) {
+		ntfs_error(vol->sb, "MFT LCN is beyond end of volume. Weird.");
+		return FALSE;
+	}
+	vol->mft_lcn = ll;
+	ntfs_debug("vol->mft_lcn = 0x%Lx", (long long)vol->mft_lcn);
+	ll = sle64_to_cpu(b->mftmirr_lcn);
+	if (ll >= vol->_VCL(nr_clusters)) {
+		ntfs_error(vol->sb, "MFTMirr LCN is beyond end of volume. "
+				"Weird.");
+		return FALSE;
+	}
+	vol->mftmirr_lcn = ll;
+	ntfs_debug("vol->mftmirr_lcn = 0x%Lx", (long long)vol->mftmirr_lcn);
+	vol->serial_no = le64_to_cpu(b->volume_serial_number);
+	ntfs_debug("vol->serial_no = 0x%Lx",
+			(unsigned long long)vol->serial_no);
+	/*
+	 * Determine MFT zone size. This is not strictly the right place to do
+	 * this, but I am too lazy to create a function especially for it...
+	 */
+	vol->mft_zone_end = vol->_VCL(nr_clusters);
 	switch (vol->mft_zone_multiplier) {  /* % of volume size in clusters */
 	case 4:
-		mft_zone_size >>= 1;			/* 50%   */
+		vol->mft_zone_end = vol->mft_zone_end >> 1;	/* 50%   */
 		break;
 	case 3:
-		mft_zone_size = mft_zone_size * 3 >> 3;	/* 37.5% */
+		vol->mft_zone_end = (vol->mft_zone_end +
+				(vol->mft_zone_end >> 1)) >> 2;	/* 37.5% */
 		break;
 	case 2:
-		mft_zone_size >>= 2;			/* 25%   */
+		vol->mft_zone_end = vol->mft_zone_end >> 2;	/* 25%   */
 		break;
-	/* case 1: */
 	default:
-		mft_zone_size >>= 3;			/* 12.5% */
+		vol->mft_zone_multiplier = 1;
+		/* Fall through into case 1. */
+	case 1:
+		vol->mft_zone_end = vol->mft_zone_end >> 3;	/* 12.5% */
 		break;
 	}
-	/* Setup mft zone. */
-	vol->mft_zone_start = vol->mft_zone_pos = vol->mft_lcn;
-	ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->mft_zone_pos = %x\n",
-			vol->mft_zone_pos);
-	/*
-	 * Calculate the mft_lcn for an unmodified NTFS volume (see mkntfs
-	 * source) and if the actual mft_lcn is in the expected place or even
-	 * further to the front of the volume, extend the mft_zone to cover the
-	 * beginning of the volume as well. This is in order to protect the
-	 * area reserved for the mft bitmap as well within the mft_zone itself.
-	 * On non-standard volumes we don't protect it as well as the overhead
-	 * would be higher than the speed increase we would get by doing it.
-	 */
-	tc = (8192 + 2 * vol->cluster_size - 1) / vol->cluster_size;
-	if (tc * vol->cluster_size < 16 * 1024)
-		tc = (16 * 1024 + vol->cluster_size - 1) / vol->cluster_size;
-	if (vol->mft_zone_start <= tc)
-		vol->mft_zone_start = (ntfs_cluster_t)0;
-	ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->mft_zone_start = %x\n",
-			vol->mft_zone_start);
-	/*
-	 * Need to cap the mft zone on non-standard volumes so that it does
-	 * not point outside the boundaries of the volume, we do this by
-	 * halving the zone size until we are inside the volume.
-	 */
-	vol->mft_zone_end = vol->mft_lcn + mft_zone_size;
-	while (vol->mft_zone_end >= vol->nr_clusters) {
-		mft_zone_size >>= 1;
-		vol->mft_zone_end = vol->mft_lcn + mft_zone_size;
-	}
-	ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->mft_zone_end = %x\n",
-			vol->mft_zone_end);
-	/*
-	 * Set the current position within each data zone to the start of the
-	 * respective zone.
-	 */
-	vol->data1_zone_pos = vol->mft_zone_end;
-	ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->data1_zone_pos = %x\n",
-			vol->data1_zone_pos);
-	vol->data2_zone_pos = (ntfs_cluster_t)0;
-	ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->data2_zone_pos = %x\n",
-			vol->data2_zone_pos);
-	/* Set the mft data allocation position to mft record 24. */
-	vol->mft_data_pos = 24UL;
-	/* This will be initialized later. */
-	vol->upcase = 0;
-	vol->upcase_length = 0;
-	vol->mft_ino = 0;
-	return 0;
+	ntfs_debug("vol->mft_zone_multiplier = 0x%x",
+			vol->mft_zone_multiplier);
+	vol->mft_zone_start = vol->mft_lcn;
+	vol->mft_zone_end += vol->mft_lcn;
+	ntfs_debug("vol->mft_zone_start = 0x%Lx",
+			(long long)vol->mft_zone_start);
+	ntfs_debug("vol->mft_zone_end = 0x%Lx", (long long)vol->mft_zone_end);
+	/* And another misplaced defaults setting. */
+	if (!vol->on_errors)
+		vol->on_errors = ON_ERRORS_PANIC;
+	return TRUE;
 }
 
-static void ntfs_init_upcase(ntfs_inode *upcase)
+/**
+ * load_and_init_upcase - load the upcase table for an ntfs volume
+ * @vol:	ntfs super block describing device whose upcase to load
+ *
+ * Return TRUE on success or FALSE on error.
+ */
+static BOOL load_and_init_upcase(ntfs_volume *vol)
 {
-	ntfs_io io;
-#define UPCASE_LENGTH  256
-	upcase->vol->upcase = ntfs_malloc(UPCASE_LENGTH << 1);
-	if (!upcase->vol->upcase)
-		return;
-	io.fn_put = ntfs_put;
-	io.fn_get = 0;
-	io.param = (char*)upcase->vol->upcase;
-	io.size = UPCASE_LENGTH << 1;
-	ntfs_read_attr(upcase, upcase->vol->at_data, 0, 0, &io);
-	upcase->vol->upcase_length = io.size >> 1;
+	struct super_block *sb = vol->sb;
+	struct inode *ino;
+	struct page *page;
+	unsigned long index, max_index;
+	unsigned int size;
+	int i, max;
+
+	ntfs_debug("Entering.");
+	/* Read upcase table and setup vol->upcase and vol->upcase_len. */
+	ino = iget(sb, FILE_UpCase);
+	if (!ino || is_bad_inode(ino)) {
+		if (ino)
+			iput(ino);
+		goto upcase_failed;
+	}
+	/*
+	 * The upcase size must not be above 64k Unicode characters, must not
+	 * be zero and must be a multiple of sizeof(uchar_t).
+	 */
+	if (!ino->i_size || ino->i_size & (sizeof(uchar_t) - 1) ||
+			ino->i_size > 64ULL * 1024 * sizeof(uchar_t))
+		goto iput_upcase_failed;
+	vol->upcase = (uchar_t*)ntfs_malloc_nofs(ino->i_size);
+	if (!vol->upcase)
+		goto iput_upcase_failed;
+	index = 0;
+	max_index = ino->i_size >> PAGE_CACHE_SHIFT;
+	size = PAGE_CACHE_SIZE;
+	while (index < max_index) {
+		/* Read the upcase table and copy it into the linear buffer. */
+read_partial_upcase_page:
+		page = ntfs_map_page(ino->i_mapping, index);
+		if (IS_ERR(page))
+			goto iput_upcase_failed;
+		memcpy((char*)vol->upcase + (index++ << PAGE_CACHE_SHIFT),
+				page_address(page), size);
+		ntfs_unmap_page(page);
+	};
+	if (size == PAGE_CACHE_SIZE) {
+		size = ino->i_size & ~PAGE_CACHE_MASK;
+		if (size)
+			goto read_partial_upcase_page;
+	}
+	vol->upcase_len = ino->i_size >> UCHAR_T_SIZE_BITS;
+	ntfs_debug("Read %Lu bytes from $UpCase (expected %u bytes).",
+			ino->i_size, 64 * 1024 * sizeof(uchar_t));
+	iput(ino);
+	down(&ntfs_lock);
+	if (!default_upcase) {
+		ntfs_debug("Using volume specified $UpCase since default is "
+				"not present.");
+		up(&ntfs_lock);
+		return TRUE;
+	}
+	max = default_upcase_len;
+	if (max > vol->upcase_len)
+		max = vol->upcase_len;
+	for (i = 0; i < max; i++)
+		if (vol->upcase[i] != default_upcase[i])
+			break;
+	if (i == max) {
+		ntfs_free(vol->upcase);
+		vol->upcase = default_upcase;
+		vol->upcase_len = max;
+		ntfs_nr_upcase_users++;
+		up(&ntfs_lock);
+		ntfs_debug("Volume specified $UpCase matches default. Using "
+				"default.");
+		return TRUE;
+	}
+	up(&ntfs_lock);
+	ntfs_debug("Using volume specified $UpCase since it does not match "
+			"the default.");
+	return TRUE;
+iput_upcase_failed:
+	iput(ino);
+	ntfs_free(vol->upcase);
+	vol->upcase = NULL;
+upcase_failed:
+	down(&ntfs_lock);
+	if (default_upcase) {
+		vol->upcase = default_upcase;
+		vol->upcase_len = default_upcase_len;
+		ntfs_nr_upcase_users++;
+		up(&ntfs_lock);
+		ntfs_error(sb, "Failed to load $UpCase from the volume. Using "
+				"default.");
+		return TRUE;
+	}
+	up(&ntfs_lock);
+	ntfs_error(sb, "Failed to initialized upcase table.");
+	return FALSE;
 }
 
-static int process_attrdef(ntfs_inode* attrdef, ntfs_u8* def)
+/**
+ * load_system_files - open the system files using normal functions
+ * @vol:	ntfs super block describing device whose system files to load
+ *
+ * Open the system files with normal access functions and complete setting up
+ * the ntfs super block @vol.
+ *
+ * Return TRUE on success or FALSE on error.
+ */
+static BOOL load_system_files(ntfs_volume *vol)
 {
-	int type = NTFS_GETU32(def+0x80);
-	int check_type = 0;
-	ntfs_volume *vol = attrdef->vol;
-	ntfs_u16* name = (ntfs_u16*)def;
-
-	if (!type) {
-		ntfs_debug(DEBUG_OTHER, "process_atrdef: finished processing "
-							"and returning 1\n");
-		return 1;
-	}
-	if (ntfs_ua_strncmp(name, "$STANDARD_INFORMATION", 64) == 0) {
-		vol->at_standard_information = type;
-		check_type = 0x10;
-	} else if (ntfs_ua_strncmp(name, "$ATTRIBUTE_LIST", 64) == 0) {
-		vol->at_attribute_list = type;
-		check_type = 0x20;
-	} else if (ntfs_ua_strncmp(name, "$FILE_NAME", 64) == 0) {
-		vol->at_file_name = type;
-		check_type = 0x30;
-	} else if (ntfs_ua_strncmp(name, "$VOLUME_VERSION", 64) == 0) {
-		vol->at_volume_version = type;
-		check_type = 0x40;
-	} else if (ntfs_ua_strncmp(name, "$SECURITY_DESCRIPTOR", 64) == 0) {
-		vol->at_security_descriptor = type;
-		check_type = 0x50;
-	} else if (ntfs_ua_strncmp(name, "$VOLUME_NAME", 64) == 0) {
-		vol->at_volume_name = type;
-		check_type = 0x60;
-	} else if (ntfs_ua_strncmp(name, "$VOLUME_INFORMATION", 64) == 0) {
-		vol->at_volume_information = type;
-		check_type = 0x70;
-	} else if (ntfs_ua_strncmp(name, "$DATA", 64) == 0) {
-		vol->at_data = type;
-		check_type = 0x80;
-	} else if (ntfs_ua_strncmp(name, "$INDEX_ROOT", 64) == 0) {
-		vol->at_index_root = type;
-		check_type = 0x90;
-	} else if (ntfs_ua_strncmp(name, "$INDEX_ALLOCATION", 64) == 0) {
-		vol->at_index_allocation = type;
-		check_type = 0xA0;
-	} else if (ntfs_ua_strncmp(name, "$BITMAP", 64) == 0) {
-		vol->at_bitmap = type;
-		check_type = 0xB0;
-	} else if (ntfs_ua_strncmp(name, "$SYMBOLIC_LINK", 64) == 0 ||
-		 ntfs_ua_strncmp(name, "$REPARSE_POINT", 64) == 0) {
-		vol->at_symlink = type;
-		check_type = 0xC0;
-	}
-	if (check_type && check_type != type) {
-		ntfs_error("process_attrdef: unexpected type 0x%x for 0x%x\n",
-							type, check_type);
-		return -EINVAL;
+	VCN next_vcn, last_vcn, highest_vcn;
+	struct super_block *sb = vol->sb;
+	struct inode *tmp_ino;
+	MFT_RECORD *m;
+	ATTR_RECORD *attr;
+	VOLUME_INFORMATION *vi;
+	attr_search_context *ctx;
+	run_list_element *rl;
+
+	ntfs_debug("Entering.");
+	/*
+	 * We have $MFT already (vol->mft_ino) but we need to setup access to
+	 * the $MFT/$BITMAP attribute.
+	 */
+	m = map_mft_record(READ, NTFS_I(vol->mft_ino));
+	if (IS_ERR(m)) {
+		ntfs_error(sb, "Failed to map $MFT.");
+		return FALSE;
+	}
+	if (!(ctx = get_attr_search_ctx(NTFS_I(vol->mft_ino), m))) {
+		ntfs_error(sb, "Failed to get attribute search context.");
+		goto unmap_err_out;
+	}
+	/* Load all attribute extents. */
+	attr = NULL;
+	rl = NULL;
+	next_vcn = last_vcn = highest_vcn = 0;
+	while (lookup_attr(AT_BITMAP, NULL, 0, 0, next_vcn, NULL, 0, ctx)) {
+		run_list_element *nrl;
+
+		/* Cache the current attribute extent. */
+		attr = ctx->attr;
+		/* $MFT/$BITMAP must be non-resident. */
+		if (!attr->non_resident) {
+			ntfs_error(sb, "$MFT/$BITMAP must be non-resident but "
+					"a resident extent was found. $MFT is "
+					"corrupt. Run chkdsk.");
+			goto put_err_out;
+		}
+		/* $MFT/$BITMAP must be uncompressed and unencrypted. */
+		if (attr->flags & ATTR_COMPRESSION_MASK ||
+				attr->flags & ATTR_IS_ENCRYPTED) {
+			ntfs_error(sb, "$MFT/$BITMAP must be uncompressed and "
+					"unencrypted but a compressed/"
+					"encrypted extent was found. $MFT is "
+					"corrupt. Run chkdsk.");
+			goto put_err_out;
+		}
+		/*
+		 * Decompress the mapping pairs array of this extent
+		 * and merge the result into the existing run list. Note we
+		 * don't need any locking at this stage as we are already
+		 * running exclusively as we are mount in progress task.
+		 */
+		nrl = decompress_mapping_pairs(vol, attr, rl);
+		if (IS_ERR(nrl)) {
+			ntfs_error(sb, "decompress_mapping_pairs() failed with "
+					"error code %ld. $MFT is corrupt.",
+					PTR_ERR(nrl));
+			goto put_err_out;
+		}
+		rl = nrl;
+
+		/* Are we in the first extent? */
+		if (!next_vcn) {
+			/* Get the last vcn in the $BITMAP attribute. */
+			last_vcn = sle64_to_cpu(attr->_ANR(allocated_size)) >>
+					vol->cluster_size_bits;
+			vol->mftbmp_size = sle64_to_cpu(attr->_ANR(data_size));
+			vol->mftbmp_initialized_size =
+					sle64_to_cpu(attr->_ANR(initialized_size));
+			vol->mftbmp_allocated_size =
+					sle64_to_cpu(attr->_ANR(allocated_size));
+			/* Consistency check. */
+			if (vol->mftbmp_size < (vol->_VMM(nr_mft_records) + 7) >> 3) {
+				ntfs_error(sb, "$MFT/$BITMAP is too short to "
+						"contain a complete mft "
+						"bitmap: impossible. $MFT is "
+						"corrupt. Run chkdsk.");
+				goto put_err_out;
+			}
+		}
+
+		/* Get the lowest vcn for the next extent. */
+		highest_vcn = sle64_to_cpu(attr->_ANR(highest_vcn));
+		next_vcn = highest_vcn + 1;
+
+		/* Only one extent or error, which we catch below. */
+		if (next_vcn <= 0)
+			break;
+
+		/* Avoid endless loops due to corruption. */
+		if (next_vcn < sle64_to_cpu(attr->_ANR(lowest_vcn))) {
+			ntfs_error(sb, "$MFT/$BITMAP has corrupt attribute "
+					"list attribute. Run chkdsk.");
+			goto put_err_out;
+		}
+
 	}
-	ntfs_debug(DEBUG_OTHER, "process_attrdef: found %s attribute of type "
-			"0x%x\n", check_type ? "known" : "unknown", type);
-	return 0;
-}
+	if (!attr) {
+		ntfs_error(sb, "Missing or invalid $BITMAP attribute in file "
+				"$MFT. $MFT is corrupt. Run chkdsk.");
+put_err_out:
+		put_attr_search_ctx(ctx);
+unmap_err_out:
+		unmap_mft_record(READ, NTFS_I(vol->mft_ino));
+		return FALSE;
+	}
+
+	/* We are finished with $MFT/$BITMAP. */
+	put_attr_search_ctx(ctx);
+	unmap_mft_record(READ, NTFS_I(vol->mft_ino));
+
+	/* Catch errors. */
+	if (highest_vcn && highest_vcn != last_vcn - 1) {
+		ntfs_error(sb, "Failed to load the complete run list for "
+				"$MFT/$BITMAP. Driver bug or corrupt $MFT. "
+				"Run chkdsk.");
+		ntfs_debug("highest_vcn = 0x%Lx, last_vcn - 1 = 0x%Lx",
+				(long long)highest_vcn,
+				(long long)last_vcn - 1);
+		return FALSE;;
+	}
+
+	/* Setup the run list and the address space in the volume structure. */
+	vol->mftbmp_rl.rl = rl;
+	vol->mftbmp_mapping.a_ops = &ntfs_mftbmp_aops;
+	
+	/*
+	 * Not inode data, set to volume. Our mft bitmap access kludge...
+	 * We can only pray this is not going to cause problems... If it does
+	 * cause problems we will need a fake inode for this.
+	 */
+	vol->mftbmp_mapping.host = (struct inode*)vol;
 
-int ntfs_init_attrdef(ntfs_inode* attrdef)
-{
-	ntfs_u8 *buf;
-	ntfs_io io;
-	__s64 offset;
-	unsigned i;
-	int error;
-	ntfs_attribute *data;
-
-	ntfs_debug(DEBUG_BSD, "Entered ntfs_init_attrdef()\n");
-	buf = ntfs_malloc(4050); /* 90*45 */
-	if (!buf)
-		return -ENOMEM;
-	io.fn_put = ntfs_put;
-	io.fn_get = ntfs_get;
-	io.do_read = 1;
-	offset = 0;
-	data = ntfs_find_attr(attrdef, attrdef->vol->at_data, 0);
-	ntfs_debug(DEBUG_BSD, "In ntfs_init_attrdef() after call to "
-			"ntfs_find_attr.\n");
-	if (!data) {
-		ntfs_free(buf);
-		return -EINVAL;
+	// FIXME: If mounting read-only, it would be ok to ignore errors when
+	// loading the mftbmp but we then need to make sure nobody remounts the
+	// volume read-write...
+
+	/* Get mft mirror inode. */
+	vol->mftmirr_ino = iget(sb, FILE_MFTMirr);
+	if (!vol->mftmirr_ino || is_bad_inode(vol->mftmirr_ino)) {
+		if (is_bad_inode(vol->mftmirr_ino))
+			iput(vol->mftmirr_ino);
+		ntfs_error(sb, "Failed to load $MFTMirr.");
+		return FALSE;
+	}
+	// FIXME: Compare mftmirr with mft and repair if appropriate and not
+	// a read-only mount.
+
+	/* Read upcase table and setup vol->upcase and vol->upcase_len. */
+	if (!load_and_init_upcase(vol))
+		goto iput_mirr_err_out;
+	/*
+	 * Get the cluster allocation bitmap inode and verify the size, no
+	 * need for any locking at this stage as we are already running
+	 * exclusively as we are mount in progress task.
+	 */
+	vol->lcnbmp_ino = iget(sb, FILE_Bitmap);
+	if (!vol->lcnbmp_ino || is_bad_inode(vol->lcnbmp_ino)) {
+		if (is_bad_inode(vol->lcnbmp_ino))
+			iput(vol->lcnbmp_ino);
+		goto bitmap_failed;
+	}
+	if ((vol->_VCL(nr_lcn_bits) + 7) >> 3 > vol->lcnbmp_ino->i_size) {
+		iput(vol->lcnbmp_ino);
+bitmap_failed:
+		ntfs_error(sb, "Failed to load $Bitmap.");
+		goto iput_mirr_err_out;
 	}
-	do {
-		io.param = buf;
-		io.size = 4050;
-		ntfs_debug(DEBUG_BSD, "In ntfs_init_attrdef() going to call "
-				"ntfs_readwrite_attr.\n");
-		error = ntfs_readwrite_attr(attrdef, data, offset, &io);
-		ntfs_debug(DEBUG_BSD, "In ntfs_init_attrdef() after call to "
-				"ntfs_readwrite_attr.\n");
-		for (i = 0; !error && i <= io.size - 0xA0; i += 0xA0) {
-			ntfs_debug(DEBUG_BSD, "In ntfs_init_attrdef() going "
-					"to call process_attrdef.\n");
-			error = process_attrdef(attrdef, buf + i);
-			ntfs_debug(DEBUG_BSD, "In ntfs_init_attrdef() after "
-					"call to process_attrdef.\n");
-		}
-		offset += 4096;
-	} while (!error && io.size);
-	ntfs_debug(DEBUG_BSD, "Exiting ntfs_init_attrdef()\n");
-	ntfs_free(buf);
-	return error == 1 ? 0 : error;
+	/*
+	 * Get the volume inode and setup our cache of the volume flags and
+	 * version.
+	 */
+	vol->vol_ino = iget(sb, FILE_Volume);
+	if (!vol->vol_ino || is_bad_inode(vol->vol_ino)) {
+		if (is_bad_inode(vol->vol_ino))
+			iput(vol->vol_ino);
+volume_failed:
+		ntfs_error(sb, "Failed to load $Volume.");
+		goto iput_bmp_mirr_err_out;
+	}
+	m = map_mft_record(READ, NTFS_I(vol->vol_ino));
+	if (IS_ERR(m)) {
+iput_volume_failed:
+		iput(vol->vol_ino);
+		goto volume_failed;
+	}
+	if (!(ctx = get_attr_search_ctx(NTFS_I(vol->vol_ino), m))) {
+		ntfs_error(sb, "Failed to get attribute search context.");
+		goto get_ctx_vol_failed;
+	}
+	if (!lookup_attr(AT_VOLUME_INFORMATION, NULL, 0, 0, 0, NULL, 0, ctx) ||
+			ctx->attr->non_resident || ctx->attr->flags) {
+err_put_vol:
+		put_attr_search_ctx(ctx);
+get_ctx_vol_failed:
+		unmap_mft_record(READ, NTFS_I(vol->vol_ino));
+		goto iput_volume_failed;
+	}
+	vi = (VOLUME_INFORMATION*)((char*)ctx->attr +
+			le16_to_cpu(ctx->attr->_ARA(value_offset)));
+	/* Some bounds checks. */
+	if ((u8*)vi < (u8*)ctx->attr || (u8*)vi +
+			le32_to_cpu(ctx->attr->_ARA(value_length)) > (u8*)ctx->attr +
+			le32_to_cpu(ctx->attr->length))
+		goto err_put_vol;
+	/* Setup volume flags and version. */
+	vol->vol_flags = vi->flags;
+	vol->major_ver = vi->major_ver;
+	vol->minor_ver = vi->minor_ver;
+	put_attr_search_ctx(ctx);
+	unmap_mft_record(READ, NTFS_I(vol->vol_ino));
+	printk(KERN_INFO "NTFS volume version %i.%i.\n", vol->major_ver,
+			vol->minor_ver);
+	/*
+	 * Get the inode for the logfile and empty it if this is a read-write
+	 * mount.
+	 */
+	tmp_ino = iget(sb, FILE_LogFile);
+	if (!tmp_ino || is_bad_inode(tmp_ino)) {
+		if (is_bad_inode(tmp_ino))
+			iput(tmp_ino);
+		ntfs_error(sb, "Failed to load $LogFile.");
+		// FIMXE: We only want to empty the thing so pointless bailing
+		// out. Can recover/ignore.
+		goto iput_vol_bmp_mirr_err_out;
+	}
+	// FIXME: Empty the logfile, but only if not read-only.
+	// FIXME: What happens if someone remounts rw? We need to empty the file
+	// then. We need a flag to tell us whether we have done it already.
+	iput(tmp_ino);
+	/*
+	 * Get the inode for the attribute definitions file and parse the
+	 * attribute definitions.
+	 */ 
+	tmp_ino = iget(sb, FILE_AttrDef);
+	if (!tmp_ino || is_bad_inode(tmp_ino)) {
+		if (is_bad_inode(tmp_ino))
+			iput(tmp_ino);
+		ntfs_error(sb, "Failed to load $AttrDef.");
+		goto iput_vol_bmp_mirr_err_out;
+	}
+	// FIXME: Parse the attribute definitions.
+	iput(tmp_ino);
+	/* Get the root directory inode. */
+	vol->root_ino = iget(sb, FILE_root);
+	if (!vol->root_ino || is_bad_inode(vol->root_ino)) {
+		if (is_bad_inode(vol->root_ino))
+			iput(vol->root_ino);
+		ntfs_error(sb, "Failed to load root directory.");
+		goto iput_vol_bmp_mirr_err_out;
+	}
+	/* If on NTFS versions before 3.0, we are done. */
+	if (vol->major_ver < 3)
+		return TRUE;
+	/* NTFS 3.0+ specific initialization. */
+	/* Get the security descriptors inode. */
+	vol->secure_ino = iget(sb, FILE_Secure);
+	if (!vol->secure_ino || is_bad_inode(vol->secure_ino)) {
+		if (is_bad_inode(vol->secure_ino))
+			iput(vol->secure_ino);
+		ntfs_error(sb, "Failed to load $Secure.");
+		goto iput_root_vol_bmp_mirr_err_out;
+	}
+	// FIXME: Initialize security.
+	/* Get the extended system files' directory inode. */
+	tmp_ino = iget(sb, FILE_Extend);
+	if (!tmp_ino || is_bad_inode(tmp_ino)) {
+		if (is_bad_inode(tmp_ino))
+			iput(tmp_ino);
+		ntfs_error(sb, "Failed to load $Extend.");
+		goto iput_sec_root_vol_bmp_mirr_err_out;
+	}
+	// FIXME: Do something. E.g. want to delete the $UsnJrnl if exists.
+	// Note we might be doing this at the wrong level; we might want to
+	// d_alloc_root() and then do a "normal" open(2) of $Extend\$UsnJrnl
+	// rather than using iget here, as we don't know the inode number for
+	// the files in $Extend directory.
+	iput(tmp_ino);
+	return TRUE;
+iput_sec_root_vol_bmp_mirr_err_out:
+	iput(vol->secure_ino);
+iput_root_vol_bmp_mirr_err_out:
+	iput(vol->root_ino);
+iput_vol_bmp_mirr_err_out:
+	iput(vol->vol_ino);
+iput_bmp_mirr_err_out:
+	iput(vol->lcnbmp_ino);
+iput_mirr_err_out:
+	iput(vol->mftmirr_ino);
+	return FALSE;
 }
 
-/* ntfs_get_version will determine the NTFS version of the volume and will
- * return the version in a BCD format, with the MSB being the major version
- * number and the LSB the minor one. Otherwise return <0 on error.
- * Example: version 3.1 will be returned as 0x0301. This has the obvious
- * limitation of not coping with version numbers above 0x80 but that shouldn't
- * be a problem... */
-int ntfs_get_version(ntfs_inode* volume)
+/**
+ * ntfs_put_super - called by the vfs to unmount a volume
+ * @vfs_sb:	vfs superblock of volume to unmount
+ *
+ * ntfs_put_super() is called by the VFS (from fs/super.c::do_umount()) when
+ * the volume is being unmounted (umount system call has been invoked) and it
+ * releases all inodes and memory belonging to the NTFS specific part of the
+ * super block.
+ */
+void ntfs_put_super(struct super_block *vfs_sb)
 {
-	ntfs_attribute *volinfo;
+	ntfs_volume *vol = NTFS_SB(vfs_sb);
 
-	volinfo = ntfs_find_attr(volume, volume->vol->at_volume_information, 0);
-	if (!volinfo) 
-		return -EINVAL;
-	if (!volinfo->resident) {
-		ntfs_error("Volume information attribute is not resident!\n");
-		return -EINVAL;
+	ntfs_debug("Entering.");
+	iput(vol->vol_ino);
+	vol->vol_ino = NULL;
+	/* NTFS 3.0+ specific clean up. */
+	if (vol->major_ver >= 3) {
+		if (vol->secure_ino) {
+			iput(vol->secure_ino);
+			vol->secure_ino = NULL;
+		}
 	}
-	return ((ntfs_u8*)volinfo->d.data)[8] << 8 | 
-	       ((ntfs_u8*)volinfo->d.data)[9];
+	iput(vol->root_ino);
+	vol->root_ino = NULL;
+	down_write(&vol->lcnbmp_lock);
+	iput(vol->lcnbmp_ino);
+	vol->lcnbmp_ino = NULL;
+	up_write(&vol->lcnbmp_lock);
+	iput(vol->mftmirr_ino);
+	vol->mftmirr_ino = NULL;
+	iput(vol->mft_ino);
+	vol->mft_ino = NULL;
+	down_write(&vol->mftbmp_lock);
+	/*
+	 * Clean up mft bitmap address space. Ignore the _inode_ bit in the
+	 * name of the function... FIXME: What does this do with dirty pages?
+	 * (ask Al Viro)
+	 */
+	truncate_inode_pages(&vol->mftbmp_mapping, 0);
+	vol->mftbmp_mapping.a_ops = NULL;
+	vol->mftbmp_mapping.host = NULL;
+	up_write(&vol->mftbmp_lock);
+	down_write(&vol->mftbmp_rl.lock);
+	ntfs_free(vol->mftbmp_rl.rl);
+	vol->mftbmp_rl.rl = NULL;
+	up_write(&vol->mftbmp_rl.lock);
+	vol->upcase_len = 0;
+	/*
+	 * Decrease the number of mounts and destroy the global default upcase
+	 * table if necessary. Also decrease the number of upcase users if we
+	 * are a user.
+	 */
+	down(&ntfs_lock);
+	ntfs_nr_mounts--;
+	if (vol->upcase == default_upcase) {
+		ntfs_nr_upcase_users--;
+		vol->upcase = NULL;
+	}
+	if (!ntfs_nr_upcase_users && default_upcase) {
+		ntfs_free(default_upcase);
+		default_upcase = NULL;
+	}
+	if (vol->cluster_size <= 4096 && !--ntfs_nr_compression_users)
+		free_compression_buffers();
+	up(&ntfs_lock);
+	if (vol->upcase) {
+		ntfs_free(vol->upcase);
+		vol->upcase = NULL;
+	}
+	if (vol->nls_map) {
+		unload_nls(vol->nls_map);
+		vol->nls_map = NULL;
+	}
+	vfs_sb->u.generic_sbp = NULL;
+	kfree(vol);
+	return;
 }
 
-int ntfs_load_special_files(ntfs_volume *vol)
+/**
+ * get_nr_free_clusters - return the number of free clusters on a volume
+ * @vol:	ntfs volume for which to obtain free cluster count
+ *
+ * Calculate the number of free clusters on the mounted NTFS volume @vol.
+ *
+ * Errors are ignored and we just return the number of free clusters we have
+ * found. This means we return an underestimate on error.
+ */
+s64 get_nr_free_clusters(ntfs_volume *vol)
 {
-	int error;
-	ntfs_inode upcase, attrdef, volume;
-
-	vol->mft_ino = (ntfs_inode*)ntfs_calloc(sizeof(ntfs_inode));
-	vol->mftmirr = (ntfs_inode*)ntfs_calloc(sizeof(ntfs_inode));
-	vol->bitmap = (ntfs_inode*)ntfs_calloc(sizeof(ntfs_inode));
-	vol->ino_flags = 4 | 2 | 1;
-	error = -ENOMEM;
-	ntfs_debug(DEBUG_BSD, "Going to load MFT\n");
-	if (!vol->mft_ino || (error = ntfs_init_inode(vol->mft_ino, vol,
-			FILE_Mft))) {
-		ntfs_error("Problem loading MFT\n");
-		return error;
-	}
-	ntfs_debug(DEBUG_BSD, "Going to load MIRR\n");
-	if ((error = ntfs_init_inode(vol->mftmirr, vol, FILE_MftMirr))) {
-		ntfs_error("Problem %d loading MFTMirr\n", error);
-		return error;
-	}
-	ntfs_debug(DEBUG_BSD, "Going to load BITMAP\n");
-	if ((error = ntfs_init_inode(vol->bitmap, vol, FILE_BitMap))) {
-		ntfs_error("Problem loading Bitmap\n");
-		return error;
-	}
-	ntfs_debug(DEBUG_BSD, "Going to load UPCASE\n");
-	error = ntfs_init_inode(&upcase, vol, FILE_UpCase);
-	if (error)
-		return error;
-	ntfs_init_upcase(&upcase);
-	ntfs_clear_inode(&upcase);
-	ntfs_debug(DEBUG_BSD, "Going to load ATTRDEF\n");
-	error = ntfs_init_inode(&attrdef, vol, FILE_AttrDef);
-	if (error)
-		return error;
-	error = ntfs_init_attrdef(&attrdef);
-	ntfs_clear_inode(&attrdef);
-	if (error)
-		return error;
-
-	/* Check for NTFS version and if Win2k version (ie. 3.0+) do not allow
-	 * write access since the driver write support is broken. */
-	ntfs_debug(DEBUG_BSD, "Going to load VOLUME\n");
-	error = ntfs_init_inode(&volume, vol, FILE_Volume);
-	if (error)
-		return error;
-	if ((error = ntfs_get_version(&volume)) >= 0x0300 &&
-	    !(NTFS_SB(vol)->s_flags & MS_RDONLY)) {
-		NTFS_SB(vol)->s_flags |= MS_RDONLY;
-		ntfs_error("Warning! NTFS volume version is Win2k+: Mounting "
-			   "read-only\n");
-	}
-	ntfs_clear_inode(&volume);
-	if (error < 0)
-		return error;
-	ntfs_debug(DEBUG_BSD, "NTFS volume is v%d.%d\n", error >> 8,
-			error & 0xff);
-	return 0;
+	struct address_space *mapping = vol->lcnbmp_ino->i_mapping;
+	filler_t *readpage = (filler_t*)mapping->a_ops->readpage;
+	struct page *page;
+	unsigned long index, max_index;
+	unsigned int max_size, i;
+	s64 nr_free = 0LL;
+	u32 *b;
+
+	ntfs_debug("Entering.");
+	/* Serialize accesses to the cluster bitmap. */
+	down_read(&vol->lcnbmp_lock);
+	/*
+	 * Convert the number of bits into bytes rounded up, then convert into
+	 * multiples of PAGE_CACHE_SIZE.
+	 */
+	max_index = (vol->_VCL(nr_clusters) + 7) >> (3 + PAGE_CACHE_SHIFT);
+	/* Use multiples of 4 bytes. */
+	max_size = PAGE_CACHE_SIZE >> 2;
+	ntfs_debug("Reading $BITMAP, max_index = 0x%lx, max_size = 0x%x.",
+			max_index, max_size);
+	for (index = 0UL; index < max_index;) {
+handle_partial_page:
+		/*
+		 * Read the page from page cache, getting it from backing store
+		 * if necessary, and increment the use count.
+		 */
+		page = read_cache_page(mapping, index++, (filler_t*)readpage,
+				NULL);
+		/* Ignore pages which errored synchronously. */
+		if (IS_ERR(page)) {
+			ntfs_debug("Sync read_cache_page() error. Skipping "
+					"page (index 0x%lx).", index - 1);
+			continue;
+		}
+		wait_on_page(page);
+		if (!Page_Uptodate(page)) {
+			ntfs_debug("Async read_cache_page() error. Skipping "
+					"page (index 0x%lx).", index - 1);
+			/* Ignore pages which errored asynchronously. */
+			page_cache_release(page);
+			continue;
+		}
+		b = (u32*)kmap(page);
+		/* For each 4 bytes, add up the number zero bits. */
+	  	for (i = 0; i < max_size; i++)
+			nr_free += (s64)(32 - hweight32(b[i]));
+		kunmap(page);
+		page_cache_release(page);
+	}
+	if (max_size == PAGE_CACHE_SIZE >> 2) {
+		/*
+		 * Get the multiples of 4 bytes in use in the final partial
+		 * page.
+		 */
+		max_size = ((((vol->_VCL(nr_clusters) + 7) >> 3) & ~PAGE_CACHE_MASK)
+				+ 3) >> 2;
+		/* If there is a partial page go back and do it. */
+		if (max_size) {
+			ntfs_debug("Handling partial page, max_size = 0x%x.",
+					max_size);
+			goto handle_partial_page;
+		}
+	}
+	ntfs_debug("Finished reading $BITMAP, last index = 0x%lx", index - 1);
+	up_read(&vol->lcnbmp_lock);
+	ntfs_debug("Exiting.");
+	return nr_free;
 }
 
-int ntfs_release_volume(ntfs_volume *vol)
+/**
+ * get_nr_free_mft_records - return the number of free inodes on a volume
+ * @vol:	ntfs volume for which to obtain free inode count
+ *
+ * Calculate the number of free mft records (inodes) on the mounted NTFS
+ * volume @vol.
+ *
+ * Errors are ignored and we just return the number of free inodes we have
+ * found. This means we return an underestimate on error.
+ */
+s64 get_nr_free_mft_records(ntfs_volume *vol)
 {
-	if (((vol->ino_flags & 1) == 1) && vol->mft_ino) {
-		ntfs_clear_inode(vol->mft_ino);
-		ntfs_free(vol->mft_ino);
-		vol->mft_ino = 0;
-	}
-	if (((vol->ino_flags & 2) == 2) && vol->mftmirr) {
-		ntfs_clear_inode(vol->mftmirr);
-		ntfs_free(vol->mftmirr);
-		vol->mftmirr = 0;
-	}
-	if (((vol->ino_flags & 4) == 4) && vol->bitmap) {
-		ntfs_clear_inode(vol->bitmap);
-		ntfs_free(vol->bitmap);
-		vol->bitmap = 0;
+	struct address_space *mapping;
+	filler_t *readpage;
+	struct page *page;
+	unsigned long index, max_index;
+	unsigned int max_size, i;
+	s64 nr_free = 0LL;
+	u32 *b;
+
+	ntfs_debug("Entering.");
+	/* Serialize accesses to the inode bitmap. */
+	down_read(&vol->mftbmp_lock);
+	mapping = &vol->mftbmp_mapping;
+	readpage = (filler_t*)mapping->a_ops->readpage;
+	/*
+	 * Convert the number of bits into bytes rounded up, then convert into
+	 * multiples of PAGE_CACHE_SIZE.
+	 */
+	max_index = (vol->_VMM(nr_mft_records) + 7) >> (3 + PAGE_CACHE_SHIFT);
+	/* Use multiples of 4 bytes. */
+	max_size = PAGE_CACHE_SIZE >> 2;
+	ntfs_debug("Reading $MFT/$BITMAP, max_index = 0x%lx, max_size = "
+			"0x%x.", max_index, max_size);
+	for (index = 0UL; index < max_index;) {
+handle_partial_page:
+		/*
+		 * Read the page from page cache, getting it from backing store
+		 * if necessary, and increment the use count.
+		 */
+		page = read_cache_page(mapping, index++, (filler_t*)readpage,
+				vol);
+		/* Ignore pages which errored synchronously. */
+		if (IS_ERR(page)) {
+			ntfs_debug("Sync read_cache_page() error. Skipping "
+					"page (index 0x%lx).", index - 1);
+			continue;
+		}
+		wait_on_page(page);
+		if (!Page_Uptodate(page)) {
+			ntfs_debug("Async read_cache_page() error. Skipping "
+					"page (index 0x%lx).", index - 1);
+			/* Ignore pages which errored asynchronously. */
+			page_cache_release(page);
+			continue;
+		}
+		b = (u32*)kmap(page);
+		/* For each 4 bytes, add up the number of zero bits. */
+	  	for (i = 0; i < max_size; i++)
+			nr_free += (s64)(32 - hweight32(b[i]));
+		kunmap(page);
+		page_cache_release(page);
 	}
-	ntfs_free(vol->mft);
-	ntfs_free(vol->upcase);
-	return 0;
+	if (index == max_index) {
+		/*
+		 * Get the multiples of 4 bytes in use in the final partial
+		 * page.
+		 */
+		max_size = ((((vol->_VMM(nr_mft_records) + 7) >> 3) &
+				~PAGE_CACHE_MASK) + 3) >> 2;
+		/* If there is a partial page go back and do it. */
+		if (max_size) {
+			/* Compensate for out of bounds zero bits. */
+			if ((i = vol->_VMM(nr_mft_records) & 31))
+				nr_free -= (s64)(32 - i);
+			ntfs_debug("Handling partial page, max_size = 0x%x",
+					max_size);
+			goto handle_partial_page;
+		}
+	}
+	ntfs_debug("Finished reading $MFT/$BITMAP, last index = 0x%lx",
+			index - 1);
+	up_read(&vol->mftbmp_lock);
+	ntfs_debug("Exiting.");
+	return nr_free;
 }
 
-/*
- * Writes the volume size (units of clusters) into vol_size.
- * Returns 0 if successful or error.
+/**
+ * ntfs_statfs - return information about mounted NTFS volume
+ * @sb:		super block of mounted volume
+ * @sfs:	statfs structure in which to return the information
+ *
+ * Return information about the mounted NTFS volume @sb in the statfs structure
+ * pointed to by @sfs (this is initialized with zeros before ntfs_statfs is
+ * called). We interpret the values to be correct of the moment in time at
+ * which we are called. Most values are variable otherwise and this isn't just
+ * the free values but the totals as well. For example we can increase the
+ * total number of file nodes if we run out and we can keep doing this until
+ * there is no more space on the volume left at all.
+ *
+ * Called from vfs_statfs which is used to handle the statfs, fstatfs, and
+ * ustat system calls.
+ *
+ * Return 0 on success or -errno on error.
  */
-int ntfs_get_volumesize(ntfs_volume *vol, ntfs_s64 *vol_size)
+int ntfs_statfs(struct super_block *sb, struct statfs *sfs)
 {
-	ntfs_io io;
-	char *cluster0;
+	ntfs_volume *vol = NTFS_SB(sb);
+	s64 size;
 
-	if (!vol_size)
-		return -EFAULT;
-	cluster0 = ntfs_malloc(vol->cluster_size);
-	if (!cluster0)
-		return -ENOMEM;
-	io.fn_put = ntfs_put;
-	io.fn_get = ntfs_get;
-	io.param = cluster0;
-	io.do_read = 1;
-	io.size = vol->cluster_size;
-	ntfs_getput_clusters(vol, 0, 0, &io);
-	*vol_size = NTFS_GETU64(cluster0 + 0x28) >>
-					(ffs(NTFS_GETU8(cluster0 + 0xD)) - 1);
-	ntfs_free(cluster0);
+	ntfs_debug("Entering.");
+	/* Type of filesystem. */
+	sfs->f_type   = NTFS_SB_MAGIC;
+	/* Optimal transfer block size. */
+	sfs->f_bsize  = PAGE_CACHE_SIZE;
+	/*
+	 * Total data blocks in file system in units of f_bsize and since
+	 * inodes are also stored in data blocs ($MFT is a file) this is just
+	 * the total clusters.
+	 */
+	sfs->f_blocks = vol->_VCL(nr_clusters) << vol->cluster_size_bits >>
+				PAGE_CACHE_SHIFT;
+	/* Free data blocks in file system in units of f_bsize. */
+	size	      = get_nr_free_clusters(vol) << vol->cluster_size_bits >>
+				PAGE_CACHE_SHIFT;
+	if (size < 0LL)
+		size = 0LL;
+	/* Free blocks avail to non-superuser, same as above on NTFS. */
+	sfs->f_bavail = sfs->f_bfree = size;
+	/* Total file nodes in file system (at this moment in time). */
+	sfs->f_files  = vol->mft_ino->i_size >> vol->mft_record_size_bits;
+	/* Free file nodes in fs (based on current total count). */
+	size	      = get_nr_free_mft_records(vol);
+	if (size < 0LL)
+		size = 0LL;
+	sfs->f_ffree = size;
+	/*
+	 * File system id. This is extremely *nix flavour dependent and even
+	 * within Linux itself all fs do their own thing. I interpret this to
+	 * mean a unique id associated with the mounted fs and not the id
+	 * associated with the file system driver, the latter is already given
+	 * by the file system type in sfs->f_type. Thus we use the 64-bit
+	 * volume serial number splitting it into two 32-bit parts. We enter
+	 * the least significant 32-bits in f_fsid[0] and the most significant
+	 * 32-bits in f_fsid[1].
+	 */
+	sfs->f_fsid.val[0] = vol->serial_no & 0xffffffff;
+	sfs->f_fsid.val[1] = (vol->serial_no >> 32) & 0xffffffff;
+	/* Maximum length of filenames. */
+	sfs->f_namelen	   = NTFS_MAX_NAME_LEN;
 	return 0;
 }
 
-static int nc[16]={4,3,3,2,3,2,2,1,3,2,2,1,2,1,1,0};
+/**
+ * Super operations for mount time when we don't have enough setup to use the
+ * proper functions.
+ */
+struct super_operations ntfs_mount_sops = {
+	alloc_inode:	ntfs_alloc_big_inode,	/* VFS: Allocate a new inode. */
+	destroy_inode:	ntfs_destroy_big_inode,	/* VFS: Deallocate an inode. */
+	read_inode:	ntfs_read_inode_mount,	/* VFS: Load inode from disk,
+						   called from iget(). */
+	clear_inode:	ntfs_clear_big_inode,	/* VFS: Called when an inode is
+						   removed from memory. */
+};
 
-int ntfs_get_free_cluster_count(ntfs_inode *bitmap)
-{
-	ntfs_io io;
-	int offset, error, clusters;
-	unsigned char *bits = ntfs_malloc(2048);
-	if (!bits)
-		return -ENOMEM;
-	offset = clusters = 0;
-	io.fn_put = ntfs_put;
-	io.fn_get = ntfs_get;
-	while (1) {
-		register int i;
-		io.param = bits;
-		io.size = 2048;
-		error = ntfs_read_attr(bitmap, bitmap->vol->at_data, 0, offset,
-									&io);
-		if (error || io.size == 0)
-			break;
-		/* I never thought I would do loop unrolling some day */
-		for (i = 0; i < io.size - 8; ) {
-			clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
-			clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
-			clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
-			clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
-			clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
-			clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
-			clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
-			clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
-		}
-		while (i < io.size) {
-			clusters += nc[bits[i] >> 4];
-			clusters += nc[bits[i++] & 0xF];
-		}
-		offset += io.size;
-	}
-	ntfs_free(bits);
-	return clusters;
-}
+/**
+ * The complete super operations.
+ */
+struct super_operations ntfs_sops = {
+	alloc_inode:	ntfs_alloc_big_inode,	/* VFS: Allocate a new inode. */
+	destroy_inode:	ntfs_destroy_big_inode,	/* VFS: Deallocate an inode. */
+	read_inode:	ntfs_read_inode,	/* VFS: Load inode from disk,
+						   called from iget(). */
+	dirty_inode:	ntfs_dirty_inode,	/* VFS: Called from
+						   __mark_inode_dirty(). */
+	write_inode:	NULL,		/* VFS: Write dirty inode to disk. */
+	put_inode:	NULL,		/* VFS: Called whenever the reference
+					   count (i_count) of the inode is
+					   going to be decreased but before the
+					   actual decrease. */
+	delete_inode:	NULL,		/* VFS: Delete inode from disk. Called
+					   when i_count becomes 0 and i_nlink is
+					   also 0. */
+	put_super:	ntfs_put_super,	/* Syscall: umount. */
+	write_super:	NULL,		/* Flush dirty super block to disk. */
+	write_super_lockfs:	NULL,	/* ? */
+	unlockfs:	NULL,		/* ? */
+	statfs:		ntfs_statfs,	/* Syscall: statfs */
+	remount_fs:	ntfs_remount,	/* Syscall: mount -o remount. */
+	clear_inode:	ntfs_clear_big_inode,	/* VFS: Called when an inode is
+						   removed from memory. */
+	umount_begin:	NULL,		/* Forced umount. */
+	/*
+	 * These are NFSd support functions but NTFS is a standard fs so
+	 * shouldn't need to implement these manually. At least we can try
+	 * without and if it doesn't work in some way we can always implement
+	 * something here.
+	 */
+	fh_to_dentry:	NULL,		/* Get dentry for given file handle. */
+	dentry_to_fh:	NULL,		/* Get file handle for given dentry. */
+	show_options:	ntfs_show_options, /* Show mount options in proc. */
+};
 
-/*
- * Insert the fixups for the record. The number and location of the fixes
- * is obtained from the record header but we double check with @rec_size and
- * use that as the upper boundary, if necessary overwriting the count value in
- * the record header.
+/**
+ * ntfs_fill_super - mount an ntfs files system
+ * @sb:		super block of ntfs file system to mount
+ * @opt:	string containing the mount options
+ * @silent:	silence error output
+ *
+ * ntfs_fill_super() is called by the VFS to mount the device described by @sb
+ * with the mount otions in @data with the NTFS file system.
  *
- * We return 0 on success or -1 if fixup header indicated the beginning of the
- * update sequence array to be beyond the valid limit.
+ * If @silent is true, remain silent even if errors are detected. This is used
+ * during bootup, when the kernel tries to mount the root file system with all
+ * registered file systems one after the other until one succeeds. This implies
+ * that all file systems except the correct one will quite correctly and
+ * expectedly return an error, but nobody wants to see error messages when in
+ * fact this is what is supposed to happen.
+ *
+ * NOTE: @sb->s_flags contains the mount options flags.
  */
-int ntfs_insert_fixups(unsigned char *rec, int rec_size)
+static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent)
 {
-	int first;
-	int count;
-	int offset = -2;
-	ntfs_u16 fix;
+	ntfs_volume *vol;
+	struct buffer_head *bh;
+	struct inode *tmp_ino;
+	int result;
+
+	ntfs_debug("Entering.");
+	/* Allocate a new ntfs_volume and place it in sb->u.generic_sbp. */
+	sb->u.generic_sbp = kmalloc(sizeof(ntfs_volume), GFP_NOFS);
+	vol = NTFS_SB(sb);
+	if (!vol) {
+		if (!silent)
+			ntfs_error(sb, "Allocation of NTFS volume structure "
+					"failed. Aborting mount...");
+		return -ENOMEM;
+	}
+	/* Initialize ntfs_volume structure. */
+	memset(vol, 0, sizeof(ntfs_volume));
+	vol->sb = sb;
+	vol->upcase = NULL;
+	vol->mft_ino = NULL;
+	init_rwsem(&vol->mftbmp_lock);
+	INIT_LIST_HEAD(&vol->mftbmp_mapping.clean_pages);
+	INIT_LIST_HEAD(&vol->mftbmp_mapping.dirty_pages);
+	INIT_LIST_HEAD(&vol->mftbmp_mapping.locked_pages);
+	vol->mftbmp_mapping.a_ops = NULL;
+	vol->mftbmp_mapping.host = NULL;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,6)
+	vol->mftbmp_mapping.i_mmap = NULL;
+	vol->mftbmp_mapping.i_mmap_shared = NULL;
+#else
+	INIT_LIST_HEAD(&vol->mftbmp_mapping.i_mmap);
+	INIT_LIST_HEAD(&vol->mftbmp_mapping.i_mmap_shared);
+#endif
+	spin_lock_init(&vol->mftbmp_mapping.i_shared_lock);
+	init_run_list(&vol->mftbmp_rl);
+	vol->mftmirr_ino = NULL;
+	vol->lcnbmp_ino = NULL;
+	init_rwsem(&vol->lcnbmp_lock);
+	vol->vol_ino = NULL;
+	vol->root_ino = NULL;
+	vol->secure_ino = NULL;
+	vol->uid = vol->gid = 0;
+	vol->on_errors = 0;
+	vol->mft_zone_multiplier = 0;
+	vol->nls_map = NULL;
+
+	/*
+	 * Default is group and other don't have any access to files or
+	 * directories while owner has full access. Further files by default
+	 * are not executable but directories are of course browseable.
+	 */
+	vol->fmask = 0177;
+	vol->dmask = 0077;
+
+	/*
+	 * Default is to show long file names (including POSIX file names), and
+	 * not to show system files and short file names.
+	 */
+	vol->readdir_opts = SHOW_WIN32;
+
+	/* Important to get the mount options dealt with now. */
+	if (!parse_options(vol, (char*)opt))
+		goto err_out_now;
 	
-	first = NTFS_GETU16(rec + 4);
-	count = (rec_size >> NTFS_SECTOR_BITS) + 1;
-	if (first + count * 2 > NTFS_SECTOR_SIZE - 2) {
-		printk(KERN_CRIT "NTFS: ntfs_insert_fixups() detected corrupt "
-				"NTFS record update sequence array position. - "
-				"Cannot hotfix.\n");
-		return -1;
-	}
-	if (count != NTFS_GETU16(rec + 6)) {
-		printk(KERN_ERR "NTFS: ntfs_insert_fixups() detected corrupt "
-				"NTFS record update sequence array size. - "
-				"Applying hotfix.\n");
-		NTFS_PUTU16(rec + 6, count);
-	}
-	fix = (NTFS_GETU16(rec + first) + 1) & 0xffff;
-	if (fix == 0xffff || !fix)
-		fix = 1;
-	NTFS_PUTU16(rec + first, fix);
-	count--;
-	while (count--) {
-		first += 2;
-		offset += NTFS_SECTOR_SIZE;
-		NTFS_PUTU16(rec + first, NTFS_GETU16(rec + offset));
-		NTFS_PUTU16(rec + offset, fix);
+	/* We are just a read-only fs at the moment. */
+	sb->s_flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME;
+
+	/*
+	 * TODO: Fail safety check. In the future we should really be able to
+	 * cope with this being the case, but for now just bail out.
+	 */
+	if (bdev_hardsect_size(sb->s_bdev) > NTFS_BLOCK_SIZE) {
+		if (!silent)
+			ntfs_error(sb, "Device has unsupported hardsect_size.");
+		goto err_out_now;
+	}
+
+	/* Setup the device access block size to NTFS_BLOCK_SIZE. */
+	if (sb_set_blocksize(sb, NTFS_BLOCK_SIZE) != NTFS_BLOCK_SIZE) {
+		if (!silent)
+			ntfs_error(sb, "Unable to set block size.");
+		goto set_blk_size_err_out_now;
+	}
+
+	/* Get the size of the device in units of NTFS_BLOCK_SIZE bytes. */
+	vol->nr_blocks = sb->s_bdev->bd_inode->i_size >> NTFS_BLOCK_SIZE_BITS;
+
+	/* Read the boot sector and return unlocked buffer head to it. */
+	if (!(bh = read_ntfs_boot_sector(sb, silent))) {
+		if (!silent)
+			ntfs_error(sb, "Not an NTFS volume.");
+		goto set_blk_size_err_out_now;
 	}
-	return 0;
-}
+	
+	/*
+	 * Extract the data from the boot sector and setup the ntfs super block
+	 * using it.
+	 */
+	result = parse_ntfs_boot_sector(vol, (NTFS_BOOT_SECTOR*)bh->b_data);
 
-/**
- * ntfs_allocate_clusters - allocate logical clusters on an ntfs volume
- * @vol:	volume on which to allocate clusters
- * @location:	preferred location for first allocated cluster
- * @count:	number of clusters to allocate
- * @rl:		address of pointer in which to return the allocated run list
- * @rl_len:	the number of elements returned in @*rl
- *
- * Allocate @*count clusters (LCNs), preferably beginning at @*location in the
- * bitmap of the volume @vol. If @*location is -1, it does not matter where the
- * clusters are. @rl is the address of a ntfs_runlist pointer which this
- * function will allocate and fill with the runlist of the allocated clusters.
- * It is the callers responsibility to ntfs_vfree() @*rl after she is finished
- * with it. If the function was not successful, @*rl will be set to NULL.
- * @*rl_len will contain the number of ntfs_runlist elements in @*rl or 0 if
- * @*rl is NULL.
- *
- * Return 0 on success, or -errno on error. On success, @*location and @*count
- * say what was really allocated. On -ENOSPC, @*location and @*count say what
- * could have been allocated. If nothing could be allocated or a different
- * error occured, @*location = -1 and @*count = 0.
- *
- * There are two data zones. First is the area between the end of the mft zone
- * and the end of the volume, and second is the area between the start of the
- * volume and the start of the mft zone. On unmodified/standard volumes, the
- * second mft zone doesn't exist due to the mft zone being expanded to cover
- * the start of volume in order to reserve space for the mft bitmap attribute.
- *
- * This is not the prettiest function but the complexity stems from the need of
- * implementing the mft vs data zoned approach and from the fact that we have
- * access to the lcn bitmap in portions of PAGE_SIZE bytes at a time, so we
- * need to cope with crossing over boundaries of two pages. Further, the fact
- * that the allocator allows for caller supplied hints as to the location of
- * where allocation should begin and the fact that the allocator keeps track of
- * where in the data zones the next natural allocation should occur, contribute
- * to the complexity of the function. But it should all be worthwhile, because
- * this allocator should: 1) be a full implementation of the MFT zone approach
- * used by Windows, 2) cause reduction in fragmentation as much as possible,
- * and 3) be speedy in allocations (the code is not optimized for speed, but
- * the algorithm is, so further speed improvements are probably possible).
- *
- * FIXME: Really need finer-grained locking but this will do for the moment. I
- * just want to kill all races and have a working allocator. When that is done,
- * we can beautify... (AIA)
- * 
- * FIXME: We should be monitoring cluster allocation and increment the MFT zone
- * size dynamically but this is something for the future. We will just cause
- * heavier fragmentation by not doing it and I am not even sure Windows would
- * grow the MFT zone dynamically, so might even be correct not doing this. The
- * overhead in doing dynamic MFT zone expansion would be very large and unlikely
- * worth the effort. (AIA)
- *
- * TODO: I have added in double the required zone position pointer wrap around
- * logic which can be optimized to having only one of the two logic sets.
- * However, having the double logic will work fine, but if we have only one of
- * the sets and we get it wrong somewhere, then we get into trouble, so
- * removing the duplicate logic requires _very_ careful consideration of _all_
- * possible code paths. So at least for now, I am leaving the double logic -
- * better safe than sorry... (AIA)
- */
-int ntfs_allocate_clusters(ntfs_volume *vol, ntfs_cluster_t *location,
-		ntfs_cluster_t *count, ntfs_runlist **rl, int *rl_len,
-		const NTFS_CLUSTER_ALLOCATION_ZONES zone)
-{
-	ntfs_runlist *rl2 = NULL, *rlt;
-	ntfs_attribute *data;
-	ntfs_cluster_t buf_pos, zone_start, zone_end, mft_zone_size;
-	ntfs_cluster_t lcn, last_read_pos, prev_lcn = (ntfs_cluster_t)0;
-	ntfs_cluster_t initial_location, prev_run_len = (ntfs_cluster_t)0;
-	ntfs_cluster_t clusters = (ntfs_cluster_t)0;
-	unsigned char *buf, *byte, bit, search_zone, done_zones;
-	unsigned char pass, need_writeback;
-	int rlpos = 0, rlsize, buf_size, err = 0;
-	ntfs_io io;
-
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Entering with *location = "
-			"0x%x, *count = 0x%x, zone = %s_ZONE.\n", *location,
-			*count, zone == DATA_ZONE ? "DATA" : "MFT");
-	buf = (char*)__get_free_page(GFP_NOFS);
-	if (!buf) {
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Returning "
-				"-ENOMEM.\n");
-		return -ENOMEM;
+	brelse(bh);
+
+	if (!result) {
+		if (!silent)
+			ntfs_error(sb, "Unsupported NTFS filesystem.");
+		goto set_blk_size_err_out_now;
 	}
-	io.fn_put = ntfs_put;
-	io.fn_get = ntfs_get;
-	lock_kernel();
-	/* Get the $DATA attribute of $Bitmap. */
-	data = ntfs_find_attr(vol->bitmap, vol->at_data, 0);
-	if (!data) {
-		err = -EINVAL;
-		goto err_ret;
-	}
-	/*
-	 * If no specific location was requested, use the current data zone
-	 * position, otherwise use the requested location but make sure it lies
-	 * outside the mft zone. Also set done_zones to 0 (no zones done) and
-	 * pass depending on whether we are starting inside a zone (1) or
-	 * at the beginning of a zone (2). If requesting from the MFT_ZONE, then
-	 * we either start at the current position within the mft zone or at the
-	 * specified position and if the latter is out of bounds then we start
-	 * at the beginning of the MFT_ZONE.
-	 */
-	done_zones = 0;
-	pass = 1;
-	/*
-	 * zone_start and zone_end are the current search range. search_zone
-	 * is 1 for mft zone, 2 for data zone 1 (end of mft zone till end of
-	 * volume) and 4 for data zone 2 (start of volume till start of mft
-	 * zone).
-	 */
-	zone_start = *location;
-	if (zone_start < 0) {
-		if (zone == DATA_ZONE)
-			zone_start = vol->data1_zone_pos;
-		else
-			zone_start = vol->mft_zone_pos;
-		if (!zone_start)
-			/*
-			 * Zone starts at beginning of volume which means a
-			 * single pass is sufficient.
-			 */
-			pass = 2;
-	} else if (zone_start >= vol->mft_zone_start && zone_start <
-			vol->mft_zone_end && zone == DATA_ZONE) {
-		zone_start = vol->mft_zone_end;
-		pass = 2;
-	} else if ((zone_start < vol->mft_zone_start || zone_start >=
-			vol->mft_zone_end) && zone == MFT_ZONE) {
-		zone_start = vol->mft_lcn;
-		if (!vol->mft_zone_end)
-			zone_start = (ntfs_cluster_t)0;
-		pass = 2;
-	}
-	if (zone == DATA_ZONE) {
-		/* Skip searching the mft zone. */
-		done_zones |= 1;
-		if (zone_start >= vol->mft_zone_end) {
-			zone_end = vol->nr_clusters;
-			search_zone = 2;
-		} else {
-			zone_end = vol->mft_zone_start;
-			search_zone = 4;
-		}
-	} else /* if (zone == MFT_ZONE) */ {
-		zone_end = vol->mft_zone_end;
-		search_zone = 1;
-	}
-	/*
-	 * buf_pos is the current bit position inside the bitmap. We use
-	 * initial_location to determine whether or not to do a zone switch.
-	 */
-	buf_pos = initial_location = zone_start;
-	/* Loop until all clusters are allocated, i.e. clusters == 0. */
-	clusters = *count;
-	rlpos = rlsize = 0;
-	if (*count <= 0) {
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): *count <= 0, "
-				"returning -EINVAL.\n");
-		err = -EINVAL;
-		goto err_ret;
-	}
-	while (1) {
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Start of outer while "
-				"loop: done_zones = 0x%x, search_zone = %i, "
-				"pass = %i, zone_start = 0x%x, zone_end = "
-				"0x%x, initial_location = 0x%x, buf_pos = "
-				"0x%x, rlpos = %i, rlsize = %i.\n",
-				done_zones, search_zone, pass, zone_start,
-				zone_end, initial_location, buf_pos, rlpos,
-				rlsize);
-		/* Loop until we run out of free clusters. */
-		io.param = buf;
-		io.size = PAGE_SIZE;
-		io.do_read = 1;
-		last_read_pos = buf_pos >> 3;
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): last_read_pos = "
-				"0x%x.\n", last_read_pos);
-		err = ntfs_readwrite_attr(vol->bitmap, data, last_read_pos,
-				&io);
-		if (err) {
-			ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): "
-					"ntfs_read_attr failed with error "
-					"code %i, going to err_ret.\n", -err);
-			goto err_ret;
-		}
-		if (!io.size) {
-			ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): !io.size, "
-					"going to zone_pass_done.\n");
-			goto zone_pass_done;
-		}
-		buf_size = io.size << 3;
-		lcn = buf_pos & 7;
-		buf_pos &= ~7;
-		need_writeback = 0;
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Before inner while "
-				"loop: buf_size = 0x%x, lcn = 0x%x, buf_pos = "
-				"0x%x, need_writeback = %i.\n", buf_size, lcn,
-				buf_pos, need_writeback);
-		while (lcn < buf_size && lcn + buf_pos < zone_end) {
-			byte = buf + (lcn >> 3);
-			ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): In inner "
-					"while loop: buf_size = 0x%x, lcn = "
-					"0x%x, buf_pos = 0x%x, need_writeback "
-					"= %i, byte ofs = 0x%x, *byte = "
-					"0x%x.\n", buf_size, lcn, buf_pos,
-					need_writeback, lcn >> 3, *byte);
-			/* Skip full bytes. */
-			if (*byte == 0xff) {
-				lcn += 8;
-				ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): "
-						"continuing while loop 1.\n");
-				continue;
-			}
-			bit = 1 << (lcn & 7);
-			ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): bit = %i.\n",
-					bit);
-			/* If the bit is already set, go onto the next one. */
-			if (*byte & bit) {
-				lcn++;
-				ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): "
-						"continuing while loop 2.\n");
-				continue;
-			}
-			/* Allocate the bitmap bit. */
-			*byte |= bit;
-			/* We need to write this bitmap buffer back to disk! */
-			need_writeback = 1;
-			ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): *byte = "
-					"0x%x, need_writeback = %i.\n", *byte,
-					need_writeback);
-			/* Reallocate memory if necessary. */
-			if ((rlpos + 2) * sizeof(ntfs_runlist) >= rlsize) {
-				ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): "
-						"Reallocating space.\n");
-				/* Setup first free bit return value. */
-				if (!rl2) {
-					*location = lcn + buf_pos;
-					ntfs_debug(DEBUG_OTHER, __FUNCTION__
-							"(): *location = "
-							"0x%x.\n", *location);
-				}
-				rlsize += PAGE_SIZE;
-				rlt = ntfs_vmalloc(rlsize);
-				if (!rlt) {
-					err = -ENOMEM;
-					ntfs_debug(DEBUG_OTHER, __FUNCTION__
-							"(): Failed to "
-							"allocate memory, "
-							"returning -ENOMEM, "
-							"going to "
-							"wb_err_ret.\n");
-					goto wb_err_ret;
-				}
-				if (rl2) {
-					ntfs_memcpy(rlt, rl2, rlsize -
-							PAGE_SIZE);
-					ntfs_vfree(rl2);
-				}
-				rl2 = rlt;
-				ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): "
-						"Reallocated memory, rlsize = "
-						"0x%x.\n", rlsize);
-			}
-			/*
-			 * Coalesce with previous run if adjacent LCNs.
-			 * Otherwise, append a new run.
-			 */
-			ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Adding run "
-					"(lcn 0x%x, len 0x%x), prev_lcn = "
-					"0x%x, lcn = 0x%x, buf_pos = 0x%x, "
-					"prev_run_len = 0x%x, rlpos = %i.\n",
-					lcn + buf_pos, 1, prev_lcn, lcn,
-					buf_pos, prev_run_len, rlpos);
-			if (prev_lcn == lcn + buf_pos - prev_run_len && rlpos) {
-				ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): "
-						"Coalescing to run (lcn 0x%x, "
-						"len 0x%x).\n",
-						rl2[rlpos - 1].lcn,
-						rl2[rlpos - 1].len);
-				rl2[rlpos - 1].len = ++prev_run_len;
-				ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): "
-						"Run now (lcn 0x%x, len 0x%x), "
-						"prev_run_len = 0x%x.\n",
-						rl2[rlpos - 1].lcn,
-						rl2[rlpos - 1].len,
-						prev_run_len);
-			} else {
-				if (rlpos)
-					ntfs_debug(DEBUG_OTHER, __FUNCTION__
-							"(): Adding new run, "
-							"(previous run lcn "
-							"0x%x, len 0x%x).\n",
-							rl2[rlpos - 1].lcn,
-							rl2[rlpos - 1].len);
-				else
-					ntfs_debug(DEBUG_OTHER, __FUNCTION__
-							"(): Adding new run, "
-							"is first run.\n");
-				rl2[rlpos].lcn = prev_lcn = lcn + buf_pos;
-				rl2[rlpos].len = prev_run_len =
-						(ntfs_cluster_t)1;
-				
-				rlpos++;
-			}
-			/* Done? */
-			if (!--clusters) {
-				ntfs_cluster_t tc;
-				/*
-				 * Update the current zone position. Positions
-				 * of already scanned zones have been updated
-				 * during the respective zone switches.
-				 */
-				tc = lcn + buf_pos + 1;
-				ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): "
-						"Done. Updating current zone "
-						"position, tc = 0x%x, "
-						"search_zone = %i.\n", tc,
-						search_zone);
-				switch (search_zone) {
-				case 1:
-					ntfs_debug(DEBUG_OTHER, __FUNCTION__
-							"(): Before checks, "
-							"vol->mft_zone_pos = "
-							"0x%x.\n",
-							vol->mft_zone_pos);
-					if (tc >= vol->mft_zone_end) {
-						vol->mft_zone_pos =
-								vol->mft_lcn;
-						if (!vol->mft_zone_end)
-							vol->mft_zone_pos =
-							     (ntfs_cluster_t)0;
-					} else if ((initial_location >=
-							vol->mft_zone_pos ||
-							tc > vol->mft_zone_pos)
-							&& tc >= vol->mft_lcn)
-						vol->mft_zone_pos = tc;
-					ntfs_debug(DEBUG_OTHER, __FUNCTION__
-							"(): After checks, "
-							"vol->mft_zone_pos = "
-							"0x%x.\n",
-							vol->mft_zone_pos);
-					break;
-				case 2:
-					ntfs_debug(DEBUG_OTHER, __FUNCTION__
-							"(): Before checks, "
-							"vol->data1_zone_pos = "
-							"0x%x.\n",
-							vol->data1_zone_pos);
-					if (tc >= vol->nr_clusters)
-						vol->data1_zone_pos =
-							     vol->mft_zone_end;
-					else if ((initial_location >=
-						    vol->data1_zone_pos ||
-						    tc > vol->data1_zone_pos)
-						    && tc >= vol->mft_zone_end)
-						vol->data1_zone_pos = tc;
-					ntfs_debug(DEBUG_OTHER, __FUNCTION__
-							"(): After checks, "
-							"vol->data1_zone_pos = "
-							"0x%x.\n",
-							vol->data1_zone_pos);
-					break;
-				case 4:
-					ntfs_debug(DEBUG_OTHER, __FUNCTION__
-							"(): Before checks, "
-							"vol->data2_zone_pos = "
-							"0x%x.\n",
-							vol->data2_zone_pos);
-					if (tc >= vol->mft_zone_start)
-						vol->data2_zone_pos =
-							(ntfs_cluster_t)0;
-					else if (initial_location >=
-						      vol->data2_zone_pos ||
-						      tc > vol->data2_zone_pos)
-						vol->data2_zone_pos = tc;
-					ntfs_debug(DEBUG_OTHER, __FUNCTION__
-							"(): After checks, "
-							"vol->data2_zone_pos = "
-							"0x%x.\n",
-							vol->data2_zone_pos);
-					break;
-				default:
-					BUG();
-				}
-				ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): "
-						"Going to done_ret.\n");
-				goto done_ret;
-			}
-			lcn++;
-		}
-		buf_pos += buf_size;
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After inner while "
-				"loop: buf_size = 0x%x, lcn = 0x%x, buf_pos = "
-				"0x%x, need_writeback = %i.\n", buf_size, lcn,
-				buf_pos, need_writeback);
-		if (need_writeback) {
-			ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Writing "
-					"back.\n");
-			need_writeback = 0;
-			io.param = buf;
-			io.do_read = 0;
-			err = ntfs_readwrite_attr(vol->bitmap, data,
-					last_read_pos, &io);
-			if (err) {
-				ntfs_error(__FUNCTION__ "(): Bitmap writeback "
-						"failed in read next buffer "
-						"code path with error code "
-						"%i.\n", -err);
-				goto err_ret;
-			}
+
+	/* 
+	 * TODO: When we start coping with sector sizes different from
+	 * NTFS_BLOCK_SIZE, we now probably need to set the blocksize of the
+	 * device (probably to NTFS_BLOCK_SIZE).
+	 */
+
+	/* Setup remaining fields in the super block. */
+	sb->s_magic = NTFS_SB_MAGIC;
+
+	/*
+	 * Ntfs allows 63 bits for the file size, i.e. correct would be:
+	 * 	sb->s_maxbytes = ~0ULL >> 1;
+	 * But the kernel uses a long as the page cache page index which on
+	 * 32-bit architectures is only 32-bits. MAX_LFS_FILESIZE is kernel
+	 * defined to the maximum the page cache page index can cope with
+	 * without overflowing the index or to 2^63 - 1, whichever is smaller.
+	 */
+	sb->s_maxbytes = MAX_LFS_FILESIZE;
+
+	/*
+	 * Now load the metadata required for the page cache and our address
+	 * space operations to function. We do this by setting up a specialised
+	 * read_inode method and then just calling iget() to obtain the inode
+	 * for $MFT which is sufficient to allow our normal inode operations
+	 * and associated address space operations to function.
+	 */
+	/*
+	 * Poison vol->mft_ino so we know whether iget() called into our
+	 * ntfs_read_inode_mount() method.
+	 */
+#define OGIN	((struct inode*)le32_to_cpu(0x4e49474f))	/* OGIN */
+	vol->mft_ino = OGIN;
+	sb->s_op = &ntfs_mount_sops;
+	tmp_ino = iget(vol->sb, FILE_MFT);
+	if (!tmp_ino || tmp_ino != vol->mft_ino || is_bad_inode(tmp_ino)) {
+		if (!silent)
+			ntfs_error(sb, "Failed to load essential metadata.");
+		if (tmp_ino && vol->mft_ino == OGIN)
+			ntfs_error(sb, "BUG: iget() did not call "
+					"ntfs_read_inode_mount() method!\n");
+		if (!tmp_ino)
+			goto cond_iput_mft_ino_err_out_now;
+		goto iput_tmp_ino_err_out_now;
+	}
+	/*
+	 * Note: sb->s_op has already been set to &ntfs_sops by our specialized
+	 * ntfs_read_inode_mount() method when it was invoked by iget().
+	 */
+
+	down(&ntfs_lock);
+
+	/*
+	 * The current mount is a compression user if the cluster size is
+	 * less than or equal 4kiB.
+	 */
+	if (vol->cluster_size <= 4096 && !ntfs_nr_compression_users++) {
+		result = allocate_compression_buffers();
+		if (result) {
+			ntfs_error(NULL, "Failed to allocate per CPU buffers "
+					"for compression engine.");
+			ntfs_nr_compression_users--;
+			up(&ntfs_lock);
+			goto iput_tmp_ino_err_out_now;
 		}
-		if (buf_pos < zone_end) {
-			ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Continuing "
-					"outer while loop, buf_pos = 0x%x, "
-					"zone_end = 0x%x.\n", buf_pos,
-					zone_end);
-			continue;
+	}
+
+	/*
+	 * Increment the number of mounts and generate the global default
+	 * upcase table if necessary. Also temporarily increment the number of
+	 * upcase users to avoid race conditions with concurrent (u)mounts.
+	 */
+	if (!ntfs_nr_mounts++)
+		default_upcase = generate_default_upcase();
+	ntfs_nr_upcase_users++;
+
+	up(&ntfs_lock);
+
+	/*
+	 * From now on, ignore @silent parameter. If we fail below this line,
+	 * it will be due to a corrupt fs or a system error, so we report it.
+	 */
+
+	/*
+	 * Open the system files with normal access functions and complete
+	 * setting up the ntfs super block.
+	 */
+	if (!load_system_files(vol)) {
+		ntfs_error(sb, "Failed to load system files.");
+		goto unl_upcase_iput_tmp_ino_err_out_now;
+	}
+
+	if ((sb->s_root = d_alloc_root(vol->root_ino))) {
+		/* We increment i_count simulating an iget(). */
+		atomic_inc(&vol->root_ino->i_count);
+		ntfs_debug("Exiting, status successful.");
+		/* Release the default upcase if it has no users. */
+		down(&ntfs_lock);
+		if (!--ntfs_nr_upcase_users && default_upcase) {
+			ntfs_free(default_upcase);
+			default_upcase = NULL;
 		}
-zone_pass_done:	/* Finished with the current zone pass. */
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At zone_pass_done, "
-				"pass = %i.\n", pass);
-		if (pass == 1) {
-			/*
-			 * Now do pass 2, scanning the first part of the zone
-			 * we omitted in pass 1.
-			 */
-			pass = 2;
-			zone_end = zone_start;
-			switch (search_zone) {
-			case 1: /* mft_zone */
-				zone_start = vol->mft_zone_start;
-				break;
-			case 2: /* data1_zone */
-				zone_start = vol->mft_zone_end;
-				break;
-			case 4: /* data2_zone */
-				zone_start = (ntfs_cluster_t)0;
-				break;
-			default:
-				BUG();
-			}
-			/* Sanity check. */
-			if (zone_end < zone_start)
-				zone_end = zone_start;
-			buf_pos = zone_start;
-			ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Continuing "
-					"outer while loop, pass = 2, "
-					"zone_start = 0x%x, zone_end = 0x%x, "
-					"buf_pos = 0x%x.\n");
-			continue;
-		} /* pass == 2 */
-done_zones_check:
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At done_zones_check, "
-				"search_zone = %i, done_zones before = 0x%x, "
-				"done_zones after = 0x%x.\n",
-				search_zone, done_zones, done_zones |
-				search_zone);
-		done_zones |= search_zone;
-		if (done_zones < 7) {
-			ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Switching "
-					"zone.\n");
-			/* Now switch to the next zone we haven't done yet. */
-			pass = 1;
-			switch (search_zone) {
-			case 1:
-				ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): "
-						"Switching from mft zone to "
-						"data1 zone.\n");
-				/* Update mft zone position. */
-				if (rlpos) {
-					ntfs_cluster_t tc;
-					ntfs_debug(DEBUG_OTHER, __FUNCTION__
-							"(): Before checks, "
-							"vol->mft_zone_pos = "
-							"0x%x.\n",
-							vol->mft_zone_pos);
-					tc = rl2[rlpos - 1].lcn +
-							rl2[rlpos - 1].len;
-					if (tc >= vol->mft_zone_end) {
-						vol->mft_zone_pos =
-								vol->mft_lcn;
-						if (!vol->mft_zone_end)
-							vol->mft_zone_pos =
-							     (ntfs_cluster_t)0;
-					} else if ((initial_location >=
-							vol->mft_zone_pos ||
-							tc > vol->mft_zone_pos)
-							&& tc >= vol->mft_lcn)
-						vol->mft_zone_pos = tc;
-					ntfs_debug(DEBUG_OTHER, __FUNCTION__
-							"(): After checks, "
-							"vol->mft_zone_pos = "
-							"0x%x.\n",
-							vol->mft_zone_pos);
-				}
-				/* Switch from mft zone to data1 zone. */
-switch_to_data1_zone:		search_zone = 2;
-				zone_start = initial_location =
-						vol->data1_zone_pos;
-				zone_end = vol->nr_clusters;
-				if (zone_start == vol->mft_zone_end)
-					pass = 2;
-				if (zone_start >= zone_end) {
-					vol->data1_zone_pos = zone_start =
-							vol->mft_zone_end;
-					pass = 2;
-				}
-				break;
-			case 2:
-				ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): "
-						"Switching from data1 zone to "
-						"data2 zone.\n");
-				/* Update data1 zone position. */
-				if (rlpos) {
-					ntfs_cluster_t tc;
-					ntfs_debug(DEBUG_OTHER, __FUNCTION__
-							"(): Before checks, "
-							"vol->data1_zone_pos = "
-							"0x%x.\n",
-							vol->data1_zone_pos);
-					tc = rl2[rlpos - 1].lcn +
-							rl2[rlpos - 1].len;
-					if (tc >= vol->nr_clusters)
-						vol->data1_zone_pos =
-							     vol->mft_zone_end;
-					else if ((initial_location >=
-						    vol->data1_zone_pos ||
-						    tc > vol->data1_zone_pos)
-						    && tc >= vol->mft_zone_end)
-						vol->data1_zone_pos = tc;
-					ntfs_debug(DEBUG_OTHER, __FUNCTION__
-							"(): After checks, "
-							"vol->data1_zone_pos = "
-							"0x%x.\n",
-							vol->data1_zone_pos);
-				}
-				/* Switch from data1 zone to data2 zone. */
-				search_zone = 4;
-				zone_start = initial_location =
-						vol->data2_zone_pos;
-				zone_end = vol->mft_zone_start;
-				if (!zone_start)
-					pass = 2;
-				if (zone_start >= zone_end) {
-					vol->data2_zone_pos = zone_start =
-							initial_location =
-							(ntfs_cluster_t)0;
-					pass = 2;
-				}
-				break;
-			case 4:
-				ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): "
-						"Switching from data2 zone to "
-						"data1 zone.\n");
-				/* Update data2 zone position. */
-				if (rlpos) {
-					ntfs_cluster_t tc;
-					ntfs_debug(DEBUG_OTHER, __FUNCTION__
-							"(): Before checks, "
-							"vol->data2_zone_pos = "
-							"0x%x.\n",
-							vol->data2_zone_pos);
-					tc = rl2[rlpos - 1].lcn +
-							rl2[rlpos - 1].len;
-					if (tc >= vol->mft_zone_start)
-						vol->data2_zone_pos =
-							     (ntfs_cluster_t)0;
-					else if (initial_location >=
-						      vol->data2_zone_pos ||
-						      tc > vol->data2_zone_pos)
-						vol->data2_zone_pos = tc;
-					ntfs_debug(DEBUG_OTHER, __FUNCTION__
-							"(): After checks, "
-							"vol->data2_zone_pos = "
-							"0x%x.\n",
-							vol->data2_zone_pos);
-				}
-				/* Switch from data2 zone to data1 zone. */
-				goto switch_to_data1_zone; /* See above. */
-			default:
-				BUG();
-			}
-			ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After zone "
-					"switch, search_zone = %i, pass = %i, "
-					"initial_location = 0x%x, zone_start "
-					"= 0x%x, zone_end = 0x%x.\n",
-					search_zone, pass, initial_location,
-					zone_start, zone_end);
-			buf_pos = zone_start;
-			if (zone_start == zone_end) {
-				ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): "
-						"Empty zone, going to "
-						"done_zones_check.\n");
-				/* Empty zone. Don't bother searching it. */
-				goto done_zones_check;
-			}
-			ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Continuing "
-					"outer while loop.\n");
-			continue;
-		} /* done_zones == 7 */
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): All zones are "
-				"finished.\n");
-		/*
-		 * All zones are finished! If DATA_ZONE, shrink mft zone. If
-		 * MFT_ZONE, we have really run out of space.
-		 */
-		mft_zone_size = vol->mft_zone_end - vol->mft_zone_start;
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): vol->mft_zone_start "
-				"= 0x%x, vol->mft_zone_end = 0x%x, "
-				"mft_zone_size = 0x%x.\n", vol->mft_zone_start,
-				vol->mft_zone_end, mft_zone_size);
-		if (zone == MFT_ZONE || mft_zone_size <= (ntfs_cluster_t)0) {
-			ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): No free "
-					"clusters left, returning -ENOSPC, "
-					"going to fail_ret.\n");
-			/* Really no more space left on device. */
-			err = -ENOSPC;
-			goto fail_ret;
-		} /* zone == DATA_ZONE && mft_zone_size > 0 */
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Shrinking mft "
-				"zone.\n");
-		zone_end = vol->mft_zone_end;
-		mft_zone_size >>= 1;
-		if (mft_zone_size > (ntfs_cluster_t)0)
-			vol->mft_zone_end = vol->mft_zone_start + mft_zone_size;
-		else /* mft zone and data2 zone no longer exist. */
-			vol->data2_zone_pos = vol->mft_zone_start =
-					vol->mft_zone_end = (ntfs_cluster_t)0;
-		if (vol->mft_zone_pos >= vol->mft_zone_end) {
-			vol->mft_zone_pos = vol->mft_lcn;
-			if (!vol->mft_zone_end)
-				vol->mft_zone_pos = (ntfs_cluster_t)0;
-		}
-		buf_pos = zone_start = initial_location =
-				vol->data1_zone_pos = vol->mft_zone_end;
-		search_zone = 2;
-		pass = 2;
-		done_zones &= ~2;
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After shrinking mft "
-				"zone, mft_zone_size = 0x%x, "
-				"vol->mft_zone_start = 0x%x, vol->mft_zone_end "
-				"= 0x%x, vol->mft_zone_pos = 0x%x, search_zone "
-				"= 2, pass = 2, dones_zones = 0x%x, zone_start "
-				"= 0x%x, zone_end = 0x%x, vol->data1_zone_pos "
-				"= 0x%x, continuing outer while loop.\n",
-				mft_zone_size, vol->mft_zone_start,
-				vol->mft_zone_end, vol->mft_zone_pos,
-				search_zone, pass, done_zones, zone_start,
-				zone_end, vol->data1_zone_pos);
-	}
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): After outer while loop.\n");
-done_ret:
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At done_ret.\n");
-	rl2[rlpos].lcn = (ntfs_cluster_t)-1;
-	rl2[rlpos].len = (ntfs_cluster_t)0;
-	*rl = rl2;
-	*rl_len = rlpos;
-	if (need_writeback) {
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Writing back.\n");
-		need_writeback = 0;
-		io.param = buf;
-		io.do_read = 0;
-		err = ntfs_readwrite_attr(vol->bitmap, data, last_read_pos,
-				&io);
-		if (err) {
-			ntfs_error(__FUNCTION__ "(): Bitmap writeback failed "
-					"in done code path with error code "
-					"%i.\n", -err);
-			goto err_ret;
-		}
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Wrote 0x%Lx bytes.\n",
-				io.size);
-	}
-done_fail_ret:
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At done_fail_ret (follows "
-			"done_ret).\n");
-	unlock_kernel();
-	free_page((unsigned long)buf);
-	if (err)
-		ntfs_debug(DEBUG_FILE3, __FUNCTION__ "(): Failed to allocate "
-				"clusters. Returning with error code %i.\n",
-				-err);
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Syncing $Bitmap inode.\n");
-	if (ntfs_update_inode(vol->bitmap))
-		ntfs_error(__FUNCTION__ "(): Failed to sync inode $Bitmap. "
-				"Continuing anyway.\n");
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Returning with code %i.\n",
-			err);
-	return err;
-fail_ret:
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At fail_ret.\n");
-	if (rl2) {
-		if (err == -ENOSPC) {
-			/* Return first free lcn and count of free clusters. */
-			*location = rl2[0].lcn;
-			*count -= clusters;
-			ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): err = "
-					"-ENOSPC, *location = 0x%x, *count = "
-					"0x%x.\n", *location, *count);
-		}
-		/* Deallocate all allocated clusters. */
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Deallocating "
-				"allocated clusters.\n");
-		ntfs_deallocate_clusters(vol, rl2, rlpos);
-		/* Free the runlist. */
-		ntfs_vfree(rl2);
-	} else {
-		if (err == -ENOSPC) {
-			/* Nothing free at all. */
-			*location = vol->data1_zone_pos; /* Irrelevant... */
-			*count = 0;
-			ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): No space "
-					"left at all, err = -ENOSPC, *location "
-					"= 0x%x, *count = 0.\n", *location);
-		}
-	}
-	*rl = NULL;
-	*rl_len = 0;
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): *rl = NULL, *rl_len = 0, "
-			"going to done_fail_ret.\n");
-	goto done_fail_ret;
-wb_err_ret:
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At wb_err_ret.\n");
-	if (need_writeback) {
-		int __err;
-		ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): Writing back.\n");
-		io.param = buf;
-		io.do_read = 0;
-		__err = ntfs_readwrite_attr(vol->bitmap, data, last_read_pos,
-				&io);
-		if (__err)
-			ntfs_error(__FUNCTION__ "(): Bitmap writeback failed "
-					"in error code path with error code "
-					"%i.\n", -__err);
-		need_writeback = 0;
-	}
-err_ret:
-	ntfs_debug(DEBUG_OTHER, __FUNCTION__ "(): At err_ret, *location = -1, "
-			"*count = 0, going to fail_ret.\n");
-	*location = -1;
-	*count = 0;
-	goto fail_ret;
+		up(&ntfs_lock);
+		return 0;
+	}
+	ntfs_error(sb, "Failed to allocate root directory.");
+	/* Clean up after the successful load_system_files() call from above. */
+	iput(vol->vol_ino);
+	vol->vol_ino = NULL;
+	/* NTFS 3.0+ specific clean up. */
+	if (vol->major_ver >= 3) {
+		iput(vol->secure_ino);
+		vol->secure_ino = NULL;
+	}
+	iput(vol->root_ino);
+	vol->root_ino = NULL;
+	iput(vol->lcnbmp_ino);
+	vol->lcnbmp_ino = NULL;
+	iput(vol->mftmirr_ino);
+	vol->mftmirr_ino = NULL;
+	truncate_inode_pages(&vol->mftbmp_mapping, 0);
+	vol->mftbmp_mapping.a_ops = NULL;
+	vol->mftbmp_mapping.host = NULL;
+	ntfs_free(vol->mftbmp_rl.rl);
+	vol->mftbmp_rl.rl = NULL;
+	vol->upcase_len = 0;
+	if (vol->upcase != default_upcase)
+		ntfs_free(vol->upcase);
+	vol->upcase = NULL;
+	if (vol->nls_map) {
+		unload_nls(vol->nls_map);
+		vol->nls_map = NULL;
+	}
+	/* Error exit code path. */
+unl_upcase_iput_tmp_ino_err_out_now:
+	/*
+	 * Decrease the number of mounts and destroy the global default upcase
+	 * table if necessary.
+	 */
+	down(&ntfs_lock);
+	ntfs_nr_mounts--;
+	if (!--ntfs_nr_upcase_users && default_upcase) {
+		ntfs_free(default_upcase);
+		default_upcase = NULL;
+	}
+	if (vol->cluster_size <= 4096 && !--ntfs_nr_compression_users)
+		free_compression_buffers();
+	up(&ntfs_lock);
+iput_tmp_ino_err_out_now:
+	iput(tmp_ino);
+cond_iput_mft_ino_err_out_now:
+	if (vol->mft_ino && vol->mft_ino != OGIN && vol->mft_ino != tmp_ino) {
+		iput(vol->mft_ino);
+		vol->mft_ino = NULL;
+	}
+#undef OGIN
+	/*
+	 * This is needed to get ntfs_clear_inode() called for each inode we
+	 * have ever called iget()/iput() on, otherwise we A) leak resources
+	 * and B) a subsequent mount fails automatically due to iget() never
+	 * calling down into our ntfs_read_inode{_mount}() methods again...
+	 */
+	if (invalidate_inodes(sb)) {
+		ntfs_error(sb, "Busy inodes left. This is most likely a NTFS "
+				"driver bug.");
+		/* Copied from fs/super.c. I just love this message. (-; */
+		printk("VFS: Busy inodes after umount. Self-destruct in 5 "
+				"seconds.  Have a nice day...\n");
+	}
+set_blk_size_err_out_now:
+	/* Errors at this stage are irrelevant. */
+	// FIXME: This should be done in fs/super.c::get_sb_bdev() itself! (AIA)
+	sb_set_blocksize(sb, sb->s_old_blocksize);
+err_out_now:
+	sb->u.generic_sbp = NULL;
+	kfree(vol);
+	ntfs_debug("Failed, returning -EINVAL.");
+	return -EINVAL;
 }
 
 /*
- * IMPORTANT: Caller has to hold big kernel lock or the race monster will come
- * to get you! (-;
- * TODO: Need our own lock for bitmap accesses but BKL is more secure for now,
- * considering we might not have covered all places with a lock yet. In that
- * case the BKL offers a one way exclusion which is better than no exclusion
- * at all... (AIA)
+ * This is a slab cache to optimize allocations and deallocations of Unicode
+ * strings of the maximum length allowed by NTFS, which is NTFS_MAX_NAME_LEN
+ * (255) Unicode characters + a terminating NULL Unicode character.
  */
-static int ntfs_clear_bitrange(ntfs_inode *bitmap,
-		const ntfs_cluster_t start_bit, const ntfs_cluster_t count)
+kmem_cache_t *ntfs_name_cache;
+
+/* Slab caches for efficient allocation/deallocation of of inodes. */
+kmem_cache_t *ntfs_inode_cache;
+kmem_cache_t *ntfs_big_inode_cache;
+
+/* Init once constructor for the inode slab cache. */
+static void ntfs_big_inode_init_once(void *foo, kmem_cache_t *cachep,
+		unsigned long flags)
 {
-	ntfs_cluster_t buf_size, bit, nr_bits = count;
-	unsigned char *buf, *byte;
-	int err;
-	ntfs_io io;
-
-	io.fn_put = ntfs_put;
-	io.fn_get = ntfs_get;
-	/* Calculate the required buffer size in bytes. */
-	buf_size = (ntfs_cluster_t)((start_bit & 7) + nr_bits + 7) >> 3;
-	if (buf_size <= (ntfs_cluster_t)(64 * 1024))
-		buf = ntfs_malloc(buf_size);
-	else
-		buf = ntfs_vmalloc(buf_size);
-	if (!buf)
-		return -ENOMEM;
-	/* Read the bitmap from the data attribute. */
-	io.param = byte = buf;
-	io.size = buf_size;
-	err = ntfs_read_attr(bitmap, bitmap->vol->at_data, 0, start_bit >> 3,
-			&io);
-	if (err || io.size != buf_size)
-		goto err_out;
-	/* Now clear the bits in the read bitmap. */
-	bit = start_bit & 7;
-	while (bit && nr_bits) { /* Process first partial byte, if present. */
-		*byte &= ~(1 << bit++);
-		nr_bits--;
-		bit &= 7;
-		if (!bit)
-			byte++;
-	}
-	while (nr_bits >= 8) { /* Process full bytes. */
-		*byte = 0;
-		nr_bits -= 8;
-		byte++;
-	}
-	bit = 0;
-	while (nr_bits) { /* Process last partial byte, if present. */
-		*byte &= ~(1 << bit);
-		nr_bits--;
-		bit++;
-	}
-	/* Write the modified bitmap back to disk. */
-	io.param = buf;
-	io.size = buf_size;
-	err = ntfs_write_attr(bitmap, bitmap->vol->at_data, 0, start_bit >> 3,
-			&io);
-err_out:
-	if (buf_size <= (ntfs_cluster_t)(64 * 1024))
-		ntfs_free(buf);
-	else
-		ntfs_vfree(buf);
-	if (!err && io.size != buf_size)
-		err = -EIO;
-	return err;
+	ntfs_inode *ni = (ntfs_inode *)foo;
+
+	if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+			SLAB_CTOR_CONSTRUCTOR)
+		inode_init_once(VFS_I(ni));
 }
 
 /*
- * See comments for lack of zone adjustments below in the description of the
- * function ntfs_deallocate_clusters().
+ * Slab cache to optimize allocations and deallocations of attribute search
+ * contexts.
  */
-int ntfs_deallocate_cluster_run(const ntfs_volume *vol,
-		const ntfs_cluster_t lcn, const ntfs_cluster_t len)
+kmem_cache_t *ntfs_attr_ctx_cache;
+
+/* A global default upcase table and a corresponding reference count. */
+wchar_t *default_upcase = NULL;
+unsigned long ntfs_nr_upcase_users = 0;
+
+/* The number of mounted filesystems. */
+unsigned long ntfs_nr_mounts = 0;
+
+/* Driver wide semaphore. */
+DECLARE_MUTEX(ntfs_lock);
+
+static struct super_block *ntfs_get_sb(struct file_system_type *fs_type,
+	int flags, char *dev_name, void *data)
+{
+	return get_sb_bdev(fs_type, flags, dev_name, data, ntfs_fill_super);
+}
+
+static struct file_system_type ntfs_fs_type = {
+	owner:		THIS_MODULE,
+	name:		"ntfs",
+	get_sb:		ntfs_get_sb,
+	kill_sb:	kill_block_super,
+	fs_flags:	FS_REQUIRES_DEV,
+};
+
+/* Stable names for the slab caches. */
+static const char *ntfs_attr_ctx_cache_name = "ntfs_attr_ctx_cache";
+static const char *ntfs_name_cache_name = "ntfs_name_cache";
+static const char *ntfs_inode_cache_name = "ntfs_inode_cache";
+static const char *ntfs_big_inode_cache_name = "ntfs_big_inode_cache";
+
+static int __init init_ntfs_fs(void)
 {
-	int err;
+	int err = 0;
 
-	lock_kernel();
-	err = ntfs_clear_bitrange(vol->bitmap, lcn, len);
-	unlock_kernel();
+	/* This may be ugly but it results in pretty output so who cares. (-8 */
+	printk(KERN_INFO "NTFS driver " NTFS_VERSION " [Flags: R/"
+#ifdef CONFIG_NTFS_RW
+			"W"
+#else
+			"O"
+#endif
+#ifdef DEBUG
+			" DEBUG"
+#endif
+#ifdef MODULE
+			" MODULE"
+#endif
+			"]. Copyright (c) 2001,2002 Anton Altaparmakov.\n");
+
+	ntfs_debug("Debug messages are enabled.");
+
+	ntfs_attr_ctx_cache = kmem_cache_create(ntfs_attr_ctx_cache_name,
+			sizeof(attr_search_context), 0 /* offset */,
+			SLAB_HWCACHE_ALIGN, NULL /* ctor */, NULL /* dtor */);
+	if (!ntfs_attr_ctx_cache) {
+		printk(KERN_CRIT "NTFS: Failed to create %s!\n",
+				ntfs_attr_ctx_cache_name);
+		goto ctx_err_out;
+	}
+
+	ntfs_name_cache = kmem_cache_create(ntfs_name_cache_name,
+			(NTFS_MAX_NAME_LEN+1) * sizeof(uchar_t), 0,
+			SLAB_HWCACHE_ALIGN, NULL, NULL);
+	if (!ntfs_name_cache) {
+		printk(KERN_CRIT "NTFS: Failed to create %s!\n",
+				ntfs_name_cache_name);
+		goto name_err_out;
+	}
+
+	ntfs_inode_cache = kmem_cache_create(ntfs_inode_cache_name,
+			sizeof(ntfs_inode), 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+	if (!ntfs_inode_cache) {
+		printk(KERN_CRIT "NTFS: Failed to create %s!\n",
+				ntfs_inode_cache_name);
+		goto inode_err_out;
+	}
+
+	ntfs_big_inode_cache = kmem_cache_create(ntfs_big_inode_cache_name,
+			sizeof(big_ntfs_inode), 0, SLAB_HWCACHE_ALIGN,
+			ntfs_big_inode_init_once, NULL);
+	if (!ntfs_big_inode_cache) {
+		printk(KERN_CRIT "NTFS: Failed to create %s!\n",
+				ntfs_big_inode_cache_name);
+		goto big_inode_err_out;
+	}
+
+	/* Register the ntfs sysctls. */
+	err = ntfs_sysctl(1);
+	if (err) {
+		printk(KERN_CRIT "NTFS: Failed to register NTFS sysctls!\n");
+		goto sysctl_err_out;
+	}
+
+	err = register_filesystem(&ntfs_fs_type);
+	if (!err) {
+		ntfs_debug("NTFS driver registered successfully.");
+		return 0; /* Success! */
+	}
+	printk(KERN_CRIT "NTFS: Failed to register NTFS file system driver!\n");
+
+sysctl_err_out:
+	kmem_cache_destroy(ntfs_big_inode_cache);
+big_inode_err_out:
+	kmem_cache_destroy(ntfs_inode_cache);
+inode_err_out:
+	kmem_cache_destroy(ntfs_name_cache);
+name_err_out:
+	kmem_cache_destroy(ntfs_attr_ctx_cache);
+ctx_err_out:
+	if (!err) {
+		printk(KERN_CRIT "NTFS: Aborting NTFS file system driver "
+				"registration...\n");
+		err = -ENOMEM;
+	}
 	return err;
 }
 
-/*
- * This is inefficient, but logically trivial, so will do for now. Note, we
- * do not touch the mft nor the data zones here because we want to minimize
- * recycling of clusters to enhance the chances of data being undeleteable.
- * Also we don't want the overhead. Instead we do one additional sweep of the
- * current data zone during cluster allocation to check for freed clusters.
- */
-int ntfs_deallocate_clusters(const ntfs_volume *vol, const ntfs_runlist *rl,
-		const int rl_len)
+static void __exit exit_ntfs_fs(void)
 {
-	int i, err;
+	int err = 0;
 
-	lock_kernel();
-	for (i = err = 0; i < rl_len && !err; i++)
-		err = ntfs_clear_bitrange(vol->bitmap, rl[i].lcn, rl[i].len);
-	unlock_kernel();
-	return err;
+	ntfs_debug("Unregistering NTFS driver.");
+
+	unregister_filesystem(&ntfs_fs_type);
+
+	if (kmem_cache_destroy(ntfs_big_inode_cache) && (err = 1))
+		printk(KERN_CRIT "NTFS: Failed to destory %s.\n",
+				ntfs_big_inode_cache_name);
+	if (kmem_cache_destroy(ntfs_inode_cache) && (err = 1))
+		printk(KERN_CRIT "NTFS: Failed to destory %s.\n",
+				ntfs_inode_cache_name);
+	if (kmem_cache_destroy(ntfs_name_cache) && (err = 1))
+		printk(KERN_CRIT "NTFS: Failed to destory %s.\n",
+				ntfs_name_cache_name);
+	if (kmem_cache_destroy(ntfs_attr_ctx_cache) && (err = 1))
+		printk(KERN_CRIT "NTFS: Failed to destory %s.\n",
+				ntfs_attr_ctx_cache_name);
+	if (err)
+		printk(KERN_CRIT "NTFS: This causes memory to leak! There is "
+				"probably a BUG in the driver! Please report "
+				"you saw this message to "
+				"linux-ntfs-dev@lists.sf.net\n");
+	/* Unregister the ntfs sysctls. */
+	ntfs_sysctl(0);
 }
+
+EXPORT_NO_SYMBOLS;
+MODULE_AUTHOR("Anton Altaparmakov <aia21@cam.ac.uk>");
+MODULE_DESCRIPTION("NTFS 1.2/3.x driver");
+MODULE_LICENSE("GPL");
+#ifdef DEBUG
+MODULE_PARM(debug_msgs, "i");
+MODULE_PARM_DESC(debug_msgs, "Enable debug messages.");
+#endif
+
+module_init(init_ntfs_fs)
+module_exit(exit_ntfs_fs)
 
diff -Nru a/fs/ntfs/super.h b/fs/ntfs/super.h
--- a/fs/ntfs/super.h	Sun Apr 28 20:10:37 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,32 +0,0 @@
-/*
- * super.h - Header file for super.c
- *
- * Copyright (C) 1995-1997 Martin von Löwis
- * Copyright (C) 1996-1997 Régis Duchesne
- * Copyright (c) 2001 Anton Altaparmakov
- */
-
-int ntfs_get_free_cluster_count(ntfs_inode *bitmap);
-
-int ntfs_get_volumesize(ntfs_volume *vol, __s64 *vol_size);
-
-int ntfs_init_volume(ntfs_volume *vol, char *boot);
-
-int ntfs_load_special_files(ntfs_volume *vol);
-
-int ntfs_release_volume(ntfs_volume *vol);
-
-int ntfs_insert_fixups(unsigned char *rec, int rec_size);
-
-int ntfs_fixup_record(char *record, char *magic, int size);
-
-int ntfs_allocate_clusters(ntfs_volume *vol, ntfs_cluster_t *location,
-		ntfs_cluster_t *count, ntfs_runlist **rl, int *rl_len,
-		const NTFS_CLUSTER_ALLOCATION_ZONES zone);
-
-int ntfs_deallocate_cluster_run(const ntfs_volume *vol,
-		const ntfs_cluster_t lcn, const ntfs_cluster_t len);
-
-int ntfs_deallocate_clusters(const ntfs_volume *vol, const ntfs_runlist *rl,
-		const int rl_len);
-
diff -Nru a/fs/ntfs/support.c b/fs/ntfs/support.c
--- a/fs/ntfs/support.c	Sun Apr 28 20:10:31 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,316 +0,0 @@
-/*
- * support.c -  Specific support functions
- *
- * Copyright (C) 1997 Martin von Löwis
- * Copyright (C) 1997 Régis Duchesne
- * Copyright (C) 2001 Anton Altaparmakov (AIA)
- */
-
-#include "ntfstypes.h"
-#include "struct.h"
-#include "support.h"
-
-#include <stdarg.h>
-#include <linux/slab.h>
-#include <linux/locks.h>
-#include <linux/fs.h>
-#include "util.h"
-#include "inode.h"
-#include "macros.h"
-#include <linux/nls.h>
-
-static char print_buf[1024];
-
-#ifdef DEBUG
-#include "sysctl.h"
-#include <linux/kernel.h>
-
-/* Debugging output */
-void ntfs_debug(int mask, const char *fmt, ...)
-{
-	va_list ap;
-
-	/* Filter it with the debugging level required */
-	if (ntdebug & mask) {
-		va_start(ap,fmt);
-		strcpy(print_buf, KERN_DEBUG "NTFS: ");
-		vsprintf(print_buf + 9, fmt, ap);
-		printk(print_buf);
-		va_end(ap);
-	}
-}
-
-#ifndef ntfs_malloc
-/* Verbose kmalloc */
-void *ntfs_malloc(int size)
-{
-	void *ret;
-
-	ret = kmalloc(size, GFP_KERNEL);
-	ntfs_debug(DEBUG_MALLOC, "Allocating %x at %p\n", size, ret);
-
-	return ret;
-}
-#endif
-
-#ifndef ntfs_free
-/* Verbose kfree() */
-void ntfs_free(void *block)
-{
-        ntfs_debug(DEBUG_MALLOC, "Freeing memory at %p\n", block);
-	kfree(block);
-}
-#endif
-#else /* End of DEBUG functions. Normal ones below... */
-
-#ifndef ntfs_malloc
-void *ntfs_malloc(int size)
-{
-	return kmalloc(size, GFP_KERNEL);
-}
-#endif
-
-#ifndef ntfs_free
-void ntfs_free(void *block)
-{
-	kfree(block);
-}
-#endif
-#endif /* DEBUG */
-
-void ntfs_bzero(void *s, int n)
-{
-	memset(s, 0, n);
-}
-
-/* These functions deliberately return no value. It is dest, anyway,
-   and not used anywhere in the NTFS code.  */
-
-void ntfs_memcpy(void *dest, const void *src, ntfs_size_t n)
-{
-	memcpy(dest, src, n);
-}
-
-void ntfs_memmove(void *dest, const void *src, ntfs_size_t n)
-{
-	memmove(dest, src, n);
-}
-
-/* Warn that an error occurred. */
-void ntfs_error(const char *fmt,...)
-{
-        va_list ap;
-
-        va_start(ap, fmt);
-        strcpy(print_buf, KERN_ERR "NTFS: ");
-        vsprintf(print_buf + 9, fmt, ap);
-        printk(print_buf);
-        va_end(ap);
-}
-
-int ntfs_read_mft_record(ntfs_volume *vol, int mftno, char *buf)
-{
-	int error;
-	ntfs_io io;
-
-	ntfs_debug(DEBUG_OTHER, "read_mft_record 0x%x\n", mftno);
-	if (mftno == FILE_Mft)
-	{
-		ntfs_memcpy(buf, vol->mft, vol->mft_record_size);
-		return 0;
-	}
-	if (!vol->mft_ino)
-	{
-		printk(KERN_ERR "NTFS: mft_ino is NULL. Something is terribly "
-				"wrong here!\n");
-		return -ENODATA;
-	}
- 	io.fn_put = ntfs_put;
-	io.fn_get = 0;
-	io.param = buf;
-	io.size = vol->mft_record_size;
-	ntfs_debug(DEBUG_OTHER, "read_mft_record: calling ntfs_read_attr with: "
-		"mftno = 0x%x, vol->mft_record_size_bits = 0x%x, "
-		"mftno << vol->mft_record_size_bits = 0x%Lx\n", mftno,
-		vol->mft_record_size_bits,
-		(__s64)mftno << vol->mft_record_size_bits);
-	error = ntfs_read_attr(vol->mft_ino, vol->at_data, NULL,
-				(__s64)mftno << vol->mft_record_size_bits, &io);
-	if (error || (io.size != vol->mft_record_size)) {
-		ntfs_debug(DEBUG_OTHER, "read_mft_record: read 0x%x failed "
-				   	"(%d,%d,%d)\n", mftno, error, io.size,
-				   	vol->mft_record_size);
-		return error ? error : -ENODATA;
-	}
-	ntfs_debug(DEBUG_OTHER, "read_mft_record: finished read 0x%x\n", mftno);
-	if (!ntfs_check_mft_record(vol, buf)) {
-		/* FIXME: This is incomplete behaviour. We might be able to
-		 * recover at this stage. ntfs_check_mft_record() is too
-		 * conservative at aborting it's operations. It is OK for
-		 * now as we just can't handle some on disk structures
-		 * this way. (AIA) */
-		printk(KERN_WARNING "NTFS: Invalid MFT record for 0x%x\n", mftno);
-		return -EIO;
-	}
-	ntfs_debug(DEBUG_OTHER, "read_mft_record: Done 0x%x\n", mftno);
-	return 0;
-}
-
-int ntfs_getput_clusters(ntfs_volume *vol, int cluster, ntfs_size_t start_offs,
-		ntfs_io *buf)
-{
-	struct super_block *sb = NTFS_SB(vol);
-	struct buffer_head *bh;
-	int length = buf->size;
-	int error = 0;
-	ntfs_size_t to_copy;
-
-	ntfs_debug(DEBUG_OTHER, "%s_clusters %d %d %d\n", 
-		   buf->do_read ? "get" : "put", cluster, start_offs, length);
-	to_copy = vol->cluster_size - start_offs;
-	while (length) {
-		if (!(bh = sb_bread(sb, cluster))) {
-			ntfs_debug(DEBUG_OTHER, "%s failed\n",
-				   buf->do_read ? "Reading" : "Writing");
-			error = -EIO;
-			goto error_ret;
-		}
-		if (to_copy > length)
-			to_copy = length;
-		lock_buffer(bh);
-		if (buf->do_read) {
-			buf->fn_put(buf, bh->b_data + start_offs, to_copy);
-			unlock_buffer(bh);
-		} else {
-			buf->fn_get(bh->b_data + start_offs, buf, to_copy);
-			mark_buffer_dirty(bh);
-			unlock_buffer(bh);
-			/*
-			 * Note: We treat synchronous IO on a per volume basis
-			 * disregarding flags of individual inodes. This can
-			 * lead to some strange write ordering effects upon a
-			 * remount with a change in the sync flag but it should
-			 * not break anything. [Except if the system crashes
-			 * at that point in time but there would be more thigs
-			 * to worry about than that in that case...]. (AIA)
-			 */
-			if (sb->s_flags & MS_SYNCHRONOUS) {
-				ll_rw_block(WRITE, 1, &bh);
-				wait_on_buffer(bh);
-				if (buffer_req(bh) && !buffer_uptodate(bh)) {
-					printk(KERN_ERR "IO error syncing NTFS "
-					       "cluster [%s:%i]\n",
-					       sb->s_id, cluster);
-					brelse(bh);
-					error = -EIO;
-					goto error_ret;
-				}
-			}
-		}
-		brelse(bh);
-		length -= to_copy;
-		start_offs = 0;
-		to_copy = vol->cluster_size;
-		cluster++;
-	}
-error_ret:
-	return error;
-}
-
-ntfs_time64_t ntfs_now(void)
-{
-	return ntfs_unixutc2ntutc(CURRENT_TIME);
-}
-
-int ntfs_dupuni2map(ntfs_volume *vol, ntfs_u16 *in, int in_len, char **out,
-		int *out_len)
-{
-	int i, o, chl, chi;
-	char *result, *buf, charbuf[NLS_MAX_CHARSET_SIZE];
-	struct nls_table *nls = vol->nls_map;
-
-	result = ntfs_malloc(in_len + 1);
-	if (!result)
-		return -ENOMEM;
-	*out_len = in_len;
-	for (i = o = 0; i < in_len; i++) {
-		/* FIXME: Byte order? */
-		wchar_t uni = in[i];
-		if ((chl = nls->uni2char(uni, charbuf,
-				NLS_MAX_CHARSET_SIZE)) > 0) {
-			/* Adjust result buffer. */
-			if (chl > 1) {
-				buf = ntfs_malloc(*out_len + chl - 1);
-				if (!buf) {
-					i = -ENOMEM;
-					goto err_ret;
-				}
-				memcpy(buf, result, o);
-				ntfs_free(result);
-				result = buf;
-				*out_len += (chl - 1);
-			}
-			for (chi = 0; chi < chl; chi++)
-				result[o++] = charbuf[chi];
-		} else {
-			/* Invalid character. */
-			printk(KERN_ERR "NTFS: Unicode name contains a "
-					"character that cannot be converted "
-					"to chosen character set. Remount "
-					"with utf8 encoding and this should "
-					"work.\n");
-			i = -EILSEQ;
-			goto err_ret;
-		}
-	}
-	result[*out_len] = '\0';
-	*out = result;
-	return 0;
-err_ret:
-	ntfs_free(result);
-	*out_len = 0;
-	*out = NULL;
-	return i;
-}
-
-int ntfs_dupmap2uni(ntfs_volume *vol, char* in, int in_len, ntfs_u16 **out,
-		int *out_len)
-{
-	int i, o;
-	ntfs_u16 *result;
-	struct nls_table *nls = vol->nls_map;
-
-	*out = result = ntfs_malloc(2 * in_len);
-	if (!result) {
-		*out_len = 0;
-		return -ENOMEM;
-	}
-	*out_len = in_len;
-	for (i = o = 0; i < in_len; i++, o++) {
-		wchar_t uni;
-		int charlen;
-
-		charlen = nls->char2uni(&in[i], in_len - i, &uni);
-		if (charlen < 0) {
-			i = charlen;
-			goto err_ret;
-		}
-		*out_len -= charlen - 1;
-		i += charlen - 1;
-		/* FIXME: Byte order? */
-		result[o] = uni;
-		if (!result[o]) {
-			i = -EILSEQ;
-			goto err_ret;
-		}
-	}
-	return 0;
-err_ret:
-	printk(KERN_ERR "NTFS: Name contains a character that cannot be "
-			"converted to Unicode.\n");
-	ntfs_free(result);
-	*out_len = 0;
-	*out = NULL;
-	return i;
-}
-
diff -Nru a/fs/ntfs/support.h b/fs/ntfs/support.h
--- a/fs/ntfs/support.h	Sun Apr 28 20:10:30 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,89 +0,0 @@
-/*
- * support.h - Header file for specific support.c
- *
- * Copyright (C) 1997 Régis Duchesne
- * Copyright (c) 2001 Anton Altaparmakov (AIA)
- */
-
-/* Debug levels */
-#define DEBUG_OTHER	1
-#define DEBUG_MALLOC	2
-#define DEBUG_BSD       4
-#define DEBUG_LINUX     8
-#define DEBUG_DIR1     16
-#define DEBUG_DIR2     32
-#define DEBUG_DIR3     64
-#define DEBUG_FILE1   128
-#define DEBUG_FILE2   256
-#define DEBUG_FILE3   512
-#define DEBUG_NAME1  1024
-#define DEBUG_NAME2  2048
-
-#ifdef DEBUG
-void ntfs_debug(int mask, const char *fmt, ...);
-#else
-#define ntfs_debug(mask, fmt...)	do {} while (0)
-#endif
-
-#include <linux/slab.h>
-#include <linux/vmalloc.h>
-
-#define ntfs_malloc(size)  kmalloc(size, GFP_KERNEL)
-
-#define ntfs_free(ptr)     kfree(ptr)
-
-/**
- * ntfs_vmalloc - allocate memory in multiples of pages
- * @size	number of bytes to allocate
- *
- * Allocates @size bytes of memory, rounded up to multiples of PAGE_SIZE and
- * returns a pointer to the allocated memory.
- *
- * If there was insufficient memory to complete the request, return NULL.
- */
-static inline void *ntfs_vmalloc(unsigned long size)
-{
-	if (size <= PAGE_SIZE) {
-		if (size) {
-			/* kmalloc() has per-CPU caches so if faster for now. */
-			return kmalloc(PAGE_SIZE, GFP_NOFS);
-			/* return (void *)__get_free_page(GFP_NOFS |
-					__GFP_HIGHMEM); */
-		}
-		BUG();
-	}
-	if (size >> PAGE_SHIFT < num_physpages)
-		return __vmalloc(size, GFP_NOFS | __GFP_HIGHMEM, PAGE_KERNEL);
-	return NULL;
-}
-
-static inline void ntfs_vfree(void *addr)
-{
-	if ((unsigned long)addr < VMALLOC_START) {
-		return kfree(addr);
-		/* return free_page((unsigned long)addr); */
-	}
-	vfree(addr);
-}
-
-void ntfs_bzero(void *s, int n);
-
-void ntfs_memcpy(void *dest, const void *src, ntfs_size_t n);
-
-void ntfs_memmove(void *dest, const void *src, ntfs_size_t n);
-
-void ntfs_error(const char *fmt,...);
-
-int ntfs_read_mft_record(ntfs_volume *vol, int mftno, char *buf);
-
-int ntfs_getput_clusters(ntfs_volume *pvol, int cluster, ntfs_size_t offs,
-			 ntfs_io *buf);
-
-ntfs_time64_t ntfs_now(void);
-
-int ntfs_dupuni2map(ntfs_volume *vol, ntfs_u16 *in, int in_len, char **out,
-		    int *out_len);
-
-int ntfs_dupmap2uni(ntfs_volume *vol, char* in, int in_len, ntfs_u16 **out,
-		    int *out_len);
-
diff -Nru a/fs/ntfs/sysctl.c b/fs/ntfs/sysctl.c
--- a/fs/ntfs/sysctl.c	Sun Apr 28 20:10:34 2002
+++ b/fs/ntfs/sysctl.c	Sun Apr 28 20:10:39 2002
@@ -1,55 +1,86 @@
 /*
- * sysctl.c - System control stuff
+ * sysctl.c - Code for sysctl handling in NTFS Linux kernel driver. Part of
+ *	      the Linux-NTFS project. Adapted from the old NTFS driver,
+ *	      Copyright (C) 1997 Martin von Löwis, Régis Duchesne.
  *
- * Copyright (C) 1997 Martin von Löwis
- * Copyright (C) 1997 Régis Duchesne
+ * Copyright (c) 2002 Anton Altaparmakov.
+ *
+ * This program/include file 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/include file 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 (in the main directory of the Linux-NTFS 
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
-#include "sysctl.h"
-
 #ifdef DEBUG
-#include <linux/locks.h>
+
+#include <linux/module.h>
+
+#ifdef CONFIG_SYSCTL
+
+#include <linux/proc_fs.h>
 #include <linux/sysctl.h>
 
-int ntdebug = 0;
+#include "sysctl.h"
+#include "debug.h"
+
+#define FS_NTFS	1
+
+/* Definition of the ntfs sysctl. */
+static ctl_table ntfs_sysctls[] = {
+	{ FS_NTFS, "ntfs-debug", 		/* Binary and text IDs. */
+	  &debug_msgs,sizeof(debug_msgs),	/* Data pointer and size. */
+	  0644,	NULL, &proc_dointvec },		/* Mode, child, proc handler. */
+	{ 0 }
+};
+
+/* Define the parent directory /proc/sys/fs. */
+static ctl_table sysctls_root[] = {
+	{ CTL_FS, "fs", NULL, 0, 0555, ntfs_sysctls },
+	{ 0 }
+};
 
-/* Add or remove the debug sysctl
- * Is this really the only file system with sysctls ?
+/* Storage for the sysctls header. */
+static struct ctl_table_header *sysctls_root_table = NULL;
+
+/**
+ * ntfs_sysctl - add or remove the debug sysctl
+ * @add:	add (1) or remove (0) the sysctl
+ *
+ * Add or remove the debug sysctl. Return 0 on success or -errno on error.
  */
-void ntfs_sysctl(int add)
+int ntfs_sysctl(int add)
 {
-#define FS_NTFS             1
-	/* Definition of the sysctl */
-	static ctl_table ntfs_sysctls[]={
-		{FS_NTFS,                  /* ID */
-		 "ntfs-debug",             /* name in /proc */
-		 &ntdebug,sizeof(ntdebug), /* data ptr, data size */
-		 0644,                     /* mode */
-		 0,                        /* child */
-		 proc_dointvec,            /* proc handler */
-		 0,                        /* strategy */
-		 0,                        /* proc control block */
-		 0,0},                     /* extra */
-		{0}
-	};
-	/* Define the parent file : /proc/sys/fs */
-	static ctl_table sysctls_root[]={
-		{CTL_FS,
-		 "fs",
-		 NULL,0,
-		 0555,
-		 ntfs_sysctls},
-		{0}
-	};
-	static struct ctl_table_header *sysctls_root_header = NULL;
-
-	if(add){
-		if(!sysctls_root_header)
-			sysctls_root_header = register_sysctl_table(sysctls_root, 0);
-	} else if(sysctls_root_header) {
-		unregister_sysctl_table(sysctls_root_header);
-		sysctls_root_header = NULL;
+	if (add) {
+		BUG_ON(sysctls_root_table);
+		sysctls_root_table = register_sysctl_table(sysctls_root, 0);
+		if (!sysctls_root_table)
+			return -ENOMEM;
+#ifdef CONFIG_PROC_FS
+		/*
+		 * If the proc file system is in use and we are a module, need
+		 * to set the owner of our proc entry to our module. In the
+		 * non-modular case, THIS_MODULE is NULL, so this is ok.
+		 */
+		ntfs_sysctls[0].de->owner = THIS_MODULE;
+#endif
+	} else {
+		BUG_ON(!sysctls_root_table);
+		unregister_sysctl_table(sysctls_root_table);
+		sysctls_root_table = NULL;
 	}
+	return 0;
 }
+
+#endif /* CONFIG_SYSCTL */
 #endif /* DEBUG */
 
diff -Nru a/fs/ntfs/sysctl.h b/fs/ntfs/sysctl.h
--- a/fs/ntfs/sysctl.h	Sun Apr 28 20:10:32 2002
+++ b/fs/ntfs/sysctl.h	Sun Apr 28 20:10:39 2002
@@ -1,17 +1,43 @@
 /*
- * sysctl.h - Header file for sysctl.c
+ * sysctl.h - Defines for sysctl handling in NTFS Linux kernel driver. Part of
+ *	      the Linux-NTFS project. Adapted from the old NTFS driver,
+ *	      Copyright (C) 1997 Martin von Löwis, Régis Duchesne.
+ * 
+ * Copyright (c) 2002 Anton Altaparmakov.
  *
- * Copyright (C) 1997 Martin von Löwis
- * Copyright (C) 1997 Régis Duchesne
+ * This program/include file 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/include file 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 (in the main directory of the Linux-NTFS 
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
-#ifdef DEBUG
-	extern int ntdebug;
+#ifndef _LINUX_NTFS_SYSCTL_H
+#define _LINUX_NTFS_SYSCTL_H
+
+#include <linux/config.h>
 
-	void ntfs_sysctl(int add);
+#if (DEBUG && CONFIG_SYSCTL)
+
+extern int ntfs_sysctl(int add);
 
-	#define SYSCTL(x)	ntfs_sysctl(x)
 #else
-	#define SYSCTL(x)
-#endif /* DEBUG */
+
+/* Just return success. */
+static inline int ntfs_sysctl(int add)
+{
+	return 0;
+}
+
+#endif /* DEBUG && CONFIG_SYSCTL */
+#endif /* _LINUX_NTFS_SYSCTL_H */
 
diff -Nru a/fs/ntfs/time.c b/fs/ntfs/time.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/fs/ntfs/time.c	Sun Apr 28 20:10:39 2002
@@ -0,0 +1,81 @@
+/*
+ * time.c - NTFS time conversion functions. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2001 Anton Altaparmakov.
+ *
+ * This program/include file 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/include file 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 (in the main directory of the Linux-NTFS 
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/sched.h>	/* For CURRENT_TIME. */
+#include <asm/div64.h>		/* For do_div(). */
+
+#include "ntfs.h"
+
+#define NTFS_TIME_OFFSET ((s64)(369 * 365 + 89) * 24 * 3600 * 10000000)
+
+/**
+ * utc2ntfs - convert Linux time to NTFS time
+ * @time:		Linux time to convert to NTFS
+ *
+ * Convert the Linux time @time to its corresponding NTFS time and return that
+ * in little endian format.
+ *
+ * Linux stores time in a long at present and measures it as the number of
+ * 1-second intervals since 1st January 1970, 00:00:00 UTC.
+ *
+ * NTFS uses Microsoft's standard time format which is stored in a s64 and is
+ * measured as the number of 100 nano-second intervals since 1st January 1601,
+ * 00:00:00 UTC.
+ */
+inline s64 utc2ntfs(const time_t time)
+{
+	/* Convert to 100ns intervals and then add the NTFS time offset. */
+	return cpu_to_sle64((s64)time * 10000000 + NTFS_TIME_OFFSET);
+}
+
+/**
+ * get_current_ntfs_time - get the current time in little endian NTFS format
+ *
+ * Get the current time from the Linux kernel, convert it to its corresponding
+ * NTFS time and return that in little endian format.
+ */
+inline s64 get_current_ntfs_time(void)
+{
+	return utc2ntfs(CURRENT_TIME);
+}
+
+/**
+ * ntfs2utc - convert NTFS time to Linux time
+ * @time:		NTFS time (little endian) to convert to Linux
+ *
+ * Convert the little endian NTFS time @time to its corresponding Linux time
+ * and return that in cpu format.
+ *
+ * Linux stores time in a long at present and measures it as the number of
+ * 1-second intervals since 1st January 1970, 00:00:00 UTC.
+ *
+ * NTFS uses Microsoft's standard time format which is stored in a s64 and is
+ * measured as the number of 100 nano-second intervals since 1st January 1601,
+ * 00:00:00 UTC.
+ */
+inline time_t ntfs2utc(const s64 time)
+{
+	/* Subtract the NTFS time offset, then convert to 1s intervals. */
+	s64 t = sle64_to_cpu(time) - NTFS_TIME_OFFSET;
+	do_div(t, 10000000);
+	return (time_t)t;
+}
+
diff -Nru a/fs/ntfs/types.h b/fs/ntfs/types.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/fs/ntfs/types.h	Sun Apr 28 20:10:39 2002
@@ -0,0 +1,84 @@
+/*
+ * types.h - Defines for NTFS Linux kernel driver specific types.
+ *	     Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2001,2002 Anton Altaparmakov.
+ *
+ * This program/include file 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/include file 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 (in the main directory of the Linux-NTFS 
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _LINUX_NTFS_TYPES_H
+#define _LINUX_NTFS_TYPES_H
+
+#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 96)
+#define SN(X)   X	/* Struct Name */
+#define SC(P,N) P.N	/* ShortCut: Prefix, Name */
+#else
+#define SN(X)
+#define SC(P,N) N
+#endif
+
+/* 2-byte Unicode character type. */
+typedef u16 uchar_t;
+#define UCHAR_T_SIZE_BITS 1
+
+/*
+ * Clusters are signed 64-bit values on NTFS volumes. We define two types, LCN
+ * and VCN, to allow for type checking and better code readability.
+ */
+typedef s64 VCN;
+typedef s64 LCN;
+
+/**
+ * run_list_element - in memory vcn to lcn mapping array element
+ * @vcn:	starting vcn of the current array element
+ * @lcn:	starting lcn of the current array element
+ * @length:	length in clusters of the current array element
+ * 
+ * The last vcn (in fact the last vcn + 1) is reached when length == 0.
+ * 
+ * When lcn == -1 this means that the count vcns starting at vcn are not 
+ * physically allocated (i.e. this is a hole / data is sparse).
+ */
+typedef struct {	/* In memory vcn to lcn mapping structure element. */
+	VCN vcn;	/* vcn = Starting virtual cluster number. */
+	LCN lcn;	/* lcn = Starting logical cluster number. */
+	s64 length;	/* Run length in clusters. */
+} run_list_element;
+
+/**
+ * run_list - in memory vcn to lcn mapping array including a read/write lock
+ * @rl:		pointer to an array of run list elements
+ * @lock:	read/write spinlock for serializing access to @rl
+ * 
+ */
+typedef struct {
+	run_list_element *rl;
+	struct rw_semaphore lock;
+} run_list;
+
+typedef enum {
+	FALSE = 0,
+	TRUE = 1
+} BOOL;
+
+typedef enum {
+	CASE_SENSITIVE = 0,
+	IGNORE_CASE = 1,
+} IGNORE_CASE_BOOL;
+
+#endif /* _LINUX_NTFS_TYPES_H */
+
diff -Nru a/fs/ntfs/unistr.c b/fs/ntfs/unistr.c
--- a/fs/ntfs/unistr.c	Sun Apr 28 20:10:36 2002
+++ b/fs/ntfs/unistr.c	Sun Apr 28 20:10:39 2002
@@ -1,7 +1,7 @@
 /*
- * unistr.c - Unicode string handling. Part of the Linux-NTFS project.
+ * unistr.c - NTFS Unicode string handling. Part of the Linux-NTFS project.
  *
- * Copyright (c) 2000,2001 Anton Altaparmakov.
+ * Copyright (c) 2001 Anton Altaparmakov.
  *
  * This program/include file is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as published
@@ -19,18 +19,21 @@
  * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
-#include <linux/string.h>
-#include <linux/fs.h>
-#include <asm/byteorder.h>
+#include "ntfs.h"
 
-#include "unistr.h"
-#include "macros.h"
+/*
+ * IMPORTANT
+ * =========
+ *
+ * All these routines assume that the Unicode characters are in little endian
+ * encoding inside the strings!!!
+ */
 
 /*
  * This is used by the name collation functions to quickly determine what
  * characters are (in)valid.
  */
-const __u8 legal_ansi_char_array[0x40] = {
+static const u8 legal_ansi_char_array[0x40] = {
 	0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
 	0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
 
@@ -58,46 +61,45 @@
  * identical, or FALSE (0) if they are not identical. If @ic is IGNORE_CASE,
  * the @upcase table is used to performa a case insensitive comparison.
  */
-int ntfs_are_names_equal(wchar_t *s1, size_t s1_len,
-		     wchar_t *s2, size_t s2_len, int ic,
-		     wchar_t *upcase, __u32 upcase_size)
+BOOL ntfs_are_names_equal(const uchar_t *s1, size_t s1_len,
+		     const uchar_t *s2, size_t s2_len,
+		     const IGNORE_CASE_BOOL ic,
+		     const uchar_t *upcase, const u32 upcase_size)
 {
 	if (s1_len != s2_len)
-		return 0;
-	if (!ic)
-		return memcmp(s1, s2, s1_len << 1) ? 0: 1;
-	return ntfs_wcsncasecmp(s1, s2, s1_len, upcase, upcase_size) ? 0: 1;
+		return FALSE;
+	if (ic == CASE_SENSITIVE)
+		return !ntfs_ucsncmp(s1, s2, s1_len);
+	return !ntfs_ucsncasecmp(s1, s2, s1_len, upcase, upcase_size);
 }
 
 /**
  * ntfs_collate_names - collate two Unicode names
- * @upcase:	upcase table (ignored if @ic is CASE_SENSITIVE)
- * @upcase_len:	upcase table size (ignored if @ic is CASE_SENSITIVE)
  * @name1:	first Unicode name to compare
  * @name2:	second Unicode name to compare
- * @ic:         either CASE_SENSITIVE or IGNORE_CASE
  * @err_val:	if @name1 contains an invalid character return this value
+ * @ic:         either CASE_SENSITIVE or IGNORE_CASE
+ * @upcase:	upcase table (ignored if @ic is CASE_SENSITIVE)
+ * @upcase_len:	upcase table size (ignored if @ic is CASE_SENSITIVE)
  *
  * ntfs_collate_names collates two Unicode names and returns:
  * 
  *  -1 if the first name collates before the second one,
  *   0 if the names match,
  *   1 if the second name collates before the first one, or
- * @ec if an invalid character is encountered in @name1 during the comparison.
+ * @err_val if an invalid character is found in @name1 during the comparison.
  *
  * The following characters are considered invalid: '"', '*', '<', '>' and '?'.
  */
-int ntfs_collate_names(wchar_t *upcase, __u32 upcase_len,
-		       wchar_t *name1, __u32 name1_len,
-		       wchar_t *name2, __u32 name2_len,
-		       int ic, int err_val)
-{
-	__u32 cnt, min_len;
-	wchar_t c1, c2;
-
-	min_len = name1_len;
-	if (min_len > name2_len)
-		min_len = name2_len;
+int ntfs_collate_names(const uchar_t *name1, const u32 name1_len,
+		const uchar_t *name2, const u32 name2_len,
+		const int err_val, const IGNORE_CASE_BOOL ic,
+		const uchar_t *upcase, const u32 upcase_len)
+{
+	u32 cnt, min_len;
+	uchar_t c1, c2;
+
+	min_len = min(name1_len, name2_len);
 	for (cnt = 0; cnt < min_len; ++cnt) {
 		c1 = le16_to_cpu(*name1++);
 		c2 = le16_to_cpu(*name2++);
@@ -113,8 +115,6 @@
 			return -1;
 		if (c1 > c2)
 			return 1;
-		++name1;
-		++name2;
 	}
 	if (name1_len < name2_len)
 		return -1;
@@ -128,7 +128,39 @@
 }
 
 /**
- * ntfs_wcsncasecmp - compare two little endian Unicode strings, ignoring case
+ * ntfs_ucsncmp - compare two little endian Unicode strings
+ * @s1:		first string
+ * @s2:		second string
+ * @n:		maximum unicode characters to compare
+ *
+ * Compare the first @n characters of the Unicode strings @s1 and @s2,
+ * The strings in little endian format and appropriate le16_to_cpu()
+ * conversion is performed on non-little endian machines.
+ * 
+ * The function returns an integer less than, equal to, or greater than zero
+ * if @s1 (or the first @n Unicode characters thereof) is found, respectively,
+ * to be less than, to match, or be greater than @s2.
+ */
+int ntfs_ucsncmp(const uchar_t *s1, const uchar_t *s2, size_t n)
+{
+	uchar_t c1, c2;
+	size_t i;
+
+	for (i = 0; i < n; ++i) {
+		c1 = le16_to_cpu(s1[i]);
+		c2 = le16_to_cpu(s2[i]);
+		if (c1 < c2)
+			return -1;
+		if (c1 > c2)
+			return 1;
+		if (!c1)
+			break;
+	}
+	return 0;
+}
+
+/**
+ * ntfs_ucsncasecmp - compare two little endian Unicode strings, ignoring case
  * @s1:			first string
  * @s2:			second string
  * @n:			maximum unicode characters to compare
@@ -145,10 +177,10 @@
  * if @s1 (or the first @n Unicode characters thereof) is found, respectively,
  * to be less than, to match, or be greater than @s2.
  */
-int ntfs_wcsncasecmp(wchar_t *s1, wchar_t *s2, size_t n,
-		     wchar_t *upcase, __u32 upcase_size)
+int ntfs_ucsncasecmp(const uchar_t *s1, const uchar_t *s2, size_t n,
+		     const uchar_t *upcase, const u32 upcase_size)
 {
-	wchar_t c1, c2;
+	uchar_t c1, c2;
 	size_t i;
 
 	for (i = 0; i < n; ++i) {
@@ -164,5 +196,186 @@
 			break;
 	}
 	return 0;
+}
+
+void ntfs_upcase_name(uchar_t *name, u32 name_len, const uchar_t *upcase,
+		const u32 upcase_len)
+{
+	u32 i;
+	uchar_t u;
+
+	for (i = 0; i < name_len; i++)
+		if ((u = le16_to_cpu(name[i])) < upcase_len)
+			name[i] = upcase[u];
+}
+
+void ntfs_file_upcase_value(FILE_NAME_ATTR *file_name_attr,
+		const uchar_t *upcase, const u32 upcase_len)
+{
+	ntfs_upcase_name((uchar_t*)&file_name_attr->file_name,
+			file_name_attr->file_name_length, upcase, upcase_len);
+}
+
+int ntfs_file_compare_values(FILE_NAME_ATTR *file_name_attr1,
+		FILE_NAME_ATTR *file_name_attr2,
+		const int err_val, const IGNORE_CASE_BOOL ic,
+		const uchar_t *upcase, const u32 upcase_len)
+{
+	return ntfs_collate_names((uchar_t*)&file_name_attr1->file_name,
+			file_name_attr1->file_name_length,
+			(uchar_t*)&file_name_attr2->file_name,
+			file_name_attr2->file_name_length,
+			err_val, ic, upcase, upcase_len);
+}
+
+/**
+ * ntfs_nlstoucs - convert NLS string to little endian Unicode string
+ * @vol:	ntfs volume which we are working with
+ * @ins:	input NLS string buffer
+ * @ins_len:	length of input string in bytes
+ * @outs:	on return contains the allocated output Unicode string buffer
+ *
+ * Convert the input string @ins, which is in whatever format the loaded NLS
+ * map dictates, into a little endian, 2-byte Unicode string.
+ *
+ * This function allocates the string and the caller is responsible for
+ * calling kmem_cache_free(ntfs_name_cache, @outs); when finished with it.
+ *
+ * On success the function returns the number of Unicode characters written to
+ * the output string *@outs (>= 0), not counting the terminating Unicode NULL
+ * character. *@outs is set to the allocated output string buffer.
+ *
+ * On error, a negative number corresponding to the error code is returned. In
+ * that case the output string is not allocated. Both *@outs and *@outs_len
+ * are then undefined.
+ *
+ * This might look a bit odd due to fast path optimization...
+ */
+int ntfs_nlstoucs(const ntfs_volume *vol, const char *ins,
+		const int ins_len, uchar_t **outs)
+{
+	struct nls_table *nls = vol->nls_map;
+	uchar_t *ucs;
+	wchar_t wc;
+	int i, o, wc_len;
+
+	/* We don't trust outside sources. */
+	if (ins) {
+		ucs = (uchar_t*)kmem_cache_alloc(ntfs_name_cache, SLAB_NOFS);
+		if (ucs) {
+			for (i = o = 0; i < ins_len; i += wc_len) {
+				wc_len = nls->char2uni(ins + i, ins_len - i,
+						&wc);
+				if (wc_len >= 0) {
+					if (wc) {
+						ucs[o++] = cpu_to_le16(wc);
+						continue;
+					} /* else (!wc) */
+					break;
+				} /* else (wc_len < 0) */
+				goto conversion_err;
+			}
+			ucs[o] = cpu_to_le16('\0');
+			*outs = ucs;
+			return o;
+		} /* else (!ucs) */
+		ntfs_error(vol->sb, "Failed to allocate name from "
+				"ntfs_name_cache!");
+		return -ENOMEM;
+	} /* else (!ins) */
+	ntfs_error(NULL, "Received NULL pointer.");
+	return -EINVAL;
+conversion_err:
+	ntfs_error(vol->sb, "Name using character set %s contains characters "
+			"that cannot be converted to Unicode.", nls->charset);
+	kmem_cache_free(ntfs_name_cache, ucs);
+	return -EILSEQ;
+}
+
+/**
+ * ntfs_ucstonls - convert little endian Unicode string to NLS string
+ * @vol:	ntfs volume which we are working with
+ * @ins:	input Unicode string buffer
+ * @ins_len:	length of input string in Unicode characters
+ * @outs:	on return contains the (allocated) output NLS string buffer
+ * @outs_len:	length of output string buffer in bytes
+ *
+ * Convert the input little endian, 2-byte Unicode string @ins, of length
+ * @ins_len into the string format dictated by the loaded NLS.
+ *
+ * If @outs is NULL, this function allocates the string and the caller is
+ * responsible for calling kfree(@outs); when finished with it.
+ *
+ * On success the function returns the number of bytes written to the output
+ * string *@outs (>= 0), not counting the terminating NULL byte. If the output
+ * string buffer was allocated, *@outs is set to it.
+ *
+ * On error, a negative number corresponding to the error code is returned. In
+ * that case the output string is not allocated. The contents of *@outs are
+ * then undefined.
+ *
+ * This might look a bit odd due to fast path optimization...
+ */
+int ntfs_ucstonls(const ntfs_volume *vol, const uchar_t *ins,
+		const int ins_len, unsigned char **outs, int outs_len)
+{
+	struct nls_table *nls = vol->nls_map;
+	unsigned char *ns;
+	int i, o, ns_len, wc;
+
+	/* We don't trust outside sources. */
+	if (ins) {
+		ns = *outs;
+		ns_len = outs_len;
+		if (ns && !ns_len) {
+			wc = -ENAMETOOLONG;
+			goto conversion_err;
+		}
+		if (!ns) {
+			ns_len = ins_len * NLS_MAX_CHARSET_SIZE;
+			ns = (unsigned char*)kmalloc(ns_len, GFP_NOFS);
+			if (!ns)
+				goto mem_err_out;
+		}
+		for (i = o = 0; i < ins_len; i++) {
+retry:			wc = nls->uni2char(le16_to_cpu(ins[i]), ns + o,
+					ns_len - o);
+			if (wc > 0) {
+				o += wc;
+				continue;
+			} else if (!wc)
+				break;
+			else if (wc == -ENAMETOOLONG && ns != *outs) {
+				unsigned char *tc;
+				/* Grow in multiples of 64 bytes. */
+				tc = (unsigned char*)kmalloc((ns_len + 64) &
+						~63, GFP_NOFS);
+				if (tc) {
+					memcpy(tc, ns, ns_len);
+					ns_len = (ns_len + 64) & ~63;
+					kfree(ns);
+					ns = tc;
+					goto retry;
+				} /* No memory so goto conversion_error; */
+			} /* wc < 0, real error. */
+			goto conversion_err;
+		}
+		ns[o] = '\0';
+		*outs = ns;
+		return o;
+	} /* else (!ins) */
+	ntfs_error(vol->sb, "Received NULL pointer.");
+	return -EINVAL;
+conversion_err:
+	ntfs_error(vol->sb, "Unicode name contains characters that cannot be "
+			"converted to character set %s.", nls->charset);
+	if (ns != *outs)
+		kfree(ns);
+	if (wc != -ENAMETOOLONG)
+		wc = -EILSEQ;
+	return wc;
+mem_err_out:
+	ntfs_error(vol->sb, "Failed to allocate name!");
+	return -ENOMEM;
 }
 
diff -Nru a/fs/ntfs/unistr.h b/fs/ntfs/unistr.h
--- a/fs/ntfs/unistr.h	Sun Apr 28 20:10:37 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,44 +0,0 @@
-/*
- * unistr.h - Exports for unicode string handling. Part of the Linux-NTFS
- *	      project.
- *
- * Copyright (c) 2000,2001 Anton Altaparmakov.
- *
- * This program/include file 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/include file 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 (in the main directory of the Linux-NTFS 
- * distribution in the file COPYING); if not, write to the Free Software
- * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#ifndef _LINUX_NTFS_UNISTR_H
-#define _LINUX_NTFS_UNISTR_H
-
-#include <linux/types.h>
-#include <linux/nls.h>
-
-extern const __u8 legal_ansi_char_array[0x40];
-
-int ntfs_are_names_equal(wchar_t *s1, size_t s1_len,
-			  wchar_t *s2, size_t s2_len, int ic,
-			  wchar_t *upcase, __u32 upcase_size);
-
-int ntfs_collate_names(wchar_t *upcase, __u32 upcase_len,
-		       wchar_t *name1, __u32 name1_len,
-		       wchar_t *name2, __u32 name2_len,
-		       int ic, int err_val);
-
-int ntfs_wcsncasecmp(wchar_t *s1, wchar_t *s2, size_t n,
-		     wchar_t *upcase, __u32 upcase_size);
-
-#endif /* defined _LINUX_NTFS_UNISTR_H */
-
diff -Nru a/fs/ntfs/upcase.c b/fs/ntfs/upcase.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/fs/ntfs/upcase.c	Sun Apr 28 20:10:39 2002
@@ -0,0 +1,90 @@
+/*
+ * upcase.c - Generate the full NTFS Unicode upcase table in little endian.
+ *	      Part of the Linux-NTFS project.
+ *
+ * Copyright (C) 2001 Richard Russon <ntfs@flatcap.org>
+ * Copyright (c) 2001 Anton Altaparmakov <aia21@cam.ac.uk>
+ *
+ * Modified for mkntfs inclusion 9 June 2001 by Anton Altaparmakov.
+ * Modified for kernel inclusion 10 September 2001 by Anton Altparmakov.
+ *
+ * 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 (in the main directory of the Linux-NTFS source
+ * in the file COPYING); if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "ntfs.h"
+
+uchar_t *generate_default_upcase(void)
+{
+	const int uc_run_table[][3] = { /* Start, End, Add */
+	{0x0061, 0x007B,  -32}, {0x0451, 0x045D, -80}, {0x1F70, 0x1F72,  74},
+	{0x00E0, 0x00F7,  -32}, {0x045E, 0x0460, -80}, {0x1F72, 0x1F76,  86},
+	{0x00F8, 0x00FF,  -32}, {0x0561, 0x0587, -48}, {0x1F76, 0x1F78, 100},
+	{0x0256, 0x0258, -205}, {0x1F00, 0x1F08,   8}, {0x1F78, 0x1F7A, 128},
+	{0x028A, 0x028C, -217}, {0x1F10, 0x1F16,   8}, {0x1F7A, 0x1F7C, 112},
+	{0x03AC, 0x03AD,  -38}, {0x1F20, 0x1F28,   8}, {0x1F7C, 0x1F7E, 126},
+	{0x03AD, 0x03B0,  -37}, {0x1F30, 0x1F38,   8}, {0x1FB0, 0x1FB2,   8},
+	{0x03B1, 0x03C2,  -32}, {0x1F40, 0x1F46,   8}, {0x1FD0, 0x1FD2,   8},
+	{0x03C2, 0x03C3,  -31}, {0x1F51, 0x1F52,   8}, {0x1FE0, 0x1FE2,   8},
+	{0x03C3, 0x03CC,  -32}, {0x1F53, 0x1F54,   8}, {0x1FE5, 0x1FE6,   7},
+	{0x03CC, 0x03CD,  -64}, {0x1F55, 0x1F56,   8}, {0x2170, 0x2180, -16},
+	{0x03CD, 0x03CF,  -63}, {0x1F57, 0x1F58,   8}, {0x24D0, 0x24EA, -26},
+	{0x0430, 0x0450,  -32}, {0x1F60, 0x1F68,   8}, {0xFF41, 0xFF5B, -32},
+	{0}
+	};
+
+	const int uc_dup_table[][2] = { /* Start, End */
+	{0x0100, 0x012F}, {0x01A0, 0x01A6}, {0x03E2, 0x03EF}, {0x04CB, 0x04CC},
+	{0x0132, 0x0137}, {0x01B3, 0x01B7}, {0x0460, 0x0481}, {0x04D0, 0x04EB},
+	{0x0139, 0x0149}, {0x01CD, 0x01DD}, {0x0490, 0x04BF}, {0x04EE, 0x04F5},
+	{0x014A, 0x0178}, {0x01DE, 0x01EF}, {0x04BF, 0x04BF}, {0x04F8, 0x04F9},
+	{0x0179, 0x017E}, {0x01F4, 0x01F5}, {0x04C1, 0x04C4}, {0x1E00, 0x1E95},
+	{0x018B, 0x018B}, {0x01FA, 0x0218}, {0x04C7, 0x04C8}, {0x1EA0, 0x1EF9},
+	{0}
+	};
+
+	const int uc_word_table[][2] = { /* Offset, Value */
+	{0x00FF, 0x0178}, {0x01AD, 0x01AC}, {0x01F3, 0x01F1}, {0x0269, 0x0196},
+	{0x0183, 0x0182}, {0x01B0, 0x01AF}, {0x0253, 0x0181}, {0x026F, 0x019C},
+	{0x0185, 0x0184}, {0x01B9, 0x01B8}, {0x0254, 0x0186}, {0x0272, 0x019D},
+	{0x0188, 0x0187}, {0x01BD, 0x01BC}, {0x0259, 0x018F}, {0x0275, 0x019F},
+	{0x018C, 0x018B}, {0x01C6, 0x01C4}, {0x025B, 0x0190}, {0x0283, 0x01A9},
+	{0x0192, 0x0191}, {0x01C9, 0x01C7}, {0x0260, 0x0193}, {0x0288, 0x01AE},
+	{0x0199, 0x0198}, {0x01CC, 0x01CA}, {0x0263, 0x0194}, {0x0292, 0x01B7},
+	{0x01A8, 0x01A7}, {0x01DD, 0x018E}, {0x0268, 0x0197},
+	{0}
+	};
+
+	int i, r;
+	uchar_t *uc;
+
+	uc = ntfs_malloc_nofs(default_upcase_len * sizeof(uchar_t));
+	if (!uc)
+		return uc;
+	memset(uc, 0, default_upcase_len * sizeof(uchar_t));
+	for (i = 0; i < default_upcase_len; i++)
+		uc[i] = cpu_to_le16(i);
+	for (r = 0; uc_run_table[r][0]; r++)
+		for (i = uc_run_table[r][0]; i < uc_run_table[r][1]; i++)
+			uc[i] = cpu_to_le16((le16_to_cpu(uc[i]) +
+					uc_run_table[r][2]));
+	for (r = 0; uc_dup_table[r][0]; r++)
+		for (i = uc_dup_table[r][0]; i < uc_dup_table[r][1]; i += 2)
+			uc[i + 1] = cpu_to_le16(le16_to_cpu(uc[i + 1]) - 1);
+	for (r = 0; uc_word_table[r][0]; r++)
+		uc[uc_word_table[r][0]] = cpu_to_le16(uc_word_table[r][1]);
+	return uc;
+}
+
diff -Nru a/fs/ntfs/util.c b/fs/ntfs/util.c
--- a/fs/ntfs/util.c	Sun Apr 28 20:10:30 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,265 +0,0 @@
-/*
- * util.c -  Miscellaneous support
- *
- * Copyright (C) 1997,1999 Martin von Löwis
- * Copyright (C) 1997 Régis Duchesne
- * Copyright (C) 2001 Anton Altaparmakov (AIA)
- *
- * The utf8 routines are copied from Python wstrop module.
- */
-
-#include "ntfstypes.h"
-#include "struct.h"
-#include "util.h"
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <asm/div64.h>		/* For do_div(). */
-#include "support.h"
-
-/*
- * Converts a single wide character to a sequence of utf8 bytes.
- * The character is represented in host byte order.
- * Returns the number of bytes, or 0 on error.
- */
-static int to_utf8(ntfs_u16 c, unsigned char *buf)
-{
-	if (c == 0)
-		return 0; /* No support for embedded 0 runes. */
-	if (c < 0x80) {
-		if (buf)
-			buf[0] = (unsigned char)c;
-		return 1;
-	}
-	if (c < 0x800) {
-		if (buf) {
-			buf[0] = 0xc0 | (c >> 6);
-			buf[1] = 0x80 | (c & 0x3f);
-		}
-		return 2;
-	}
-	/* c < 0x10000 */
-	if (buf) {
-		buf[0] = 0xe0 | (c >> 12);
-		buf[1] = 0x80 | ((c >> 6) & 0x3f);
-		buf[2] = 0x80 | (c & 0x3f);
-	}
-	return 3;
-}
-
-/*
- * Decodes a sequence of utf8 bytes into a single wide character.
- * The character is returned in host byte order.
- * Returns the number of bytes consumed, or 0 on error.
- */
-static int from_utf8(const unsigned char *str, ntfs_u16 *c)
-{
-	int l = 0, i;
-
-	if (*str < 0x80) {
-		*c = *str;
-		return 1;
-	}
-	if (*str < 0xc0)	/* Lead byte must not be 10xxxxxx. */
-		return 0;	/* Is c0 a possible lead byte? */
-	if (*str < 0xe0) {		/* 110xxxxx */
-		*c = *str & 0x1f;
-		l = 2;
-	} else if (*str < 0xf0) {	/* 1110xxxx */
-		*c = *str & 0xf;
-		l = 3;
-	} else if (*str < 0xf8) {   	/* 11110xxx */
-		*c = *str & 7;
-		l = 4;
-	} else /* We don't support characters above 0xFFFF in NTFS. */
-		return 0;
-	for (i = 1; i < l; i++) {
-		/* All other bytes must be 10xxxxxx. */
-		if ((str[i] & 0xc0) != 0x80)
-			return 0;
-		*c <<= 6;
-		*c |= str[i] & 0x3f;
-	}
-	return l;
-}
-
-/*
- * Converts wide string to UTF-8. Expects two in- and two out-parameters.
- * Returns 0 on success, or error code. 
- * The caller has to free the result string.
- */
-static int ntfs_dupuni2utf8(ntfs_u16 *in, int in_len, char **out, int *out_len)
-{
-	int i, tmp;
-	int len8;
-	unsigned char *result;
-
-	ntfs_debug(DEBUG_NAME1, "converting l = %d\n", in_len);
-	/* Count the length of the resulting UTF-8. */
-	for (i = len8 = 0; i < in_len; i++) {
-		tmp = to_utf8(NTFS_GETU16(in + i), 0);
-		if (!tmp)
-			/* Invalid character. */
-			return -EILSEQ;
-		len8 += tmp;
-	}
-	*out = result = ntfs_malloc(len8 + 1); /* allow for zero-termination */
-	if (!result)
-		return -ENOMEM;
-	result[len8] = '\0';
-	*out_len = len8;
-	for (i = len8 = 0; i < in_len; i++)
-		len8 += to_utf8(NTFS_GETU16(in + i), result + len8);
-	ntfs_debug(DEBUG_NAME1, "result %p:%s\n", result, result);
-	return 0;
-}
-
-/*
- * Converts an UTF-8 sequence to a wide string. Same conventions as the
- * previous function.
- */
-static int ntfs_duputf82uni(unsigned char* in, int in_len, ntfs_u16** out,
-		int *out_len)
-{
-	int i, tmp;
-	int len16;
-	ntfs_u16* result;
-	ntfs_u16 wtmp;
-
-	for (i = len16 = 0; i < in_len; i += tmp, len16++) {
-		tmp = from_utf8(in + i, &wtmp);
-		if (!tmp)
-			return -EILSEQ;
-	}
-	*out = result = ntfs_malloc(2 * (len16 + 1));
-	if (!result)
-		return -ENOMEM;
-	result[len16] = 0;
-	*out_len = len16;
-	for (i = len16 = 0; i < in_len; i += tmp, len16++) {
-		tmp = from_utf8(in + i, &wtmp);
-		NTFS_PUTU16(result + len16, wtmp);
-	}
-	return 0;
-}
-
-/* Encodings dispatchers. */
-int ntfs_encodeuni(ntfs_volume *vol, ntfs_u16 *in, int in_len, char **out,
-		int *out_len)
-{
-	if (vol->nls_map)
-		return ntfs_dupuni2map(vol, in, in_len, out, out_len);
-	else
-		return ntfs_dupuni2utf8(in, in_len, out, out_len);
-}
-
-int ntfs_decodeuni(ntfs_volume *vol, char *in, int in_len, ntfs_u16 **out,
-		int *out_len)
-{
-	if (vol->nls_map)
-		return ntfs_dupmap2uni(vol, in, in_len, out, out_len);
-	else
-		return ntfs_duputf82uni(in, in_len, out, out_len);
-}
-
-/* Same address space copies. */
-void ntfs_put(ntfs_io *dest, void *src, ntfs_size_t n)
-{
-	ntfs_memcpy(dest->param, src, n);
-	((char*)dest->param) += n;
-}
-
-void ntfs_get(void* dest, ntfs_io *src, ntfs_size_t n)
-{
-	ntfs_memcpy(dest, src->param, n);
-	((char*)src->param) += n;
-}
-
-void *ntfs_calloc(int size)
-{
-	void *result = ntfs_malloc(size);
-	if (result)
-		ntfs_bzero(result, size);
-	return result;
-}
-
-/* Copy len ascii characters from from to to. :) */
-void ntfs_ascii2uni(short int *to, char *from, int len)
-{
-	int i;
-
-	for (i = 0; i < len; i++)
-		NTFS_PUTU16(to + i, from[i]);
-	to[i] = 0;
-}
-
-/* strncmp for Unicode strings. */
-int ntfs_uni_strncmp(short int* a, short int *b, int n)
-{
-	int i;
-
-	for(i = 0; i < n; i++)
-	{
-		if (NTFS_GETU16(a + i) < NTFS_GETU16(b + i))
-			return -1;
-		if (NTFS_GETU16(b + i) < NTFS_GETU16(a + i))
-			return 1;
-		if (NTFS_GETU16(a + i) == 0)
-			break;
-	}
-	return 0;
-}
-
-/* strncmp between Unicode and ASCII strings. */
-int ntfs_ua_strncmp(short int* a, char* b, int n)
-{
-	int i;
-
-	for (i = 0; i < n; i++)	{
-		if(NTFS_GETU16(a + i) < b[i])
-			return -1;
-		if(b[i] < NTFS_GETU16(a + i))
-			return 1;
-		if (b[i] == 0)
-			return 0;
-	}
-	return 0;
-}
-
-#define NTFS_TIME_OFFSET ((ntfs_time64_t)(369*365 + 89) * 24 * 3600 * 10000000)
-
-/* Convert the NT UTC (based 1.1.1601, in hundred nanosecond units)
- * into Unix UTC (based 1.1.1970, in seconds). */
-ntfs_time_t ntfs_ntutc2unixutc(ntfs_time64_t ntutc)
-{
-	/* Subtract the NTFS time offset, then convert to 1s intervals. */
-	ntfs_time64_t t = ntutc - NTFS_TIME_OFFSET;
-	do_div(t, 10000000);
-	return (ntfs_time_t)t;
-}
-
-/* Convert the Unix UTC into NT UTC. */
-ntfs_time64_t ntfs_unixutc2ntutc(ntfs_time_t t)
-{
-	/* Convert to 100ns intervals and then add the NTFS time offset. */
-	return (ntfs_time64_t)t * 10000000 + NTFS_TIME_OFFSET;
-}
-
-#undef NTFS_TIME_OFFSET
-
-/* Fill index name. */
-void ntfs_indexname(char *buf, int type)
-{
-	char hex[] = "0123456789ABCDEF";
-	int index;
-	*buf++ = '$';
-	*buf++ = 'I';
-	for (index = 24; index > 0; index -= 4)
-		if ((0xF << index) & type)
-			break;
-	while (index >= 0) {
-		*buf++ = hex[(type >> index) & 0xF];
-		index -= 4;
-	}
-	*buf = '\0';
-}
-
diff -Nru a/fs/ntfs/util.h b/fs/ntfs/util.h
--- a/fs/ntfs/util.h	Sun Apr 28 20:10:37 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,56 +0,0 @@
-/*
- * util.h - Header file for util.c
- *
- * Copyright (C) 1997 Régis Duchesne
- * Copyright (C) 2001 Anton Altaparmakov (AIA)
- */
-
-/* The first 16 inodes correspond to NTFS special files. */
-typedef enum {
-	FILE_Mft	= 0,
-	FILE_MftMirr	= 1,
-	FILE_LogFile	= 2,
-	FILE_Volume	= 3,
-	FILE_AttrDef	= 4,
-	FILE_root	= 5,
-	FILE_BitMap	= 6,
-	FILE_Boot	= 7,
-	FILE_BadClus	= 8,
-	FILE_Secure	= 9,
-	FILE_UpCase	= 10,
-	FILE_Extend	= 11,
-	FILE_Reserved12	= 12,
-	FILE_Reserved13	= 13,
-	FILE_Reserved14	= 14,
-	FILE_Reserved15	= 15,
-} NTFS_SYSTEM_FILES;
- 
-/* Memory management */
-void *ntfs_calloc(int size);
-
-/* String operations */
-/*  Copy Unicode <-> ASCII */
-void ntfs_ascii2uni(short int *to, char *from, int len);
-
-/*  Comparison */
-int ntfs_uni_strncmp(short int* a, short int *b, int n);
-int ntfs_ua_strncmp(short int* a, char* b, int n);
-
-/* Same address space copies */
-void ntfs_put(ntfs_io *dest, void *src, ntfs_size_t n);
-void ntfs_get(void* dest, ntfs_io *src, ntfs_size_t n);
-
-/* Charset conversion */
-int ntfs_encodeuni(ntfs_volume *vol, ntfs_u16 *in, int in_len, char **out,
-		   int *out_len);
-int ntfs_decodeuni(ntfs_volume *vol, char *in, int in_len, ntfs_u16 **out,
-		   int *out_len);
-
-/* Time conversion */
-/*  NT <-> Unix */
-ntfs_time_t ntfs_ntutc2unixutc(ntfs_time64_t ntutc);
-ntfs_time64_t ntfs_unixutc2ntutc(ntfs_time_t t);
-
-/* Attribute names */
-void ntfs_indexname(char *buf, int type);
-
diff -Nru a/fs/ntfs/volume.h b/fs/ntfs/volume.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/fs/ntfs/volume.h	Sun Apr 28 20:10:39 2002
@@ -0,0 +1,123 @@
+/*
+ * volume.h - Defines for volume structures in NTFS Linux kernel driver. Part
+ *	      of the Linux-NTFS project.
+ *
+ * Copyright (c) 2001,2002 Anton Altaparmakov.
+ * Copyright (C) 2002 Richard Russon.
+ *
+ * This program/include file 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/include file 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 (in the main directory of the Linux-NTFS 
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _LINUX_NTFS_VOLUME_H
+#define _LINUX_NTFS_VOLUME_H
+
+#include "types.h"
+
+/* These are used to determine which inode names are returned by readdir(). */
+typedef enum {
+	SHOW_SYSTEM	= 1,
+	SHOW_WIN32	= 2,
+	SHOW_DOS	= 4,
+	SHOW_POSIX	= SHOW_WIN32 | SHOW_DOS,
+	SHOW_ALL	= SHOW_SYSTEM | SHOW_POSIX,
+} READDIR_OPTIONS;
+
+#define RHideSystemFiles(x)	(!((x) & SHOW_SYSTEM))
+#define RHideLongNames(x)	(!((x) & SHOW_WIN32))
+#define RHideDosNames(x)	(!((x) & SHOW_DOS))
+
+/*
+ * The NTFS in memory super block structure.
+ */
+typedef struct {
+	/*
+	 * FIXME: Reorder to have commonly used together element within the
+	 * same cache line, aiming at a cache line size of 32 bytes. Aim for
+	 * 64 bytes for less commonly used together elements. Put most commonly
+	 * used elements to front of structure. Obviously do this only when the
+	 * structure has stabilized... (AIA)
+	 */
+	/* Device specifics. */
+	struct super_block *sb;		/* Pointer back to the super_block,
+					   so we don't have to get the offset
+					   every time. */
+	LCN nr_blocks;			/* Number of NTFS_BLOCK_SIZE bytes
+					   sized blocks on the device. */
+	/* Configuration provided by user at mount time. */
+	uid_t uid;			/* uid that files will be mounted as. */
+	gid_t gid;			/* gid that files will be mounted as. */
+	mode_t fmask;			/* The mask for file permissions. */
+	mode_t dmask;			/* The mask for directory
+					   permissions. */
+	READDIR_OPTIONS readdir_opts;	/* Namespace of inode names to show. */
+	u8 mft_zone_multiplier;	/* Initial mft zone multiplier. */
+	u8 on_errors;			/* What to do on file system errors. */
+	/* NTFS bootsector provided information. */
+	u16 sector_size;		/* in bytes */
+	u8 sector_size_bits;		/* log2(sector_size) */
+	u32 cluster_size;		/* in bytes */
+	u32 cluster_size_mask;		/* cluster_size - 1 */
+	u8 cluster_size_bits;		/* log2(cluster_size) */
+	u32 mft_record_size;		/* in bytes */
+	u32 mft_record_size_mask;	/* mft_record_size - 1 */
+	u8 mft_record_size_bits;	/* log2(mft_record_size) */
+	u32 index_record_size;		/* in bytes */
+	u32 index_record_size_mask;	/* index_record_size - 1 */
+	u8 index_record_size_bits;	/* log2(index_record_size) */
+	union {
+		LCN nr_clusters;	/* Volume size in clusters. */
+		LCN nr_lcn_bits;	/* Number of bits in lcn bitmap. */
+	} SN(vcl);
+	LCN mft_lcn;			/* Cluster location of mft data. */
+	LCN mftmirr_lcn;		/* Cluster location of copy of mft. */
+	u64 serial_no;			/* The volume serial number. */
+	/* Mount specific NTFS information. */
+	u32 upcase_len;			/* Number of entries in upcase[]. */
+	uchar_t *upcase;		/* The upcase table. */
+	LCN mft_zone_start;		/* First cluster of the mft zone. */
+	LCN mft_zone_end;		/* First cluster beyond the mft zone. */
+	struct inode *mft_ino;		/* The VFS inode of $MFT. */
+	struct rw_semaphore mftbmp_lock; /* Lock for serializing accesses to the
+					    mft record bitmap ($MFT/$BITMAP). */
+	union {
+		s64 nr_mft_records;	/* Number of records in the mft. */
+		s64 nr_mft_bits;	/* Number of bits in mft bitmap. */
+	} SN(vmm);
+	struct address_space mftbmp_mapping; /* Page cache for $MFT/$BITMAP. */
+	run_list mftbmp_rl;		/* Run list for $MFT/$BITMAP. */
+	s64 mftbmp_size;		/* Data size of $MFT/$BITMAP. */
+	s64 mftbmp_initialized_size;	/* Initialized size of $MFT/$BITMAP. */
+	s64 mftbmp_allocated_size;	/* Allocated size of $MFT/$BITMAP. */
+	struct inode *mftmirr_ino;	/* The VFS inode of $MFTMirr. */
+	struct inode *lcnbmp_ino;	/* The VFS inode of $Bitmap. */
+	struct rw_semaphore lcnbmp_lock; /* Lock for serializing accesses to the
+					    cluster bitmap ($Bitmap/$DATA). */
+	struct inode *vol_ino;		/* The VFS inode of $Volume. */
+	unsigned long vol_flags;	/* Volume flags (VOLUME_*). */
+	u8 major_ver;			/* Ntfs major version of volume. */
+	u8 minor_ver;			/* Ntfs minor version of volume. */
+	struct inode *root_ino;		/* The VFS inode of the root
+					   directory. */
+	struct inode *secure_ino;	/* The VFS inode of $Secure (NTFS3.0+
+					   only, otherwise NULL). */
+	struct nls_table *nls_map;
+} ntfs_volume;
+
+#define _VCL(X)  SC(vcl,X)
+#define _VMM(X)  SC(vmm,X)
+
+#endif /* _LINUX_NTFS_VOLUME_H */
+
diff -Nru a/fs/partitions/acorn.c b/fs/partitions/acorn.c
--- a/fs/partitions/acorn.c	Sun Apr 28 20:10:31 2002
+++ b/fs/partitions/acorn.c	Sun Apr 28 20:10:31 2002
@@ -261,7 +261,7 @@
 	/*
 	 * Work out start of non-adfs partition.
 	 */
-	nr_sects = hd->part[minor(to_kdev_t(bdev->bd_dev))].nr_sects - start_sect;
+	nr_sects = (bdev->bd_inode->i_size >> 9) - start_sect;
 
 	if (start_sect) {
 		first_sector += start_sect;
diff -Nru a/fs/partitions/amiga.c b/fs/partitions/amiga.c
--- a/fs/partitions/amiga.c	Sun Apr 28 20:10:36 2002
+++ b/fs/partitions/amiga.c	Sun Apr 28 20:10:36 2002
@@ -39,7 +39,6 @@
 	struct RigidDiskBlock *rdb;
 	struct PartitionBlock *pb;
 	int start_sect, nr_sects, blk, part, res = 0;
-	kdev_t dev = to_kdev_t(bdev->bd_dev);
 
 	for (blk = 0; ; blk++, put_dev_sector(sect)) {
 		if (blk == RDB_ALLOCATION_LIMIT)
@@ -48,7 +47,7 @@
 		if (!data) {
 			if (warn_no_part)
 				printk("Dev %s: unable to read RDB block %d\n",
-				       bdevname(dev), blk);
+				       bdevname(bdev), blk);
 			goto rdb_done;
 		}
 		if (*(u32 *)data != cpu_to_be32(IDNAME_RIGIDDISK))
@@ -69,7 +68,7 @@
 		}
 
 		printk("Dev %s: RDB in block %d has bad checksum\n",
-			       bdevname(dev),blk);
+			       bdevname(bdev),blk);
 	}
 
 	printk(" RDSK");
@@ -80,7 +79,7 @@
 		if (!data) {
 			if (warn_no_part)
 				printk("Dev %s: unable to read partition block %d\n",
-				       bdevname(dev),blk);
+				       bdevname(bdev),blk);
 			goto rdb_done;
 		}
 		pb  = (struct PartitionBlock *)data;
diff -Nru a/fs/partitions/check.c b/fs/partitions/check.c
--- a/fs/partitions/check.c	Sun Apr 28 20:10:35 2002
+++ b/fs/partitions/check.c	Sun Apr 28 20:10:35 2002
@@ -251,6 +251,7 @@
 	else
 		printk(KERN_INFO " %s:", disk_name(hd, minor(dev), buf));
 	bdev = bdget(kdev_t_to_nr(dev));
+	bdev->bd_contains = bdev;
 	bdev->bd_inode->i_size = (loff_t)hd->part[minor(dev)].nr_sects << 9;
 	bdev->bd_inode->i_blkbits = blksize_bits(block_size(dev));
 	for (i = 0; check_part[i]; i++) {
diff -Nru a/fs/partitions/efi.c b/fs/partitions/efi.c
--- a/fs/partitions/efi.c	Sun Apr 28 20:10:30 2002
+++ b/fs/partitions/efi.c	Sun Apr 28 20:10:30 2002
@@ -225,7 +225,7 @@
 	if (!hd || !bdev || !buffer || !count)
 		return 0;
 
-	blocksize = get_hardsect_size(to_kdev_t(bdev->bd_dev));
+	blocksize = bdev_hardsect_size(bdev);
 	if (!blocksize)
 		blocksize = 512;
 
@@ -714,14 +714,11 @@
 {
 
 	kdev_t dev = to_kdev_t(bdev->bd_dev);
-	int hardblocksize = get_hardsect_size(dev);
-	int orig_blksize_size = BLOCK_SIZE;
+	int hardblocksize = bdev_hardsect_size(bdev);
+	int orig_blksize_size = block_size(dev);
 	int rc = 0;
 
 	/* Need to change the block size that the block layer uses */
-	if (blksize_size[major(dev)]) {
-		orig_blksize_size = blksize_size[major(dev)][minor(dev)];
-	}
 
 	if (orig_blksize_size != hardblocksize)
 		set_blocksize(dev, hardblocksize);
diff -Nru a/fs/partitions/ibm.c b/fs/partitions/ibm.c
--- a/fs/partitions/ibm.c	Sun Apr 28 20:10:36 2002
+++ b/fs/partitions/ibm.c	Sun Apr 28 20:10:36 2002
@@ -125,11 +125,7 @@
 		return 0;
 	if (ioctl_by_bdev(bdev, HDIO_GETGEO, (unsigned long)geo);
 		return 0;
-	blocksize = hardsect_size[major(dev)][minor(dev)];
-	if ( blocksize <= 0 ) {
-		return 0;
-	}
-	blocksize >>= 9;
+	blocksize = bdev_hardsect_size(bdev) >> 9;
 	
 	data = read_dev_sector(bdev, inode->label_block*blocksize, &sect);
 	if (!data)
diff -Nru a/fs/partitions/msdos.c b/fs/partitions/msdos.c
--- a/fs/partitions/msdos.c	Sun Apr 28 20:10:31 2002
+++ b/fs/partitions/msdos.c	Sun Apr 28 20:10:31 2002
@@ -117,7 +117,7 @@
 	unsigned char *data;
 	unsigned long first_sector, this_sector, this_size;
 	int mask = (1 << hd->minor_shift) - 1;
-	int sector_size = get_hardsect_size(to_kdev_t(bdev->bd_dev)) / 512;
+	int sector_size = bdev_hardsect_size(bdev) / 512;
 	int loopct = 0;		/* number of links followed
 				   without finding a data partition */
 	int i;
@@ -522,7 +522,7 @@
 	struct partition *p;
 	unsigned char *data;
 	int mask = (1 << hd->minor_shift) - 1;
-	int sector_size = get_hardsect_size(to_kdev_t(bdev->bd_dev)) / 512;
+	int sector_size = bdev_hardsect_size(bdev) / 512;
 	int current_minor = first_part_minor;
 	int err;
 
diff -Nru a/fs/partitions/sgi.c b/fs/partitions/sgi.c
--- a/fs/partitions/sgi.c	Sun Apr 28 20:10:32 2002
+++ b/fs/partitions/sgi.c	Sun Apr 28 20:10:32 2002
@@ -22,7 +22,6 @@
 	int i, csum, magic;
 	unsigned int *ui, start, blocks, cs;
 	Sector sect;
-	kdev_t dev = to_kdev_t(bdev->bd_dev);
 	struct sgi_disklabel {
 		int magic_mushroom;         /* Big fat spliff... */
 		short root_part_num;        /* Root partition number */
@@ -51,7 +50,7 @@
 	magic = label->magic_mushroom;
 	if(be32_to_cpu(magic) != SGI_LABEL_MAGIC) {
 		/*printk("Dev %s SGI disklabel: bad magic %08x\n",
-		       bdevname(dev), magic);*/
+		       bdevname(bdev), magic);*/
 		put_dev_sector(sect);
 		return 0;
 	}
@@ -62,7 +61,7 @@
 	}
 	if(csum) {
 		printk(KERN_WARNING "Dev %s SGI disklabel: csum bad, label corrupted\n",
-		       bdevname(dev));
+		       bdevname(bdev));
 		put_dev_sector(sect);
 		return 0;
 	}
diff -Nru a/fs/partitions/sun.c b/fs/partitions/sun.c
--- a/fs/partitions/sun.c	Sun Apr 28 20:10:35 2002
+++ b/fs/partitions/sun.c	Sun Apr 28 20:10:35 2002
@@ -24,7 +24,6 @@
 	int i, csum;
 	unsigned short *ush;
 	Sector sect;
-	kdev_t dev = to_kdev_t(bdev->bd_dev);
 	struct sun_disklabel {
 		unsigned char info[128];   /* Informative text string */
 		unsigned char spare[292];  /* Boot information etc. */
@@ -55,7 +54,7 @@
 	p = label->partitions;
 	if (be16_to_cpu(label->magic) != SUN_LABEL_MAGIC) {
 /*		printk(KERN_INFO "Dev %s Sun disklabel: bad magic %04x\n",
-		       bdevname(dev), be16_to_cpu(label->magic)); */
+		       bdevname(bdev), be16_to_cpu(label->magic)); */
 		put_dev_sector(sect);
 		return 0;
 	}
@@ -65,7 +64,7 @@
 		csum ^= *ush--;
 	if(csum) {
 		printk("Dev %s Sun disklabel: Csum bad, label corrupted\n",
-		       bdevname(dev));
+		       bdevname(bdev));
 		put_dev_sector(sect);
 		return 0;
 	}
diff -Nru a/fs/reiserfs/journal.c b/fs/reiserfs/journal.c
--- a/fs/reiserfs/journal.c	Sun Apr 28 20:10:37 2002
+++ b/fs/reiserfs/journal.c	Sun Apr 28 20:10:37 2002
@@ -893,7 +893,7 @@
 static void reiserfs_end_buffer_io_sync(struct buffer_head *bh, int uptodate) {
     if (buffer_journaled(bh)) {
         reiserfs_warning("clm-2084: pinned buffer %lu:%s sent to disk\n",
-	                 bh->b_blocknr, kdevname(bh->b_dev)) ;
+	                 bh->b_blocknr, bdevname(bh->b_bdev)) ;
     }
     mark_buffer_uptodate(bh, uptodate) ;
     unlock_buffer(bh) ;
@@ -1666,7 +1666,7 @@
 
   cur_dblock = SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) ;
   printk("reiserfs: checking transaction log (%s) for (%s)\n",
-	 bdevname(SB_JOURNAL_DEV(p_s_sb)), p_s_sb->s_id) ;
+	 __bdevname(SB_JOURNAL_DEV(p_s_sb)), p_s_sb->s_id) ;
   start = CURRENT_TIME ;
 
   /* step 1, read in the journal header block.  Check the transaction it says 
diff -Nru a/fs/reiserfs/prints.c b/fs/reiserfs/prints.c
--- a/fs/reiserfs/prints.c	Sun Apr 28 20:10:31 2002
+++ b/fs/reiserfs/prints.c	Sun Apr 28 20:10:31 2002
@@ -139,7 +139,7 @@
 static void sprintf_buffer_head (char * buf, struct buffer_head * bh) 
 {
   sprintf (buf, "dev %s, size %d, blocknr %ld, count %d, list %d, state 0x%lx, page %p, (%s, %s, %s)",
-	   kdevname (bh->b_dev), bh->b_size, bh->b_blocknr, atomic_read (&(bh->b_count)), bh->b_list,
+	   bdevname (bh->b_bdev), bh->b_size, bh->b_blocknr, atomic_read (&(bh->b_count)), bh->b_list,
 	   bh->b_state, bh->b_page,
 	   buffer_uptodate (bh) ? "UPTODATE" : "!UPTODATE",
 	   buffer_dirty (bh) ? "DIRTY" : "CLEAN",
@@ -508,7 +508,7 @@
     }
 
     printk ("%s\'s super block in block %ld\n======================\n",
-            kdevname (bh->b_dev), bh->b_blocknr);
+            bdevname (bh->b_bdev), bh->b_blocknr);
     printk ("Reiserfs version %s\n", version );
     printk ("Block count %u\n", sb_block_count(rs));
     printk ("Blocksize %d\n", sb_blocksize(rs));
diff -Nru a/fs/reiserfs/procfs.c b/fs/reiserfs/procfs.c
--- a/fs/reiserfs/procfs.c	Sun Apr 28 20:10:34 2002
+++ b/fs/reiserfs/procfs.c	Sun Apr 28 20:10:34 2002
@@ -501,7 +501,7 @@
 			"prepare_retry: \t%12lu\n",
 
                         DJP( jp_journal_1st_block ),
-                        DJP( jp_journal_dev ) == 0 ? "none" : bdevname(to_kdev_t(DJP( jp_journal_dev ))),
+                        DJP( jp_journal_dev ) == 0 ? "none" : __bdevname(to_kdev_t(DJP( jp_journal_dev ))),
                         DJP( jp_journal_dev ),
                         DJP( jp_journal_size ),
                         DJP( jp_journal_trans_max ),
diff -Nru a/fs/reiserfs/stree.c b/fs/reiserfs/stree.c
--- a/fs/reiserfs/stree.c	Sun Apr 28 20:10:37 2002
+++ b/fs/reiserfs/stree.c	Sun Apr 28 20:10:37 2002
@@ -395,7 +395,7 @@
 	  p_s_chk_path->path_length > MAX_HEIGHT,
 	  "PAP-5050: pointer to the key(%p) is NULL or illegal path length(%d)",
 	  p_s_key, p_s_chk_path->path_length);
-  RFALSE( kdev_same(PATH_PLAST_BUFFER(p_s_chk_path)->b_dev, NODEV),
+  RFALSE( !PATH_PLAST_BUFFER(p_s_chk_path)->b_bdev,
 	  "PAP-5060: device must not be NODEV");
 
   if ( COMP_KEYS(get_lkey(p_s_chk_path, p_s_sb), p_s_key) == 1 )
diff -Nru a/fs/stat.c b/fs/stat.c
--- a/fs/stat.c	Sun Apr 28 20:10:32 2002
+++ b/fs/stat.c	Sun Apr 28 20:10:32 2002
@@ -125,7 +125,9 @@
 	return error;
 }
 
-#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__) && !defined(CONFIG_ARCH_S390) && !defined(__hppa__) && !defined(__x86_64__)
+#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__) \
+ && !defined(CONFIG_ARCH_S390) && !defined(__hppa__) && !defined(__x86_64__) \
+ && !defined(__arm__)
 
 /*
  * For backward compatibility?  Maybe this should be moved
diff -Nru a/fs/super.c b/fs/super.c
--- a/fs/super.c	Sun Apr 28 20:10:36 2002
+++ b/fs/super.c	Sun Apr 28 20:10:36 2002
@@ -515,7 +515,7 @@
 		blkdev_put(bdev, BDEV_FS);
 	} else {
 		s->s_flags = flags;
-		strncpy(s->s_id, bdevname(dev), sizeof(s->s_id));
+		strncpy(s->s_id, bdevname(bdev), sizeof(s->s_id));
 		s->s_old_blocksize = block_size(dev);
 		sb_set_blocksize(s, s->s_old_blocksize);
 		error = fill_super(s, data, flags & MS_VERBOSE ? 1 : 0);
diff -Nru a/fs/xattr.c b/fs/xattr.c
--- a/fs/xattr.c	Sun Apr 28 20:10:35 2002
+++ b/fs/xattr.c	Sun Apr 28 20:10:35 2002
@@ -84,9 +84,9 @@
 
 	error = -EOPNOTSUPP;
 	if (d->d_inode->i_op && d->d_inode->i_op->setxattr) {
-		lock_kernel();
+		down(&d->d_inode->i_sem);
 		error = d->d_inode->i_op->setxattr(d, kname, kvalue, size, flags);
-		unlock_kernel();
+		up(&d->d_inode->i_sem);
 	}
 
 	xattr_free(kvalue, size);
@@ -157,9 +157,9 @@
 
 	error = -EOPNOTSUPP;
 	if (d->d_inode->i_op && d->d_inode->i_op->getxattr) {
-		lock_kernel();
+		down(&d->d_inode->i_sem);
 		error = d->d_inode->i_op->getxattr(d, kname, kvalue, size);
-		unlock_kernel();
+		up(&d->d_inode->i_sem);
 	}
 
 	if (kvalue && error > 0)
@@ -226,9 +226,9 @@
 
 	error = -EOPNOTSUPP;
 	if (d->d_inode->i_op && d->d_inode->i_op->listxattr) {
-		lock_kernel();
+		down(&d->d_inode->i_sem);
 		error = d->d_inode->i_op->listxattr(d, klist, size);
-		unlock_kernel();
+		up(&d->d_inode->i_sem);
 	}
 
 	if (klist && error > 0)
@@ -297,9 +297,9 @@
 
 	error = -EOPNOTSUPP;
 	if (d->d_inode->i_op && d->d_inode->i_op->removexattr) {
-		lock_kernel();
+		down(&d->d_inode->i_sem);
 		error = d->d_inode->i_op->removexattr(d, kname);
-		unlock_kernel();
+		up(&d->d_inode->i_sem);
 	}
 	return error;
 }
diff -Nru a/include/asm-alpha/ide.h b/include/asm-alpha/ide.h
--- a/include/asm-alpha/ide.h	Sun Apr 28 20:10:34 2002
+++ b/include/asm-alpha/ide.h	Sun Apr 28 20:10:34 2002
@@ -105,12 +105,6 @@
 	} b;
 } control_t;
 
-#define ide_request_irq(irq,hand,flg,dev,id)	request_irq((irq),(hand),(flg),(dev),(id))
-#define ide_free_irq(irq,dev_id)		free_irq((irq), (dev_id))
-#define ide_check_region(from,extent)		check_region((from), (extent))
-#define ide_request_region(from,extent,name)	request_region((from), (extent), (name))
-#define ide_release_region(from,extent)		release_region((from), (extent))
-
 /*
  * The following are not needed for the non-m68k ports
  */
diff -Nru a/include/asm-arm/ide.h b/include/asm-arm/ide.h
--- a/include/asm-arm/ide.h	Sun Apr 28 20:10:35 2002
+++ b/include/asm-arm/ide.h	Sun Apr 28 20:10:35 2002
@@ -44,12 +44,6 @@
 	} b;
 } control_t;
 
-#define ide_request_irq(irq,hand,flg,dev,id)	request_irq((irq),(hand),(flg),(dev),(id))
-#define ide_free_irq(irq,dev_id)		free_irq((irq), (dev_id))
-#define ide_check_region(from,extent)		check_region((from), (extent))
-#define ide_request_region(from,extent,name)	request_region((from), (extent), (name))
-#define ide_release_region(from,extent)		release_region((from), (extent))
-
 /*
  * The following are not needed for the non-m68k ports
  */
diff -Nru a/include/asm-arm/proc-armo/locks.h b/include/asm-arm/proc-armo/locks.h
--- a/include/asm-arm/proc-armo/locks.h	Sun Apr 28 20:10:36 2002
+++ b/include/asm-arm/proc-armo/locks.h	Sun Apr 28 20:10:36 2002
@@ -28,7 +28,7 @@
 "	orrmi	ip, ip, #0x80000000	@ set N\n" \
 "	teqp	ip, #0\n"			\
 "	movmi	ip, %0\n"			\
-"	blmi	" SYMBOL_NAME_STR(fail)		\
+"	blmi	" #fail				\
 	:					\
 	: "r" (ptr)				\
 	: "ip", "lr", "cc");			\
@@ -50,7 +50,7 @@
 "	teqp	ip, #0\n"			\
 "	movmi	ip, %1\n"			\
 "	movpl	ip, #0\n"			\
-"	blmi	" SYMBOL_NAME_STR(fail) "\n"	\
+"	blmi	" #fail "\n"			\
 "	mov	%0, ip"				\
 	: "=&r" (result)			\
 	: "r" (ptr)				\
@@ -72,7 +72,7 @@
 "	orrle	ip, ip, #0x80000000	@ set N - should this be mi ??? DAG ! \n" \
 "	teqp	ip, #0\n"			\
 "	movmi	ip, %0\n"			\
-"	blmi	" SYMBOL_NAME_STR(wake)		\
+"	blmi	" #wake				\
 	:					\
 	: "r" (ptr)				\
 	: "ip", "lr", "cc");			\
@@ -104,7 +104,7 @@
 " orreq ip, ip, #0x40000000 @ set Z \n"\
 "	teqp	ip, #0\n"			\
 "	movne	ip, %0\n"			\
-"	blne	" SYMBOL_NAME_STR(fail)		\
+"	blne	" #fail				\
 	:					\
 	: "r" (ptr), "I" (RW_LOCK_BIAS)		\
 	: "ip", "lr", "cc");			\
@@ -127,7 +127,7 @@
 " orrcs ip, ip, #0x20000000 @ set C\n" \
 "	teqp	ip, #0\n"			\
 "	movcs	ip, %0\n"			\
-"	blcs	" SYMBOL_NAME_STR(wake)		\
+"	blcs	" #wake				\
 	:					\
 	: "r" (ptr), "I" (RW_LOCK_BIAS)		\
 	: "ip", "lr", "cc");			\
@@ -152,7 +152,7 @@
 " orreq ip, ip, #0x40000000 @ Set Z \n" \
 "	teqp	ip, #0\n"			\
 "	moveq	ip, %0\n"			\
-"	bleq	" SYMBOL_NAME_STR(wake)		\
+"	bleq	" #wake				\
 	:					\
 	: "r" (ptr), "I" (1)			\
 	: "ip", "lr", "cc");			\
diff -Nru a/include/asm-arm/proc-armv/locks.h b/include/asm-arm/proc-armv/locks.h
--- a/include/asm-arm/proc-armv/locks.h	Sun Apr 28 20:10:38 2002
+++ b/include/asm-arm/proc-armv/locks.h	Sun Apr 28 20:10:38 2002
@@ -24,7 +24,7 @@
 "	str	lr, [%0]\n"			\
 "	msr	cpsr_c, ip\n"			\
 "	movmi	ip, %0\n"			\
-"	blmi	" SYMBOL_NAME_STR(fail)		\
+"	blmi	" #fail				\
 	:					\
 	: "r" (ptr), "I" (1)			\
 	: "ip", "lr", "cc");			\
@@ -44,7 +44,7 @@
 "	msr	cpsr_c, ip\n"			\
 "	movmi	ip, %1\n"			\
 "	movpl	ip, #0\n"			\
-"	blmi	" SYMBOL_NAME_STR(fail) "\n"	\
+"	blmi	" #fail "\n"			\
 "	mov	%0, ip"				\
 	: "=&r" (ret)				\
 	: "r" (ptr), "I" (1)			\
@@ -64,7 +64,7 @@
 "	str	lr, [%0]\n"			\
 "	msr	cpsr_c, ip\n"			\
 "	movle	ip, %0\n"			\
-"	blle	" SYMBOL_NAME_STR(wake)		\
+"	blle	" #wake				\
 	:					\
 	: "r" (ptr), "I" (1)			\
 	: "ip", "lr", "cc");			\
@@ -91,7 +91,7 @@
 "	str	lr, [%0]\n"			\
 "	msr	cpsr_c, ip\n"			\
 "	movne	ip, %0\n"			\
-"	blne	" SYMBOL_NAME_STR(fail)		\
+"	blne	" #fail				\
 	:					\
 	: "r" (ptr), "I" (RW_LOCK_BIAS)		\
 	: "ip", "lr", "cc");			\
@@ -109,7 +109,7 @@
 "	str	lr, [%0]\n"			\
 "	msr	cpsr_c, ip\n"			\
 "	movcs	ip, %0\n"			\
-"	blcs	" SYMBOL_NAME_STR(wake)		\
+"	blcs	" #wake				\
 	:					\
 	: "r" (ptr), "I" (RW_LOCK_BIAS)		\
 	: "ip", "lr", "cc");			\
@@ -130,7 +130,7 @@
 "	str	lr, [%0]\n"			\
 "	msr	cpsr_c, ip\n"			\
 "	moveq	ip, %0\n"			\
-"	bleq	" SYMBOL_NAME_STR(wake)		\
+"	bleq	" #wake				\
 	:					\
 	: "r" (ptr), "I" (1)			\
 	: "ip", "lr", "cc");			\
diff -Nru a/include/asm-cris/ide.h b/include/asm-cris/ide.h
--- a/include/asm-cris/ide.h	Sun Apr 28 20:10:32 2002
+++ b/include/asm-cris/ide.h	Sun Apr 28 20:10:32 2002
@@ -119,20 +119,6 @@
 #undef SUPPORT_SLOW_DATA_PORTS
 #define SUPPORT_SLOW_DATA_PORTS	0
 
-/* request and free a normal interrupt */
-
-#define ide_request_irq(irq,hand,flg,dev,id)	request_irq((irq),(hand),(flg),(dev),(id))
-#define ide_free_irq(irq,dev_id)		free_irq((irq), (dev_id))
-
-/* ide-probe.c calls ide_request_region and stuff on the io_ports defined,
- * but since they are not actually memory-mapped in the ETRAX driver, we don't
- * do anything.
- */
-
-#define ide_check_region(from,extent)		(0)
-#define ide_request_region(from,extent,name)	do {} while(0)
-#define ide_release_region(from,extent)		do {} while(0)
-
 /*
  * The following are not needed for the non-m68k ports
  */
diff -Nru a/include/asm-i386/desc.h b/include/asm-i386/desc.h
--- a/include/asm-i386/desc.h	Sun Apr 28 20:10:30 2002
+++ b/include/asm-i386/desc.h	Sun Apr 28 20:10:30 2002
@@ -87,13 +87,13 @@
 /*
  * load one particular LDT into the current CPU
  */
-static inline void load_LDT (struct mm_struct *mm)
+static inline void load_LDT (mm_context_t *pc)
 {
 	int cpu = smp_processor_id();
-	void *segments = mm->context.segments;
-	int count = LDT_ENTRIES;
+	void *segments = pc->ldt;
+	int count = pc->size;
 
-	if (!segments) {
+	if (!count) {
 		segments = &default_ldt[0];
 		count = 5;
 	}
diff -Nru a/include/asm-i386/hw_irq.h b/include/asm-i386/hw_irq.h
--- a/include/asm-i386/hw_irq.h	Sun Apr 28 20:10:36 2002
+++ b/include/asm-i386/hw_irq.h	Sun Apr 28 20:10:36 2002
@@ -28,6 +28,8 @@
 
 extern void (*interrupt[NR_IRQS])(void);
 
+extern asmlinkage void smp_thermal_interrupt(struct pt_regs);
+
 #ifdef CONFIG_SMP
 extern asmlinkage void reschedule_interrupt(void);
 extern asmlinkage void invalidate_interrupt(void);
diff -Nru a/include/asm-i386/ide.h b/include/asm-i386/ide.h
--- a/include/asm-i386/ide.h	Sun Apr 28 20:10:31 2002
+++ b/include/asm-i386/ide.h	Sun Apr 28 20:10:31 2002
@@ -109,12 +109,6 @@
 	} b;
 } control_t;
 
-#define ide_request_irq(irq,hand,flg,dev,id)	request_irq((irq),(hand),(flg),(dev),(id))
-#define ide_free_irq(irq,dev_id)		free_irq((irq), (dev_id))
-#define ide_check_region(from,extent)		check_region((from), (extent))
-#define ide_request_region(from,extent,name)	request_region((from), (extent), (name))
-#define ide_release_region(from,extent)		release_region((from), (extent))
-
 /*
  * The following are not needed for the non-m68k ports
  */
diff -Nru a/include/asm-i386/mmu.h b/include/asm-i386/mmu.h
--- a/include/asm-i386/mmu.h	Sun Apr 28 20:10:31 2002
+++ b/include/asm-i386/mmu.h	Sun Apr 28 20:10:31 2002
@@ -4,10 +4,13 @@
 /*
  * The i386 doesn't have a mmu context, but
  * we put the segment information here.
+ *
+ * cpu_vm_mask is used to optimize ldt flushing.
  */
 typedef struct { 
-	void *segments;
-	unsigned long cpuvalid;
+	int size;
+	struct semaphore sem;
+	void *	ldt;
 } mm_context_t;
 
 #endif
diff -Nru a/include/asm-i386/mmu_context.h b/include/asm-i386/mmu_context.h
--- a/include/asm-i386/mmu_context.h	Sun Apr 28 20:10:30 2002
+++ b/include/asm-i386/mmu_context.h	Sun Apr 28 20:10:30 2002
@@ -11,7 +11,7 @@
  * possibly do the LDT unload here?
  */
 #define destroy_context(mm)		do { } while(0)
-#define init_new_context(tsk,mm)	0
+int init_new_context(struct task_struct *tsk, struct mm_struct *mm);
 
 #ifdef CONFIG_SMP
 
@@ -31,19 +31,20 @@
 	if (likely(prev != next)) {
 		/* stop flush ipis for the previous mm */
 		clear_bit(cpu, &prev->cpu_vm_mask);
-		/*
-		 * Re-load LDT if necessary
-		 */
-		if (unlikely(prev->context.segments != next->context.segments))
-			load_LDT(next);
 #ifdef CONFIG_SMP
 		cpu_tlbstate[cpu].state = TLBSTATE_OK;
 		cpu_tlbstate[cpu].active_mm = next;
 #endif
 		set_bit(cpu, &next->cpu_vm_mask);
-		set_bit(cpu, &next->context.cpuvalid);
+
 		/* Re-load page tables */
 		asm volatile("movl %0,%%cr3": :"r" (__pa(next->pgd)));
+
+		/* load_LDT, if either the previous or next thread
+		 * has a non-default LDT.
+		 */
+		if (next->context.size+prev->context.size)
+			load_LDT(&next->context);
 	}
 #ifdef CONFIG_SMP
 	else {
@@ -55,9 +56,8 @@
 			 * tlb flush IPI delivery. We must flush our tlb.
 			 */
 			local_flush_tlb();
+			load_LDT(&next->context);
 		}
-		if (!test_and_set_bit(cpu, &next->context.cpuvalid))
-			load_LDT(next);
 	}
 #endif
 }
diff -Nru a/include/asm-i386/processor.h b/include/asm-i386/processor.h
--- a/include/asm-i386/processor.h	Sun Apr 28 20:10:30 2002
+++ b/include/asm-i386/processor.h	Sun Apr 28 20:10:30 2002
@@ -429,8 +429,7 @@
  */
 extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
 
-/* Copy and release all segment info associated with a VM */
-extern void copy_segments(struct task_struct *p, struct mm_struct * mm);
+/* Release all segment info associated with a VM */
 extern void release_segments(struct mm_struct * mm);
 
 extern unsigned long thread_saved_pc(struct task_struct *tsk);
diff -Nru a/include/asm-ia64/acpi.h b/include/asm-ia64/acpi.h
--- a/include/asm-ia64/acpi.h	Sun Apr 28 20:10:34 2002
+++ b/include/asm-ia64/acpi.h	Sun Apr 28 20:10:34 2002
@@ -32,6 +32,7 @@
 
 #define __acpi_map_table(phys_addr, size) __va(phys_addr)
 
+const char *acpi_get_sysname (void);
 int acpi_boot_init (char *cdline);
 int acpi_find_rsdp (unsigned long *phys_addr);
 int acpi_request_vector (u32 int_type);
diff -Nru a/include/asm-ia64/errno.h b/include/asm-ia64/errno.h
--- a/include/asm-ia64/errno.h	Sun Apr 28 20:10:35 2002
+++ b/include/asm-ia64/errno.h	Sun Apr 28 20:10:35 2002
@@ -4,8 +4,8 @@
 /*
  * This is derived from the Linux/x86 version.
  *
- * Copyright (C) 1998, 1999 Hewlett-Packard Co
- * Copyright (C) 1998, 1999 David Mosberger-Tang <davidm@hpl.hp.com>
+ * Copyright (C) 1998, 1999, 2002 Hewlett-Packard Co
+ *	David Mosberger-Tang <davidm@hpl.hp.com>
  */
 
 #define	EPERM		 1	/* Operation not permitted */
@@ -14,7 +14,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-ia64/ide.h b/include/asm-ia64/ide.h
--- a/include/asm-ia64/ide.h	Sun Apr 28 20:10:30 2002
+++ b/include/asm-ia64/ide.h	Sun Apr 28 20:10:30 2002
@@ -115,12 +115,6 @@
 	} b;
 } control_t;
 
-#define ide_request_irq(irq,hand,flg,dev,id)	request_irq((irq),(hand),(flg),(dev),(id))
-#define ide_free_irq(irq,dev_id)		free_irq((irq), (dev_id))
-#define ide_check_region(from,extent)		check_region((from), (extent))
-#define ide_request_region(from,extent,name)	request_region((from), (extent), (name))
-#define ide_release_region(from,extent)		release_region((from), (extent))
-
 /*
  * The following are not needed for the non-m68k ports
  */
diff -Nru a/include/asm-ia64/ioctls.h b/include/asm-ia64/ioctls.h
--- a/include/asm-ia64/ioctls.h	Sun Apr 28 20:10:31 2002
+++ b/include/asm-ia64/ioctls.h	Sun Apr 28 20:10:31 2002
@@ -2,8 +2,8 @@
 #define _ASM_IA64_IOCTLS_H
 
 /*
- * Copyright (C) 1998, 1999 Hewlett-Packard Co
- * Copyright (C) 1998, 1999 David Mosberger-Tang <davidm@hpl.hp.com>
+ * Copyright (C) 1998, 1999, 2002 Hewlett-Packard Co
+ *	David Mosberger-Tang <davidm@hpl.hp.com>
  */
 
 #include <asm/ioctl.h>
@@ -11,7 +11,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-ia64/page.h b/include/asm-ia64/page.h
--- a/include/asm-ia64/page.h	Sun Apr 28 20:10:31 2002
+++ b/include/asm-ia64/page.h	Sun Apr 28 20:10:31 2002
@@ -71,7 +71,7 @@
 #ifdef CONFIG_IA64_GENERIC
 # include <asm/machvec.h>
 # define virt_to_page(kaddr)	(mem_map + platform_map_nr(kaddr))
-# define page_to_phys(page)	XXX fix me
+# define page_to_phys(page)	((page - mem_map) << PAGE_SHIFT)
 #elif defined (CONFIG_IA64_SGI_SN1)
 # ifndef CONFIG_DISCONTIGMEM
 #  define virt_to_page(kaddr)	(mem_map + MAP_NR_DENSE(kaddr))
diff -Nru a/include/asm-ia64/processor.h b/include/asm-ia64/processor.h
--- a/include/asm-ia64/processor.h	Sun Apr 28 20:10:35 2002
+++ b/include/asm-ia64/processor.h	Sun Apr 28 20:10:35 2002
@@ -154,6 +154,7 @@
 #define IA64_ISR_SP_BIT		36	/* speculative load exception */
 #define IA64_ISR_RS_BIT		37	/* mandatory register-stack exception */
 #define IA64_ISR_IR_BIT		38	/* invalid register frame exception */
+#define IA64_ISR_CODE_MASK	0xf
 
 #define IA64_ISR_X	(__IA64_UL(1) << IA64_ISR_X_BIT)
 #define IA64_ISR_W	(__IA64_UL(1) << IA64_ISR_W_BIT)
@@ -162,6 +163,14 @@
 #define IA64_ISR_SP	(__IA64_UL(1) << IA64_ISR_SP_BIT)
 #define IA64_ISR_RS	(__IA64_UL(1) << IA64_ISR_RS_BIT)
 #define IA64_ISR_IR	(__IA64_UL(1) << IA64_ISR_IR_BIT)
+
+/* ISR code field for non-access instructions */
+#define IA64_ISR_CODE_TPA	0
+#define IA64_ISR_CODE_FC	1
+#define IA64_ISR_CODE_PROBE	2
+#define IA64_ISR_CODE_TAK	3
+#define IA64_ISR_CODE_LFETCH	4
+#define IA64_ISR_CODE_PROBEF	5
 
 #define IA64_THREAD_FPH_VALID	(__IA64_UL(1) << 0)	/* floating-point high state valid? */
 #define IA64_THREAD_DBG_VALID	(__IA64_UL(1) << 1)	/* debug registers valid? */
diff -Nru a/include/asm-ia64/siginfo.h b/include/asm-ia64/siginfo.h
--- a/include/asm-ia64/siginfo.h	Sun Apr 28 20:10:35 2002
+++ b/include/asm-ia64/siginfo.h	Sun Apr 28 20:10:35 2002
@@ -154,7 +154,8 @@
 #define ILL_BADSTK	(__SI_FAULT|8)	/* internal stack error */
 #define ILL_BADIADDR	(__SI_FAULT|9)	/* unimplemented instruction address */
 #define __ILL_BREAK	(__SI_FAULT|10)	/* illegal break */
-#define NSIGILL		10
+#define __ILL_BNDMOD	(__SI_FAULT|11)	/* bundle-update (modification) in progress */
+#define NSIGILL		11
 
 /*
  * SIGFPE si_codes
diff -Nru a/include/asm-ia64/sn/sn2/shub_md.h b/include/asm-ia64/sn/sn2/shub_md.h
--- a/include/asm-ia64/sn/sn2/shub_md.h	Sun Apr 28 20:10:35 2002
+++ b/include/asm-ia64/sn/sn2/shub_md.h	Sun Apr 28 20:10:35 2002
@@ -1,14 +1,12 @@
-/**************************************************************************
- *                                                                        *
- *  Copyright (C) 2001 Silicon Graphics, Inc. All rights reserved.        *
- *                                                                        *
- *  These coded instructions, statements, and computer programs  contain  *
- *  unpublished  proprietary  information of Silicon Graphics, Inc., and  *
- *  are protected by Federal copyright law.  They  may  not be disclosed  *
- *  to  third  parties  or copied or duplicated in any form, in whole or  *
- *  in part, without the prior written consent of Silicon Graphics, Inc.  *
- *                                                                        *
- **************************************************************************/
+/*
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (c) 2001, 2002 Silicon Graphics, Inc.  All rights reserved.
+ */
+
 
 #ifndef _SHUB_MD_H
 #define _SHUB_MD_H
diff -Nru a/include/asm-ia64/string.h b/include/asm-ia64/string.h
--- a/include/asm-ia64/string.h	Sun Apr 28 20:10:35 2002
+++ b/include/asm-ia64/string.h	Sun Apr 28 20:10:35 2002
@@ -18,20 +18,6 @@
 
 extern __kernel_size_t strlen (const char *);
 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);	\
-})
+extern void *memset (void *, int, __kernel_size_t);
 
 #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	Sun Apr 28 20:10:35 2002
+++ b/include/asm-ia64/system.h	Sun Apr 28 20:10:35 2002
@@ -353,6 +353,9 @@
 # define IS_IA32_PROCESS(regs)	(ia64_psr(regs)->is != 0)
 #else
 # define IS_IA32_PROCESS(regs)		0
+struct task_struct;
+static inline void ia32_save_state(struct task_struct *t __attribute__((unused))){}
+static inline void ia32_load_state(struct task_struct *t __attribute__((unused))){}
 #endif
 
 /*
diff -Nru a/include/asm-m68k/ide.h b/include/asm-m68k/ide.h
--- a/include/asm-m68k/ide.h	Sun Apr 28 20:10:30 2002
+++ b/include/asm-m68k/ide.h	Sun Apr 28 20:10:30 2002
@@ -103,64 +103,6 @@
 	} b;
 } control_t;
 
-static __inline__ int ide_request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
-			unsigned long flags, const char *device, void *dev_id)
-{
-#ifdef CONFIG_AMIGA
-	if (MACH_IS_AMIGA)
-		return request_irq(irq, handler, 0, device, dev_id);
-#endif /* CONFIG_AMIGA */
-#ifdef CONFIG_Q40
-	if (MACH_IS_Q40)
-	  	return request_irq(irq, handler, 0, device, dev_id);
-#endif /* CONFIG_Q40*/
-#ifdef CONFIG_MAC
-	if (MACH_IS_MAC)
-		return request_irq(irq, handler, 0, device, dev_id);
-#endif /* CONFIG_MAC */
-	return 0;
-}
-
-static __inline__ void ide_free_irq(unsigned int irq, void *dev_id)
-{
-#ifdef CONFIG_AMIGA
-	if (MACH_IS_AMIGA)
-		free_irq(irq, dev_id);
-#endif /* CONFIG_AMIGA */
-#ifdef CONFIG_Q40
-	if (MACH_IS_Q40)
-	  	free_irq(irq, dev_id);
-#endif /* CONFIG_Q40*/
-#ifdef CONFIG_MAC
-	if (MACH_IS_MAC)
-		free_irq(irq, dev_id);
-#endif /* CONFIG_MAC */
-}
-
-/*
- * We should really implement those some day.
- */
-static __inline__ int ide_check_region (ide_ioreg_t from, unsigned int extent)
-{
-	return 0;
-}
-
-static __inline__ void ide_request_region (ide_ioreg_t from, unsigned int extent, const char *name)
-{
-#ifdef CONFIG_Q40
-        if (MACH_IS_Q40)
-            request_region((q40ide_ioreg_t)from,extent,name);
-#endif
-}
-
-static __inline__ void ide_release_region (ide_ioreg_t from, unsigned int extent)
-{
-#ifdef CONFIG_Q40
-        if (MACH_IS_Q40)
-            release_region((q40ide_ioreg_t)from,extent);
-#endif
-}
-
 #undef SUPPORT_SLOW_DATA_PORTS
 #define SUPPORT_SLOW_DATA_PORTS 0
 
diff -Nru a/include/asm-m68k/system.h b/include/asm-m68k/system.h
--- a/include/asm-m68k/system.h	Sun Apr 28 20:10:32 2002
+++ b/include/asm-m68k/system.h	Sun Apr 28 20:10:32 2002
@@ -40,7 +40,7 @@
   register void *_prev __asm__ ("a0") = (prev); \
   register void *_next __asm__ ("a1") = (next); \
   register void *_last __asm__ ("d1"); \
-  __asm__ __volatile__("jbsr " SYMBOL_NAME_STR(resume) \
+  __asm__ __volatile__("jbsr resume" \
 		       : "=d" (_last) : "a" (_prev), "a" (_next) \
 		       : "d0", /* "d1", */ "d2", "d3", "d4", "d5", "a0", "a1"); \
   (last) = _last; \
diff -Nru a/include/asm-mips/ide.h b/include/asm-mips/ide.h
--- a/include/asm-mips/ide.h	Sun Apr 28 20:10:32 2002
+++ b/include/asm-mips/ide.h	Sun Apr 28 20:10:32 2002
@@ -31,14 +31,6 @@
 	ide_ioreg_t (*ide_default_io_base)(int index);
 	void (*ide_init_hwif_ports)(hw_regs_t *hw, ide_ioreg_t data_port,
 	                            ide_ioreg_t ctrl_port, int *irq);
-	int (*ide_request_irq)(unsigned int irq, void (*handler)(int, void *,
-	                       struct pt_regs *), unsigned long flags,
-	                       const char *device, void *dev_id);
-	void (*ide_free_irq)(unsigned int irq, void *dev_id);
-	int (*ide_check_region) (ide_ioreg_t from, unsigned int extent);
-	void (*ide_request_region)(ide_ioreg_t from, unsigned int extent,
-	                        const char *name);
-	void (*ide_release_region)(ide_ioreg_t from, unsigned int extent);
 };
 
 extern struct ide_ops *ide_ops;
@@ -111,34 +103,6 @@
 #endif
 	} b;
 } control_t;
-
-static __inline__ int ide_request_irq(unsigned int irq, void (*handler)(int,void *, struct pt_regs *),
-			unsigned long flags, const char *device, void *dev_id)
-{
-	return ide_ops->ide_request_irq(irq, handler, flags, device, dev_id);
-}
-
-static __inline__ void ide_free_irq(unsigned int irq, void *dev_id)
-{
-	ide_ops->ide_free_irq(irq, dev_id);
-}
-
-static __inline__ int ide_check_region (ide_ioreg_t from, unsigned int extent)
-{
-	return ide_ops->ide_check_region(from, extent);
-}
-
-static __inline__ void ide_request_region(ide_ioreg_t from,
-                                          unsigned int extent, const char *name)
-{
-	ide_ops->ide_request_region(from, extent, name);
-}
-
-static __inline__ void ide_release_region(ide_ioreg_t from,
-                                          unsigned int extent)
-{
-	ide_ops->ide_release_region(from, extent);
-}
 
 #undef  SUPPORT_VLB_SYNC
 #define SUPPORT_VLB_SYNC 0
diff -Nru a/include/asm-mips64/ide.h b/include/asm-mips64/ide.h
--- a/include/asm-mips64/ide.h	Sun Apr 28 20:10:30 2002
+++ b/include/asm-mips64/ide.h	Sun Apr 28 20:10:30 2002
@@ -34,14 +34,6 @@
 	ide_ioreg_t (*ide_default_io_base)(int index);
 	void (*ide_init_hwif_ports)(hw_regs_t *hw, ide_ioreg_t data_port,
 	                            ide_ioreg_t ctrl_port, int *irq);
-	int (*ide_request_irq)(unsigned int irq, void (*handler)(int, void *,
-	                       struct pt_regs *), unsigned long flags,
-	                       const char *device, void *dev_id);
-	void (*ide_free_irq)(unsigned int irq, void *dev_id);
-	int (*ide_check_region) (ide_ioreg_t from, unsigned int extent);
-	void (*ide_request_region)(ide_ioreg_t from, unsigned int extent,
-	                        const char *name);
-	void (*ide_release_region)(ide_ioreg_t from, unsigned int extent);
 };
 
 extern struct ide_ops *ide_ops;
@@ -98,35 +90,6 @@
 		unsigned HOB		: 1;	/* 48-bit address ordering */
 	} b;
 } control_t;
-
-static __inline__ int ide_request_irq(unsigned int irq, void (*handler)(int,void *, struct pt_regs *),
-			unsigned long flags, const char *device, void *dev_id)
-{
-	return ide_ops->ide_request_irq(irq, handler, flags, device, dev_id);
-}
-
-static __inline__ void ide_free_irq(unsigned int irq, void *dev_id)
-{
-	ide_ops->ide_free_irq(irq, dev_id);
-}
-
-static __inline__ int ide_check_region (ide_ioreg_t from, unsigned int extent)
-{
-	return ide_ops->ide_check_region(from, extent);
-}
-
-static __inline__ void ide_request_region(ide_ioreg_t from,
-                                          unsigned int extent, const char *name)
-{
-	ide_ops->ide_request_region(from, extent, name);
-}
-
-static __inline__ void ide_release_region(ide_ioreg_t from,
-                                          unsigned int extent)
-{
-	ide_ops->ide_release_region(from, extent);
-}
-
 
 #if defined(CONFIG_SWAP_IO_SPACE) && defined(__MIPSEB__)
 
diff -Nru a/include/asm-parisc/ide.h b/include/asm-parisc/ide.h
--- a/include/asm-parisc/ide.h	Sun Apr 28 20:10:35 2002
+++ b/include/asm-parisc/ide.h	Sun Apr 28 20:10:35 2002
@@ -104,12 +104,6 @@
 	} b;
 } control_t;
 
-#define ide_request_irq(irq,hand,flg,dev,id)	request_irq((irq),(hand),(flg),(dev),(id))
-#define ide_free_irq(irq,dev_id)		free_irq((irq), (dev_id))
-#define ide_check_region(from,extent)		check_region((from), (extent))
-#define ide_request_region(from,extent,name)	request_region((from), (extent), (name))
-#define ide_release_region(from,extent)		release_region((from), (extent))
-
 /*
  * The following are not needed for the non-m68k ports
  */
diff -Nru a/include/asm-ppc/ide.h b/include/asm-ppc/ide.h
--- a/include/asm-ppc/ide.h	Sun Apr 28 20:10:35 2002
+++ b/include/asm-ppc/ide.h	Sun Apr 28 20:10:35 2002
@@ -35,12 +35,6 @@
 struct ide_machdep_calls {
         int         (*default_irq)(ide_ioreg_t base);
         ide_ioreg_t (*default_io_base)(int index);
-        int         (*ide_check_region)(ide_ioreg_t from, unsigned int extent);
-        void        (*ide_request_region)(ide_ioreg_t from,
-                                      unsigned int extent,
-                                      const char *name);
-        void        (*ide_release_region)(ide_ioreg_t from,
-                                      unsigned int extent);
         void        (*ide_init_hwif)(hw_regs_t *hw,
                                      ide_ioreg_t data_port,
                                      ide_ioreg_t ctrl_port,
@@ -118,25 +112,6 @@
 #endif /* CONFIG_BLK_DEV_IDEPCI */
 }
 
-static __inline__ int ide_check_region (ide_ioreg_t from, unsigned int extent)
-{
-	if (ppc_ide_md.ide_check_region)
-		return ppc_ide_md.ide_check_region(from, extent);
-	return 0;
-}
-
-static __inline__ void ide_request_region (ide_ioreg_t from, unsigned int extent, const char *name)
-{
-	if (ppc_ide_md.ide_request_region)
-		ppc_ide_md.ide_request_region(from, extent, name);
-}
-
-static __inline__ void ide_release_region (ide_ioreg_t from, unsigned int extent)
-{
-	if (ppc_ide_md.ide_release_region)
-		ppc_ide_md.ide_release_region(from, extent);
-}
-
 typedef union {
 	unsigned all			: 8;	/* all of the bits together */
 	struct {
@@ -159,14 +134,6 @@
 		unsigned bit0		: 1;
 	} b;
 } control_t;
-
-#if !defined(ide_request_irq)
-#define ide_request_irq(irq,hand,flg,dev,id)	request_irq((irq),(hand),(flg),(dev),(id))
-#endif
-
-#if !defined(ide_free_irq)
-#define ide_free_irq(irq,dev_id)		free_irq((irq), (dev_id))
-#endif
 
 /*
  * The following are not needed for the non-m68k ports
diff -Nru a/include/asm-ppc64/ide.h b/include/asm-ppc64/ide.h
--- a/include/asm-ppc64/ide.h	Sun Apr 28 20:10:36 2002
+++ b/include/asm-ppc64/ide.h	Sun Apr 28 20:10:36 2002
@@ -77,12 +77,6 @@
 	} b;
 } control_t;
 
-#define ide_request_irq(irq,hand,flg,dev,id)	request_irq((irq),(hand),(flg),(dev),(id))
-#define ide_free_irq(irq,dev_id)		free_irq((irq), (dev_id))
-#define ide_check_region(from,extent)		check_region((from), (extent))
-#define ide_request_region(from,extent,name)	request_region((from), (extent), (name))
-#define ide_release_region(from,extent)		release_region((from), (extent))
-
 /*
  * The following are not needed for the non-m68k ports
  */
diff -Nru a/include/asm-s390/ide.h b/include/asm-s390/ide.h
--- a/include/asm-s390/ide.h	Sun Apr 28 20:10:37 2002
+++ b/include/asm-s390/ide.h	Sun Apr 28 20:10:37 2002
@@ -40,12 +40,6 @@
 	} b;
 } control_t;
 
-#define ide_request_irq(irq,hand,flg,dev,id)	do {} while (0)
-#define ide_free_irq(irq,dev_id)		do {} while (0)
-#define ide_check_region(from,extent)		do {} while (0)
-#define ide_request_region(from,extent,name)	do {} while (0)
-#define ide_release_region(from,extent)		do {} while (0)
-
 /*
  * The following are not needed for the non-m68k ports
  */
diff -Nru a/include/asm-s390x/ide.h b/include/asm-s390x/ide.h
--- a/include/asm-s390x/ide.h	Sun Apr 28 20:10:30 2002
+++ b/include/asm-s390x/ide.h	Sun Apr 28 20:10:30 2002
@@ -40,12 +40,6 @@
 	} b;
 } control_t;
 
-#define ide_request_irq(irq,hand,flg,dev,id)	do {} while (0)
-#define ide_free_irq(irq,dev_id)		do {} while (0)
-#define ide_check_region(from,extent)		do {} while (0)
-#define ide_request_region(from,extent,name)	do {} while (0)
-#define ide_release_region(from,extent)		do {} while (0)
-
 /*
  * The following are not needed for the non-m68k ports
  */
diff -Nru a/include/asm-sh/ide.h b/include/asm-sh/ide.h
--- a/include/asm-sh/ide.h	Sun Apr 28 20:10:33 2002
+++ b/include/asm-sh/ide.h	Sun Apr 28 20:10:33 2002
@@ -130,12 +130,6 @@
 	} b;
 } control_t;
 
-#define ide_request_irq(irq,hand,flg,dev,id)	request_irq((irq),(hand),(flg),(dev),(id))
-#define ide_free_irq(irq,dev_id)		free_irq((irq), (dev_id))
-#define ide_check_region(from,extent)		check_region((from), (extent))
-#define ide_request_region(from,extent,name)	request_region((from), (extent), (name))
-#define ide_release_region(from,extent)		release_region((from), (extent))
-
 /*
  * The following are not needed for the non-m68k ports
  */
diff -Nru a/include/asm-sparc/ide.h b/include/asm-sparc/ide.h
--- a/include/asm-sparc/ide.h	Sun Apr 28 20:10:37 2002
+++ b/include/asm-sparc/ide.h	Sun Apr 28 20:10:37 2002
@@ -96,33 +96,6 @@
 	} b;
 } control_t;
 
-static __inline__ int ide_request_irq(unsigned int irq,
-				      void (*handler)(int, void *, struct pt_regs *),
-				      unsigned long flags, const char *name, void *devid)
-{
-	return request_irq(irq, handler, SA_SHIRQ, name, devid);
-}
-
-static __inline__ void ide_free_irq(unsigned int irq, void *dev_id)
-{
-	free_irq(irq, dev_id);
-}
-
-static __inline__ int ide_check_region(ide_ioreg_t base, unsigned int size)
-{
-	/* We leave these empty because pcic.c calls sparc_alloc_io() */
-	return 0;
-}
-
-static __inline__ void ide_request_region(ide_ioreg_t base, unsigned int size,
-					  const char *name)
-{
-}
-
-static __inline__ void ide_release_region(ide_ioreg_t base, unsigned int size)
-{
-}
-
 #undef  SUPPORT_SLOW_DATA_PORTS
 #define SUPPORT_SLOW_DATA_PORTS 0
 
diff -Nru a/include/asm-sparc64/ide.h b/include/asm-sparc64/ide.h
--- a/include/asm-sparc64/ide.h	Sun Apr 28 20:10:35 2002
+++ b/include/asm-sparc64/ide.h	Sun Apr 28 20:10:35 2002
@@ -92,34 +92,6 @@
 	} b;
 } control_t;
 
-static __inline__ int ide_request_irq(unsigned int irq,
-				      void (*handler)(int, void *, struct pt_regs *),
-				      unsigned long flags, const char *name, void *devid)
-{
-	return request_irq(irq, handler, SA_SHIRQ, name, devid);
-}
-
-static __inline__ void ide_free_irq(unsigned int irq, void *dev_id)
-{
-	free_irq(irq, dev_id);
-}
-
-static __inline__ int ide_check_region(ide_ioreg_t base, unsigned int size)
-{
-	return check_region(base, size);
-}
-
-static __inline__ void ide_request_region(ide_ioreg_t base, unsigned int size,
-					  const char *name)
-{
-	request_region(base, size, name);
-}
-
-static __inline__ void ide_release_region(ide_ioreg_t base, unsigned int size)
-{
-	release_region(base, size);
-}
-
 #undef  SUPPORT_SLOW_DATA_PORTS
 #define SUPPORT_SLOW_DATA_PORTS 0
 
diff -Nru a/include/asm-x86_64/ide.h b/include/asm-x86_64/ide.h
--- a/include/asm-x86_64/ide.h	Sun Apr 28 20:10:36 2002
+++ b/include/asm-x86_64/ide.h	Sun Apr 28 20:10:36 2002
@@ -109,12 +109,6 @@
 	} b;
 } control_t;
 
-#define ide_request_irq(irq,hand,flg,dev,id)	request_irq((irq),(hand),(flg),(dev),(id))
-#define ide_free_irq(irq,dev_id)		free_irq((irq), (dev_id))
-#define ide_check_region(from,extent)		check_region((from), (extent))
-#define ide_request_region(from,extent,name)	request_region((from), (extent), (name))
-#define ide_release_region(from,extent)		release_region((from), (extent))
-
 /*
  * The following are not needed for the non-m68k ports
  */
diff -Nru a/include/linux/agp_backend.h b/include/linux/agp_backend.h
--- a/include/linux/agp_backend.h	Sun Apr 28 20:10:32 2002
+++ b/include/linux/agp_backend.h	Sun Apr 28 20:10:32 2002
@@ -69,6 +69,7 @@
 	ALI_M1631,
 	ALI_M1632,
 	ALI_M1641,
+	ALI_M1644,
 	ALI_M1647,
 	ALI_M1651,
 	ALI_GENERIC,
diff -Nru a/include/linux/bio.h b/include/linux/bio.h
--- a/include/linux/bio.h	Sun Apr 28 20:10:37 2002
+++ b/include/linux/bio.h	Sun Apr 28 20:10:37 2002
@@ -60,7 +60,7 @@
 struct bio {
 	sector_t		bi_sector;
 	struct bio		*bi_next;	/* request queue link */
-	kdev_t			bi_dev;		/* will be block device */
+	struct block_device	*bi_bdev;
 	unsigned long		bi_flags;	/* status, command, etc */
 	unsigned long		bi_rw;		/* bottom bits READ/WRITE,
 						 * top bits priority
diff -Nru a/include/linux/blk.h b/include/linux/blk.h
--- a/include/linux/blk.h	Sun Apr 28 20:10:34 2002
+++ b/include/linux/blk.h	Sun Apr 28 20:10:34 2002
@@ -100,9 +100,6 @@
 	
 #if defined(MAJOR_NR) || defined(IDE_DRIVER)
 
-#undef DEVICE_ON
-#undef DEVICE_OFF
-
 /*
  * Add entries as needed.
  */
@@ -117,13 +114,11 @@
 /* ram disk */
 #define DEVICE_NAME "ramdisk"
 #define DEVICE_NR(device) (minor(device))
-#define DEVICE_NO_RANDOM
 
 #elif (MAJOR_NR == Z2RAM_MAJOR)
 
 /* Zorro II Ram */
 #define DEVICE_NAME "Z2RAM"
-#define DEVICE_REQUEST do_z2_request
 #define DEVICE_NR(device) (minor(device))
 
 #elif (MAJOR_NR == FLOPPY_MAJOR)
@@ -132,30 +127,24 @@
 
 #define DEVICE_NAME "floppy"
 #define DEVICE_INTR do_floppy
-#define DEVICE_REQUEST do_fd_request
 #define DEVICE_NR(device) ( (minor(device) & 3) | ((minor(device) & 0x80 ) >> 5 ))
-#define DEVICE_OFF(device) floppy_off(DEVICE_NR(device))
 
 #elif (MAJOR_NR == HD_MAJOR)
 
 /* Hard disk:  timeout is 6 seconds. */
 #define DEVICE_NAME "hard disk"
 #define DEVICE_INTR do_hd
-#define TIMEOUT_VALUE (6*HZ)
-#define DEVICE_REQUEST do_hd_request
 #define DEVICE_NR(device) (minor(device)>>6)
 
 #elif (SCSI_DISK_MAJOR(MAJOR_NR))
 
 #define DEVICE_NAME "scsidisk"
-#define TIMEOUT_VALUE (2*HZ)
 #define DEVICE_NR(device) (((major(device) & SD_MAJOR_MASK) << (8 - 4)) + (minor(device) >> 4))
 
 /* Kludge to use the same number for both char and block major numbers */
 #elif  (MAJOR_NR == MD_MAJOR) && defined(MD_DRIVER)
 
 #define DEVICE_NAME "Multiple devices driver"
-#define DEVICE_REQUEST do_md_request
 #define DEVICE_NR(device) (minor(device))
 
 #elif (MAJOR_NR == SCSI_TAPE_MAJOR)
@@ -169,8 +158,6 @@
 #define DEVICE_NAME "onstream" 
 #define DEVICE_INTR do_osst
 #define DEVICE_NR(device) (minor(device) & 0x7f) 
-#define DEVICE_ON(device) 
-#define DEVICE_OFF(device) 
 
 #elif (MAJOR_NR == SCSI_CDROM_MAJOR)
 
@@ -180,163 +167,129 @@
 #elif (MAJOR_NR == XT_DISK_MAJOR)
 
 #define DEVICE_NAME "xt disk"
-#define DEVICE_REQUEST do_xd_request
 #define DEVICE_NR(device) (minor(device) >> 6)
 
 #elif (MAJOR_NR == PS2ESDI_MAJOR)
 
 #define DEVICE_NAME "PS/2 ESDI"
-#define DEVICE_REQUEST do_ps2esdi_request
 #define DEVICE_NR(device) (minor(device) >> 6)
 
 #elif (MAJOR_NR == CDU31A_CDROM_MAJOR)
 
 #define DEVICE_NAME "CDU31A"
-#define DEVICE_REQUEST do_cdu31a_request
 #define DEVICE_NR(device) (minor(device))
 
 #elif (MAJOR_NR == ACSI_MAJOR) && (defined(CONFIG_ATARI_ACSI) || defined(CONFIG_ATARI_ACSI_MODULE))
 
 #define DEVICE_NAME "ACSI"
 #define DEVICE_INTR do_acsi
-#define DEVICE_REQUEST do_acsi_request
 #define DEVICE_NR(device) (minor(device) >> 4)
 
 #elif (MAJOR_NR == MITSUMI_CDROM_MAJOR)
 
 #define DEVICE_NAME "Mitsumi CD-ROM"
 /* #define DEVICE_INTR do_mcd */
-#define DEVICE_REQUEST do_mcd_request
 #define DEVICE_NR(device) (minor(device))
 
 #elif (MAJOR_NR == MITSUMI_X_CDROM_MAJOR)
 
 #define DEVICE_NAME "Mitsumi CD-ROM"
 /* #define DEVICE_INTR do_mcdx */
-#define DEVICE_REQUEST do_mcdx_request
 #define DEVICE_NR(device) (minor(device))
 
 #elif (MAJOR_NR == MATSUSHITA_CDROM_MAJOR)
 
 #define DEVICE_NAME "Matsushita CD-ROM controller #1"
-#define DEVICE_REQUEST do_sbpcd_request
 #define DEVICE_NR(device) (minor(device))
 
 #elif (MAJOR_NR == MATSUSHITA_CDROM2_MAJOR)
 
 #define DEVICE_NAME "Matsushita CD-ROM controller #2"
-#define DEVICE_REQUEST do_sbpcd2_request
 #define DEVICE_NR(device) (minor(device))
 
 #elif (MAJOR_NR == MATSUSHITA_CDROM3_MAJOR)
 
 #define DEVICE_NAME "Matsushita CD-ROM controller #3"
-#define DEVICE_REQUEST do_sbpcd3_request
 #define DEVICE_NR(device) (minor(device))
 
 #elif (MAJOR_NR == MATSUSHITA_CDROM4_MAJOR)
 
 #define DEVICE_NAME "Matsushita CD-ROM controller #4"
-#define DEVICE_REQUEST do_sbpcd4_request
 #define DEVICE_NR(device) (minor(device))
 
 #elif (MAJOR_NR == AZTECH_CDROM_MAJOR)
 
 #define DEVICE_NAME "Aztech CD-ROM"
-#define DEVICE_REQUEST do_aztcd_request
 #define DEVICE_NR(device) (minor(device))
 
 #elif (MAJOR_NR == CDU535_CDROM_MAJOR)
 
 #define DEVICE_NAME "SONY-CDU535"
 #define DEVICE_INTR do_cdu535
-#define DEVICE_REQUEST do_cdu535_request
 #define DEVICE_NR(device) (minor(device))
 
 #elif (MAJOR_NR == GOLDSTAR_CDROM_MAJOR)
 
 #define DEVICE_NAME "Goldstar R420"
-#define DEVICE_REQUEST do_gscd_request
 #define DEVICE_NR(device) (minor(device))
 
 #elif (MAJOR_NR == CM206_CDROM_MAJOR)
 #define DEVICE_NAME "Philips/LMS CD-ROM cm206"
-#define DEVICE_REQUEST do_cm206_request
 #define DEVICE_NR(device) (minor(device))
 
 #elif (MAJOR_NR == OPTICS_CDROM_MAJOR)
 
 #define DEVICE_NAME "DOLPHIN 8000AT CD-ROM"
-#define DEVICE_REQUEST do_optcd_request
 #define DEVICE_NR(device) (minor(device))
 
 #elif (MAJOR_NR == SANYO_CDROM_MAJOR)
 
 #define DEVICE_NAME "Sanyo H94A CD-ROM"
-#define DEVICE_REQUEST do_sjcd_request
 #define DEVICE_NR(device) (minor(device))
 
 #elif (MAJOR_NR == APBLOCK_MAJOR)
 
 #define DEVICE_NAME "apblock"
-#define DEVICE_REQUEST ap_request
 #define DEVICE_NR(device) (minor(device))
 
 #elif (MAJOR_NR == DDV_MAJOR)
 
 #define DEVICE_NAME "ddv"
-#define DEVICE_REQUEST ddv_request
 #define DEVICE_NR(device) (minor(device)>>PARTN_BITS)
 
 #elif (MAJOR_NR == MFM_ACORN_MAJOR)
 
 #define DEVICE_NAME "mfm disk"
 #define DEVICE_INTR do_mfm
-#define DEVICE_REQUEST do_mfm_request
 #define DEVICE_NR(device) (minor(device) >> 6)
 
 #elif (MAJOR_NR == NBD_MAJOR)
 
 #define DEVICE_NAME "nbd"
-#define DEVICE_REQUEST do_nbd_request
 #define DEVICE_NR(device) (minor(device))
 
 #elif (MAJOR_NR == MDISK_MAJOR)
 
 #define DEVICE_NAME "mdisk"
-#define DEVICE_REQUEST mdisk_request
 #define DEVICE_NR(device) (minor(device))
 
 #elif (MAJOR_NR == DASD_MAJOR)
 
 #define DEVICE_NAME "dasd"
-#define DEVICE_REQUEST do_dasd_request
 #define DEVICE_NR(device) (minor(device) >> PARTN_BITS)
 
 #elif (MAJOR_NR == I2O_MAJOR)
 
 #define DEVICE_NAME "I2O block"
-#define DEVICE_REQUEST i2ob_request
 #define DEVICE_NR(device) (minor(device)>>4)
 
 #elif (MAJOR_NR == COMPAQ_SMART2_MAJOR)
 
 #define DEVICE_NAME "ida"
-#define TIMEOUT_VALUE (25*HZ)
-#define DEVICE_REQUEST do_ida_request
 #define DEVICE_NR(device) (minor(device) >> 4)
 
 #endif /* MAJOR_NR == whatever */
 
-/* provide DEVICE_xxx defaults, if not explicitly defined
- * above in the MAJOR_NR==xxx if-elif tree */
-#ifndef DEVICE_ON
-#define DEVICE_ON(device) do {} while (0)
-#endif
-#ifndef DEVICE_OFF
-#define DEVICE_OFF(device) do {} while (0)
-#endif
-
 #if (MAJOR_NR != SCSI_TAPE_MAJOR) && (MAJOR_NR != OSST_MAJOR)
 #if !defined(IDE_DRIVER)
 
@@ -349,8 +302,6 @@
 #ifndef QUEUE_EMPTY
 #define QUEUE_EMPTY blk_queue_empty(QUEUE)
 #endif
-
-
 #ifndef DEVICE_NAME
 #define DEVICE_NAME "unknown"
 #endif
@@ -363,10 +314,6 @@
 
 #define SET_INTR(x) (DEVICE_INTR = (x))
 
-#ifdef DEVICE_REQUEST
-static void (DEVICE_REQUEST)(request_queue_t *);
-#endif 
-  
 #ifdef DEVICE_INTR
 #define CLEAR_INTR SET_INTR(NULL)
 #else
@@ -385,11 +332,10 @@
 
 #endif /* !defined(IDE_DRIVER) */
 
-
-#ifndef LOCAL_END_REQUEST	/* If we have our own end_request, we do not want to include this mess */
-
-#if ! SCSI_BLK_MAJOR(MAJOR_NR) && (MAJOR_NR != COMPAQ_SMART2_MAJOR)
-
+/*
+ * If we have our own end_request, we do not want to include this mess
+ */
+#ifndef LOCAL_END_REQUEST
 static inline void end_request(int uptodate)
 {
 	struct request *req = CURRENT;
@@ -397,17 +343,11 @@
 	if (end_that_request_first(req, uptodate, CURRENT->hard_cur_sectors))
 		return;
 
-#ifndef DEVICE_NO_RANDOM
 	add_blkdev_randomness(major(req->rq_dev));
-#endif
-	DEVICE_OFF(req->rq_dev);
 	blkdev_dequeue_request(req);
 	end_that_request_last(req);
 }
-
-#endif /* ! SCSI_BLK_MAJOR(MAJOR_NR) */
-#endif /* LOCAL_END_REQUEST */
-
+#endif /* !LOCAL_END_REQUEST */
 #endif /* (MAJOR_NR != SCSI_TAPE_MAJOR) */
 #endif /* defined(MAJOR_NR) || defined(IDE_DRIVER) */
 
diff -Nru a/include/linux/blkdev.h b/include/linux/blkdev.h
--- a/include/linux/blkdev.h	Sun Apr 28 20:10:32 2002
+++ b/include/linux/blkdev.h	Sun Apr 28 20:10:32 2002
@@ -76,17 +76,16 @@
 	__REQ_STARTED,	/* drive already may have started this one */
 	__REQ_DONTPREP,	/* don't call prep for this one */
 	/*
-	 * for IDE
- 	*/
+	 * for ATA/ATAPI devices
+	 */
 	__REQ_DRIVE_CMD,
-	__REQ_DRIVE_TASK,
 	__REQ_DRIVE_ACB,
 
 	__REQ_PC,	/* packet command (special) */
 	__REQ_BLOCK_PC,	/* queued down pc from block layer */
 	__REQ_SENSE,	/* sense retrival */
 
-	__REQ_SPECIAL,	/* driver special command */
+	__REQ_SPECIAL,	/* driver special command (currently reset) */
 
 	__REQ_NR_BITS,	/* stops here */
 };
@@ -99,15 +98,12 @@
 #define REQ_STARTED	(1 << __REQ_STARTED)
 #define REQ_DONTPREP	(1 << __REQ_DONTPREP)
 #define REQ_DRIVE_CMD	(1 << __REQ_DRIVE_CMD)
-#define REQ_DRIVE_TASK	(1 << __REQ_DRIVE_TASK)
 #define REQ_DRIVE_ACB	(1 << __REQ_DRIVE_ACB)
 #define REQ_PC		(1 << __REQ_PC)
-#define REQ_SENSE	(1 << __REQ_SENSE)
 #define REQ_BLOCK_PC	(1 << __REQ_BLOCK_PC)
+#define REQ_SENSE	(1 << __REQ_SENSE)
 #define REQ_SPECIAL	(1 << __REQ_SPECIAL)
 
-#define REQ_DRIVE_TASKFILE	REQ_DRIVE_ACB
-
 #include <linux/elevator.h>
 
 typedef int (merge_request_fn) (request_queue_t *, struct request *,
@@ -291,7 +287,7 @@
 extern void blk_recount_segments(request_queue_t *, struct bio *);
 extern inline int blk_phys_contig_segment(request_queue_t *q, struct bio *, struct bio *);
 extern inline int blk_hw_contig_segment(request_queue_t *q, struct bio *, struct bio *);
-extern int block_ioctl(kdev_t, unsigned int, unsigned long);
+extern int block_ioctl(struct block_device *, unsigned int, unsigned long);
 extern int ll_10byte_cmd_build(request_queue_t *, struct request *);
 
 /*
@@ -314,8 +310,8 @@
 extern void blk_queue_segment_boundary(request_queue_t *q, unsigned long);
 extern void blk_queue_assign_lock(request_queue_t *q, spinlock_t *);
 extern void blk_queue_prep_rq(request_queue_t *q, prep_rq_fn *pfn);
-extern int blk_set_readahead(kdev_t dev, unsigned sectors);
-extern unsigned blk_get_readahead(kdev_t dev);
+extern int blk_set_readahead(struct block_device *bdev, unsigned sectors);
+extern unsigned blk_get_readahead(struct block_device *bdev);
 
 extern int blk_rq_map_sg(request_queue_t *, struct request *, struct scatterlist *);
 extern void blk_dump_rq_flags(struct request *, char *);
@@ -343,15 +339,24 @@
 	blksize_size[major] = NULL;
 }
 
-extern inline int get_hardsect_size(kdev_t dev)
+extern inline int queue_hardsect_size(request_queue_t *q)
 {
-	request_queue_t *q = blk_get_queue(dev);
 	int retval = 512;
 
 	if (q && q->hardsect_size)
 		retval = q->hardsect_size;
 
 	return retval;
+}
+
+extern inline int get_hardsect_size(kdev_t dev)
+{
+	return queue_hardsect_size(blk_get_queue(dev));
+}
+
+extern inline int bdev_hardsect_size(struct block_device *bdev)
+{
+	return queue_hardsect_size(blk_get_queue(to_kdev_t(bdev->bd_dev)));
 }
 
 #define blk_finished_io(nsects)	do { } while (0)
diff -Nru a/include/linux/brlvger.h b/include/linux/brlvger.h
--- a/include/linux/brlvger.h	Sun Apr 28 20:10:34 2002
+++ b/include/linux/brlvger.h	Sun Apr 28 20:10:34 2002
@@ -30,11 +30,16 @@
 #define BRLVGER_DISPLAY_OFF	3
 #define BRLVGER_BUZZ		4
 
+#ifdef CONFIG_USB_DYNAMIC_MINORS
+#define MAX_NR_BRLVGER_DEVS	256
+#define BRLVGER_MINOR		0
+#else
 /* Number of supported devices, and range of covered minors */
-#define MAX_NR_BRLVGER_DEVS	2
+#define MAX_NR_BRLVGER_DEVS	4
 
 /* Base minor for the char devices */
 #define BRLVGER_MINOR		128
+#endif
 
 /* Size of some fields */
 #define BRLVGER_HWVER_SIZE	2
diff -Nru a/include/linux/dcache.h b/include/linux/dcache.h
--- a/include/linux/dcache.h	Sun Apr 28 20:10:36 2002
+++ b/include/linux/dcache.h	Sun Apr 28 20:10:36 2002
@@ -229,6 +229,7 @@
 
 /* appendix may either be NULL or be used for transname suffixes */
 extern struct dentry * d_lookup(struct dentry *, struct qstr *);
+extern struct dentry * __d_lookup(struct dentry *, struct qstr *);
 
 /* validate "insecure" dentry pointer */
 extern int d_validate(struct dentry *, struct dentry *);
diff -Nru a/include/linux/fb.h b/include/linux/fb.h
--- a/include/linux/fb.h	Sun Apr 28 20:10:34 2002
+++ b/include/linux/fb.h	Sun Apr 28 20:10:34 2002
@@ -379,9 +379,6 @@
 					/* tell fb to switch consoles */
    int (*updatevar)(int, struct fb_info*);
 					/* tell fb to update the vars */
-   void (*blank)(int, struct fb_info*);	/* tell fb to (un)blank the screen */
-					/* arg = 0: unblank */
-					/* arg > 0: VESA level (arg-1) */
    void *pseudo_palette;                /* Fake palette of 16 colors and 
 					   the cursor's color for non
                                            palette mode */
@@ -413,8 +410,6 @@
     void (*set_par)(const void *par, struct fb_info_gen *info);
     int (*getcolreg)(unsigned regno, unsigned *red, unsigned *green,
 		     unsigned *blue, unsigned *transp, struct fb_info *info);
-    int (*setcolreg)(unsigned regno, unsigned red, unsigned green,
-		     unsigned blue, unsigned transp, struct fb_info *info);
     int (*pan_display)(const struct fb_var_screeninfo *var,
 		       struct fb_info_gen *info);
     int (*blank)(int blank_mode, struct fb_info_gen *info);
@@ -470,11 +465,11 @@
 extern int fbgen_do_set_var(struct fb_var_screeninfo *var, int isactive,
 			    struct fb_info_gen *info);
 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 void do_install_cmap(int con, struct fb_info *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 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);
@@ -499,10 +494,7 @@
 		       int (*getcolreg)(u_int, u_int *, u_int *, u_int *,
 					u_int *, struct fb_info *),
 		       struct fb_info *fb_info);
-extern int fb_set_cmap(struct fb_cmap *cmap, int kspc,
-		       int (*setcolreg)(u_int, u_int, u_int, u_int, u_int,
-					struct fb_info *),
-		       struct fb_info *fb_info);
+extern int fb_set_cmap(struct fb_cmap *cmap, int kspc, struct fb_info *fb_info);
 extern struct fb_cmap *fb_default_cmap(int len);
 extern void fb_invert_cmaps(void);
 
diff -Nru a/include/linux/fs.h b/include/linux/fs.h
--- a/include/linux/fs.h	Sun Apr 28 20:10:32 2002
+++ b/include/linux/fs.h	Sun Apr 28 20:10:32 2002
@@ -244,7 +244,6 @@
 	sector_t b_blocknr;		/* block number */
 	unsigned short b_size;		/* block size */
 	unsigned short b_list;		/* List that this buffer appears */
-	kdev_t b_dev;			/* device (B_FREE = free) */
 	struct block_device *b_bdev;
 
 	atomic_t b_count;		/* users using this block */
@@ -420,6 +419,7 @@
 	struct list_head	bd_inodes;
 	void *			bd_holder;
 	int			bd_holders;
+	struct block_device *	bd_contains;
 };
 
 struct inode {
@@ -679,7 +679,6 @@
 
 #include <linux/ext3_fs_sb.h>
 #include <linux/hpfs_fs_sb.h>
-#include <linux/ntfs_fs_sb.h>
 #include <linux/sysv_fs_sb.h>
 #include <linux/ufs_fs_sb.h>
 #include <linux/romfs_fs_sb.h>
@@ -724,7 +723,6 @@
 	union {
 		struct ext3_sb_info	ext3_sb;
 		struct hpfs_sb_info	hpfs_sb;
-		struct ntfs_sb_info	ntfs_sb;
 		struct sysv_sb_info	sysv_sb;
 		struct ufs_sb_info	ufs_sb;
 		struct romfs_sb_info	romfs_sb;
@@ -1191,7 +1189,11 @@
 extern int register_chrdev(unsigned int, const char *, struct file_operations *);
 extern int unregister_chrdev(unsigned int, const char *);
 extern int chrdev_open(struct inode *, struct file *);
-extern const char * bdevname(kdev_t);
+extern const char *__bdevname(kdev_t);
+extern inline const char *bdevname(struct block_device *bdev)
+{
+	return __bdevname(to_kdev_t(bdev->bd_dev));
+}
 extern const char * cdevname(kdev_t);
 extern const char * kdevname(kdev_t);
 extern void init_special_inode(struct inode *, umode_t, int);
@@ -1386,12 +1388,15 @@
  *  - require a directory
  *  - ending slashes ok even for nonexistent files
  *  - internal "there are more path compnents" flag
+ *  - locked when lookup done with dcache_lock held
  */
 #define LOOKUP_FOLLOW		(1)
 #define LOOKUP_DIRECTORY	(2)
 #define LOOKUP_CONTINUE		(4)
 #define LOOKUP_PARENT		(16)
 #define LOOKUP_NOALT		(32)
+#define LOOKUP_LOCKED		(64)
+
 /*
  * Type of the last component on LOOKUP_PARENT
  */
@@ -1421,14 +1426,8 @@
 extern int FASTCALL(__user_walk(const char *, unsigned, struct nameidata *));
 extern int FASTCALL(path_init(const char *, unsigned, struct nameidata *));
 extern int FASTCALL(path_walk(const char *, struct nameidata *));
+extern int FASTCALL(path_lookup(const char *, unsigned, struct nameidata *));
 extern int FASTCALL(link_path_walk(const char *, struct nameidata *));
-static inline int path_lookup(const char *path, unsigned flags, struct nameidata *nd)
-{
-	int error = 0;
-	if (path_init(path, flags, nd))
-		error = path_walk(path, nd);
-	return error;
-}
 extern void path_release(struct nameidata *);
 extern int follow_down(struct vfsmount **, struct dentry **);
 extern int follow_up(struct vfsmount **, struct dentry **);
@@ -1464,7 +1463,7 @@
 	struct buffer_head *bh;
 	bdev = bdget(kdev_t_to_nr(dev));
 	if (!bdev) {
-		printk("No block device for %s\n", bdevname(dev));
+		printk("No block device for %s\n", __bdevname(dev));
 		BUG();
 	}
 	bh = __get_hash_table(bdev, block, size);
@@ -1478,7 +1477,7 @@
 	struct buffer_head *bh;
 	bdev = bdget(kdev_t_to_nr(dev));
 	if (!bdev) {
-		printk("No block device for %s\n", bdevname(dev));
+		printk("No block device for %s\n", __bdevname(dev));
 		BUG();
 	}
 	bh = __getblk(bdev, block, size);
@@ -1512,7 +1511,7 @@
 	struct buffer_head *bh;
 	bdev = bdget(kdev_t_to_nr(dev));
 	if (!bdev) {
-		printk("No block device for %s\n", bdevname(dev));
+		printk("No block device for %s\n", __bdevname(dev));
 		BUG();
 	}
 	bh = __bread(bdev, block, size);
@@ -1535,7 +1534,6 @@
 {
 	bh->b_state |= 1 << BH_Mapped;
 	bh->b_bdev = sb->s_bdev;
-	bh->b_dev = sb->s_dev;
 	bh->b_blocknr = block;
 }
 extern void wakeup_bdflush(void);
diff -Nru a/include/linux/hdreg.h b/include/linux/hdreg.h
--- a/include/linux/hdreg.h	Sun Apr 28 20:10:36 2002
+++ b/include/linux/hdreg.h	Sun Apr 28 20:10:36 2002
@@ -3,7 +3,7 @@
 
 /*
  * This file contains some defines for the AT-hd-controller.
- * Various sources.  
+ * Various sources.
  */
 
 #define HD_IRQ 14			/* the standard disk interrupt */
@@ -34,7 +34,6 @@
 #define ECC_STAT		0x04	/* Corrected error */
 #define DRQ_STAT		0x08
 #define SEEK_STAT		0x10
-#define SERVICE_STAT		SEEK_STAT
 #define WRERR_STAT		0x20
 #define READY_STAT		0x40
 #define BUSY_STAT		0x80
@@ -51,19 +50,10 @@
 #define ICRC_ERR		0x80	/* new meaning:  CRC error during transfer */
 
 /*
- * bits of NSECTOR reg
- */
-#define NSEC_CD			0x1
-#define NSEC_IO			0x2
-#define NSEC_REL		0x4
-
-/*
  * Command Header sizes for IOCTL commands
- *	HDIO_DRIVE_CMD and HDIO_DRIVE_TASK
  */
 
 #define HDIO_DRIVE_CMD_HDR_SIZE		(4 * sizeof(u8))
-#define HDIO_DRIVE_TASK_HDR_SIZE	(8 * sizeof(u8))
 #define HDIO_DRIVE_HOB_HDR_SIZE		(8 * sizeof(u8))
 
 #define IDE_DRIVE_TASK_INVALID		-1
@@ -295,7 +285,6 @@
 #define HDIO_GET_UNMASKINTR	0x0302	/* get current unmask setting */
 #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_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 */
@@ -306,12 +295,8 @@
 #define	HDIO_GET_ADDRESS	0x0310	/* */
 
 #define HDIO_GET_BUSSTATE	0x031a	/* get the bus state of the hwif */
-#define HDIO_TRISTATE_HWIF	0x031b	/* execute a channel tristate */
-#define HDIO_DRIVE_TASK		0x031e	/* execute task and special drive command */
 #define HDIO_DRIVE_CMD		0x031f	/* execute a special drive command */
 
-#define HDIO_DRIVE_CMD_AEB	HDIO_DRIVE_TASK
-
 /* 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 */
@@ -529,11 +514,7 @@
 					 *  7:0	current value
 					 */
 	unsigned short	words95_99[5];	/* reserved words 95-99 */
-#if 0
-	unsigned short	words100_103[4]	;/* reserved words 100-103 */
-#else
 	unsigned long long lba_capacity_2;/* 48-bit total number of sectors */
-#endif
 	unsigned short	words104_125[22];/* reserved words 104-125 */
 	unsigned short	last_lun;	/* (word 126) */
 	unsigned short	word127;	/* (word 127) Feature Set
@@ -581,7 +562,7 @@
 					 * 15:8 Checksum
 					 *  7:0 Signature
 					 */
-};
+} __attribute__((packed));
 
 /*
  * IDE "nice" flags. These are used on a per drive basis to determine
diff -Nru a/include/linux/ide.h b/include/linux/ide.h
--- a/include/linux/ide.h	Sun Apr 28 20:10:32 2002
+++ b/include/linux/ide.h	Sun Apr 28 20:10:32 2002
@@ -33,23 +33,26 @@
  */
 #define INITIAL_MULT_COUNT	0	/* off=0; on=2,4,8,16,32, etc.. */
 
-#ifndef SUPPORT_SLOW_DATA_PORTS		/* 1 to support slow data ports */
-#define SUPPORT_SLOW_DATA_PORTS	1	/* 0 to reduce kernel size */
+#ifndef SUPPORT_SLOW_DATA_PORTS			/* 1 to support slow data ports */
+# define SUPPORT_SLOW_DATA_PORTS	1	/* 0 to reduce kernel size */
 #endif
+
+/* Right now this is only needed by a promise controlled.
+ */
 #ifndef SUPPORT_VLB_SYNC		/* 1 to support weird 32-bit chips */
-#define SUPPORT_VLB_SYNC	1	/* 0 to reduce kernel size */
+# define SUPPORT_VLB_SYNC	1	/* 0 to reduce kernel size */
 #endif
 #ifndef DISK_RECOVERY_TIME		/* off=0; on=access_delay_time */
-#define DISK_RECOVERY_TIME	0	/*  for hardware that needs it */
+# define DISK_RECOVERY_TIME	0	/*  for hardware that needs it */
 #endif
 #ifndef OK_TO_RESET_CONTROLLER		/* 1 needed for good error recovery */
-#define OK_TO_RESET_CONTROLLER	1	/* 0 for use with AH2372A/B interface */
+# define OK_TO_RESET_CONTROLLER	1	/* 0 for use with AH2372A/B interface */
 #endif
 #ifndef FANCY_STATUS_DUMPS		/* 1 for human-readable drive errors */
-#define FANCY_STATUS_DUMPS	1	/* 0 to reduce kernel size */
+# define FANCY_STATUS_DUMPS	1	/* 0 to reduce kernel size */
 #endif
 #ifndef DISABLE_IRQ_NOSYNC
-#define DISABLE_IRQ_NOSYNC	0
+# define DISABLE_IRQ_NOSYNC	0
 #endif
 
 /*
@@ -70,7 +73,6 @@
  */
 #define DMA_PIO_RETRY	1	/* retrying in PIO */
 
-#define HWIF(drive)		((drive)->channel)
 #define HWGROUP(drive)		(drive->channel->hwgroup)
 
 /*
@@ -251,7 +253,7 @@
 #endif
 
 /*
- * Now for the data we need to maintain per-drive:  ide_drive_t
+ * Device types - the nomenclature is analogous to SCSI code.
  */
 
 #define ATA_DISK        0x20
@@ -262,58 +264,11 @@
 #define ATA_SCSI	0x21
 #define ATA_NO_LUN      0x7f
 
-typedef union {
-	unsigned all			: 8;	/* all of the bits together */
-	struct {
-		unsigned set_geometry	: 1;	/* respecify drive geometry */
-		unsigned recalibrate	: 1;	/* seek to cyl 0      */
-		unsigned set_multmode	: 1;	/* set multmode count */
-		unsigned set_tune	: 1;	/* tune interface for drive */
-		unsigned reserved	: 4;	/* unused */
-	} b;
-} special_t;
-
-#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 */
-
-	/*
-	 * Either the command completed immediately after being started
-	 * (immed_comp), or the device did a bus release before dma was
-	 * started (immed_rel).
-	 */
-	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)	\
-	do {						\
-		((drive)->tcq->active_tag = (tag));	\
-		if ((tag) == IDE_INACTIVE_TAG)		\
-			HWGROUP((drive))->rq = NULL;	\
-	} while (0);
-
-#define IDE_CUR_AR(drive)	(HWGROUP((drive))->rq->special)
-
 struct ide_settings_s;
-/* structure describing an ATA/ATAPI device */
+
+/*
+ * ATA/ATAPI device structure :
+ */
 typedef
 struct ata_device {
 	struct ata_channel *	channel;
@@ -326,11 +281,9 @@
 	 * 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 ata_device *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!!!
@@ -341,9 +294,18 @@
 	unsigned long PADAM_service_time;	/* service time of last request */
 	unsigned long PADAM_timeout;		/* max time to wait for irq */
 
-	special_t	special;	/* special action flags */
+	/* Flags requesting/indicating one of the following special commands
+	 * executed on the request queue.
+	 */
+#define ATA_SPECIAL_GEOMETRY		0x01
+#define ATA_SPECIAL_RECALIBRATE		0x02
+#define ATA_SPECIAL_MMODE		0x04
+#define ATA_SPECIAL_TUNE		0x08
+	unsigned char special_cmd;
+	u8 mult_req;			/* requested multiple sector setting */
+	u8 tune_req;			/* requested drive tuning setting */
+
 	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     dsc_overlap;		/* flag: DSC overlap */
@@ -369,8 +331,6 @@
 	byte		ctl;		/* "normal" value for IDE_CONTROL_REG */
 	byte		ready_stat;	/* min status value for drive ready */
 	byte		mult_count;	/* current multiple sector setting */
-	byte		mult_req;	/* requested multiple sector setting */
-	byte		tune_req;	/* requested drive tuning setting */
 	byte		bad_wstat;	/* used for ignoring WRERR_STAT */
 	byte		nowerr;		/* used for ignoring WRERR_STAT */
 	byte		sect0;		/* offset of first sector for DM6:DDO */
@@ -380,8 +340,7 @@
 	byte		bios_sect;	/* BIOS/fdisk/LILO sectors per track */
 	unsigned int	bios_cyl;	/* BIOS/fdisk/LILO number of cyls */
 	unsigned int	cyl;		/* "real" number of cyls */
-	unsigned long	capacity;	/* total number of sectors */
-	unsigned long long capacity48;	/* total number of sectors */
+	u64		capacity;	/* total number of sectors */
 	unsigned int	drive_data;	/* for use by tuneproc/selectproc as needed */
 
 	wait_queue_head_t wqueue;	/* used to wait for drive in open() */
@@ -412,8 +371,6 @@
 	unsigned int	failures;	/* current failure count */
 	unsigned int	max_failures;	/* maximum allowed failure count */
 	struct device	device;		/* global device tree handle */
-	unsigned int	queue_depth;
-	ide_tag_info_t	*tcq;
 } ide_drive_t;
 
 /*
@@ -432,10 +389,7 @@
 		ide_dma_off,	ide_dma_off_quietly,	ide_dma_test_irq,
 		ide_dma_bad_drive,			ide_dma_good_drive,
 		ide_dma_verbose,			ide_dma_retune,
-		ide_dma_lostirq,			ide_dma_timeout,
-		ide_dma_read_queued,			ide_dma_write_queued,
-		ide_dma_queued_start,			ide_dma_queued_on,
-		ide_dma_queued_off,
+		ide_dma_lostirq,			ide_dma_timeout
 } ide_dma_action_t;
 
 typedef int (ide_dmaproc_t)(ide_dma_action_t, ide_drive_t *);
@@ -449,7 +403,6 @@
 	struct device	dev;		/* device handle */
 	int		unit;		/* channel number */
 
-	struct ata_channel *next;	/* for linked-list in ide_hwgroup_t */
 	struct hwgroup_s *hwgroup;	/* actually (ide_hwgroup_t *) */
 
 	ide_ioreg_t	io_ports[IDE_NR_PORTS];	/* task file registers */
@@ -494,6 +447,11 @@
 	void (*atapi_write)(ide_drive_t *, void *, unsigned int);
 
 	ide_dmaproc_t	*dmaproc;	/* dma read/write/abort routine */
+	unsigned int	*dmatable_cpu;	/* dma physical region descriptor table (cpu view) */
+	dma_addr_t	dmatable_dma;	/* dma physical region descriptor table (dma view) */
+	struct scatterlist *sg_table;	/* Scatter-gather list used to build the above */
+	int sg_nents;			/* Current number of entries in it */
+	int sg_dma_direction;		/* dma transfer direction */
 	unsigned long	dma_base;	/* base addr for dma ports */
 	unsigned	dma_extra;	/* extra addr for dma ports */
 	unsigned long	config_data;	/* for use by chipset-specific code */
@@ -504,20 +462,20 @@
 	char		name[8];	/* name of interface */
 	int		index;		/* 0 for ide0; 1 for ide1; ... */
 	hwif_chipset_t	chipset;	/* sub-module for tuning.. */
-	unsigned	noprobe    : 1;	/* don't probe for this interface */
-	unsigned	present    : 1;	/* there is a device on this interface */
-	unsigned	serialized : 1;	/* serialized operation between channels */
-	unsigned	sharing_irq: 1;	/* 1 = sharing irq with another hwif */
-	unsigned	reset      : 1;	/* reset after probe */
-	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 */
+	unsigned noprobe	: 1;	/* don't probe for this interface */
+	unsigned present	: 1;	/* there is a device on this interface */
+	unsigned serialized	: 1;	/* serialized operation between channels */
+	unsigned sharing_irq	: 1;	/* 1 = sharing irq with another hwif */
+	unsigned reset		: 1;	/* reset after probe */
+	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 */
+	unsigned no_io_32bit	: 1;	/* disallow enabling 32bit I/O */
+	unsigned no_unmask	: 1;	/* disallow setting unmask bit */
 	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 */
+	byte		slow;		/* flag: slow data port */
+
 #if (DISK_RECOVERY_TIME > 0)
 	unsigned long	last_time;	/* time when previous rq was done */
 #endif
@@ -538,7 +496,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, handler set, bus released */
+	ide_released	/* started and released bus */
 } ide_startstop_t;
 
 /*
@@ -559,19 +517,18 @@
 #define IDE_DMA		2	/* DMA in progress */
 
 typedef struct hwgroup_s {
-	ide_handler_t		*handler;	/* irq handler, if active */
-	unsigned long		flags;		/* BUSY, SLEEPING */
-	ide_drive_t		*drive;		/* current drive */
-	struct ata_channel	*hwif;		/* ptr to current hwif in linked-list */
-
-	struct request		*rq;		/* current request */
-
-	struct timer_list	timer;		/* failsafe timer */
-	struct request		wrq;		/* local copy of current write rq */
-	unsigned long		poll_timeout;	/* timeout value during long polls */
-	ide_expiry_t		*expiry;	/* queried upon timeouts */
+	ide_handler_t *handler;		/* irq handler, if active */
+	unsigned long flags;		/* BUSY, SLEEPING */
+	struct ata_device *drive;	/* current drive */
+	struct request *rq;		/* current request */
+	struct timer_list timer;	/* failsafe timer */
+	struct request wrq;		/* local copy of current write rq */
+	unsigned long poll_timeout;	/* timeout value during long polls */
+	ide_expiry_t *expiry;		/* queried upon timeouts */
 } ide_hwgroup_t;
 
+/* structure attached to the request for IDE_TASK_CMDS */
+
 /*
  * configurable drive settings
  */
@@ -656,20 +613,20 @@
 
 struct ata_operations {
 	struct module *owner;
-	int (*cleanup)(ide_drive_t *);
-	int (*standby)(ide_drive_t *);
-	ide_startstop_t	(*do_request)(ide_drive_t *, struct request *, unsigned long);
-	int (*end_request)(ide_drive_t *drive, int uptodate);
-
-	int (*ioctl)(ide_drive_t *, struct inode *, struct file *, unsigned int, unsigned long);
-	int (*open)(struct inode *, struct file *, ide_drive_t *);
-	void (*release)(struct inode *, struct file *, ide_drive_t *);
-	int (*check_media_change)(ide_drive_t *);
-	void (*revalidate)(ide_drive_t *);
-
-	void (*pre_reset)(ide_drive_t *);
-	unsigned long (*capacity)(ide_drive_t *);
-	ide_startstop_t	(*special)(ide_drive_t *);
+	int (*cleanup)(struct ata_device *);
+	int (*standby)(struct ata_device *);
+	ide_startstop_t	(*do_request)(struct ata_device *, struct request *, sector_t);
+	int (*end_request)(struct ata_device *, int);
+
+	int (*ioctl)(struct ata_device *, struct inode *, struct file *, unsigned int, unsigned long);
+	int (*open)(struct inode *, struct file *, struct ata_device *);
+	void (*release)(struct inode *, struct file *, struct ata_device *);
+	int (*check_media_change)(struct ata_device *);
+	void (*revalidate)(struct ata_device *);
+
+	void (*pre_reset)(struct ata_device *);
+	sector_t (*capacity)(struct ata_device *);
+	ide_startstop_t	(*special)(struct ata_device *);
 
 	ide_proc_entry_t *proc;
 };
@@ -686,7 +643,7 @@
 		__MOD_DEC_USE_COUNT((ata)->owner);	\
 } while(0)
 
-extern unsigned long ata_capacity(ide_drive_t *drive);
+extern sector_t ata_capacity(struct ata_device *drive);
 
 /* FIXME: Actually implement and use them as soon as possible!  to make the
  * ide_scan_devices() go away! */
@@ -708,7 +665,6 @@
 
 extern int __ide_end_request(ide_drive_t *drive, int uptodate, int nr_secs);
 extern int ide_end_request(ide_drive_t *drive, int uptodate);
-extern void ide_end_queued_request(ide_drive_t *drive, int, struct request *);
 
 /*
  * This is used on exit from the driver, to designate the next irq handler
@@ -774,7 +730,6 @@
  * This function is intended to be used prior to invoking ide_do_drive_cmd().
  */
 extern void ide_init_drive_cmd(struct request *rq);
-extern void init_taskfile_request(struct request *rq);
 
 /*
  * "action" parameter type for ide_do_drive_cmd() below.
@@ -804,33 +759,8 @@
 	int			command_type;
 	ide_pre_handler_t	*prehandler;
 	ide_handler_t		*handler;
-	struct ata_request	*ar;
-};
-
-/*
- * Merge this with the above struct soon.
- */
-struct ata_request {
-	struct request		*ar_rq;		/* real request */
-	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 */
-	struct ata_taskfile	ar_task;	/* associated taskfile */
-	unsigned long		ar_time;
-
-	/* DMA stuff, PCI layer */
-	struct scatterlist	*ar_sg_table;
-	int			ar_sg_nents;
-	int			ar_sg_ddir;
-
-	/* CPU related DMA stuff  */
-	unsigned int		*ar_dmatable_cpu;
-	dma_addr_t		ar_dmatable;
 };
 
-#define AR_TASK_CMD(ar)	((ar)->ar_task.taskfile.command)
-
 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);
 
@@ -853,10 +783,8 @@
 /* This is setting up all fields in args, which depend upon the command type.
  */
 extern void ide_cmd_type_parser(struct ata_taskfile *args);
-extern int ide_raw_taskfile(ide_drive_t *drive, struct ata_taskfile *cmd, byte *buf);
-
-extern int ide_cmd_ioctl(ide_drive_t *drive, unsigned long arg);
-extern int ide_task_ioctl(ide_drive_t *drive, unsigned long arg);
+extern int ide_raw_taskfile(struct ata_device *drive, struct ata_taskfile *cmd, byte *buf);
+extern int ide_cmd_ioctl(struct ata_device *drive, unsigned long arg);
 
 void ide_delay_50ms(void);
 
@@ -897,7 +825,7 @@
 
 int ide_spin_wait_hwgroup (ide_drive_t *drive);
 void ide_timer_expiry (unsigned long data);
-void ide_intr (int irq, void *dev_id, struct pt_regs *regs);
+extern void ata_irq_request(int irq, void *data, struct pt_regs *regs);
 void do_ide_request (request_queue_t * q);
 void ide_init_subdrivers (void);
 
@@ -938,16 +866,14 @@
 #  define OFF_BOARD		NEVER_BOARD
 # endif
 
-/* FIXME: This should go away possible. */
-extern void __init ide_scan_pcibus(int scan_direction);
+void __init ide_scan_pcibus(int scan_direction);
 #endif
 #ifdef CONFIG_BLK_DEV_IDEDMA
-extern int ide_build_dmatable(ide_drive_t *drive, struct request *rq, ide_dma_action_t func);
-extern void ide_destroy_dmatable(ide_drive_t *drive);
-extern int ide_start_dma(struct ata_channel *, ide_drive_t *, ide_dma_action_t);
-extern ide_startstop_t ide_dma_intr(ide_drive_t *drive);
-extern int check_drive_lists(ide_drive_t *drive, int good_bad);
-extern int ide_dmaproc(ide_dma_action_t func, ide_drive_t *drive);
+int ide_build_dmatable (ide_drive_t *drive, ide_dma_action_t func);
+void ide_destroy_dmatable (ide_drive_t *drive);
+ide_startstop_t ide_dma_intr (ide_drive_t *drive);
+int check_drive_lists (ide_drive_t *drive, int good_bad);
+int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive);
 extern void ide_release_dma(struct ata_channel *hwif);
 extern void ide_setup_dma(struct ata_channel *hwif,
 		unsigned long dmabase, unsigned int num_ports) __init;
@@ -959,113 +885,5 @@
 
 extern int drive_is_ready(ide_drive_t *drive);
 extern void revalidate_drives(void);
-
-/*
- * Tagged Command Queueing:
- */
-
-/*
- * ata_request flag bits
- */
-#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)
-
-static inline void ata_ar_init(ide_drive_t *drive, struct ata_request *ar)
-{
-	ar->ar_rq = NULL;
-	ar->ar_drive = drive;
-	ar->ar_flags = 0;
-	ar->ar_tag = 0;
-	memset(&ar->ar_task, 0, sizeof(ar->ar_task));
-	ar->ar_sg_nents = 0;
-	ar->ar_sg_ddir = 0;
-}
-
-/*
- * Return a free command, automatically add it to busy list.
- */
-static inline struct ata_request *ata_ar_get(ide_drive_t *drive)
-{
-	struct ata_request *ar = NULL;
-
-	if (drive->tcq && drive->tcq->queued >= drive->queue_depth)
-		return NULL;
-
-	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;
-}
-
-static inline void ata_ar_put(ide_drive_t *drive, struct ata_request *ar)
-{
-	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 */
-		drive->tcq->ar[ar->ar_tag] = NULL;
-		__clear_bit(ar->ar_tag, &drive->tcq->tag_mask);
-		drive->tcq->queued--;
-	}
-
-	ar->ar_rq = NULL;
-}
-
-static inline int ide_get_tag(ide_drive_t *drive)
-{
-	int tag = ffz(drive->tcq->tag_mask);
-
-	BUG_ON(drive->tcq->tag_mask == 0xffffffff);
-
-	__set_bit(tag, &drive->tcq->tag_mask);
-
-	if (tag + 1 > drive->tcq->max_depth)
-		drive->tcq->max_depth = tag + 1;
-	if (tag + 1 > drive->tcq->max_last_depth)
-		drive->tcq->max_last_depth = tag + 1;
-
-	return tag;
-}
-
-#ifdef CONFIG_BLK_DEV_IDE_TCQ
-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_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 *);
 
 #endif
diff -Nru a/include/linux/loop.h b/include/linux/loop.h
--- a/include/linux/loop.h	Sun Apr 28 20:10:36 2002
+++ b/include/linux/loop.h	Sun Apr 28 20:10:36 2002
@@ -27,7 +27,6 @@
 struct loop_device {
 	int		lo_number;
 	int		lo_refcnt;
-	kdev_t		lo_device;
 	int		lo_offset;
 	int		lo_encrypt_type;
 	int		lo_encrypt_key_size;
@@ -43,6 +42,7 @@
 				 unsigned long arg); 
 
 	struct file *	lo_backing_file;
+	struct block_device *lo_device;
 	void		*key_data; 
 	char		key_reserved[48]; /* for use by the filter modules */
 
diff -Nru a/include/linux/mempool.h b/include/linux/mempool.h
--- a/include/linux/mempool.h	Sun Apr 28 20:10:30 2002
+++ b/include/linux/mempool.h	Sun Apr 28 20:10:30 2002
@@ -4,28 +4,25 @@
 #ifndef _LINUX_MEMPOOL_H
 #define _LINUX_MEMPOOL_H
 
-#include <linux/list.h>
 #include <linux/wait.h>
 
-struct mempool_s;
-typedef struct mempool_s mempool_t;
-
 typedef void * (mempool_alloc_t)(int gfp_mask, void *pool_data);
 typedef void (mempool_free_t)(void *element, void *pool_data);
 
-struct mempool_s {
+typedef struct mempool_s {
 	spinlock_t lock;
-	int min_nr, curr_nr;
-	struct list_head elements;
+	int min_nr;		/* nr of elements at *elements */
+	int curr_nr;		/* Current nr of elements at *elements */
+	void **elements;
 
 	void *pool_data;
 	mempool_alloc_t *alloc;
 	mempool_free_t *free;
 	wait_queue_head_t wait;
-};
+} mempool_t;
 extern mempool_t * mempool_create(int min_nr, mempool_alloc_t *alloc_fn,
 				 mempool_free_t *free_fn, void *pool_data);
-extern void mempool_resize(mempool_t *pool, int new_min_nr, int gfp_mask);
+extern int mempool_resize(mempool_t *pool, int new_min_nr, int gfp_mask);
 extern void mempool_destroy(mempool_t *pool);
 extern void * mempool_alloc(mempool_t *pool, int gfp_mask);
 extern void mempool_free(void *element, mempool_t *pool);
diff -Nru a/include/linux/mm.h b/include/linux/mm.h
--- a/include/linux/mm.h	Sun Apr 28 20:10:30 2002
+++ b/include/linux/mm.h	Sun Apr 28 20:10:30 2002
@@ -539,6 +539,8 @@
 extern struct page *filemap_nopage(struct vm_area_struct *, unsigned long, int);
 
 /* readahead.c */
+#define VM_MAX_READAHEAD	128	/* kbytes */
+#define VM_MIN_READAHEAD	16	/* kbytes (includes current page) */
 void do_page_cache_readahead(struct file *file,
 			unsigned long offset, unsigned long nr_to_read);
 void page_cache_readahead(struct file *file, unsigned long offset);
diff -Nru a/include/linux/msdos_fs.h b/include/linux/msdos_fs.h
--- a/include/linux/msdos_fs.h	Sun Apr 28 20:10:37 2002
+++ b/include/linux/msdos_fs.h	Sun Apr 28 20:10:37 2002
@@ -54,12 +54,29 @@
 
 #define MSDOS_FAT12 4084 /* maximum number of clusters in a 12 bit FAT */
 
-#define EOF_FAT12 0xFF8		/* standard EOF */
+/* media of boot sector */
+#define FAT_VALID_MEDIA(x)	((0xF8 <= (x) && (x) <= 0xFF) || (x) == 0xF0)
+#define FAT_FIRST_ENT(s, x)	((MSDOS_SB(s)->fat_bits == 32 ? 0x0FFFFF00 : \
+	MSDOS_SB(s)->fat_bits == 16 ? 0xFF00 : 0xF00) | (x))
+
+/* bad cluster mark */
+#define BAD_FAT12 0xFF7
+#define BAD_FAT16 0xFFF7
+#define BAD_FAT32 0xFFFFFF7
+#define BAD_FAT(s) (MSDOS_SB(s)->fat_bits == 32 ? BAD_FAT32 : \
+	MSDOS_SB(s)->fat_bits == 16 ? BAD_FAT16 : BAD_FAT12)
+
+/* standard EOF */
+#define EOF_FAT12 0xFF8
 #define EOF_FAT16 0xFFF8
 #define EOF_FAT32 0xFFFFFF8
 #define EOF_FAT(s) (MSDOS_SB(s)->fat_bits == 32 ? EOF_FAT32 : \
 	MSDOS_SB(s)->fat_bits == 16 ? EOF_FAT16 : EOF_FAT12)
 
+#define FAT_ENT_FREE	(0)
+#define FAT_ENT_BAD	(BAD_FAT32)
+#define FAT_ENT_EOF	(EOF_FAT32)
+
 #define FAT_FSINFO_SIG1		0x41615252
 #define FAT_FSINFO_SIG2		0x61417272
 #define IS_FSINFO(x)	(CF_LE_L((x)->signature1) == FAT_FSINFO_SIG1	\
@@ -102,7 +119,7 @@
 	__u8	fats;		/* number of FATs */
 	__u8	dir_entries[2];	/* root directory entries */
 	__u8	sectors[2];	/* number of sectors */
-	__u8	media;		/* media code (unused) */
+	__u8	media;		/* media code */
 	__u16	fat_length;	/* sectors/FAT */
 	__u16	secs_track;	/* sectors per track */
 	__u16	heads;		/* number of heads */
@@ -230,6 +247,7 @@
 
 /* fat/cache.c */
 extern int fat_access(struct super_block *sb, int nr, int new_value);
+extern int __fat_access(struct super_block *sb, int nr, int new_value);
 extern int fat_bmap(struct inode *inode, int sector);
 extern void fat_cache_init(void);
 extern void fat_cache_lookup(struct inode *inode, int cluster, int *f_clu,
@@ -237,7 +255,6 @@
 extern void fat_cache_add(struct inode *inode, int f_clu, int d_clu);
 extern void fat_cache_inval_inode(struct inode *inode);
 extern void fat_cache_inval_dev(struct super_block *sb);
-extern int fat_get_cluster(struct inode *inode, int cluster);
 extern int fat_free(struct inode *inode, int skip);
 
 /* fat/dir.c */
diff -Nru a/include/linux/ntfs_fs.h b/include/linux/ntfs_fs.h
--- a/include/linux/ntfs_fs.h	Sun Apr 28 20:10:36 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,29 +0,0 @@
-#ifndef _LINUX_NTFS_FS_H
-#define _LINUX_NTFS_FS_H
-
-#include <asm/byteorder.h>
-
-#define NTFS_SECTOR_BITS 9
-#define NTFS_SECTOR_SIZE 512
-
-/*
- * Attribute flags (16-bit).
- */
-typedef enum {
-	ATTR_IS_COMPRESSED      = __constant_cpu_to_le16(0x0001),
-	ATTR_COMPRESSION_MASK   = __constant_cpu_to_le16(0x00ff),
-					/* Compression method mask. Also,
-					 * first illegal value. */
-	ATTR_IS_ENCRYPTED       = __constant_cpu_to_le16(0x4000),
-	ATTR_IS_SPARSE          = __constant_cpu_to_le16(0x8000),
-} __attribute__ ((__packed__)) ATTR_FLAGS;
-
-/*
- * The two zones from which to allocate clusters.
- */
-typedef enum {
-	MFT_ZONE,
-	DATA_ZONE
-} NTFS_CLUSTER_ALLOCATION_ZONES;
-
-#endif
diff -Nru a/include/linux/ntfs_fs_i.h b/include/linux/ntfs_fs_i.h
--- a/include/linux/ntfs_fs_i.h	Sun Apr 28 20:10:38 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,97 +0,0 @@
-#ifndef _LINUX_NTFS_FS_I_H
-#define _LINUX_NTFS_FS_I_H
-
-#include <linux/types.h>
-
-/* Forward declarations, to keep number of mutual includes low */
-struct ntfs_attribute;
-struct ntfs_sb_info;
-
-/* Duplicate definitions from ntfs/ntfstypes.h */
-#ifndef NTFS_INTEGRAL_TYPES
-#define NTFS_INTEGRAL_TYPES
-typedef u8  ntfs_u8;
-typedef u16 ntfs_u16;
-typedef u32 ntfs_u32;
-typedef u64 ntfs_u64;
-typedef s8  ntfs_s8;
-typedef s16 ntfs_s16;
-typedef s32 ntfs_s32;
-typedef s64 ntfs_s64;
-#endif
-
-#ifndef NTMODE_T
-#define NTMODE_T
-typedef __kernel_mode_t ntmode_t;
-#endif
-#ifndef NTFS_UID_T
-#define NTFS_UID_T
-typedef uid_t ntfs_uid_t;
-#endif
-#ifndef NTFS_GID_T
-#define NTFS_GID_T
-typedef gid_t ntfs_gid_t;
-#endif
-#ifndef NTFS_SIZE_T
-#define NTFS_SIZE_T
-typedef __kernel_size_t ntfs_size_t;
-#endif
-#ifndef NTFS_TIME_T
-#define NTFS_TIME_T
-typedef __kernel_time_t ntfs_time_t;
-#endif
-
-/* unicode character type */
-#ifndef NTFS_WCHAR_T
-#define NTFS_WCHAR_T
-typedef u16 ntfs_wchar_t;
-#endif
-/* file offset */
-#ifndef NTFS_OFFSET_T
-#define NTFS_OFFSET_T
-typedef s64 ntfs_offset_t;
-#endif
-/* UTC */
-#ifndef NTFS_TIME64_T
-#define NTFS_TIME64_T
-typedef u64 ntfs_time64_t;
-#endif
-/*
- * This is really signed long long. So we support only volumes up to 2Tb. This
- * is ok as Win2k also only uses 32-bits to store clusters.
- * Whatever you do keep this a SIGNED value or a lot of NTFS users with
- * corrupted filesystems will lynch you! It causes massive fs corruption when
- * unsigned due to the nature of many checks relying on being performed on
- * signed quantities. (AIA)
- */
-#ifndef NTFS_CLUSTER_T
-#define NTFS_CLUSTER_T
-typedef s32 ntfs_cluster_t;
-#endif
-
-/* Definition of the NTFS in-memory inode structure. */
-struct ntfs_inode_info {
-	struct ntfs_sb_info *vol;
-	unsigned long i_number;		/* Should be really 48 bits. */
-	__u16 sequence_number;		/* The current sequence number. */
-	unsigned char *attr;		/* Array of the attributes. */
-	int attr_count;			/* Size of attrs[]. */
-	struct ntfs_attribute *attrs;
-	int record_count;		/* Size of records[]. */
-	int *records; /* Array of the record numbers of the $Mft whose 
-		       * attributes have been inserted in the inode. */
-	union {
-		struct {
-			int recordsize;
-			int clusters_per_record;
-		} index;
-	} u;	
-};
-
-/* this is a kludge */
-struct ntfs_i {
-	struct ntfs_inode_info n;
-	struct inode vfs_inode;
-};
-
-#endif
diff -Nru a/include/linux/ntfs_fs_sb.h b/include/linux/ntfs_fs_sb.h
--- a/include/linux/ntfs_fs_sb.h	Sun Apr 28 20:10:32 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,61 +0,0 @@
-#ifndef _LINUX_NTFS_FS_SB_H
-#define _LINUX_NTFS_FS_SB_H
-
-#include <linux/ntfs_fs_i.h>
-
-struct ntfs_sb_info{
-	/* Configuration provided by user at mount time. */
-	ntfs_uid_t uid;
-	ntfs_gid_t gid;
-	ntmode_t umask;
-	void *nls_map;
-	unsigned int ngt;
-	char mft_zone_multiplier;
-	unsigned long mft_data_pos;
-	ntfs_cluster_t mft_zone_pos;
-	ntfs_cluster_t mft_zone_start;
-	ntfs_cluster_t mft_zone_end;
-	ntfs_cluster_t data1_zone_pos;
-	ntfs_cluster_t data2_zone_pos;
-	/* Configuration provided by user with the ntfstools.
-	 * FIXME: This is no longer possible. What is this good for? (AIA) */
-	ntfs_size_t partition_bias;	/* For access to underlying device. */
-	/* Attribute definitions. */
-	ntfs_u32 at_standard_information;
-	ntfs_u32 at_attribute_list;
-	ntfs_u32 at_file_name;
-	ntfs_u32 at_volume_version;
-	ntfs_u32 at_security_descriptor;
-	ntfs_u32 at_volume_name;
-	ntfs_u32 at_volume_information;
-	ntfs_u32 at_data;
-	ntfs_u32 at_index_root;
-	ntfs_u32 at_index_allocation;
-	ntfs_u32 at_bitmap;
-	ntfs_u32 at_symlink; /* aka SYMBOLIC_LINK or REPARSE_POINT */
-	/* Data read / calculated from the boot file. */
-	int sector_size;
-	int cluster_size;
-	int cluster_size_bits;
-	int mft_clusters_per_record;
-	int mft_record_size;
-	int mft_record_size_bits;
-	int index_clusters_per_record;
-	int index_record_size;
-	int index_record_size_bits;
-	ntfs_cluster_t nr_clusters;
-	ntfs_cluster_t mft_lcn;
-	ntfs_cluster_t mft_mirr_lcn;
-	/* Data read from special files. */
-	unsigned char *mft;
-	unsigned short *upcase;
-	unsigned int upcase_length;
-	/* Inodes we always hold onto. */
-	struct ntfs_inode_info *mft_ino;
-	struct ntfs_inode_info *mftmirr;
-	struct ntfs_inode_info *bitmap;
-	struct super_block *sb;
-	unsigned char ino_flags;
-};
-
-#endif
diff -Nru a/include/linux/raid/linear.h b/include/linux/raid/linear.h
--- a/include/linux/raid/linear.h	Sun Apr 28 20:10:35 2002
+++ b/include/linux/raid/linear.h	Sun Apr 28 20:10:35 2002
@@ -5,6 +5,7 @@
 
 struct dev_info {
 	kdev_t		dev;
+	struct block_device *bdev;
 	unsigned long	size;
 	unsigned long	offset;
 };
diff -Nru a/include/linux/raid/md.h b/include/linux/raid/md.h
--- a/include/linux/raid/md.h	Sun Apr 28 20:10:31 2002
+++ b/include/linux/raid/md.h	Sun Apr 28 20:10:31 2002
@@ -67,6 +67,10 @@
 extern void add_mddev_mapping (mddev_t *mddev, kdev_t dev, void *data);
 extern void del_mddev_mapping (mddev_t *mddev, kdev_t dev);
 extern char * partition_name (kdev_t dev);
+extern inline char * bdev_partition_name (struct block_device *bdev)
+{
+	return partition_name(to_kdev_t(bdev->bd_dev));
+}
 extern int register_md_personality (int p_num, mdk_personality_t *p);
 extern int unregister_md_personality (int p_num);
 extern mdk_thread_t * md_register_thread (void (*run) (void *data),
@@ -82,7 +86,7 @@
 extern int md_check_ordering (mddev_t *mddev);
 extern int md_notify_reboot(struct notifier_block *this,
 					unsigned long code, void *x);
-extern int md_error (mddev_t *mddev, kdev_t rdev);
+extern int md_error (mddev_t *mddev, struct block_device *bdev);
 extern int md_run_setup(void);
 
 extern void md_print_devices (void);
diff -Nru a/include/linux/raid/multipath.h b/include/linux/raid/multipath.h
--- a/include/linux/raid/multipath.h	Sun Apr 28 20:10:37 2002
+++ b/include/linux/raid/multipath.h	Sun Apr 28 20:10:37 2002
@@ -7,6 +7,7 @@
 	int		number;
 	int		raid_disk;
 	kdev_t		dev;
+	struct block_device *bdev;
 
 	/*
 	 * State bits:
@@ -25,7 +26,7 @@
 	int			working_disks;
 	mdk_thread_t		*thread;
 	struct multipath_info	*spare;
-	md_spinlock_t		device_lock;
+	spinlock_t		device_lock;
 
 	/* buffer pool */
 	/* buffer_heads that we have pre-allocated have b_pprev -> &freebh
@@ -36,7 +37,7 @@
 	struct multipath_bh	*freer1;
 	int			freer1_blocked;
 	int			freer1_cnt;
-	md_wait_queue_head_t	wait_buffer;
+	wait_queue_head_t	wait_buffer;
 };
 
 typedef struct multipath_private_data multipath_conf_t;
@@ -60,8 +61,8 @@
 	int			cmd;
 	unsigned long		state;
 	mddev_t			*mddev;
-	struct buffer_head	*master_bh;
-	struct buffer_head	bh_req;
+	struct bio		*master_bio;
+	struct bio		*bio;
 	struct multipath_bh	*next_mp; /* next for retry or in free list */
 };
 /* bits for multipath_bh.state */
diff -Nru a/include/linux/raid/raid1.h b/include/linux/raid/raid1.h
--- a/include/linux/raid/raid1.h	Sun Apr 28 20:10:37 2002
+++ b/include/linux/raid/raid1.h	Sun Apr 28 20:10:37 2002
@@ -9,6 +9,7 @@
 	int		number;
 	int		raid_disk;
 	kdev_t		dev;
+	struct block_device *bdev;
 	sector_t	head_position;
 	atomic_t	nr_pending;
 
diff -Nru a/include/linux/usb.h b/include/linux/usb.h
--- a/include/linux/usb.h	Sun Apr 28 20:10:35 2002
+++ b/include/linux/usb.h	Sun Apr 28 20:10:35 2002
@@ -2,6 +2,7 @@
 #define __LINUX_USB_H
 
 #include <linux/device.h>
+#include <linux/errno.h>
 
 /* USB constants */
 
@@ -317,6 +318,223 @@
 	__usb_get_extra_descriptor((ifpoint)->extra,(ifpoint)->extralen,\
 		type,(void**)ptr)
 
+/* -------------------------------------------------------------------------- */
+
+/* Host Controller Driver (HCD) support */
+
+struct usb_operations;
+
+#define DEVNUM_ROUND_ROBIN	/***** OPTION *****/
+
+/*
+ * Allocated per bus we have
+ */
+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 */
+#endif /* DEVNUM_ROUND_ROBIN */
+
+	struct usb_devmap devmap;       /* device address allocation map */
+	struct usb_operations *op;      /* Operations (specific to the HC) */
+	struct usb_device *root_hub;    /* Root hub */
+	struct list_head bus_list;	/* list of busses */
+	void *hcpriv;                   /* Host Controller private data */
+
+	int bandwidth_allocated;	/* on this bus: how much of the time
+					 * reserved for periodic (intr/iso)
+					 * requests is used, on average?
+					 * Units: microseconds/frame.
+					 * Limits: Full/low speed reserve 90%,
+					 * while high speed reserves 80%.
+					 */
+	int bandwidth_int_reqs;		/* number of Interrupt requests */
+	int bandwidth_isoc_reqs;	/* number of Isoc. requests */
+
+	struct dentry *dentry;		/* usbfs dentry entry for the bus */
+
+	atomic_t refcnt;
+};
+
+// FIXME:  root_hub_string vanishes when "usb_hcd" conversion is done,
+// along with pre-hcd versions of the OHCI and UHCI drivers.
+extern int usb_root_hub_string(int id, int serial,
+		char *type, __u8 *data, int len);
+
+/*
+ * As of USB 2.0, full/low speed devices are segregated into trees.
+ * One type grows from USB 1.1 host controllers (OHCI, UHCI etc).
+ * The other type grows from high speed hubs when they connect to
+ * full/low speed devices using "Transaction Translators" (TTs).
+ *
+ * TTs should only be known to the hub driver, and high speed bus
+ * drivers (only EHCI for now).  They affect periodic scheduling and
+ * sometimes control/bulk error recovery.
+ */
+struct usb_tt {
+	struct usb_device	*hub;	/* upstream highspeed hub */
+	int			multi;	/* true means one TT per port */
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+/* This is arbitrary.
+ * From USB 2.0 spec Table 11-13, offset 7, a hub can
+ * have up to 255 ports. The most yet reported is 10.
+ */
+#define USB_MAXCHILDREN		(16)
+
+struct usb_device {
+	int		devnum;		/* Address on USB bus */
+	char		devpath [16];	/* Use in messages: /port/port/... */
+
+	enum {
+		USB_SPEED_UNKNOWN = 0,			/* enumerating */
+		USB_SPEED_LOW, USB_SPEED_FULL,		/* usb 1.1 */
+		USB_SPEED_HIGH				/* usb 2.0 */
+	} speed;
+
+	struct usb_tt	*tt; 		/* low/full speed dev, highspeed hub */
+	int		ttport;		/* device port on that tt hub */
+
+	atomic_t refcnt;		/* Reference count */
+	struct semaphore serialize;
+
+	unsigned int toggle[2];		/* one bit for each endpoint ([0] = IN, [1] = OUT) */
+	unsigned int halted[2];		/* endpoint halts; one bit per endpoint # & direction; */
+					/* [0] = IN, [1] = OUT */
+	int epmaxpacketin[16];		/* INput endpoint specific maximums */
+	int epmaxpacketout[16];		/* OUTput endpoint specific maximums */
+
+	struct usb_device *parent;	/* our hub, unless we're the root */
+	struct usb_bus *bus;		/* Bus we're part of */
+
+	struct device dev;		/* Generic device interface */
+
+	struct usb_device_descriptor descriptor;/* Descriptor */
+	struct usb_config_descriptor *config;	/* All of the configs */
+	struct usb_config_descriptor *actconfig;/* the active configuration */
+
+	char **rawdescriptors;		/* Raw descriptors for each config */
+
+	int have_langid;		/* whether string_langid is valid yet */
+	int string_langid;		/* language ID for strings */
+  
+	void *hcpriv;			/* Host Controller private data */
+	
+	struct list_head filelist;
+	struct dentry *dentry;		/* usbfs dentry entry for the device */
+
+	/*
+	 * Child devices - these can be either new devices
+	 * (if this is a hub device), or different instances
+	 * of this same device.
+	 *
+	 * Each instance needs its own set of data structures.
+	 */
+
+	int maxchild;			/* Number of ports if hub */
+	struct usb_device *children[USB_MAXCHILDREN];
+};
+
+/* for when layers above USB add new non-USB drivers */
+extern void usb_scan_devices(void);
+
+/* mostly for devices emulating SCSI over USB */
+extern int usb_reset_device(struct usb_device *dev);
+
+/* for drivers using iso endpoints */
+extern int usb_get_current_frame_number (struct usb_device *usb_dev);
+
+/**
+ * usb_inc_dev_use - record another reference to a device
+ * @dev: the device being referenced
+ *
+ * Each live reference to a device should be refcounted.
+ *
+ * Drivers for USB interfaces should normally record such references in
+ * their probe() methods, when they bind to an interface, and release
+ * them usb_dec_dev_use(), in their disconnect() methods.
+ */
+static inline void usb_inc_dev_use (struct usb_device *dev)
+{
+	atomic_inc (&dev->refcnt);
+}
+
+/**
+ * usb_dec_dev_use - drop a reference to a device
+ * @dev: the device no longer being referenced
+ *
+ * Each live reference to a device should be refcounted.
+ *
+ * Drivers for USB interfaces should normally release such references in
+ * their disconnect() methods, and record them in probe().
+ *
+ * Note that driver disconnect() methods must guarantee that when they
+ * return, all of their outstanding references to the device (and its
+ * interfaces) are cleaned up.  That means that all pending URBs from
+ * this driver must have completed, and that no more copies of the device
+ * handle are saved in driver records (including other kernel threads).
+ */
+static inline void usb_dec_dev_use (struct usb_device *dev)
+{
+	if (atomic_dec_and_test (&dev->refcnt)) {
+		/* May only go to zero when usbcore finishes
+		 * usb_disconnect() processing:  khubd or HCDs.
+		 *
+		 * If you hit this BUG() it's likely a problem
+		 * with some driver's disconnect() routine.
+		 */
+		BUG ();
+	}
+}
+
+
+/* used these for multi-interface device registration */
+extern int usb_find_interface_driver_for_ifnum(struct usb_device *dev, unsigned int ifnum);
+extern void usb_driver_claim_interface(struct usb_driver *driver,
+			struct usb_interface *iface, void* priv);
+extern int usb_interface_claimed(struct usb_interface *iface);
+extern void usb_driver_release_interface(struct usb_driver *driver,
+			struct usb_interface *iface);
+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;
+}
+
 /*-------------------------------------------------------------------------*/
 
 /*
@@ -559,6 +777,14 @@
 extern int usb_register(struct usb_driver *);
 extern void usb_deregister(struct usb_driver *);
 
+#ifndef CONFIG_USB_DYNAMIC_MINORS
+static inline int usb_register_dev(struct usb_driver *new_driver, int num_minors, int *start_minor) { return -ENODEV; }
+static inline void usb_deregister_dev(struct usb_driver *driver, int num_minors, int start_minor) {}
+#else
+extern int usb_register_dev(struct usb_driver *new_driver, int num_minors, int *start_minor);
+extern void usb_deregister_dev(struct usb_driver *driver, int num_minors, int start_minor);
+#endif
+
 /* -------------------------------------------------------------------------- */
 
 /*
@@ -826,10 +1052,14 @@
  * @buffer_length: length of the transfer buffer
  * @complete: pointer to the usb_complete_t function
  * @context: what to set the urb context to.
- * @interval: what to set the urb interval to.
+ * @interval: what to set the urb interval to, encoded like
+ *	the endpoint descriptor's bInterval value.
  *
  * Initializes a interrupt urb with the proper information needed to submit
  * it to a device.
+ * Note that high speed interrupt endpoints use a logarithmic encoding of
+ * the endpoint interval, and express polling intervals in microframes
+ * (eight per millisecond) rather than in frames (one per millisecond).
  */
 static inline void usb_fill_int_urb (struct urb *urb,
 				     struct usb_device *dev,
@@ -847,7 +1077,10 @@
 	urb->transfer_buffer_length = buffer_length;
 	urb->complete = complete;
 	urb->context = context;
-	urb->interval = interval;
+	if (dev->speed == USB_SPEED_HIGH)
+		urb->interval = 1 << (interval - 1);
+	else
+		urb->interval = interval;
 	urb->start_frame = -1;
 }
 
@@ -905,221 +1138,6 @@
 #endif
 
 #define USB_CTRL_SET_TIMEOUT 3
-
-/* -------------------------------------------------------------------------- */
-
-/* Host Controller Driver (HCD) support */
-
-struct usb_operations;
-
-#define DEVNUM_ROUND_ROBIN	/***** OPTION *****/
-
-/*
- * Allocated per bus we have
- */
-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 */
-#endif /* DEVNUM_ROUND_ROBIN */
-
-	struct usb_devmap devmap;       /* Device map */
-	struct usb_operations *op;      /* Operations (specific to the HC) */
-	struct usb_device *root_hub;    /* Root hub */
-	struct list_head bus_list;
-	void *hcpriv;                   /* Host Controller private data */
-
-	int bandwidth_allocated;	/* on this Host Controller; */
-					  /* applies to Int. and Isoc. pipes; */
-					  /* measured in microseconds/frame; */
-					  /* range is 0..900, where 900 = */
-					  /* 90% of a 1-millisecond frame */
-	int bandwidth_int_reqs;		/* number of Interrupt requesters */
-	int bandwidth_isoc_reqs;	/* number of Isoc. requesters */
-
-	struct dentry *dentry;		/* usbfs dentry entry for the bus */
-
-	atomic_t refcnt;
-};
-
-// FIXME:  root_hub_string vanishes when "usb_hcd" conversion is done,
-// along with pre-hcd versions of the OHCI and UHCI drivers.
-extern int usb_root_hub_string(int id, int serial,
-		char *type, __u8 *data, int len);
-
-/*
- * As of USB 2.0, full/low speed devices are segregated into trees.
- * One type grows from USB 1.1 host controllers (OHCI, UHCI etc).
- * The other type grows from high speed hubs when they connect to
- * full/low speed devices using "Transaction Translators" (TTs).
- *
- * TTs should only be known to the hub driver, and high speed bus
- * drivers (only EHCI for now).  They affect periodic scheduling and
- * sometimes control/bulk error recovery.
- */
-struct usb_tt {
-	struct usb_device	*hub;	/* upstream highspeed hub */
-	int			multi;	/* true means one TT per port */
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-/* This is arbitrary.
- * From USB 2.0 spec Table 11-13, offset 7, a hub can
- * have up to 255 ports. The most yet reported is 10.
- */
-#define USB_MAXCHILDREN		(16)
-
-struct usb_device {
-	int		devnum;		/* Address on USB bus */
-	char		devpath [16];	/* Use in messages: /port/port/... */
-
-	enum {
-		USB_SPEED_UNKNOWN = 0,			/* enumerating */
-		USB_SPEED_LOW, USB_SPEED_FULL,		/* usb 1.1 */
-		USB_SPEED_HIGH				/* usb 2.0 */
-	} speed;
-
-	struct usb_tt	*tt; 		/* low/full speed dev, highspeed hub */
-	int		ttport;		/* device port on that tt hub */
-
-	atomic_t refcnt;		/* Reference count */
-	struct semaphore serialize;
-
-	unsigned int toggle[2];		/* one bit for each endpoint ([0] = IN, [1] = OUT) */
-	unsigned int halted[2];		/* endpoint halts; one bit per endpoint # & direction; */
-					/* [0] = IN, [1] = OUT */
-	int epmaxpacketin[16];		/* INput endpoint specific maximums */
-	int epmaxpacketout[16];		/* OUTput endpoint specific maximums */
-
-	struct usb_device *parent;
-	struct usb_bus *bus;		/* Bus we're part of */
-
-	struct device dev;		/* Generic device interface */
-
-	struct usb_device_descriptor descriptor;/* Descriptor */
-	struct usb_config_descriptor *config;	/* All of the configs */
-	struct usb_config_descriptor *actconfig;/* the active configuration */
-
-	char **rawdescriptors;		/* Raw descriptors for each config */
-
-	int have_langid;		/* whether string_langid is valid yet */
-	int string_langid;		/* language ID for strings */
-  
-	void *hcpriv;			/* Host Controller private data */
-	
-	struct list_head filelist;
-	struct dentry *dentry;		/* usbfs dentry entry for the device */
-
-	/*
-	 * Child devices - these can be either new devices
-	 * (if this is a hub device), or different instances
-	 * of this same device.
-	 *
-	 * Each instance needs its own set of data structures.
-	 */
-
-	int maxchild;			/* Number of ports if hub */
-	struct usb_device *children[USB_MAXCHILDREN];
-};
-
-/* for when layers above USB add new non-USB drivers */
-extern void usb_scan_devices(void);
-
-/* mostly for devices emulating SCSI over USB */
-extern int usb_reset_device(struct usb_device *dev);
-
-/* for drivers using iso endpoints */
-extern int usb_get_current_frame_number (struct usb_device *usb_dev);
-
-/**
- * usb_inc_dev_use - record another reference to a device
- * @dev: the device being referenced
- *
- * Each live reference to a device should be refcounted.
- *
- * Drivers for USB interfaces should normally record such references in
- * their probe() methods, when they bind to an interface, and release
- * them usb_dec_dev_use(), in their disconnect() methods.
- */
-static inline void usb_inc_dev_use (struct usb_device *dev)
-{
-	atomic_inc (&dev->refcnt);
-}
-
-/**
- * usb_dec_dev_use - drop a reference to a device
- * @dev: the device no longer being referenced
- *
- * Each live reference to a device should be refcounted.
- *
- * Drivers for USB interfaces should normally release such references in
- * their disconnect() methods, and record them in probe().
- *
- * Note that driver disconnect() methods must guarantee that when they
- * return, all of their outstanding references to the device (and its
- * interfaces) are cleaned up.  That means that all pending URBs from
- * this driver must have completed, and that no more copies of the device
- * handle are saved in driver records (including other kernel threads).
- */
-static inline void usb_dec_dev_use (struct usb_device *dev)
-{
-	if (atomic_dec_and_test (&dev->refcnt)) {
-		/* May only go to zero when usbcore finishes
-		 * usb_disconnect() processing:  khubd or HCDs.
-		 *
-		 * If you hit this BUG() it's likely a problem
-		 * with some driver's disconnect() routine.
-		 */
-		BUG ();
-	}
-}
-
-
-/* used these for multi-interface device registration */
-extern int usb_find_interface_driver_for_ifnum(struct usb_device *dev, unsigned int ifnum);
-extern void usb_driver_claim_interface(struct usb_driver *driver,
-			struct usb_interface *iface, void* priv);
-extern int usb_interface_claimed(struct usb_interface *iface);
-extern void usb_driver_release_interface(struct usb_driver *driver,
-			struct usb_interface *iface);
-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/scsi/scsi.h b/include/scsi/scsi.h
--- a/include/scsi/scsi.h	Sun Apr 28 20:10:39 2002
+++ b/include/scsi/scsi.h	Sun Apr 28 20:10:39 2002
@@ -78,6 +78,7 @@
 #define MODE_SENSE_10         0x5a
 #define PERSISTENT_RESERVE_IN 0x5e
 #define PERSISTENT_RESERVE_OUT 0x5f
+#define REPORT_LUNS           0xa0
 #define MOVE_MEDIUM           0xa5
 #define READ_12               0xa8
 #define WRITE_12              0xaa
@@ -130,6 +131,7 @@
 
 #define TYPE_DISK           0x00
 #define TYPE_TAPE           0x01
+#define TYPE_PRINTER        0x02
 #define TYPE_PROCESSOR      0x03    /* HP scanners use this */
 #define TYPE_WORM           0x04    /* Treated as ROM by our system */
 #define TYPE_ROM            0x05
@@ -162,6 +164,13 @@
     u_char  block_length_med;
     u_char  block_length_lo;
 };
+
+/*
+ * ScsiLun: 8 byte LUN.
+ */
+typedef struct scsi_lun {
+	u8 scsi_lun[8];
+} ScsiLun;
 
 /*
  *  MESSAGE CODES
diff -Nru a/include/scsi/sg.h b/include/scsi/sg.h
--- a/include/scsi/sg.h	Sun Apr 28 20:10:35 2002
+++ b/include/scsi/sg.h	Sun Apr 28 20:10:35 2002
@@ -9,11 +9,18 @@
 Original driver (sg.h):
 *       Copyright (C) 1992 Lawrence Foard
 Version 2 and 3 extensions to driver:
-*       Copyright (C) 1998 - 2001 Douglas Gilbert
+*       Copyright (C) 1998 - 2002 Douglas Gilbert
 
-    Version: 3.5.23 (20011231)
+    Version: 3.5.25 (20020425)
     This version is for 2.5 series kernels.
 
+    Changes since 3.5.24 (20020319)
+    	- use Scsi_Request::upper_private_data
+    	- zero buffers for non-root users
+    Changes since 3.5.23 (20011231)
+	- change EACCES to EPERM when O_RDONLY is insufficient
+	- suppress newlines in host string
+	- fix xfer direction, old interface, short reply_len [Travers Carter]
     Changes since 3.1.22 (20011208)
     	- branch sg driver for lk 2.5 series
     	- remove lock_kernel() from sg_close()
diff -Nru a/include/sound/ac97_codec.h b/include/sound/ac97_codec.h
--- a/include/sound/ac97_codec.h	Sun Apr 28 20:10:30 2002
+++ b/include/sound/ac97_codec.h	Sun Apr 28 20:10:30 2002
@@ -123,8 +123,9 @@
 #define AC97_SCAP_CENTER_LFE_DAC (1<<1)	/* center and LFE DACs are present */
 
 /* ac97->flags */
-#define AC97_HAS_PC_BEEP	(1<<0)
+#define AC97_HAS_PC_BEEP	(1<<0)	/* force PC Speaker usage */
 #define AC97_AD_MULTI		(1<<1)	/* Analog Devices - multi codecs */
+#define AC97_CS_SPDIF		(1<<2)	/* Cirrus Logic uses funky SPDIF */
 
 /*
 
diff -Nru a/include/sound/asequencer.h b/include/sound/asequencer.h
--- a/include/sound/asequencer.h	Sun Apr 28 20:10:30 2002
+++ b/include/sound/asequencer.h	Sun Apr 28 20:10:30 2002
@@ -24,7 +24,6 @@
 
 #ifndef __KERNEL__
 #include <linux/ioctl.h>
-#include <sys/ipc.h>
 #endif
 
 #include <sound/asound.h>
@@ -110,7 +109,7 @@
  * event data type = sndrv_seq_connect_t
  */
 #define SNDRV_SEQ_EVENT_PORT_SUBSCRIBED	66	/* ports connected */
-#define SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED	67	/* ports disconnected */
+#define SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED 67	/* ports disconnected */
 
 /** synthesizer events
  * event data type = sndrv_seq_eve_sample_control_t
@@ -153,13 +152,13 @@
 #define SNDRV_SEQ_EVENT_INSTR_STATUS_RESULT 108	/* result */
 #define SNDRV_SEQ_EVENT_INSTR_PUT	109	/* put instrument to port */
 #define SNDRV_SEQ_EVENT_INSTR_GET	110	/* get instrument from port */
-#define SNDRV_SEQ_EVENT_INSTR_GET_RESULT	111	/* result */
+#define SNDRV_SEQ_EVENT_INSTR_GET_RESULT 111	/* result */
 #define SNDRV_SEQ_EVENT_INSTR_FREE	112	/* free instrument(s) */
 #define SNDRV_SEQ_EVENT_INSTR_LIST	113	/* instrument list */
 #define SNDRV_SEQ_EVENT_INSTR_LIST_RESULT 114	/* result */
 #define SNDRV_SEQ_EVENT_INSTR_CLUSTER	115	/* cluster parameters */
-#define SNDRV_SEQ_EVENT_INSTR_CLUSTER_GET	116	/* get cluster parameters */
-#define SNDRV_SEQ_EVENT_INSTR_CLUSTER_RESULT 117	/* result */
+#define SNDRV_SEQ_EVENT_INSTR_CLUSTER_GET 116	/* get cluster parameters */
+#define SNDRV_SEQ_EVENT_INSTR_CLUSTER_RESULT 117 /* result */
 #define SNDRV_SEQ_EVENT_INSTR_CHANGE	118	/* instrument change */
 /* 119-129: reserved */
 
@@ -176,18 +175,6 @@
 #define SNDRV_SEQ_EVENT_USR_VAR3	138
 #define SNDRV_SEQ_EVENT_USR_VAR4	139
 
-/* 140-149: IPC shared memory events (*NOT SUPPORTED YET*)
- * event data type = sndrv_seq_ev_ipcshm
- * (SNDRV_SEQ_EVENT_LENGTH_VARIPC must be set)
- */
-#define SNDRV_SEQ_EVENT_IPCSHM		140
-/* 141-144: reserved */
-#define SNDRV_SEQ_EVENT_USR_VARIPC0	145
-#define SNDRV_SEQ_EVENT_USR_VARIPC1	146
-#define SNDRV_SEQ_EVENT_USR_VARIPC2	147
-#define SNDRV_SEQ_EVENT_USR_VARIPC3	148
-#define SNDRV_SEQ_EVENT_USR_VARIPC4	149
-
 /* 150-151: kernel events with quote - DO NOT use in user clients */
 #define SNDRV_SEQ_EVENT_KERNEL_ERROR	150
 #define SNDRV_SEQ_EVENT_KERNEL_QUOTE	151
@@ -232,7 +219,6 @@
 #define SNDRV_SEQ_EVENT_LENGTH_FIXED	(0<<2)	/* fixed event size */
 #define SNDRV_SEQ_EVENT_LENGTH_VARIABLE	(1<<2)	/* variable event size */
 #define SNDRV_SEQ_EVENT_LENGTH_VARUSR	(2<<2)	/* variable event size - user memory space */
-#define SNDRV_SEQ_EVENT_LENGTH_VARIPC	(3<<2)	/* variable event size - IPC */
 #define SNDRV_SEQ_EVENT_LENGTH_MASK	(3<<2)
 
 #define SNDRV_SEQ_PRIORITY_NORMAL	(0<<4)	/* normal priority */
@@ -269,16 +255,10 @@
 
 	/* external stored data */
 struct sndrv_seq_ev_ext {
-	size_t len;		/* length of data */
+	unsigned int len;	/* length of data */
 	void *ptr;		/* pointer to data (note: maybe 64-bit) */
 };
 
-	/* external stored data - IPC shared memory */
-struct sndrv_seq_ev_ipcshm {
-	size_t len;		/* length of data */
-	key_t ipc;		/* IPC key */
-};
-
 /* Instrument cluster type */
 typedef unsigned int sndrv_seq_instr_cluster_t;
 
@@ -358,7 +338,7 @@
 
 
 struct sndrv_seq_real_time {
-	unsigned int tv_sec;		/* seconds */
+	unsigned int tv_sec;	/* seconds */
 	unsigned int tv_nsec;	/* nanoseconds */
 };
 
@@ -406,8 +386,8 @@
 	union sndrv_seq_timestamp time;	/* schedule time */
 
 
-	struct sndrv_seq_addr source;		/* source address */
-	struct sndrv_seq_addr dest;		/* destination address */
+	struct sndrv_seq_addr source;	/* source address */
+	struct sndrv_seq_addr dest;	/* destination address */
 
 	union {				/* event data... */
 		struct sndrv_seq_ev_note note;
@@ -415,7 +395,6 @@
 		struct sndrv_seq_ev_raw8 raw8;
 		struct sndrv_seq_ev_raw32 raw32;
 		struct sndrv_seq_ev_ext ext;
-		struct sndrv_seq_ev_ipcshm ipcshm;
 		struct sndrv_seq_ev_queue_control queue;
 		union sndrv_seq_timestamp time;
 		struct sndrv_seq_addr addr;
@@ -464,8 +443,6 @@
 #define sndrv_seq_ev_is_instr_type(ev)	((ev)->type >= 100 && (ev)->type < 130)
 /* variable length events: 130-139 */
 #define sndrv_seq_ev_is_variable_type(ev)	((ev)->type >= 130 && (ev)->type < 140)
-/* ipc shmem events: 140-149 */
-#define sndrv_seq_ev_is_varipc_type(ev)	((ev)->type >= 140 && (ev)->type < 150)
 /* reserved for kernel */
 #define sndrv_seq_ev_is_reserved(ev)	((ev)->type >= 150)
 
@@ -483,7 +460,6 @@
 #define sndrv_seq_ev_is_fixed(ev)		(sndrv_seq_ev_length_type(ev) == SNDRV_SEQ_EVENT_LENGTH_FIXED)
 #define sndrv_seq_ev_is_variable(ev)	(sndrv_seq_ev_length_type(ev) == SNDRV_SEQ_EVENT_LENGTH_VARIABLE)
 #define sndrv_seq_ev_is_varusr(ev)	(sndrv_seq_ev_length_type(ev) == SNDRV_SEQ_EVENT_LENGTH_VARUSR)
-#define sndrv_seq_ev_is_varipc(ev)	(sndrv_seq_ev_length_type(ev) == SNDRV_SEQ_EVENT_LENGTH_VARIPC)
 
 /* time-stamp type */
 #define sndrv_seq_ev_timestamp_type(ev)	((ev)->flags & SNDRV_SEQ_TIME_STAMP_MASK)
@@ -798,9 +774,9 @@
 	int result;			/* operation result */
 	unsigned int formats[8];	/* bitmap of supported formats */
 	int ram_count;			/* count of RAM banks */
-	sndrv_seq_instr_size_t ram_sizes[16];	/* size of RAM banks */
+	sndrv_seq_instr_size_t ram_sizes[16]; /* size of RAM banks */
 	int rom_count;			/* count of ROM banks */
-	sndrv_seq_instr_size_t rom_sizes[8];	/* size of ROM banks */
+	sndrv_seq_instr_size_t rom_sizes[8]; /* size of ROM banks */
 	char reserved[128];
 };
 
@@ -808,7 +784,7 @@
 
 struct sndrv_seq_instr_status {
 	int result;			/* operation result */
-	sndrv_seq_instr_size_t free_ram[16];	/* free RAM in banks */
+	sndrv_seq_instr_size_t free_ram[16]; /* free RAM in banks */
 	int instrument_count;		/* count of downloaded instruments */
 	char reserved[128];
 };
@@ -837,7 +813,7 @@
 	} data;
 };
 
-/* INSTR_PUT/GET, data are stored in one block (extended or IPC), header + data */
+/* INSTR_PUT/GET, data are stored in one block (extended), header + data */
 
 struct sndrv_seq_instr_header {
 	union {
@@ -849,7 +825,7 @@
 	unsigned int len;		/* real instrument data length (without header) */
 	int result;			/* operation result */
 	char reserved[16];		/* for the future */
-	struct sndrv_seq_instr_data data;	/* instrument data (for put/get result) */
+	struct sndrv_seq_instr_data data; /* instrument data (for put/get result) */
 };
 
 /* INSTR_CLUSTER_SET */
@@ -887,14 +863,14 @@
 #define SNDRV_SEQ_IOCTL_SET_PORT_INFO	_IOW ('S', 0x23, struct sndrv_seq_port_info)
 
 #define SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT	_IOW ('S', 0x30, struct sndrv_seq_port_subscribe)
-#define SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT	_IOW ('S', 0x31, struct sndrv_seq_port_subscribe)
+#define SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT _IOW ('S', 0x31, struct sndrv_seq_port_subscribe)
 
 #define SNDRV_SEQ_IOCTL_CREATE_QUEUE	_IOWR('S', 0x32, struct sndrv_seq_queue_info)
 #define SNDRV_SEQ_IOCTL_DELETE_QUEUE	_IOW ('S', 0x33, struct sndrv_seq_queue_info)
 #define SNDRV_SEQ_IOCTL_GET_QUEUE_INFO	_IOWR('S', 0x34, struct sndrv_seq_queue_info)
 #define SNDRV_SEQ_IOCTL_SET_QUEUE_INFO	_IOWR('S', 0x35, struct sndrv_seq_queue_info)
 #define SNDRV_SEQ_IOCTL_GET_NAMED_QUEUE	_IOWR('S', 0x36, struct sndrv_seq_queue_info)
-#define SNDRV_SEQ_IOCTL_GET_QUEUE_STATUS	_IOWR('S', 0x40, struct sndrv_seq_queue_status)
+#define SNDRV_SEQ_IOCTL_GET_QUEUE_STATUS _IOWR('S', 0x40, struct sndrv_seq_queue_status)
 #define SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO	_IOWR('S', 0x41, struct sndrv_seq_queue_tempo)
 #define SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO	_IOW ('S', 0x42, struct sndrv_seq_queue_tempo)
 #define SNDRV_SEQ_IOCTL_GET_QUEUE_OWNER	_IOWR('S', 0x43, struct sndrv_seq_queue_owner)
diff -Nru a/include/sound/asound.h b/include/sound/asound.h
--- a/include/sound/asound.h	Sun Apr 28 20:10:37 2002
+++ b/include/sound/asound.h	Sun Apr 28 20:10:37 2002
@@ -30,7 +30,6 @@
 #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	Sun Apr 28 20:10:36 2002
+++ b/include/sound/core.h	Sun Apr 28 20:10:36 2002
@@ -34,6 +34,11 @@
 typedef struct sndrv_xfern snd_xfern_t;
 typedef struct sndrv_xferv snd_xferv_t;
 
+/* forward declarations */
+#ifdef CONFIG_PCI
+struct pci_dev;
+#endif
+
 /* device allocation stuff */
 
 #define SNDRV_DEV_TYPE_RANGE_SIZE		0x1000
@@ -252,7 +257,6 @@
 #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);
@@ -298,26 +302,33 @@
 
 int snd_task_name(struct task_struct *task, char *name, size_t size);
 #ifdef CONFIG_SND_VERBOSE_PRINTK
-int snd_verbose_printk(const char *file, int line, const char *format);
+void snd_verbose_printk(const char *file, int line, const char *format, ...);
+#endif
+#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_SND_VERBOSE_PRINTK)
+void snd_verbose_printd(const char *file, int line, const char *format, ...);
+#endif
+#if defined(CONFIG_SND_DEBUG) && !defined(CONFIG_SND_VERBOSE_PRINTK)
+void snd_printd(const char *format, ...);
 #endif
 
 /* --- */
 
 #ifdef CONFIG_SND_VERBOSE_PRINTK
-#define snd_printk(format, args...) do { \
-	printk(snd_verbose_printk(__FILE__, __LINE__, format) ? format + 3 : format, ##args); \
-} while (0)
+#define snd_printk(args...) \
+	snd_verbose_printk(__FILE__, __LINE__, ##args)
 #else
-#define snd_printk(format, args...) do { \
-	printk(format, ##args); \
-} while (0)
+#define snd_printk(args...) \
+	printk(##args)
 #endif
 
 #ifdef CONFIG_SND_DEBUG
 
 #define __ASTRING__(x) #x
 
-#define snd_printd(format, args...) snd_printk(format, ##args)
+#ifdef CONFIG_SND_VERBOSE_PRINTK
+#define snd_printd(args...) \
+	snd_verbose_printd(__FILE__, __LINE__, ##args)
+#endif
 #define snd_assert(expr, args...) do {\
 	if (!(expr)) {\
 		snd_printk("BUG? (%s) (called from %p)\n", __ASTRING__(expr), __builtin_return_address(0));\
@@ -333,16 +344,16 @@
 
 #else /* !CONFIG_SND_DEBUG */
 
-#define snd_printd(format, args...)	/* nothing */
-#define snd_assert(expr, args...)	/* nothing */
+#define snd_printd(args...)	/* nothing */
+#define snd_assert(args...)	/* nothing */
 #define snd_runtime_check(expr, args...) do { if (!(expr)) { args; } } while (0)
 
 #endif /* CONFIG_SND_DEBUG */
 
 #ifdef CONFIG_SND_DEBUG_DETECT
-#define snd_printdd(format, args...) snd_printk(format, ##args)
+#define snd_printdd(args...) snd_printk(format, ##args)
 #else
-#define snd_printdd(format, args...) /* nothing */
+#define snd_printdd(args...) /* nothing */
 #endif
 
 #define snd_BUG() snd_assert(0, )
diff -Nru a/include/sound/driver.h b/include/sound/driver.h
--- a/include/sound/driver.h	Sun Apr 28 20:10:35 2002
+++ b/include/sound/driver.h	Sun Apr 28 20:10:35 2002
@@ -52,7 +52,7 @@
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0)
 #if defined(__i386__) || defined(__ppc__)
 /*
- * Here a dirty hack for 2.4 kernels.. See kernel/memory.c.
+ * Here a dirty hack for 2.4 kernels.. See sound/core/memory.c.
  */
 #define HACK_PCI_ALLOC_CONSISTENT
 #include <linux/pci.h>
diff -Nru a/include/sound/emu10k1.h b/include/sound/emu10k1.h
--- a/include/sound/emu10k1.h	Sun Apr 28 20:10:33 2002
+++ b/include/sound/emu10k1.h	Sun Apr 28 20:10:33 2002
@@ -231,6 +231,7 @@
 #define A_IOCFG			0x18		/* GPIO on Audigy card (16bits)			*/
 #define A_GPINPUT_MASK		0xff00
 #define A_GPOUTPUT_MASK		0x00ff
+#define A_IOCFG_GPOUT0		0x0040		/* analog/digital? */
 
 #define TIMER			0x1a		/* Timer terminal count register		*/
 						/* NOTE: After the rate is changed, a maximum	*/
@@ -937,7 +938,7 @@
 	int max_cache_pages;			/* max memory size / PAGE_SIZE */
 	void *silent_page;			/* silent page */
 	dma_addr_t silent_page_dmaaddr;
-	volatile unsigned int *ptb_pages;	/* page table pages */
+	volatile u32 *ptb_pages;		/* page table pages */
 	dma_addr_t ptb_pages_dmaaddr;
 	snd_util_memhdr_t *memhdr;		/* page allocation list */
 	emu10k1_memblk_t *reserved_page;	/* reserved page */
diff -Nru a/include/sound/i2c.h b/include/sound/i2c.h
--- a/include/sound/i2c.h	Sun Apr 28 20:10:32 2002
+++ b/include/sound/i2c.h	Sun Apr 28 20:10:32 2002
@@ -82,8 +82,18 @@
 int snd_i2c_device_create(snd_i2c_bus_t *bus, const char *name, unsigned char addr, snd_i2c_device_t **rdevice);
 int snd_i2c_device_free(snd_i2c_device_t *device);
 
-static inline void snd_i2c_lock(snd_i2c_bus_t *bus) { spin_lock(&(bus->master ? bus->master->lock : bus->lock)); }
-static inline void snd_i2c_unlock(snd_i2c_bus_t *bus) { spin_unlock(&(bus->master ? bus->master->lock : bus->lock)); }
+static inline void snd_i2c_lock(snd_i2c_bus_t *bus) {
+	if (bus->master)
+		spin_lock(&bus->master->lock);
+	else
+		spin_lock(&bus->lock);
+}
+static inline void snd_i2c_unlock(snd_i2c_bus_t *bus) {
+	if (bus->master)
+		spin_unlock(&bus->master->lock);
+	else
+		spin_unlock(&bus->lock);
+}
 
 int snd_i2c_sendbytes(snd_i2c_device_t *device, unsigned char *bytes, int count);
 int snd_i2c_readbytes(snd_i2c_device_t *device, unsigned char *bytes, int count);
diff -Nru a/include/sound/minors.h b/include/sound/minors.h
--- a/include/sound/minors.h	Sun Apr 28 20:10:30 2002
+++ b/include/sound/minors.h	Sun Apr 28 20:10:30 2002
@@ -31,8 +31,8 @@
 #define SNDRV_MINOR_TIMER		(1+32)
 #define SNDRV_MINOR_HWDEP		4	/* 4 - 7 */
 #define SNDRV_MINOR_HWDEPS		4
-#define SNDRV_MINOR_RAWMIDI		8	/* 8 - 11 */
-#define SNDRV_MINOR_RAWMIDIS		4
+#define SNDRV_MINOR_RAWMIDI		8	/* 8 - 15 */
+#define SNDRV_MINOR_RAWMIDIS		8
 #define SNDRV_MINOR_PCM_PLAYBACK	16	/* 16 - 23 */
 #define SNDRV_MINOR_PCM_CAPTURE		24	/* 24 - 31 */
 #define SNDRV_MINOR_PCMS		8
diff -Nru a/include/sound/rawmidi.h b/include/sound/rawmidi.h
--- a/include/sound/rawmidi.h	Sun Apr 28 20:10:33 2002
+++ b/include/sound/rawmidi.h	Sun Apr 28 20:10:33 2002
@@ -37,12 +37,12 @@
 typedef struct sndrv_rawmidi_params snd_rawmidi_params_t;
 typedef struct sndrv_rawmidi_status snd_rawmidi_status_t;
 
-#define SNDRV_RAWMIDI_DEVICES	4
+#define SNDRV_RAWMIDI_DEVICES		8
 
-#define SNDRV_RAWMIDI_LFLG_OUTPUT         (1<<0)
-#define SNDRV_RAWMIDI_LFLG_INPUT          (1<<1)
-#define SNDRV_RAWMIDI_LFLG_OPEN           (3<<0)
-#define SNDRV_RAWMIDI_LFLG_APPEND         (1<<2)
+#define SNDRV_RAWMIDI_LFLG_OUTPUT	(1<<0)
+#define SNDRV_RAWMIDI_LFLG_INPUT	(1<<1)
+#define SNDRV_RAWMIDI_LFLG_OPEN		(3<<0)
+#define SNDRV_RAWMIDI_LFLG_APPEND	(1<<2)
 
 typedef struct _snd_rawmidi_runtime snd_rawmidi_runtime_t;
 typedef struct _snd_rawmidi_substream snd_rawmidi_substream_t;
diff -Nru a/include/sound/seq_kernel.h b/include/sound/seq_kernel.h
--- a/include/sound/seq_kernel.h	Sun Apr 28 20:10:39 2002
+++ b/include/sound/seq_kernel.h	Sun Apr 28 20:10:39 2002
@@ -65,7 +65,6 @@
 #define snd_seq_ev_is_fixed_type	sndrv_seq_ev_is_fixed_type      
 #define snd_seq_ev_is_instr_type	sndrv_seq_ev_is_instr_type      
 #define snd_seq_ev_is_variable_type	sndrv_seq_ev_is_variable_type   
-#define snd_seq_ev_is_varipc_type	sndrv_seq_ev_is_varipc_type     
 #define snd_seq_ev_is_reserved		sndrv_seq_ev_is_reserved        
 #define snd_seq_ev_is_direct		sndrv_seq_ev_is_direct          
 #define snd_seq_ev_is_prior		sndrv_seq_ev_is_prior           
@@ -73,7 +72,6 @@
 #define snd_seq_ev_is_fixed		sndrv_seq_ev_is_fixed           
 #define snd_seq_ev_is_variable		sndrv_seq_ev_is_variable        
 #define snd_seq_ev_is_varusr		sndrv_seq_ev_is_varusr          
-#define snd_seq_ev_is_varipc		sndrv_seq_ev_is_varipc          
 #define snd_seq_ev_timestamp_type	sndrv_seq_ev_timestamp_type     
 #define snd_seq_ev_is_tick		sndrv_seq_ev_is_tick            
 #define snd_seq_ev_is_real		sndrv_seq_ev_is_real            
diff -Nru a/include/sound/seq_midi_event.h b/include/sound/seq_midi_event.h
--- a/include/sound/seq_midi_event.h	Sun Apr 28 20:10:34 2002
+++ b/include/sound/seq_midi_event.h	Sun Apr 28 20:10:34 2002
@@ -34,17 +34,21 @@
 	int read;	/* chars read */
 	int type;	/* current event type */
 	unsigned char lastcmd;
+	unsigned char nostat;
 	int bufsize;
-	unsigned char *buf;	/* input buffer */
+	unsigned char *buf; /* input buffer */
 	spinlock_t lock;
 };
 
+#define SND_MIDI_EVENT_NOSTATUS		(1<<0)	/* don't encode MIDI status */
+
 int snd_midi_event_new(int bufsize, snd_midi_event_t **rdev);
 int snd_midi_event_resize_buffer(snd_midi_event_t *dev, int bufsize);
 void snd_midi_event_free(snd_midi_event_t *dev);
 void snd_midi_event_init(snd_midi_event_t *dev);
 void snd_midi_event_reset_encode(snd_midi_event_t *dev);
 void snd_midi_event_reset_decode(snd_midi_event_t *dev);
+void snd_midi_event_no_status(snd_midi_event_t *dev, int on);
 /* encode from byte stream - return number of written bytes if success */
 long snd_midi_event_encode(snd_midi_event_t *dev, unsigned char *buf, long count, snd_seq_event_t *ev);
 int snd_midi_event_encode_byte(snd_midi_event_t *dev, int c, snd_seq_event_t *ev);
diff -Nru a/include/sound/sndmagic.h b/include/sound/sndmagic.h
--- a/include/sound/sndmagic.h	Sun Apr 28 20:10:34 2002
+++ b/include/sound/sndmagic.h	Sun Apr 28 20:10:34 2002
@@ -132,6 +132,9 @@
 #define korg1212_t_magic			0xa15a3800
 #define opl3sa2_t_magic				0xa15a3900
 #define serialmidi_t_magic			0xa15a3a00
+#define sa11xx_uda1341_t_magic			0xa15a3b00
+#define uda1341_t_magic                         0xa15a3c00
+#define l3_client_t_magic                       0xa15a3d00
 
 #else
 
diff -Nru a/include/sound/uda1341.h b/include/sound/uda1341.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/include/sound/uda1341.h	Sun Apr 28 20:10:39 2002
@@ -0,0 +1,237 @@
+/*
+ *  linux/include/linux/l3/uda1341.h
+ *
+ * Philips UDA1341 mixer device driver for ALSA
+ *
+ * Copyright (c) 2002 Tomas Kasparek <tomas.kasparek@seznam.cz>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License.
+ *
+ * History:
+ *
+ * 2002-03-13 Tomas Kasparek Initial release - based on uda1341.h from OSS
+ * 2002-03-30 Tomas Kasparek Proc filesystem support, complete mixer and DSP
+ *                           features support
+ */
+
+/* $Id: uda1341.h,v 1.2 2002/04/17 07:53:22 perex Exp $ */
+
+#define UDA1341_ALSA_NAME "snd-uda1341"
+
+/*
+ * UDA1341 L3 address and command types
+ */
+#define UDA1341_L3ADDR		5
+#define UDA1341_DATA0		(UDA1341_L3ADDR << 2 | 0)
+#define UDA1341_DATA1		(UDA1341_L3ADDR << 2 | 1)
+#define UDA1341_STATUS		(UDA1341_L3ADDR << 2 | 2)
+
+enum uda1341_onoff {
+	OFF=0,
+	ON,
+};
+
+const char *onoff_names[] = {
+	"Off",
+	"On",
+};
+
+enum uda1341_format {
+	I2S=0,
+	LSB16,
+	LSB18,
+	LSB20,
+	MSB,
+	LSB16MSB,
+	LSB18MSB,
+	LSB20MSB,        
+};
+
+const char *format_names[] = {
+	"I2S-bus",
+	"LSB 16bits",
+	"LSB 18bits",
+	"LSB 20bits",
+	"MSB",
+	"in LSB 16bits/out MSB",
+	"in LSB 18bits/out MSB",
+	"in LSB 20bits/out MSB",        
+};
+
+enum uda1341_fs {
+	F512=0,
+	F384,
+	F256,
+	Funused,
+};
+
+const char *fs_names[] = {
+	"512*fs",
+	"384*fs",
+	"256*fs",
+	"Unused - bad value!",
+};
+
+enum uda1341_peak {
+	BEFORE=0,
+	AFTER,
+};
+
+const char *peak_names[] = {
+	"before",
+	"after",
+};
+
+enum uda1341_filter {
+	FLAT=0,
+	MIN,
+	MIN2,
+	MAX,
+};
+
+const char *filter_names[] = {
+	"flat",
+	"min",
+	"min",
+	"max",
+};
+
+const char*bass_values[][16] = {
+	{"0 dB", "0 dB", "0 dB", "0 dB", "0 dB", "0 dB", "0 dB", "0 dB", "0 dB", "0 dB", "0 dB",
+	 "0 dB", "0 dB", "0 dB", "0 dB", "undefined", }, //flat
+	{"0 dB", "2 dB", "4 dB", "6 dB", "8 dB", "10 dB", "12 dB", "14 dB", "16 dB", "18 dB", "18 dB",
+	 "18 dB", "18 dB", "18 dB", "18 dB", "undefined",}, // min
+	{"0 dB", "2 dB", "4 dB", "6 dB", "8 dB", "10 dB", "12 dB", "14 dB", "16 dB", "18 dB", "18 dB",
+	 "18 dB", "18 dB", "18 dB", "18 dB", "undefined",}, // min
+	{"0 dB", "2 dB", "4 dB", "6 dB", "8 dB", "10 dB", "12 dB", "14 dB", "16 dB", "18 dB", "20 dB",
+	 "22 dB", "24 dB", "24 dB", "24 dB", "undefined",}, // max
+};
+
+enum uda1341_mixer {
+	DOUBLE,
+	LINE,
+	MIC,
+	MIXER,
+};
+
+const char *mixer_names[] = {
+	"double differential",
+	"input channel 1 (line in)",
+	"input channel 2 (microphone)",
+	"digital mixer",
+};
+
+enum uda1341_deemp {
+	NONE,
+	D32,
+	D44,
+	D48,
+};
+
+const char *deemp_names[] = {
+	"none",
+	"32 kHz",
+	"44.1 kHz",
+	"48 kHz",        
+};
+
+const char *mic_sens_value[] = {
+	"-3 dB", "0 dB", "3 dB", "9 dB", "15 dB", "21 dB", "27 dB", "not used",
+};
+
+const unsigned short AGC_atime[] = {
+	11, 16, 11, 16, 21, 11, 16, 21,
+};
+
+const unsigned short AGC_dtime[] = {
+	100, 100, 200, 200, 200, 400, 400, 400,
+};
+
+const char *AGC_level[] = {
+	"-9.0", "-11.5", "-15.0", "-17.5",
+};
+
+const char *ig_small_value[] = {
+	"-3.0", "-2.5", "-2.0", "-1.5", "-1.0", "-0.5",
+};
+
+/*
+ * this was computed as peak_value[i] = pow((63-i)*1.42,1.013)
+ *
+ * UDA1341 datasheet on page 21: Peak value (dB) = (Peak level - 63.5)*5*log2
+ * There is an tabel with these values [level]=value: [3]=-90.31, [7]=-84.29
+ * [61]=-2.78, [62] = -1.48, [63] = 0.0
+ * I tried to compute it, but using but even using logarithm with base either 10 or 2
+ * i was'n able to get values in the table from the formula. So I constructed another
+ * formula (see above) to interpolate the values as good as possible. If there is some
+ * mistake, please contact me on tomas.kasparek@seznam.cz. Thanks.
+ * UDA1341TS datasheet is available at:
+ *   http://www-us9.semiconductors.com/acrobat/datasheets/UDA1341TS_3.pdf 
+ */
+const char *peak_value[] = {
+	"-INF dB", "N.A.", "N.A", "90.31 dB", "N.A.", "N.A.", "N.A.", "-84.29 dB",
+	"-82.65 dB", "-81.13 dB", "-79.61 dB", "-78.09 dB", "-76.57 dB", "-75.05 dB", "-73.53 dB",
+	"-72.01 dB", "-70.49 dB", "-68.97 dB", "-67.45 dB", "-65.93 dB", "-64.41 dB", "-62.90 dB",
+	"-61.38 dB", "-59.86 dB", "-58.35 dB", "-56.83 dB", "-55.32 dB", "-53.80 dB", "-52.29 dB",
+	"-50.78 dB", "-49.26 dB", "-47.75 dB", "-46.24 dB", "-44.73 dB", "-43.22 dB", "-41.71 dB",
+	"-40.20 dB", "-38.69 dB", "-37.19 dB", "-35.68 dB", "-34.17 dB", "-32.67 dB", "-31.17 dB",
+	"-29.66 dB", "-28.16 dB", "-26.66 dB", "-25.16 dB", "-23.66 dB", "-22.16 dB", "-20.67 dB",
+	"-19.17 dB", "-17.68 dB", "-16.19 dB", "-14.70 dB", "-13.21 dB", "-11.72 dB", "-10.24 dB",
+	"-8.76 dB", "-7.28 dB", "-5.81 dB", "-4.34 dB", "-2.88 dB", "-1.43 dB", "0.00 dB",
+};
+
+enum uda1341_config {
+	CMD_READ_REG = 0,
+	CMD_RESET,
+	CMD_FS,
+	CMD_FORMAT,
+	CMD_OGAIN,
+	CMD_IGAIN,
+	CMD_DAC,
+	CMD_ADC,
+	CMD_VOLUME,
+	CMD_BASS,
+	CMD_TREBBLE,
+	CMD_PEAK,
+	CMD_DEEMP,
+	CMD_MUTE,        
+	CMD_FILTER,
+	CMD_CH1,
+	CMD_CH2,
+	CMD_MIC,       
+	CMD_MIXER,
+	CMD_AGC,
+	CMD_IG,
+	CMD_AGC_TIME,
+	CMD_AGC_LEVEL,
+	CMD_LAST,
+};
+
+enum write_through {
+	//used in update_bits (write_cfg) to avoid l3_write - just update local copy of regs.
+	REGS_ONLY=0,
+	//update local regs and write value to uda1341 - do l3_write
+	FLUSH,
+};
+
+int __init snd_chip_uda1341_mixer_new(snd_card_t *card, struct l3_client **clnt);
+void __init snd_chip_uda1341_mixer_del(snd_card_t *card);
+
+#ifdef DEBUG_MODE
+#define DEBUG(format, args...)      do{printk(format, ##args);}while(0)
+#else
+#define DEBUG(format, args...)      /* nothing */
+#endif
+
+#ifdef DEBUG_FUNCTION_NAMES
+#define DEBUG_NAME(format, args...)     do{printk(format, ##args);}while(0)
+#else
+#define DEBUG_NAME(format, args...)     /* nothing */
+#endif
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * End:
+ */
diff -Nru a/include/sound/version.h b/include/sound/version.h
--- a/include/sound/version.h	Sun Apr 28 20:10:35 2002
+++ b/include/sound/version.h	Sun Apr 28 20:10:35 2002
@@ -1,3 +1,3 @@
 /* include/version.h.  Generated automatically by configure.  */
-#define CONFIG_SND_VERSION "0.9.0beta12"
-#define CONFIG_SND_DATE " (Mon Mar 18 15:44:40 2002 UTC)"
+#define CONFIG_SND_VERSION "0.9.0rc1"
+#define CONFIG_SND_DATE " (Sun Apr 28 20:04:42 2002 UTC)"
diff -Nru a/include/video/fbcon.h b/include/video/fbcon.h
--- a/include/video/fbcon.h	Sun Apr 28 20:10:32 2002
+++ b/include/video/fbcon.h	Sun Apr 28 20:10:32 2002
@@ -55,8 +55,6 @@
     struct fb_var_screeninfo var;   /* variable infos. yoffset and vmode */
                                     /* are updated by fbcon.c */
     struct fb_cmap cmap;            /* colormap */
-    char *screen_base;              /* pointer to top of virtual screen */    
-                                    /* (virtual address) */
     int visual;
     int type;                       /* see FB_TYPE_* */
     int type_aux;                   /* Interleave for interleaved Planes */
@@ -141,6 +139,8 @@
 	(((s) >> ((p)->bgshift)) & 0x0f)
 #define	attr_bgcol_ec(p,conp) \
 	((conp) ? (((conp)->vc_video_erase_char >> ((p)->bgshift)) & 0x0f) : 0)
+#define attr_fgcol_ec(p,vc) \
+	((vc) ? (((vc)->vc_video_erase_char >> ((p)->fgshift)) & 0x0f) : 0)
 
 /* Monochrome */
 #define attr_bold(p,s) \
diff -Nru a/include/video/sbusfb.h b/include/video/sbusfb.h
--- a/include/video/sbusfb.h	Sun Apr 28 20:10:39 2002
+++ b/include/video/sbusfb.h	Sun Apr 28 20:10:39 2002
@@ -123,8 +123,8 @@
 	void (*setcurshape)(struct fb_info_sbusfb *);
 	void (*setcursormap)(struct fb_info_sbusfb *, unsigned char *, unsigned char *, unsigned char *);
 	void (*loadcmap)(struct fb_info_sbusfb *, struct display *, int, int);
-	void (*blank)(struct fb_info_sbusfb *);
-	void (*unblank)(struct fb_info_sbusfb *);
+	int (*blank)(struct fb_info_sbusfb *);
+	int (*unblank)(struct fb_info_sbusfb *);
 	void (*margins)(struct fb_info_sbusfb *, struct display *, int, int);
 	void (*reset)(struct fb_info_sbusfb *);
 	void (*fill)(struct fb_info_sbusfb *, struct display *, int, int, unsigned short *);
diff -Nru a/ipc/sem.c b/ipc/sem.c
--- a/ipc/sem.c	Sun Apr 28 20:10:35 2002
+++ b/ipc/sem.c	Sun Apr 28 20:10:35 2002
@@ -1176,8 +1176,10 @@
 	}
 
 	undo_list = current->sysvsem.undo_list;
-	if ((undo_list == NULL) || (atomic_read(&undo_list->refcnt) != 1))
+	if ((undo_list == NULL) || (atomic_read(&undo_list->refcnt) != 1)) {
+		unlock_kernel();
 		return;
+	}
 
 	/* There's no need to hold the semundo list lock, as current
          * is the last task exiting for this undo list.
diff -Nru a/kernel/exit.c b/kernel/exit.c
--- a/kernel/exit.c	Sun Apr 28 20:10:34 2002
+++ b/kernel/exit.c	Sun Apr 28 20:10:34 2002
@@ -37,7 +37,7 @@
 	list_del(&p->thread_group);
 	p->pid = 0;
 	proc_dentry = p->proc_dentry;
-	if (unlikely(proc_dentry)) {
+	if (unlikely(proc_dentry != NULL)) {
 		spin_lock(&dcache_lock);
 		if (!list_empty(&proc_dentry->d_hash)) {
 			dget_locked(proc_dentry);
@@ -47,7 +47,7 @@
 		spin_unlock(&dcache_lock);
 	}
 	write_unlock_irq(&tasklist_lock);
-	if (unlikely(proc_dentry)) {
+	if (unlikely(proc_dentry != NULL)) {
 		shrink_dcache_parent(proc_dentry);
 		dput(proc_dentry);
 	}
@@ -687,7 +687,7 @@
 	return retval;
 }
 
-#if !defined(__alpha__) && !defined(__ia64__)
+#if !defined(__alpha__) && !defined(__ia64__) && !defined(__arm__)
 
 /*
  * sys_waitpid() remains for compatibility. waitpid() should be
diff -Nru a/kernel/fork.c b/kernel/fork.c
--- a/kernel/fork.c	Sun Apr 28 20:10:31 2002
+++ b/kernel/fork.c	Sun Apr 28 20:10:31 2002
@@ -367,6 +367,13 @@
 	if (clone_flags & CLONE_VM) {
 		atomic_inc(&oldmm->mm_users);
 		mm = oldmm;
+		/*
+		 * There are cases where the PTL is held to ensure no
+		 * new threads start up in user mode using an mm, which
+		 * allows optimizing out ipis; the tlb_gather_mmu code
+		 * is an example.
+		 */
+		spin_unlock_wait(&oldmm->page_table_lock);
 		goto good_mm;
 	}
 
@@ -386,11 +393,6 @@
 
 	if (retval)
 		goto free_pt;
-
-	/*
-	 * child gets a private LDT (if there was an LDT in the parent)
-	 */
-	copy_segments(tsk, mm);
 
 	if (init_new_context(tsk,mm))
 		goto free_pt;
diff -Nru a/kernel/ksyms.c b/kernel/ksyms.c
--- a/kernel/ksyms.c	Sun Apr 28 20:10:30 2002
+++ b/kernel/ksyms.c	Sun Apr 28 20:10:30 2002
@@ -167,7 +167,8 @@
 EXPORT_SYMBOL(d_lookup);
 EXPORT_SYMBOL(__d_path);
 EXPORT_SYMBOL(mark_buffer_dirty);
-EXPORT_SYMBOL(set_buffer_async_io); /* for reiserfs_writepage */
+EXPORT_SYMBOL(end_buffer_io_sync);
+EXPORT_SYMBOL(set_buffer_async_io);
 EXPORT_SYMBOL(__mark_buffer_dirty);
 EXPORT_SYMBOL(__mark_inode_dirty);
 EXPORT_SYMBOL(get_empty_filp);
@@ -498,7 +499,7 @@
 EXPORT_SYMBOL(vsnprintf);
 EXPORT_SYMBOL(vsscanf);
 EXPORT_SYMBOL(kdevname);
-EXPORT_SYMBOL(bdevname);
+EXPORT_SYMBOL(__bdevname);
 EXPORT_SYMBOL(cdevname);
 EXPORT_SYMBOL(simple_strtoul);
 EXPORT_SYMBOL(system_utsname);	/* UTS data */
diff -Nru a/kernel/sched.c b/kernel/sched.c
--- a/kernel/sched.c	Sun Apr 28 20:10:32 2002
+++ b/kernel/sched.c	Sun Apr 28 20:10:32 2002
@@ -1760,7 +1760,7 @@
 	current->cpus_allowed = -1L;
 
 	for (cpu = 0; cpu < smp_num_cpus; cpu++)
-		while (!cpu_rq(cpu)->migration_thread)
+		while (!cpu_rq(cpu_logical_map(cpu))->migration_thread)
 			schedule_timeout(2);
 }
 #endif
diff -Nru a/kernel/signal.c b/kernel/signal.c
--- a/kernel/signal.c	Sun Apr 28 20:10:35 2002
+++ b/kernel/signal.c	Sun Apr 28 20:10:35 2002
@@ -1273,7 +1273,7 @@
 #endif /* __sparc__ */
 #endif
 
-#if !defined(__alpha__) && !defined(__ia64__)
+#if !defined(__alpha__) && !defined(__ia64__) && !defined(__arm__)
 /*
  * For backwards compatibility.  Functionality superseded by sigprocmask.
  */
@@ -1301,7 +1301,8 @@
 }
 #endif /* !defined(__alpha__) */
 
-#if !defined(__alpha__) && !defined(__ia64__) && !defined(__mips__)
+#if !defined(__alpha__) && !defined(__ia64__) && !defined(__mips__) && \
+    !defined(__arm__)
 /*
  * For backwards compatibility.  Functionality superseded by sigaction.
  */
@@ -1318,4 +1319,4 @@
 
 	return ret ? ret : (unsigned long)old_sa.sa.sa_handler;
 }
-#endif /* !alpha && !__ia64__ && !defined(__mips__) */
+#endif /* !alpha && !__ia64__ && !defined(__mips__) && !defined(__arm__) */
diff -Nru a/lib/radix-tree.c b/lib/radix-tree.c
--- a/lib/radix-tree.c	Sun Apr 28 20:10:32 2002
+++ b/lib/radix-tree.c	Sun Apr 28 20:10:32 2002
@@ -50,20 +50,10 @@
 static kmem_cache_t *radix_tree_node_cachep;
 static mempool_t *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;
+	return mempool_alloc(radix_tree_node_pool, root->gfp_mask);
 }
 
 static inline void
diff -Nru a/lib/vsprintf.c b/lib/vsprintf.c
--- a/lib/vsprintf.c	Sun Apr 28 20:10:35 2002
+++ b/lib/vsprintf.c	Sun Apr 28 20:10:35 2002
@@ -172,50 +172,41 @@
 	if (!(type&(ZEROPAD+LEFT))) {
 		while(size-->0) {
 			if (buf <= end)
-				*buf = ' ';
-			++buf;
+				*buf++ = ' ';
 		}
 	}
 	if (sign) {
 		if (buf <= end)
-			*buf = sign;
-		++buf;
+			*buf++ = sign;
 	}
 	if (type & SPECIAL) {
 		if (base==8) {
 			if (buf <= end)
-				*buf = '0';
-			++buf;
+				*buf++ = '0';
 		} else if (base==16) {
 			if (buf <= end)
-				*buf = '0';
-			++buf;
+				*buf++ = '0';
 			if (buf <= end)
-				*buf = digits[33];
-			++buf;
+				*buf++ = digits[33];
 		}
 	}
 	if (!(type & LEFT)) {
 		while (size-- > 0) {
 			if (buf <= end)
-				*buf = c;
-			++buf;
+				*buf++ = c;
 		}
 	}
 	while (i < precision--) {
 		if (buf <= end)
-			*buf = '0';
-		++buf;
+			*buf++ = '0';
 	}
 	while (i-- > 0) {
 		if (buf <= end)
-			*buf = tmp[i];
-		++buf;
+			*buf++ = tmp[i];
 	}
 	while (size-- > 0) {
 		if (buf <= end)
-			*buf = ' ';
-		++buf;
+			*buf++ = ' ';
 	}
 	return buf;
 }
@@ -247,6 +238,10 @@
 				/* 'z' support added 23/7/1999 S.H.    */
 				/* 'z' changed to 'Z' --davidm 1/25/99 */
 
+	/* Enforce absolute minimum size: one character + the trailing 0 */
+	if (size < 2)
+		return 0;
+
 	str = buf;
 	end = buf + size - 1;
 
@@ -258,8 +253,7 @@
 	for (; *fmt ; ++fmt) {
 		if (*fmt != '%') {
 			if (str <= end)
-				*str = *fmt;
-			++str;
+				*str++ = *fmt;
 			continue;
 		}
 
@@ -323,18 +317,15 @@
 				if (!(flags & LEFT)) {
 					while (--field_width > 0) {
 						if (str <= end)
-							*str = ' ';
-						++str;
+							*str++ = ' ';
 					}
 				}
 				c = (unsigned char) va_arg(args, int);
 				if (str <= end)
-					*str = c;
-				++str;
+					*str++ = c;
 				while (--field_width > 0) {
 					if (str <= end)
-						*str = ' ';
-					++str;
+						*str++ = ' ';
 				}
 				continue;
 
@@ -348,19 +339,16 @@
 				if (!(flags & LEFT)) {
 					while (len < field_width--) {
 						if (str <= end)
-							*str = ' ';
-						++str;
+							*str++ = ' ';
 					}
 				}
 				for (i = 0; i < len; ++i) {
 					if (str <= end)
-						*str = *s;
-					++str; ++s;
+						*str++ = *s++;
 				}
 				while (len < field_width--) {
 					if (str <= end)
-						*str = ' ';
-					++str;
+						*str++ = ' ';
 				}
 				continue;
 
@@ -392,8 +380,7 @@
 
 			case '%':
 				if (str <= end)
-					*str = '%';
-				++str;
+					*str++ = '%';
 				continue;
 
 				/* integer number formats - set up the flags and "break" */
@@ -415,12 +402,10 @@
 
 			default:
 				if (str <= end)
-					*str = '%';
-				++str;
+					*str++ = '%';
 				if (*fmt) {
 					if (str <= end)
-						*str = *fmt;
-					++str;
+						*str++ = *fmt;
 				} else {
 					--fmt;
 				}
diff -Nru a/mm/highmem.c b/mm/highmem.c
--- a/mm/highmem.c	Sun Apr 28 20:10:31 2002
+++ b/mm/highmem.c	Sun Apr 28 20:10:31 2002
@@ -423,7 +423,7 @@
 		}
 	}
 
-	bio->bi_dev = (*bio_orig)->bi_dev;
+	bio->bi_bdev = (*bio_orig)->bi_bdev;
 	bio->bi_sector = (*bio_orig)->bi_sector;
 	bio->bi_rw = (*bio_orig)->bi_rw;
 
diff -Nru a/mm/memory.c b/mm/memory.c
--- a/mm/memory.c	Sun Apr 28 20:10:36 2002
+++ b/mm/memory.c	Sun Apr 28 20:10:36 2002
@@ -839,8 +839,8 @@
 		address = (address + PGDIR_SIZE) & PGDIR_MASK;
 		dir++;
 	} while (address && (address < end));
-	spin_unlock(&mm->page_table_lock);
 	flush_tlb_range(vma, beg, end);
+	spin_unlock(&mm->page_table_lock);
 	return error;
 }
 
@@ -922,8 +922,8 @@
 		from = (from + PGDIR_SIZE) & PGDIR_MASK;
 		dir++;
 	} while (from && (from < end));
-	spin_unlock(&mm->page_table_lock);
 	flush_tlb_range(vma, beg, end);
+	spin_unlock(&mm->page_table_lock);
 	return error;
 }
 
diff -Nru a/mm/mempool.c b/mm/mempool.c
--- a/mm/mempool.c	Sun Apr 28 20:10:31 2002
+++ b/mm/mempool.c	Sun Apr 28 20:10:31 2002
@@ -13,6 +13,28 @@
 #include <linux/module.h>
 #include <linux/mempool.h>
 
+static void add_element(mempool_t *pool, void *element)
+{
+	BUG_ON(pool->curr_nr >= pool->min_nr);
+	pool->elements[pool->curr_nr++] = element;
+}
+
+static void *remove_element(mempool_t *pool)
+{
+	BUG_ON(pool->curr_nr <= 0);
+	return pool->elements[--pool->curr_nr];
+}
+
+static void free_pool(mempool_t *pool)
+{
+	while (pool->curr_nr) {
+		void *element = remove_element(pool);
+		pool->free(element, pool->pool_data);
+	}
+	kfree(pool->elements);
+	kfree(pool);
+}
+
 /**
  * mempool_create - create a memory pool
  * @min_nr:    the minimum number of elements guaranteed to be
@@ -25,27 +47,25 @@
  * memory pool. The pool can be used from the mempool_alloc and mempool_free
  * functions. This function might sleep. Both the alloc_fn() and the free_fn()
  * functions might sleep - as long as the mempool_alloc function is not called
- * from IRQ contexts. The element allocated by alloc_fn() must be able to
- * hold a struct list_head. (8 bytes on x86.)
+ * from IRQ contexts.
  */
 mempool_t * mempool_create(int min_nr, mempool_alloc_t *alloc_fn,
 				mempool_free_t *free_fn, void *pool_data)
 {
 	mempool_t *pool;
-	int i;
-
-	BUG_ON(!alloc_fn);
-	BUG_ON(!free_fn);
 
 	pool = kmalloc(sizeof(*pool), GFP_KERNEL);
 	if (!pool)
 		return NULL;
 	memset(pool, 0, sizeof(*pool));
-
+	pool->elements = kmalloc(min_nr * sizeof(void *), GFP_KERNEL);
+	if (!pool->elements) {
+		kfree(pool);
+		return NULL;
+	}
 	spin_lock_init(&pool->lock);
 	pool->min_nr = min_nr;
 	pool->pool_data = pool_data;
-	INIT_LIST_HEAD(&pool->elements);
 	init_waitqueue_head(&pool->wait);
 	pool->alloc = alloc_fn;
 	pool->free = free_fn;
@@ -53,27 +73,15 @@
 	/*
 	 * First pre-allocate the guaranteed number of buffers.
 	 */
-	for (i = 0; i < min_nr; i++) {
+	while (pool->curr_nr < pool->min_nr) {
 		void *element;
-		struct list_head *tmp;
-		element = pool->alloc(GFP_KERNEL, pool->pool_data);
 
+		element = pool->alloc(GFP_KERNEL, pool->pool_data);
 		if (unlikely(!element)) {
-			/*
-			 * Not enough memory - free the allocated ones
-			 * and return:
-			 */
-			list_for_each(tmp, &pool->elements) {
-				element = tmp;
-				pool->free(element, pool->pool_data);
-			}
-			kfree(pool);
-
+			free_pool(pool);
 			return NULL;
 		}
-		tmp = element;
-		list_add(tmp, &pool->elements);
-		pool->curr_nr++;
+		add_element(pool, element);
 	}
 	return pool;
 }
@@ -94,53 +102,54 @@
  * while this function is running. mempool_alloc() & mempool_free()
  * might be called (eg. from IRQ contexts) while this function executes.
  */
-void mempool_resize(mempool_t *pool, int new_min_nr, int gfp_mask)
+int mempool_resize(mempool_t *pool, int new_min_nr, int gfp_mask)
 {
-	int delta;
 	void *element;
+	void **new_elements;
 	unsigned long flags;
-	struct list_head *tmp;
 
-	if (new_min_nr <= 0)
-		BUG();
+	BUG_ON(new_min_nr <= 0);
 
 	spin_lock_irqsave(&pool->lock, flags);
 	if (new_min_nr < pool->min_nr) {
-		pool->min_nr = new_min_nr;
-		/*
-		 * Free possible excess elements.
-		 */
-		while (pool->curr_nr > pool->min_nr) {
-			tmp = pool->elements.next;
-			if (tmp == &pool->elements)
-				BUG();
-			list_del(tmp);
-			element = tmp;
-			pool->curr_nr--;
+		while (pool->curr_nr > new_min_nr) {
+			element = remove_element(pool);
 			spin_unlock_irqrestore(&pool->lock, flags);
-
 			pool->free(element, pool->pool_data);
-
 			spin_lock_irqsave(&pool->lock, flags);
 		}
-		spin_unlock_irqrestore(&pool->lock, flags);
-		return;
+		pool->min_nr = new_min_nr;
+		goto out_unlock;
 	}
-	delta = new_min_nr - pool->min_nr;
-	pool->min_nr = new_min_nr;
 	spin_unlock_irqrestore(&pool->lock, flags);
 
-	/*
-	 * We refill the pool up to the new treshold - but we dont
-	 * (cannot) guarantee that the refill succeeds.
-	 */
-	while (delta) {
+	/* Grow the pool */
+	new_elements = kmalloc(new_min_nr * sizeof(*new_elements), gfp_mask);
+	if (!new_elements)
+		return -ENOMEM;
+
+	spin_lock_irqsave(&pool->lock, flags);
+	memcpy(new_elements, pool->elements,
+			pool->curr_nr * sizeof(*new_elements));
+	kfree(pool->elements);
+	pool->elements = new_elements;
+	pool->min_nr = new_min_nr;
+
+	while (pool->curr_nr < pool->min_nr) {
+		spin_unlock_irqrestore(&pool->lock, flags);
 		element = pool->alloc(gfp_mask, pool->pool_data);
 		if (!element)
-			break;
-		mempool_free(element, pool);
-		delta--;
+			goto out;
+		spin_lock_irqsave(&pool->lock, flags);
+		if (pool->curr_nr < pool->min_nr)
+			add_element(pool, element);
+		else
+			kfree(element);		/* Raced */
 	}
+out_unlock:
+	spin_unlock_irqrestore(&pool->lock, flags);
+out:
+	return 0;
 }
 
 /**
@@ -149,27 +158,14 @@
  *             mempool_create().
  *
  * this function only sleeps if the free_fn() function sleeps. The caller
- * has to guarantee that no mempool_alloc() nor mempool_free() happens in
- * this pool when calling this function.
+ * has to guarantee that all elements have been returned to the pool (ie:
+ * freed) prior to calling mempool_destroy().
  */
 void mempool_destroy(mempool_t *pool)
 {
-	void *element;
-	struct list_head *head, *tmp;
-
-	if (!pool)
-		return;
-
-	head = &pool->elements;
-	for (tmp = head->next; tmp != head; ) {
-		element = tmp;
-		tmp = tmp->next;
-		pool->free(element, pool->pool_data);
-		pool->curr_nr--;
-	}
-	if (pool->curr_nr)
-		BUG();
-	kfree(pool);
+	if (pool->curr_nr != pool->min_nr)
+		BUG();		/* There were outstanding elements */
+	free_pool(pool);
 }
 
 /**
@@ -187,7 +183,6 @@
 {
 	void *element;
 	unsigned long flags;
-	struct list_head *tmp;
 	int curr_nr;
 	DECLARE_WAITQUEUE(wait, current);
 	int gfp_nowait = gfp_mask & ~(__GFP_WAIT | __GFP_IO);
@@ -214,10 +209,7 @@
 
 	spin_lock_irqsave(&pool->lock, flags);
 	if (likely(pool->curr_nr)) {
-		tmp = pool->elements.next;
-		list_del(tmp);
-		element = tmp;
-		pool->curr_nr--;
+		element = remove_element(pool);
 		spin_unlock_irqrestore(&pool->lock, flags);
 		return element;
 	}
@@ -260,8 +252,7 @@
 	if (pool->curr_nr < pool->min_nr) {
 		spin_lock_irqsave(&pool->lock, flags);
 		if (pool->curr_nr < pool->min_nr) {
-			list_add(element, &pool->elements);
-			pool->curr_nr++;
+			add_element(pool, element);
 			spin_unlock_irqrestore(&pool->lock, flags);
 			wake_up(&pool->wait);
 			return;
@@ -276,4 +267,3 @@
 EXPORT_SYMBOL(mempool_destroy);
 EXPORT_SYMBOL(mempool_alloc);
 EXPORT_SYMBOL(mempool_free);
-
diff -Nru a/mm/mprotect.c b/mm/mprotect.c
--- a/mm/mprotect.c	Sun Apr 28 20:10:35 2002
+++ b/mm/mprotect.c	Sun Apr 28 20:10:35 2002
@@ -92,8 +92,8 @@
 		start = (start + PGDIR_SIZE) & PGDIR_MASK;
 		dir++;
 	} while (start && (start < end));
-	spin_unlock(&current->mm->page_table_lock);
 	flush_tlb_range(vma, beg, end);
+	spin_unlock(&current->mm->page_table_lock);
 	return;
 }
 
diff -Nru a/mm/mremap.c b/mm/mremap.c
--- a/mm/mremap.c	Sun Apr 28 20:10:37 2002
+++ b/mm/mremap.c	Sun Apr 28 20:10:37 2002
@@ -81,8 +81,9 @@
 	return error;
 }
 
-static int move_one_page(struct mm_struct *mm, unsigned long old_addr, unsigned long new_addr)
+static int move_one_page(struct vm_area_struct *vma, unsigned long old_addr, unsigned long new_addr)
 {
+	struct mm_struct *mm = vma->vm_mm;
 	int error = 0;
 	pte_t *src, *dst;
 
@@ -94,6 +95,7 @@
 		pte_unmap_nested(src);
 		pte_unmap(dst);
 	}
+	flush_tlb_page(vma, old_addr);
 	spin_unlock(&mm->page_table_lock);
 	return error;
 }
@@ -101,7 +103,6 @@
 static int move_page_tables(struct vm_area_struct *vma,
 	unsigned long new_addr, unsigned long old_addr, unsigned long len)
 {
-	struct mm_struct *mm = vma->vm_mm;
 	unsigned long offset = len;
 
 	flush_cache_range(vma, old_addr, old_addr + len);
@@ -113,10 +114,9 @@
 	 */
 	while (offset) {
 		offset -= PAGE_SIZE;
-		if (move_one_page(mm, old_addr + offset, new_addr + offset))
+		if (move_one_page(vma, old_addr + offset, new_addr + offset))
 			goto oops_we_failed;
 	}
-	flush_tlb_range(vma, old_addr, old_addr + len);
 	return 0;
 
 	/*
@@ -129,7 +129,7 @@
 oops_we_failed:
 	flush_cache_range(vma, new_addr, new_addr + len);
 	while ((offset += PAGE_SIZE) < len)
-		move_one_page(mm, new_addr + offset, old_addr + offset);
+		move_one_page(vma, new_addr + offset, old_addr + offset);
 	zap_page_range(vma, new_addr, len);
 	return -1;
 }
diff -Nru a/mm/readahead.c b/mm/readahead.c
--- a/mm/readahead.c	Sun Apr 28 20:10:31 2002
+++ b/mm/readahead.c	Sun Apr 28 20:10:31 2002
@@ -25,9 +25,6 @@
  * has a zero value of ra_sectors.
  */
 
-#define VM_MAX_READAHEAD	128	/* kbytes */
-#define VM_MIN_READAHEAD	16	/* kbytes (includes current page) */
-
 /*
  * Return max readahead size for this inode in number-of-pages.
  */
@@ -35,10 +32,9 @@
 {
 	unsigned blk_ra_kbytes = 0;
 
-	blk_ra_kbytes = blk_get_readahead(inode->i_dev) / 2;
-	if (blk_ra_kbytes < VM_MIN_READAHEAD)
-		blk_ra_kbytes = VM_MAX_READAHEAD;
-
+	if (inode->i_sb->s_bdev) {
+		blk_ra_kbytes = blk_get_readahead(inode->i_sb->s_bdev) / 2;
+	}
 	return blk_ra_kbytes >> (PAGE_CACHE_SHIFT - 10);
 }
 
@@ -61,11 +57,12 @@
  * size:	Number of pages in that read
  *              Together, these form the "current window".
  *              Together, start and size represent the `readahead window'.
- * next_size:   The number of pages to read when we get the next readahead miss.
+ * next_size:   The number of pages to read on the next readahead miss.
  * prev_page:   The page which the readahead algorithm most-recently inspected.
- *              prev_page is mainly an optimisation: if page_cache_readahead sees
- *              that it is again being called for a page which it just looked at,
- *              it can return immediately without making any state changes.
+ *              prev_page is mainly an optimisation: if page_cache_readahead
+ *		sees that it is again being called for a page which it just
+ *		looked at, it can return immediately without making any state
+ *		changes.
  * ahead_start,
  * ahead_size:  Together, these form the "ahead window".
  *
@@ -87,38 +84,39 @@
  *           ahead window.
  *
  * A `readahead hit' occurs when a read request is made against a page which is
- * inside the current window.  Hits are good, and the window size (next_size) is
- * grown aggressively when hits occur.  Two pages are added to the next window
- * size on each hit, which will end up doubling the next window size by the time
- * I/O is submitted for it.
- *
- * If readahead hits are more sparse (say, the application is only reading every
- * second page) then the window will build more slowly.
- *
- * On a readahead miss (the application seeked away) the readahead window is shrunk
- * by 25%.  We don't want to drop it too aggressively, because it's a good assumption
- * that an application which has built a good readahead window will continue to
- * perform linear reads.  Either at the new file position, or at the old one after
- * another seek.
- *
- * There is a special-case: if the first page which the application tries to read
- * happens to be the first page of the file, it is assumed that a linear read is
- * about to happen and the window is immediately set to half of the device maximum.
+ * inside the current window.  Hits are good, and the window size (next_size)
+ * is grown aggressively when hits occur.  Two pages are added to the next
+ * window size on each hit, which will end up doubling the next window size by
+ * the time I/O is submitted for it.
+ *
+ * If readahead hits are more sparse (say, the application is only reading
+ * every second page) then the window will build more slowly.
+ *
+ * On a readahead miss (the application seeked away) the readahead window is
+ * shrunk by 25%.  We don't want to drop it too aggressively, because it is a
+ * good assumption that an application which has built a good readahead window
+ * will continue to perform linear reads.  Either at the new file position, or
+ * at the old one after another seek.
+ *
+ * There is a special-case: if the first page which the application tries to
+ * read happens to be the first page of the file, it is assumed that a linear
+ * read is about to happen and the window is immediately set to half of the
+ * device maximum.
  * 
  * A page request at (start + size) is not a miss at all - it's just a part of
  * sequential file reading.
  *
  * This function is to be called for every page which is read, rather than when
- * it is time to perform readahead.  This is so the readahead algorithm can centrally
- * work out the access patterns.  This could be costly with many tiny read()s, so
- * we specifically optimise for that case with prev_page.
+ * it is time to perform readahead.  This is so the readahead algorithm can
+ * centrally work out the access patterns.  This could be costly with many tiny
+ * read()s, so we specifically optimise for that case with prev_page.
  */
 
 /*
- * do_page_cache_readahead actually reads a chunk of disk.  It allocates all the
- * pages first, then submits them all for I/O. This avoids the very bad behaviour
- * which would occur if page allocations are causing VM writeback.  We really don't
- * want to intermingle reads and writes like that.
+ * do_page_cache_readahead actually reads a chunk of disk.  It allocates all
+ * the pages first, then submits them all for I/O. This avoids the very bad
+ * behaviour which would occur if page allocations are causing VM writeback.
+ * We really don't want to intermingle reads and writes like that.
  */
 void do_page_cache_readahead(struct file *file,
 			unsigned long offset, unsigned long nr_to_read)
@@ -208,8 +206,10 @@
 			goto out;
 	}
 
-	min = get_min_readahead(inode);
 	max = get_max_readahead(inode);
+	if (max == 0)
+		goto out;	/* No readahead */
+	min = get_min_readahead(inode);
 
 	if (ra->next_size == 0 && offset == 0) {
 		/*
@@ -231,7 +231,8 @@
 		ra->next_size += 2;
 	} else {
 		/*
-		 * A miss - lseek, pread, etc.  Shrink the readahead window by 25%.
+		 * A miss - lseek, pread, etc.  Shrink the readahead
+		 * window by 25%.
 		 */
 		ra->next_size -= ra->next_size / 4;
 		if (ra->next_size < min)
@@ -331,13 +332,14 @@
  * the VM.
  *
  * We shrink the readahead window by three pages.  This is because we grow it
- * by two pages on a readahead hit.  Theory being that the readahead window size
- * will stabilise around the maximum level at which there isn't any thrashing.
+ * by two pages on a readahead hit.  Theory being that the readahead window
+ * size will stabilise around the maximum level at which there isn't any
+ * thrashing.
  */
 void handle_ra_thrashing(struct file *file)
 {
-	struct inode *inode = file->f_dentry->d_inode;
-	const unsigned long min = get_min_readahead(inode);
+	struct address_space * mapping = file->f_dentry->d_inode->i_mapping;
+	const unsigned long min = get_min_readahead(mapping->host);
 
 	file->f_ra.next_size -= 3;
 	if (file->f_ra.next_size < min)
diff -Nru a/net/irda/ircomm/Makefile b/net/irda/ircomm/Makefile
--- a/net/irda/ircomm/Makefile	Sun Apr 28 20:10:31 2002
+++ b/net/irda/ircomm/Makefile	Sun Apr 28 20:10:31 2002
@@ -4,6 +4,8 @@
 
 O_TARGET := ircomm_and_tty.o
 
+export-objs := ircomm_core.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
 
diff -Nru a/net/irda/ircomm/ircomm_core.c b/net/irda/ircomm/ircomm_core.c
--- a/net/irda/ircomm/ircomm_core.c	Sun Apr 28 20:10:30 2002
+++ b/net/irda/ircomm/ircomm_core.c	Sun Apr 28 20:10:30 2002
@@ -134,6 +134,8 @@
 	return self;
 }
 
+EXPORT_SYMBOL(ircomm_open);
+
 /*
  * Function ircomm_close_instance (self)
  *
@@ -187,6 +189,8 @@
         return __ircomm_close(self);
 }
 
+EXPORT_SYMBOL(ircomm_close);
+
 /*
  * Function ircomm_connect_request (self, service_type)
  *
@@ -217,6 +221,8 @@
 	return ret;
 }
 
+EXPORT_SYMBOL(ircomm_connect_request);
+
 /*
  * Function ircomm_connect_indication (self, qos, skb)
  *
@@ -269,6 +275,8 @@
 	return ret;
 }	
 
+EXPORT_SYMBOL(ircomm_connect_response);
+
 /*
  * Function connect_confirm (self, skb)
  *
@@ -312,6 +320,8 @@
 	return ret;
 }
 
+EXPORT_SYMBOL(ircomm_data_request);
+
 /*
  * Function ircomm_data_indication (self, skb)
  *
@@ -387,6 +397,8 @@
 	return ret;
 }
 
+EXPORT_SYMBOL(ircomm_control_request);
+
 /*
  * Function ircomm_control_indication (self, skb)
  *
@@ -438,6 +450,8 @@
 	return ret;
 }
 
+EXPORT_SYMBOL(ircomm_disconnect_request);
+
 /*
  * Function disconnect_indication (self, skb)
  *
@@ -479,6 +493,8 @@
 	irttp_flow_request(self->tsap, flow);
 }
 
+EXPORT_SYMBOL(ircomm_flow_request);
+
 #ifdef CONFIG_PROC_FS
 /*
  * Function ircomm_proc_read (buf, start, offset, len, unused)
@@ -542,4 +558,3 @@
 	ircomm_cleanup();
 }
 #endif /* MODULE */
-
diff -Nru a/scripts/Configure b/scripts/Configure
--- a/scripts/Configure	Sun Apr 28 20:10:32 2002
+++ b/scripts/Configure	Sun Apr 28 20:10:32 2002
@@ -541,7 +541,10 @@
 if [ ! -f .config ]; then
   DEFAULTS=/etc/kernel-config
   if [ ! -f $DEFAULTS ]; then
-    DEFAULTS=arch/$ARCH/defconfig
+    DEFAULTS=/boot/config-`uname -r`
+    if [ ! -f $DEFAULTS ]; then
+      DEFAULTS=arch/$ARCH/defconfig
+    fi
   fi
 fi
 
diff -Nru a/scripts/patch-kernel b/scripts/patch-kernel
--- a/scripts/patch-kernel	Sun Apr 28 20:10:34 2002
+++ b/scripts/patch-kernel	Sun Apr 28 20:10:34 2002
@@ -46,6 +46,15 @@
 patchdir=${2-.}
 stopvers=${3-imnotaversion}
 
+if [ "$1" = -h -o "$1" = --help -o ! -r "$sourcedir/Makefile" ]; then
+cat << USAGE
+usage: patch-kernel [-h] [ sourcedir [ patchdir [ stopversion ] [ -acxx ] ] ]
+  The source directory defaults to /usr/src/linux, and
+  the patch directory defaults to the current directory.
+USAGE
+exit 1
+fi
+
 # See if we have any -ac options
 for PARM in $*
 do
diff -Nru a/scripts/tkparse.c b/scripts/tkparse.c
--- a/scripts/tkparse.c	Sun Apr 28 20:10:30 2002
+++ b/scripts/tkparse.c	Sun Apr 28 20:10:30 2002
@@ -77,7 +77,7 @@
  * Find index of a specyfic variable in the symbol table.
  * Create a new entry if it does not exist yet.
  */
-#define VARTABLE_SIZE 2048
+#define VARTABLE_SIZE 4096
 struct variable vartable[VARTABLE_SIZE];
 int max_varnum = 0;
 
diff -Nru a/sound/Config.in b/sound/Config.in
--- a/sound/Config.in	Sun Apr 28 20:10:35 2002
+++ b/sound/Config.in	Sun Apr 28 20:10:35 2002
@@ -28,5 +28,8 @@
 if [ "$CONFIG_SND" != "n" -a "$CONFIG_PPC" = "y" ]; then
   source sound/ppc/Config.in
 fi
+if [ "$CONFIG_SND" != "n" -a "$CONFIG_ARM" = "y" ]; then
+  source sound/arm/Config.in
+fi
 
 endmenu
diff -Nru a/sound/Makefile b/sound/Makefile
--- a/sound/Makefile	Sun Apr 28 20:10:34 2002
+++ b/sound/Makefile	Sun Apr 28 20:10:34 2002
@@ -14,7 +14,7 @@
   obj-y += oss/sounddrivers.o
 endif
 
-_subdirs := core i2c drivers isa pci ppc synth
+_subdirs := core i2c drivers isa pci ppc arm synth
 
 subdir-$(CONFIG_SND) += $(_subdirs)
 
@@ -44,6 +44,7 @@
 	   pci/trident/_trident.o \
 	   pci/ymfpci/_ymfpci.o
   obj-y += ppc/ppc.o
+  obj-y += arm/arm.o
   obj-y += synth/synth.o \
 	   synth/emux/_emux.o
   obj-y += last.o
diff -Nru a/sound/arm/Config.help b/sound/arm/Config.help
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/arm/Config.help	Sun Apr 28 20:10:39 2002
@@ -0,0 +1,3 @@
+CONFIG_SND_SA11XX_UDA1341
+  Say Y or M if you have a Compaq iPaq H3x00 handheld computer and want
+  to use its Philips UDA 1341 audio chip.
diff -Nru a/sound/arm/Config.in b/sound/arm/Config.in
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/arm/Config.in	Sun Apr 28 20:10:39 2002
@@ -0,0 +1,10 @@
+# ALSA ARM drivers
+
+mainmenu_option next_comment
+comment 'ALSA ARM devices'
+
+if [ "$CONFIG_ARCH_SA1100" = "y" ]; then
+  dep_tristate 'SA11xx UDA1341TS driver (H3600)' CONFIG_SND_SA11XX_UDA1341 $CONFIG_SND $CONFIG_L3
+fi
+
+endmenu
diff -Nru a/sound/arm/Makefile b/sound/arm/Makefile
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/arm/Makefile	Sun Apr 28 20:10:39 2002
@@ -0,0 +1,12 @@
+#
+# Makefile for ALSA
+#
+
+O_TARGET    := arm.o
+
+snd-sa11xx-uda1341-objs := sa11xx-uda1341.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_SA11XX_UDA1341) += snd-sa11xx-uda1341.o 
+
+include $(TOPDIR)/Rules.make
diff -Nru a/sound/arm/sa11xx-uda1341.c b/sound/arm/sa11xx-uda1341.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/arm/sa11xx-uda1341.c	Sun Apr 28 20:10:39 2002
@@ -0,0 +1,946 @@
+/*
+ *  Driver for Philips UDA1341TS on Compaq iPAQ H3600 soundcard
+ *  Copyright (C) 2002 Tomas Kasparek <tomas.kasparek@seznam.cz>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License.
+ * 
+ * History:
+ *
+ * 2002-03-13	Tomas Kasparek	Initial release - based on h3600-uda1341.c from OSS
+ * 2002-03-20   Tomas Kasparek  playback over ALSA is working
+ * 2002-03-28   Tomas Kasparek  playback over OSS emulation is working
+ * 2002-03-29   Tomas Kasparek  basic capture is working (native ALSA)
+ * 2002-03-29   Tomas Kasparek  capture is working (OSS emulation)
+ * 2002-04-04   Tomas Kasparek  better rates handling (allow non-standard rates)
+ */
+
+/* $Id: sa11xx-uda1341.c,v 1.2 2002/04/18 05:59:41 perex Exp $ */
+
+#include <sound/driver.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/ioctl.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include <asm/hardware.h>
+#include <asm/dma.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+
+#include <linux/l3/l3.h>
+
+#undef DEBUG_MODE
+#undef DEBUG_FUNCTION_NAMES
+#include <sound/uda1341.h>
+
+/* {{{ Type definitions */
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Tomas Kasparek <tomas.kasparek@seznam.cz>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SA1100/SA1111 + UDA1341TS driver for ALSA");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{UDA1341,iPAQ H3600 UDA1341TS}}");
+
+static char *snd_id = NULL;	/* ID for this card */
+
+MODULE_PARM(snd_id, "s");
+MODULE_PARM_DESC(snd_id, "ID string for SA1100/SA1111 + UDA1341TS soundcard.");
+
+#define chip_t sa11xx_uda1341_t
+
+#define SHIFT_16_STEREO         2
+
+typedef enum stream_id_t{
+	PLAYBACK=0,
+	CAPTURE,
+	MAX_STREAMS,
+}stream_id_t;
+
+typedef struct audio_stream {
+	char *id;		/* identification string */
+	dma_device_t dma_dev;	/* device identifier for DMA */
+	dma_regs_t *dma_regs;	/* points to our DMA registers */
+
+	int active:1;		/* we are using this stream for transfer now */
+
+	int sent_periods;       /* # of sent periods from actual DMA buffer */
+	int sent_total;         /* # of sent periods total (just for info & debug) */
+
+	int sync;               /* are we recoding - flag used to do DMA trans. for sync */
+        
+	snd_pcm_substream_t *stream;
+}audio_stream_t;
+
+/* I do not want to have substream = NULL when syncing - ALSA does not like it */
+#define SYNC_SUBSTREAM ((void *) -1)
+
+typedef struct snd_card_sa11xx_uda1341 {
+	struct pm_dev *pm_dev;        
+	snd_card_t *card;
+	struct l3_client *uda1341;
+
+	long samplerate;
+
+	audio_stream_t *s[MAX_STREAMS];
+	snd_info_entry_t *proc_entry;
+}sa11xx_uda1341_t;
+
+static struct snd_card_sa11xx_uda1341 *sa11xx_uda1341 = NULL;
+
+static unsigned int rates[] = {
+	8000,  10666, 10985, 14647,
+	16000, 21970, 22050, 24000,
+	29400, 32000, 44100, 48000,
+};
+
+#define RATES sizeof(rates) / sizeof(rates[0])
+
+static snd_pcm_hw_constraint_list_t hw_constraints_rates = {
+	count: RATES,
+	list: rates,
+	mask: 0,
+};
+
+/* }}} */
+
+/* {{{ Clock and sample rate stuff */
+
+/*
+ * Stop-gap solution until rest of hh.org HAL stuff is merged.
+ */
+#define GPIO_H3600_CLK_SET0		GPIO_GPIO (12)
+#define GPIO_H3600_CLK_SET1		GPIO_GPIO (13)
+
+#ifdef CONFIG_SA1100_H3XXX
+#define	clr_sa11xx_uda1341_egpio(x)	clr_h3600_egpio(x)
+#define set_sa11xx_uda1341_egpio(x)	set_h3600_egpio(x)
+#else
+#error This driver could serve H3x00 handhelds only!
+#endif
+
+static void sa11xx_uda1341_set_audio_clock(long val)
+{
+	switch (val) {
+	case 24000: case 32000: case 48000:	/* 00: 12.288 MHz */
+		GPCR = GPIO_H3600_CLK_SET0 | GPIO_H3600_CLK_SET1;
+		break;
+
+	case 22050: case 29400: case 44100:	/* 01: 11.2896 MHz */
+		GPSR = GPIO_H3600_CLK_SET0;
+		GPCR = GPIO_H3600_CLK_SET1;
+		break;
+
+	case 8000: case 10666: case 16000:	/* 10: 4.096 MHz */
+		GPCR = GPIO_H3600_CLK_SET0;
+		GPSR = GPIO_H3600_CLK_SET1;
+		break;
+
+	case 10985: case 14647: case 21970:	/* 11: 5.6245 MHz */
+		GPSR = GPIO_H3600_CLK_SET0 | GPIO_H3600_CLK_SET1;
+		break;
+	}
+}
+
+static void sa11xx_uda1341_set_samplerate(sa11xx_uda1341_t *sa11xx_uda1341, long rate)
+{
+	int clk_div = 0;
+	int clk=0;
+
+	DEBUG(KERN_DEBUG "set_samplerate rate: %ld\n", rate);
+        
+	/* We don't want to mess with clocks when frames are in flight */
+	Ser4SSCR0 &= ~SSCR0_SSE;
+	/* wait for any frame to complete */
+	udelay(125);
+
+	/*
+	 * We have the following clock sources:
+	 * 4.096 MHz, 5.6245 MHz, 11.2896 MHz, 12.288 MHz
+	 * Those can be divided either by 256, 384 or 512.
+	 * This makes up 12 combinations for the following samplerates...
+	 */
+	if (rate >= 48000)
+		rate = 48000;
+	else if (rate >= 44100)
+		rate = 44100;
+	else if (rate >= 32000)
+		rate = 32000;
+	else if (rate >= 29400)
+		rate = 29400;
+	else if (rate >= 24000)
+		rate = 24000;
+	else if (rate >= 22050)
+		rate = 22050;
+	else if (rate >= 21970)
+		rate = 21970;
+	else if (rate >= 16000)
+		rate = 16000;
+	else if (rate >= 14647)
+		rate = 14647;
+	else if (rate >= 10985)
+		rate = 10985;
+	else if (rate >= 10666)
+		rate = 10666;
+	else
+		rate = 8000;
+
+	/* Set the external clock generator */
+	sa11xx_uda1341_set_audio_clock(rate);
+
+	/* Select the clock divisor */
+	switch (rate) {
+	case 8000:
+	case 10985:
+	case 22050:
+	case 24000:
+		clk = F512;
+		clk_div = SSCR0_SerClkDiv(16);
+		break;
+	case 16000:
+	case 21970:
+	case 44100:
+	case 48000:
+		clk = F256;
+		clk_div = SSCR0_SerClkDiv(8);
+		break;
+	case 10666:
+	case 14647:
+	case 29400:
+	case 32000:
+		clk = F384;
+		clk_div = SSCR0_SerClkDiv(12);
+		break;
+	}
+
+	l3_command(sa11xx_uda1341->uda1341, CMD_FORMAT, (void *)LSB16);
+	l3_command(sa11xx_uda1341->uda1341, CMD_FS, (void *)clk);        
+	Ser4SSCR0 = (Ser4SSCR0 & ~0xff00) + clk_div + SSCR0_SSE;
+	DEBUG(KERN_DEBUG "set_samplerate done (new rate: %ld)\n", rate);
+	sa11xx_uda1341->samplerate = rate;
+}
+
+/* }}} */
+
+/* {{{ HW init and shutdown */
+
+static void sa11xx_uda1341_audio_init(sa11xx_uda1341_t *sa11xx_uda1341)
+{
+	unsigned long flags;
+
+	DEBUG_NAME(KERN_DEBUG "audio_init\n");
+
+	/* Setup DMA stuff */
+	if (sa11xx_uda1341->s[PLAYBACK]) {
+		sa11xx_uda1341->s[PLAYBACK]->id = "UDA1341 out";
+		sa11xx_uda1341->s[PLAYBACK]->dma_dev = DMA_Ser4SSPWr;
+	}
+
+	if (sa11xx_uda1341->s[CAPTURE]) {
+		sa11xx_uda1341->s[CAPTURE]->id = "UDA1341 in";
+		sa11xx_uda1341->s[CAPTURE]->dma_dev = DMA_Ser4SSPRd;
+	}
+
+	/* Initialize the UDA1341 internal state */
+       
+	/* Setup the uarts */
+	local_irq_save(flags);
+	GAFR |= (GPIO_SSP_CLK);
+	GPDR &= ~(GPIO_SSP_CLK);
+	Ser4SSCR0 = 0;
+	Ser4SSCR0 = SSCR0_DataSize(16) + SSCR0_TI + SSCR0_SerClkDiv(8);
+	Ser4SSCR1 = SSCR1_SClkIactL + SSCR1_SClk1P + SSCR1_ExtClk;
+	Ser4SSCR0 |= SSCR0_SSE;
+
+	/* Enable the audio power */
+	clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_CODEC_NRESET);
+	set_sa11xx_uda1341_egpio(IPAQ_EGPIO_AUDIO_ON);
+	set_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE);
+	local_irq_restore(flags);
+        
+	/* Initialize the UDA1341 internal state */
+	l3_open(sa11xx_uda1341->uda1341);
+
+	/* external clock configuration */
+	sa11xx_uda1341_set_samplerate(sa11xx_uda1341, 44100); /* default sample rate */                
+
+	/* Wait for the UDA1341 to wake up */
+	set_sa11xx_uda1341_egpio(IPAQ_EGPIO_CODEC_NRESET);
+	mdelay(1);
+
+	/* make the left and right channels unswapped (flip the WS latch ) */
+	Ser4SSDR = 0;
+       
+	clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE);        
+}
+
+static void sa11xx_uda1341_audio_shutdown(sa11xx_uda1341_t *sa11xx_uda1341)
+{
+	/* disable the audio power and all signals leading to the audio chip */
+	l3_close(sa11xx_uda1341->uda1341);
+	Ser4SSCR0 = 0;
+	clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_CODEC_NRESET);
+	clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_AUDIO_ON);
+	clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE);
+}
+
+/* }}} */
+
+/* {{{ DMA staff */
+
+#define SYNC_ADDR		(dma_addr_t)FLUSH_BASE_PHYS
+#define SYNC_SIZE		4096 // was 2048
+
+#define DMA_REQUEST(s, cb)	sa1100_request_dma((s)->dma_dev, (s)->id, cb, s, \
+                                                   &((s)->dma_regs))
+#define DMA_FREE(s)		{sa1100_free_dma((s)->dma_regs); (s)->dma_regs = 0;}
+#define DMA_START(s, d, l)	sa1100_start_dma((s)->dma_regs, d, l)
+#define DMA_STOP(s)		sa1100_stop_dma((s)->dma_regs)
+#define DMA_CLEAR(s)		sa1100_clear_dma((s)->dma_regs)
+#define DMA_RESET(s)		sa1100_reset_dma((s)->dma_regs)
+#define DMA_POS(s)              sa1100_get_dma_pos((s)->dma_regs)
+
+static void audio_dma_request(audio_stream_t *s, void (*callback)(void *))
+{
+	DMA_REQUEST(s, callback);
+}
+
+static void audio_dma_free(audio_stream_t *s)
+{
+	DMA_FREE(s);
+}
+
+static u_int audio_get_dma_pos(audio_stream_t *s)
+{
+	snd_pcm_substream_t * substream = s->stream;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned int offset;
+        
+	DEBUG_NAME(KERN_DEBUG "get_dma_pos");
+        
+	offset = DMA_POS(s) - substream->runtime->dma_addr;
+	DEBUG(" %d ->", offset);
+	offset >>= SHIFT_16_STEREO;                
+	DEBUG(" %d [fr]\n", offset);
+        
+	if (offset >= runtime->buffer_size){
+		offset = runtime->buffer_size;
+	}
+
+	DEBUG(KERN_DEBUG "  hw_ptr_interrupt: %lX\n",
+	      (unsigned long)runtime->hw_ptr_interrupt);
+	DEBUG(KERN_DEBUG "  updated pos [fr]: %ld\n",
+	      offset - (offset % runtime->min_align));
+        
+	return offset;
+}
+
+
+static void audio_stop_dma(audio_stream_t *s)
+{
+	long flags;
+
+	DEBUG_NAME(KERN_DEBUG "stop_dma\n");
+        
+	if (!s->stream)
+		return;
+
+	local_irq_save(flags);
+	s->active = 0;
+	s->sent_periods = 0;
+	s->sent_total = 0;        
+
+	DMA_STOP(s);
+	DMA_CLEAR(s);
+	local_irq_restore(flags);
+}
+
+
+static void audio_reset(audio_stream_t *s)
+{
+	DEBUG_NAME(KERN_DEBUG "dma_reset\n");
+        
+	if (s->stream) {
+		audio_stop_dma(s);
+	}
+	s->active = 0;
+}
+
+
+static void audio_process_dma(audio_stream_t *s)
+{
+	snd_pcm_substream_t * substream = s->stream;
+	snd_pcm_runtime_t *runtime;
+	int ret,i;
+                
+	DEBUG_NAME(KERN_DEBUG "process_dma\n");
+
+	if(!s->active){
+		DEBUG("!!!want to process DMA when stopped!!!\n");
+		return;
+	}
+
+	/* we are requested to process synchronization DMA transfer */
+	if (s->sync) {
+		while (1) {
+			DEBUG(KERN_DEBUG "sent sync period (dma_size[B]: %d)\n", SYNC_SIZE);
+			ret = DMA_START(s, SYNC_ADDR, SYNC_SIZE);
+			if (ret)
+				return;   
+		}
+	}
+
+	/* must be set here - for sync there is no runtime struct */
+	runtime = substream->runtime;
+        
+	while(1) {       
+		unsigned int  dma_size = runtime->period_size << SHIFT_16_STEREO;
+		unsigned int offset = dma_size * s->sent_periods;
+                
+		if (dma_size > MAX_DMA_SIZE){
+			/* this should not happen! */
+			DEBUG(KERN_DEBUG "-----> cut dma_size: %d -> ", dma_size);
+			dma_size = CUT_DMA_SIZE;
+			DEBUG("%d <-----\n", dma_size);
+		}
+
+		ret = DMA_START(s, runtime->dma_addr + offset, dma_size);
+		if (ret)
+			return;
+
+		DEBUG(KERN_DEBUG "sent period %d (%d total)(dma_size[B]: %d"
+		      "offset[B]: %06d)\n",
+		      s->sent_periods, s->sent_total, dma_size, offset);
+
+#ifdef DEBUG_MODE
+		printk(KERN_DEBUG "  dma_area:");
+		for (i=0; i < 32; i++) {
+			printk(" %02x", *(char *)(runtime->dma_addr+offset+i));
+		}
+		printk("\n");
+#endif                
+		s->sent_total++;
+		s->sent_periods++;
+		s->sent_periods %= runtime->periods;
+	}
+}
+
+
+static void audio_dma_callback(void *data)
+{
+	audio_stream_t *s = data;
+	char *buf;
+	int i;
+        
+	DEBUG_NAME(KERN_DEBUG "dma_callback\n");
+
+	/* when syncing we do not have any real stream from ALSA! */
+	if (!s->sync) {
+		snd_pcm_period_elapsed(s->stream);
+		DEBUG(KERN_DEBUG "----> period done <----\n");
+#ifdef DEBUG_MODE
+		printk(KERN_DEBUG "  dma_area:");
+		buf = s->stream->runtime->dma_addr +
+			((s->sent_periods - 1 ) *
+			 (s->stream->runtime->period_size << SHIFT_16_STEREO));
+		for (i=0; i < 32; i++) {
+			printk(" %02x", *(char *)(buf + i));
+		}
+		printk("\n");
+#endif                      
+	}
+        
+	if (s->active)
+		audio_process_dma(s);
+}
+
+/* }}} */
+
+/* {{{ PCM setting */
+
+/* {{{ trigger & timer */
+
+static int snd_card_sa11xx_uda1341_pcm_trigger(stream_id_t stream_id,
+				      snd_pcm_substream_t * substream, int cmd)
+{
+	sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int i;
+
+	DEBUG_NAME(KERN_DEBUG "pcm_trigger id: %d cmd: %d\n", stream_id, cmd);
+
+	DEBUG(KERN_DEBUG "  sound: %d x %d [Hz]\n", runtime->channels, runtime->rate);
+	DEBUG(KERN_DEBUG "  periods: %ld x %ld [fr]\n", (unsigned long)runtime->periods,
+	      (unsigned long) runtime->period_size);
+	DEBUG(KERN_DEBUG "  buffer_size: %ld [fr]\n", (unsigned long)runtime->buffer_size);
+	DEBUG(KERN_DEBUG "  dma_addr %p\n", (char *)runtime->dma_addr);
+
+#ifdef DEBUG_MODE
+	printk(KERN_DEBUG "  dma_area:");
+	for (i=0; i < 32; i++) {
+		printk(" %02x", *(char *)(runtime->dma_addr+i));
+	}
+	printk("\n");
+#endif
+        
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		/* want to capture and have no playback - run DMA syncing */
+		if (stream_id == CAPTURE && !chip->s[PLAYBACK]->active) {
+			/* we need synchronization DMA transfer (zeros) */
+			DEBUG(KERN_DEBUG "starting synchronization DMA transfer\n");
+			chip->s[PLAYBACK]->sync = 1;
+			chip->s[PLAYBACK]->active = 1;
+			chip->s[PLAYBACK]->stream = SYNC_SUBSTREAM; /* not really used! */
+			audio_process_dma(chip->s[PLAYBACK]);
+		}
+		/* want to playback and have capture - stop syncing */
+		if(stream_id == PLAYBACK && chip->s[PLAYBACK]->sync) {
+			chip->s[PLAYBACK]->sync = 0;
+		}
+
+		/* requested stream startup */
+		chip->s[stream_id]->active = 1;
+		audio_process_dma(chip->s[stream_id]);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		/* want to stop capture and use syncing - stop DMA syncing */
+		if (stream_id == CAPTURE && chip->s[PLAYBACK]->sync) {
+			/* we do not need synchronization DMA transfer now */
+			DEBUG(KERN_DEBUG "stopping synchronization DMA transfer\n");
+			chip->s[PLAYBACK]->sync = 0;
+			chip->s[PLAYBACK]->active = 0;
+			audio_stop_dma(chip->s[PLAYBACK]);
+		}
+		/* want to stop playback and have capture - run DMA syncing */
+		if(stream_id == PLAYBACK && chip->s[CAPTURE]->active) {
+			/* we need synchronization DMA transfer (zeros) */
+			DEBUG(KERN_DEBUG "starting synchronization DMA transfer\n");
+			chip->s[PLAYBACK]->sync = 1;
+			chip->s[PLAYBACK]->active = 1;
+			chip->s[PLAYBACK]->stream = SYNC_SUBSTREAM; /* not really used! */
+			audio_process_dma(chip->s[PLAYBACK]);
+		}
+
+		/* requested stream shutdown */
+		chip->s[stream_id]->active = 0;
+		audio_stop_dma(chip->s[stream_id]);
+		break;
+	default:
+		return -EINVAL;
+		break;
+	}
+	return 0;
+}
+
+/* }}} */
+
+static snd_pcm_hardware_t snd_sa11xx_uda1341_capture =
+{
+	info:			(SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			(SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+				 SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |\
+				 SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
+				 SNDRV_PCM_RATE_KNOT),
+	rate_min:		8000,
+	rate_max:		48000,
+	channels_min:		2,
+	channels_max:		2,
+	buffer_bytes_max:	16380,
+	period_bytes_min:	64,
+	period_bytes_max:	8190, /* <= MAX_DMA_SIZE from ams/arch-sa1100/dma.h */
+	periods_min:		2,
+	periods_max:		255,
+	fifo_size:		0,
+};
+
+static snd_pcm_hardware_t snd_sa11xx_uda1341_playback =
+{
+	info:			(SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
+	formats:		SNDRV_PCM_FMTBIT_S16_LE,
+	rates:			(SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+                                 SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |\
+				 SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
+				 SNDRV_PCM_RATE_KNOT),
+	rate_min:		8000,
+	rate_max:		48000,
+	channels_min:		2,
+	channels_max:		2,
+	buffer_bytes_max:	16380,
+	period_bytes_min:	64,
+	period_bytes_max:	8190, /* <= MAX_DMA_SIZE from ams/arch-sa1100/dma.h */
+	periods_min:		2,
+	periods_max:		255,
+	fifo_size:		0,
+};
+
+/* {{{ snd_card_sa11xx_uda1341_playback functions */
+
+static int snd_card_sa11xx_uda1341_playback_open(snd_pcm_substream_t * substream)
+{
+	sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+        
+	DEBUG_NAME(KERN_DEBUG "playback_open\n");
+        
+	chip->s[PLAYBACK]->stream = substream;
+	chip->s[PLAYBACK]->sent_periods = 0;
+	chip->s[PLAYBACK]->sent_total = 0;
+        
+	audio_reset(chip->s[PLAYBACK]);
+ 
+	runtime->hw = snd_sa11xx_uda1341_playback;
+	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+		return err;
+	if ((err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+					      &hw_constraints_rates)) < 0)
+		return err;
+        
+	return 0;
+}
+
+static int snd_card_sa11xx_uda1341_playback_close(snd_pcm_substream_t * substream)
+{
+	sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream);
+
+	DEBUG_NAME(KERN_DEBUG "playback_close\n");
+
+	chip->s[PLAYBACK]->stream = NULL;
+      
+	return 0;
+}
+
+static int snd_card_sa11xx_uda1341_playback_ioctl(snd_pcm_substream_t * substream,
+				         unsigned int cmd, void *arg)
+{
+	DEBUG_NAME(KERN_DEBUG "playback_ioctl cmd: %d\n", cmd);
+	return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+static int snd_card_sa11xx_uda1341_playback_prepare(snd_pcm_substream_t * substream)
+{
+	sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+        
+	DEBUG_NAME(KERN_DEBUG "playback_prepare\n");
+                
+	/* set requested samplerate */
+	sa11xx_uda1341_set_samplerate(chip, runtime->rate);
+        
+	return 0;
+}
+
+static int snd_card_sa11xx_uda1341_playback_trigger(snd_pcm_substream_t * substream, int cmd)
+{
+	DEBUG_NAME(KERN_DEBUG "playback_trigger\n");
+	return snd_card_sa11xx_uda1341_pcm_trigger(PLAYBACK, substream, cmd);
+}
+
+static snd_pcm_uframes_t snd_card_sa11xx_uda1341_playback_pointer(snd_pcm_substream_t * substream)
+{
+	sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream);
+
+	DEBUG_NAME(KERN_DEBUG "playback_pointer\n");        
+	return audio_get_dma_pos(chip->s[PLAYBACK]);
+}
+
+/* }}} */
+
+/* {{{ snd_card_sa11xx_uda1341_record functions */
+
+static int snd_card_sa11xx_uda1341_capture_open(snd_pcm_substream_t * substream)
+{
+	sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+
+	DEBUG_NAME(KERN_DEBUG "record_open\n");
+
+	chip->s[CAPTURE]->stream = substream;
+	chip->s[CAPTURE]->sent_periods = 0;
+	chip->s[CAPTURE]->sent_total = 0;
+        
+	audio_reset(chip->s[PLAYBACK]);        
+
+	runtime->hw = snd_sa11xx_uda1341_capture;
+	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+		return err;
+	if ((err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+					      &hw_constraints_rates)) < 0)
+		return err;
+        
+	return 0;
+}
+
+static int snd_card_sa11xx_uda1341_capture_close(snd_pcm_substream_t * substream)
+{
+	sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream);
+
+	DEBUG_NAME(KERN_DEBUG "record_close\n");
+        
+	chip->s[CAPTURE]->stream = NULL;
+
+	return 0;
+}
+
+static int snd_card_sa11xx_uda1341_capture_ioctl(snd_pcm_substream_t * substream,
+					unsigned int cmd, void *arg)
+{
+	DEBUG_NAME(KERN_DEBUG "record_ioctl cmd: %d\n", cmd);
+	return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+static int snd_card_sa11xx_uda1341_capture_prepare(snd_pcm_substream_t * substream)
+{
+	sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+        
+	DEBUG_NAME(KERN_DEBUG "record_prepare\n");
+
+	/* set requested samplerate */
+	sa11xx_uda1341_set_samplerate(chip, runtime->rate);
+        
+	return 0;
+}
+
+static int snd_card_sa11xx_uda1341_capture_trigger(snd_pcm_substream_t * substream, int cmd)
+{
+	DEBUG_NAME(KERN_DEBUG "record_trigger\n");
+	return snd_card_sa11xx_uda1341_pcm_trigger(CAPTURE, substream, cmd);
+}
+
+static snd_pcm_uframes_t snd_card_sa11xx_uda1341_capture_pointer(snd_pcm_substream_t * substream)
+{
+	sa11xx_uda1341_t *chip = snd_pcm_substream_chip(substream);
+
+	DEBUG_NAME(KERN_DEBUG "record_pointer\n");        
+	return audio_get_dma_pos(chip->s[CAPTURE]);
+}
+
+/* }}} */
+
+/* {{{ HW params & free */
+
+static int snd_sa11xx_uda1341_hw_params(snd_pcm_substream_t * substream,
+			       snd_pcm_hw_params_t * hw_params)
+{
+        
+	DEBUG_NAME(KERN_DEBUG "hw_params\n");
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_sa11xx_uda1341_hw_free(snd_pcm_substream_t * substream)
+{
+	DEBUG_NAME(KERN_DEBUG "hw_free\n");        
+	return snd_pcm_lib_free_pages(substream);
+}
+
+/* }}} */
+
+static snd_pcm_ops_t snd_card_sa11xx_uda1341_playback_ops = {
+	open:			snd_card_sa11xx_uda1341_playback_open,
+	close:			snd_card_sa11xx_uda1341_playback_close,
+	ioctl:			snd_card_sa11xx_uda1341_playback_ioctl,
+	hw_params:	        snd_sa11xx_uda1341_hw_params,
+	hw_free:	        snd_sa11xx_uda1341_hw_free,
+	prepare:		snd_card_sa11xx_uda1341_playback_prepare,
+	trigger:		snd_card_sa11xx_uda1341_playback_trigger,
+	pointer:		snd_card_sa11xx_uda1341_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_card_sa11xx_uda1341_capture_ops = {
+	open:			snd_card_sa11xx_uda1341_capture_open,
+	close:			snd_card_sa11xx_uda1341_capture_close,
+	ioctl:			snd_card_sa11xx_uda1341_capture_ioctl,
+	hw_params:	        snd_sa11xx_uda1341_hw_params,
+	hw_free:	        snd_sa11xx_uda1341_hw_free,
+	prepare:		snd_card_sa11xx_uda1341_capture_prepare,
+	trigger:		snd_card_sa11xx_uda1341_capture_trigger,
+	pointer:		snd_card_sa11xx_uda1341_capture_pointer,
+};
+
+static int __init snd_card_sa11xx_uda1341_pcm(sa11xx_uda1341_t *sa11xx_uda1341, int device, int substreams)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	DEBUG_NAME(KERN_DEBUG "sa11xx_uda1341_pcm\n");
+
+	if ((err = snd_pcm_new(sa11xx_uda1341->card, "UDA1341 PCM", device,
+			       substreams, substreams, &pcm)) < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_card_sa11xx_uda1341_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_sa11xx_uda1341_capture_ops);
+	pcm->private_data = sa11xx_uda1341;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "UDA1341 PCM");
+
+	sa11xx_uda1341->s[PLAYBACK] = snd_kcalloc(sizeof(audio_stream_t), GFP_KERNEL);
+	sa11xx_uda1341->s[CAPTURE] = snd_kcalloc(sizeof(audio_stream_t), GFP_KERNEL);
+
+	sa11xx_uda1341_audio_init(sa11xx_uda1341);
+
+	/* setup DMA controller */
+	audio_dma_request(sa11xx_uda1341->s[PLAYBACK], audio_dma_callback);
+	audio_dma_request(sa11xx_uda1341->s[CAPTURE], audio_dma_callback);
+
+	return 0;
+}
+
+/* }}} */
+
+/* {{{ module init & exit */
+
+#ifdef CONFIG_PM
+
+static int sa11xx_uda1341_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data)
+{
+	sa11xx_uda1341_t *sa11xx_uda1341 = pm_dev->data;
+	audio_stream_t *is, *os;
+	int stopstate;
+
+	DEBUG_NAME(KERN_DEBUG "pm_callback\n");
+
+	is = sa11xx_uda1341->s[PLAYBACK];
+	os = sa11xx_uda1341->s[CAPTURE];
+        
+	switch (req) {
+	case PM_SUSPEND: /* enter D1-D3 */
+		if (is && is->dma_regs) {
+			stopstate = is->active;
+			audio_stop_dma(is);
+			DMA_CLEAR(is);
+			is->active = stopstate;
+		}
+		if (os && os->dma_regs) {
+			stopstate = os->active;
+			audio_stop_dma(os);
+			DMA_CLEAR(os);
+			os->active = stopstate;
+		}
+		if (is->stream || os->stream)
+			sa11xx_uda1341_audio_shutdown(sa11xx_uda1341);
+		break;
+	case PM_RESUME:  /* enter D0 */
+		if (is->stream || os->stream)
+			sa11xx_uda1341_audio_init(sa11xx_uda1341);
+		if (os && os->dma_regs) {
+			DMA_RESET(os);
+			audio_process_dma(os);
+		}
+		if (is && is->dma_regs) {
+			DMA_RESET(is);
+			audio_process_dma(is);
+		}
+		break;
+	}
+	return 0;
+}
+
+#endif
+
+void snd_sa11xx_uda1341_free(snd_card_t *card)
+{
+	sa11xx_uda1341_t *chip = snd_magic_cast(sa11xx_uda1341_t, card->private_data, return);
+
+	DEBUG_NAME(KERN_DEBUG "snd_sa11xx_uda1341_free\n");
+
+	audio_dma_free(chip->s[PLAYBACK]);
+	audio_dma_free(chip->s[CAPTURE]);
+
+	kfree(chip->s[PLAYBACK]);
+	kfree(chip->s[CAPTURE]);
+
+	chip->s[PLAYBACK] = NULL;
+	chip->s[CAPTURE] = NULL;
+
+	snd_magic_kfree(chip);
+	card->private_data = NULL;
+}
+
+static int __init sa11xx_uda1341_init(void)
+{
+	int err;
+	snd_card_t *card;
+
+	DEBUG_NAME(KERN_DEBUG "sa11xx_uda1341_uda1341_init\n");
+        
+	if (!machine_is_h3xxx())
+		return -ENODEV;
+
+	/* register the soundcard */
+	card = snd_card_new(-1, snd_id, THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+	sa11xx_uda1341 = snd_magic_kcalloc(sa11xx_uda1341_t, 0, GFP_KERNEL);
+	if (sa11xx_uda1341 == NULL)
+		return -ENOMEM;
+         
+	card->private_data = (void *)sa11xx_uda1341;
+	card->private_free = snd_sa11xx_uda1341_free;
+        
+	sa11xx_uda1341->card = card;
+
+	// mixer
+	if ((err = snd_chip_uda1341_mixer_new(sa11xx_uda1341->card, &sa11xx_uda1341->uda1341)))
+		goto nodev;
+
+	// PCM
+	if ((err = snd_card_sa11xx_uda1341_pcm(sa11xx_uda1341, 0, 2)) < 0)
+		goto nodev;
+        
+       
+#ifdef CONFIG_PM
+	sa11xx_uda1341->pm_dev = pm_register(PM_SYS_DEV, 0, sa11xx_uda1341_pm_callback);
+	if (sa11xx_uda1341->pm_dev)
+		sa11xx_uda1341->pm_dev->data = sa11xx_uda1341;
+#endif
+        
+	strcpy(card->driver, "UDA1341");
+	strcpy(card->shortname, "H3600 UDA1341TS");
+	sprintf(card->longname, "Compaq iPAQ H3600 with Philips UDA1341TS");
+        
+	if ((err = snd_card_register(card)) == 0) {
+		printk( KERN_INFO "iPAQ audio support initialized\n" );
+		return 0;
+	}
+        
+ nodev:
+	snd_card_free(card);
+	return err;
+}
+
+static void __exit sa11xx_uda1341_exit(void)
+{
+	snd_chip_uda1341_mixer_del(sa11xx_uda1341->card);
+	snd_card_free(sa11xx_uda1341->card);
+	sa11xx_uda1341 = NULL;
+}
+
+module_init(sa11xx_uda1341_init);
+module_exit(sa11xx_uda1341_exit);
+
+/* }}} */
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * End:
+ */
diff -Nru a/sound/core/Config.in b/sound/core/Config.in
--- a/sound/core/Config.in	Sun Apr 28 20:10:35 2002
+++ b/sound/core/Config.in	Sun Apr 28 20:10:35 2002
@@ -21,7 +21,7 @@
     dep_tristate '    OSS Sequencer API' CONFIG_SND_SEQUENCER_OSS $CONFIG_SND_SEQUENCER
   fi
 fi
-dep_tristate '  RTC Timer support' CONFIG_SND_RTCTIMER $CONFIG_SND
+dep_tristate '  RTC Timer support' CONFIG_SND_RTCTIMER $CONFIG_SND $CONFIG_RTC
 bool '  Verbose printk' CONFIG_SND_VERBOSE_PRINTK
 bool '  Debug' CONFIG_SND_DEBUG
 if [ "$CONFIG_SND_DEBUG" = "y" ]; then
diff -Nru a/sound/core/Makefile b/sound/core/Makefile
--- a/sound/core/Makefile	Sun Apr 28 20:10:31 2002
+++ b/sound/core/Makefile	Sun Apr 28 20:10:31 2002
@@ -22,7 +22,7 @@
 snd-hwdep-objs    := hwdep.o
 
 obj-$(CONFIG_SND) += snd.o
-ifeq ($(CONFIG_RTC),y)
+ifeq ($(subst m,y,$(CONFIG_RTC)),y)
   obj-$(CONFIG_SND_RTCTIMER) += snd-timer.o
   obj-$(CONFIG_SND_RTCTIMER) += snd-rtctimer.o
 endif
@@ -93,6 +93,7 @@
 obj-$(CONFIG_SND_ICE1712) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o
 obj-$(CONFIG_SND_INTEL8X0) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o
 obj-$(CONFIG_SND_MAESTRO3) += snd-pcm.o snd-timer.o snd.o
+obj-$(CONFIG_SND_RME32) += snd-pcm.o snd-timer.o snd.o
 obj-$(CONFIG_SND_RME96) += snd-pcm.o snd-timer.o snd.o
 obj-$(CONFIG_SND_SONICVIBES) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o
 obj-$(CONFIG_SND_VIA686) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o
@@ -110,5 +111,7 @@
   obj-$(CONFIG_SND_SB16) += snd-hwdep.o
   obj-$(CONFIG_SND_SBAWE) += snd-hwdep.o
 endif
+
+obj-m := $(sort $(obj-m))
 
 include $(TOPDIR)/Rules.make
diff -Nru a/sound/core/device.c b/sound/core/device.c
--- a/sound/core/device.c	Sun Apr 28 20:10:31 2002
+++ b/sound/core/device.c	Sun Apr 28 20:10:31 2002
@@ -24,7 +24,6 @@
 #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	Sun Apr 28 20:10:31 2002
+++ b/sound/core/memory.c	Sun Apr 28 20:10:31 2002
@@ -26,7 +26,6 @@
 #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/misc.c b/sound/core/misc.c
--- a/sound/core/misc.c	Sun Apr 28 20:10:34 2002
+++ b/sound/core/misc.c	Sun Apr 28 20:10:34 2002
@@ -38,16 +38,67 @@
 }
 
 #ifdef CONFIG_SND_VERBOSE_PRINTK
-int snd_verbose_printk(const char *file, int line, const char *format)
+void snd_verbose_printk(const char *file, int line, const char *format, ...)
 {
+	va_list args;
+	char tmpbuf[512];
+	
 	if (format[0] == '<' && format[1] >= '0' && format[1] <= '9' && format[2] == '>') {
 		char tmp[] = "<0>";
 		tmp[1] = format[1];
 		printk("%sALSA %s:%d: ", tmp, file, line);
-		return 1;
+		format += 3;
 	} else {
 		printk("ALSA %s:%d: ", file, line);
-		return 0;
 	}
+	va_start(args, format);
+	vsnprintf(tmpbuf, sizeof(tmpbuf)-1, format, args);
+	va_end(args);
+	tmpbuf[sizeof(tmpbuf)-1] = '\0';
+	printk(tmpbuf);
+}
+#endif
+
+#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_SND_VERBOSE_PRINTK)
+void snd_verbose_printd(const char *file, int line, const char *format, ...)
+{
+	va_list args;
+	char tmpbuf[512];
+	
+	if (format[0] == '<' && format[1] >= '0' && format[1] <= '9' && format[2] == '>') {
+		char tmp[] = "<0>";
+		tmp[1] = format[1];
+		printk("%sALSA %s:%d: ", tmp, file, line);
+		format += 3;
+	} else {
+		printk(KERN_DEBUG "ALSA %s:%d: ", file, line);
+	}
+	va_start(args, format);
+	vsnprintf(tmpbuf, sizeof(tmpbuf)-1, format, args);
+	va_end(args);
+	tmpbuf[sizeof(tmpbuf)-1] = '\0';
+	printk(tmpbuf);
+}
+#endif
+
+#if defined(CONFIG_SND_DEBUG) && !defined(CONFIG_SND_VERBOSE_PRINTK)
+void snd_printd(const char *format, ...)
+{
+	va_list args;
+	char tmpbuf[512];
+	
+	if (format[0] == '<' && format[1] >= '0' && format[1] <= '9' && format[2] == '>') {
+		char tmp[] = "<0>";
+		tmp[1] = format[1];
+		printk("%sALSA %s:%d: ", tmp, file, line);
+		format += 3;
+	} else {
+		printk(KERN_DEBUG "ALSA %s:%d: ", file, line);
+	}
+	va_start(args, format);
+	vsnprintf(tmpbuf, sizeof(tmpbuf)-1, format, args);
+	va_end(args);
+	tmpbuf[sizeof(tmpbuf)-1] = '\0';
+	printk(tmpbuf);
 }
 #endif
diff -Nru a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c
--- a/sound/core/oss/pcm_oss.c	Sun Apr 28 20:10:34 2002
+++ b/sound/core/oss/pcm_oss.c	Sun Apr 28 20:10:34 2002
@@ -557,7 +557,6 @@
 			if (ret < 0)
 				break;
 		}
-		ret = snd_pcm_lib_read(substream, ptr, frames);
 		if (in_kernel) {
 			mm_segment_t fs;
 			fs = snd_enter_user();
@@ -1567,16 +1566,14 @@
 			nonblock = 1;
 		else if (psetup->block)
 			nonblock = 0;
-		else if (!nonblock)
-			nonblock = snd_nonblock_open;
 	} else if (csetup && !csetup->disable) {
 		if (csetup->nonblock)
 			nonblock = 1;
 		else if (csetup->block)
 			nonblock = 0;
-		else if (!nonblock)
-			nonblock = snd_nonblock_open;
 	}
+	if (!nonblock)
+		nonblock = snd_nonblock_open;
 
 	init_waitqueue_entry(&wait, current);
 	add_wait_queue(&pcm->open_wait, &wait);
diff -Nru a/sound/core/pcm.c b/sound/core/pcm.c
--- a/sound/core/pcm.c	Sun Apr 28 20:10:36 2002
+++ b/sound/core/pcm.c	Sun Apr 28 20:10:36 2002
@@ -144,7 +144,9 @@
 	STATE(PREPARED),
 	STATE(RUNNING),
 	STATE(XRUN),
+	STATE(DRAINING),
 	STATE(PAUSED),
+	STATE(SUSPENDED),
 };
 
 char *snd_pcm_access_names[] = {
diff -Nru a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
--- a/sound/core/pcm_lib.c	Sun Apr 28 20:10:35 2002
+++ b/sound/core/pcm_lib.c	Sun Apr 28 20:10:35 2002
@@ -34,7 +34,7 @@
 void snd_pcm_playback_silence(snd_pcm_substream_t *substream)
 {
 	snd_pcm_runtime_t *runtime = substream->runtime;
-	snd_pcm_uframes_t frames, ofs;
+	snd_pcm_uframes_t frames, ofs, transfer;
 	snd_pcm_sframes_t noise_dist;
 	if (runtime->silenced_start != runtime->control->appl_ptr) {
 		snd_pcm_sframes_t n = runtime->control->appl_ptr - runtime->silenced_start;
@@ -60,32 +60,36 @@
 	ofs = runtime->silenced_start % runtime->buffer_size + runtime->silenced_size;
 	if (ofs >= runtime->buffer_size)
 		ofs -= runtime->buffer_size;
-	if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED ||
-	    runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) {
-		if (substream->ops->silence) {
-			int err;
-			err = substream->ops->silence(substream, -1, ofs, frames);
-			snd_assert(err >= 0, );
-		} else {
-			char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, ofs);
-			snd_pcm_format_set_silence(runtime->format, hwbuf, frames * runtime->channels);
-		}
-	} else {
-		unsigned int c;
-		unsigned int channels = runtime->channels;
-		if (substream->ops->silence) {
-			for (c = 0; c < channels; ++c) {
+	while (frames > 0) {
+		transfer = ofs + frames > runtime->buffer_size ? runtime->buffer_size - ofs : frames;
+		if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED ||
+		    runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) {
+			if (substream->ops->silence) {
 				int err;
-				err = substream->ops->silence(substream, c, ofs, frames);
+				err = substream->ops->silence(substream, -1, ofs, transfer);
 				snd_assert(err >= 0, );
+			} else {
+				char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, ofs);
+				snd_pcm_format_set_silence(runtime->format, hwbuf, transfer * runtime->channels);
 			}
 		} else {
-			size_t dma_csize = runtime->dma_bytes / channels;
-			for (c = 0; c < channels; ++c) {
-				char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, ofs);
-				snd_pcm_format_set_silence(runtime->format, hwbuf, frames);
+			unsigned int c;
+			unsigned int channels = runtime->channels;
+			if (substream->ops->silence) {
+				for (c = 0; c < channels; ++c) {
+					int err;
+					err = substream->ops->silence(substream, c, ofs, transfer);
+					snd_assert(err >= 0, );
+				}
+			} else {
+				size_t dma_csize = runtime->dma_bytes / channels;
+				for (c = 0; c < channels; ++c) {
+					char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, ofs);
+					snd_pcm_format_set_silence(runtime->format, hwbuf, transfer);
+				}
 			}
 		}
+		frames -= transfer;
 	}
 	runtime->silenced_size += frames;
 }
diff -Nru a/sound/core/pcm_native.c b/sound/core/pcm_native.c
--- a/sound/core/pcm_native.c	Sun Apr 28 20:10:35 2002
+++ b/sound/core/pcm_native.c	Sun Apr 28 20:10:35 2002
@@ -991,6 +991,7 @@
 	int err, result = 0;
 	wait_queue_t wait;
 	enum { READY, EXPIRED, SUSPENDED, SIGNALED } state = READY;
+	snd_pcm_uframes_t stop_threshold;
 
 	snd_assert(substream != NULL, return -ENXIO);
 	snd_assert(substream->stream == SNDRV_PCM_STREAM_PLAYBACK, return -EINVAL);
@@ -999,6 +1000,13 @@
 
 	snd_power_lock(card);
 	spin_lock_irq(&runtime->lock);
+
+	/* stop_threshold fixup to avoid endless loop when */
+	/* stop_threshold > buffer_size */
+	stop_threshold = runtime->stop_threshold;
+	if (runtime->stop_threshold > runtime->buffer_size)
+		runtime->stop_threshold = runtime->buffer_size;
+
 	switch (runtime->status->state) {
 	case SNDRV_PCM_STATE_PAUSED:
 		snd_pcm_pause(substream, 0);
@@ -1090,6 +1098,7 @@
 	}
 
       _end:
+	runtime->stop_threshold = stop_threshold;
 	spin_unlock_irq(&runtime->lock);
 	snd_power_unlock(card);
 	if (state == EXPIRED)
diff -Nru a/sound/core/rtctimer.c b/sound/core/rtctimer.c
--- a/sound/core/rtctimer.c	Sun Apr 28 20:10:33 2002
+++ b/sound/core/rtctimer.c	Sun Apr 28 20:10:33 2002
@@ -52,7 +52,7 @@
 
 
 /*
- * The hardware dependant description for this timer.
+ * The hardware dependent description for this timer.
  */
 static struct _snd_timer_hardware rtc_hw = {
 	flags:		SNDRV_TIMER_HW_FIRST|SNDRV_TIMER_HW_AUTO,
diff -Nru a/sound/core/seq/Makefile b/sound/core/seq/Makefile
--- a/sound/core/seq/Makefile	Sun Apr 28 20:10:38 2002
+++ b/sound/core/seq/Makefile	Sun Apr 28 20:10:38 2002
@@ -77,4 +77,6 @@
 obj-$(CONFIG_SND_TRIDENT) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
 obj-$(CONFIG_SND_YMFPCI) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o
 
+obj-m := $(sort $(obj-m))
+
 include $(TOPDIR)/Rules.make
diff -Nru a/sound/core/seq/instr/Makefile b/sound/core/seq/instr/Makefile
--- a/sound/core/seq/instr/Makefile	Sun Apr 28 20:10:34 2002
+++ b/sound/core/seq/instr/Makefile	Sun Apr 28 20:10:34 2002
@@ -43,4 +43,6 @@
 obj-$(CONFIG_SND_TRIDENT) += snd-ainstr-simple.o
 obj-$(CONFIG_SND_YMFPCI) += snd-ainstr-fm.o
 
+obj-m := $(sort $(obj-m))
+
 include $(TOPDIR)/Rules.make
diff -Nru a/sound/core/seq/oss/seq_oss_midi.c b/sound/core/seq/oss/seq_oss_midi.c
--- a/sound/core/seq/oss/seq_oss_midi.c	Sun Apr 28 20:10:30 2002
+++ b/sound/core/seq/oss/seq_oss_midi.c	Sun Apr 28 20:10:30 2002
@@ -194,6 +194,8 @@
 		kfree(mdev);
 		return -ENOMEM;
 	}
+	/* OSS sequencer adds running status to all sequences */
+	snd_midi_event_no_status(mdev->coder, 1);
 
 	/*
 	 * look for en empty slot
diff -Nru a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
--- a/sound/core/seq/seq_clientmgr.c	Sun Apr 28 20:10:31 2002
+++ b/sound/core/seq/seq_clientmgr.c	Sun Apr 28 20:10:31 2002
@@ -129,31 +129,31 @@
 	}
 	spin_unlock_irqrestore(&clients_lock, flags);
 #ifdef CONFIG_KMOD
-	if (!in_interrupt()) {
+	if (!in_interrupt() && current->fs->root) {
+		static char client_requested[64];
+		static char card_requested[SNDRV_CARDS];
 		if (clientid < 64) {
 			int idx;
 			char name[32];
 			
-			for (idx = 0; idx < 64; idx++) {
-				if (snd_seq_client_load[idx] < 0)
-					break;
-				if (snd_seq_client_load[idx] == clientid) {
-					sprintf(name, "snd-seq-client-%i", clientid);
-					request_module(name);
-					break;
+			if (! client_requested[clientid]) {
+				client_requested[clientid] = 1;
+				for (idx = 0; idx < 64; idx++) {
+					if (snd_seq_client_load[idx] < 0)
+						break;
+					if (snd_seq_client_load[idx] == clientid) {
+						sprintf(name, "snd-seq-client-%i", clientid);
+						request_module(name);
+						break;
+					}
 				}
 			}
 		} else if (clientid >= 64 && clientid < 128) {
 			int card = (clientid - 64) / 8;
-			if (card < snd_ecards_limit) {
-#ifndef MODULE
-				if (current->fs->root) {
-#endif
-					snd_request_card(card);
-					snd_seq_device_load_drivers();
-#ifndef MODULE
-				}
-#endif
+			if (card < snd_ecards_limit && ! card_requested[card]) {
+				card_requested[card] = 1;
+				snd_request_card(card);
+				snd_seq_device_load_drivers();
 			}
 		}
 		spin_lock_irqsave(&clients_lock, flags);
@@ -933,8 +933,7 @@
 {
 	switch (snd_seq_ev_length_type(ev)) {
 	case SNDRV_SEQ_EVENT_LENGTH_FIXED:
-		if (snd_seq_ev_is_variable_type(ev) ||
-		    snd_seq_ev_is_varipc_type(ev))
+		if (snd_seq_ev_is_variable_type(ev))
 			return -EINVAL;
 		break;
 	case SNDRV_SEQ_EVENT_LENGTH_VARIABLE:
@@ -947,10 +946,6 @@
 		    ! snd_seq_ev_is_direct(ev))
 			return -EINVAL;
 		break;
-	case SNDRV_SEQ_EVENT_LENGTH_VARIPC:
-		if (! snd_seq_ev_is_varipc_type(ev))
-			return -EINVAL;
-		break;
 	}
 	return 0;
 }
@@ -2391,8 +2386,10 @@
 		client = snd_seq_client_use_ptr(c);
 		if (client == NULL)
 			continue;
-		if (client->type == NO_CLIENT)
+		if (client->type == NO_CLIENT) {
+			snd_seq_client_unlock(client);
 			continue;
+		}
 
 		snd_iprintf(buffer, "Client %3d : \"%s\" [%s]\n",
 			    c, client->name,
diff -Nru a/sound/core/seq/seq_dummy.c b/sound/core/seq/seq_dummy.c
--- a/sound/core/seq/seq_dummy.c	Sun Apr 28 20:10:37 2002
+++ b/sound/core/seq/seq_dummy.c	Sun Apr 28 20:10:37 2002
@@ -20,7 +20,6 @@
 
 #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	Sun Apr 28 20:10:34 2002
+++ b/sound/core/seq/seq_midi.c	Sun Apr 28 20:10:34 2002
@@ -29,8 +29,6 @@
 #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	Sun Apr 28 20:10:30 2002
+++ b/sound/core/seq/seq_midi_event.c	Sun Apr 28 20:10:30 2002
@@ -21,7 +21,6 @@
 
 #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>
@@ -178,6 +177,11 @@
 	snd_midi_event_reset_decode(dev);
 }
 
+void snd_midi_event_no_status(snd_midi_event_t *dev, int on)
+{
+	dev->nostat = on ? 1 : 0;
+}
+
 /*
  * resize buffer
  */
@@ -375,7 +379,7 @@
 		unsigned long flags;
 
 		spin_lock_irqsave(&dev->lock, flags);
-		if ((cmd & 0xf0) == 0xf0 || dev->lastcmd != cmd) {
+		if ((cmd & 0xf0) == 0xf0 || dev->lastcmd != cmd || dev->nostat) {
 			dev->lastcmd = cmd;
 			spin_unlock_irqrestore(&dev->lock, flags);
 			xbuf[0] = cmd;
@@ -434,24 +438,39 @@
 /* decode 14bit control */
 static int extra_decode_ctrl14(snd_midi_event_t *dev, unsigned char *buf, int count, snd_seq_event_t *ev)
 {
+	unsigned char cmd;
+	int idx = 0;
+
 	if (ev->data.control.param < 32) {
-		if (count < 5)
+		if (count < 4)
 			return -ENOMEM;
-		buf[0] = MIDI_CMD_CONTROL|(ev->data.control.channel & 0x0f);
-		buf[1] = ev->data.control.param;
-		buf[2] = (ev->data.control.value >> 7) & 0x7f;
-		buf[3] = ev->data.control.param + 32;
-		buf[4] = ev->data.control.value & 0x7f;
-		dev->lastcmd = buf[0];
-		return 5;
+		if (dev->nostat && count < 6)
+			return -ENOMEM;
+		cmd = MIDI_CMD_CONTROL|(ev->data.control.channel & 0x0f);
+		if (cmd != dev->lastcmd || dev->nostat) {
+			if (count < 5)
+				return -ENOMEM;
+			buf[idx++] = dev->lastcmd = cmd;
+		}
+		buf[idx++] = ev->data.control.param;
+		buf[idx++] = (ev->data.control.value >> 7) & 0x7f;
+		if (dev->nostat)
+			buf[idx++] = cmd;
+		buf[idx++] = ev->data.control.param + 32;
+		buf[idx++] = ev->data.control.value & 0x7f;
+		return idx;
 	} else {
-		if (count < 3)
+		if (count < 2)
 			return -ENOMEM;
-		buf[0] = MIDI_CMD_CONTROL|(ev->data.control.channel & 0x0f);
-		buf[1] = ev->data.control.param & 0x7f;
-		buf[4] = ev->data.control.value & 0x7f;
-		dev->lastcmd = buf[0];
-		return 3;
+		cmd = MIDI_CMD_CONTROL|(ev->data.control.channel & 0x0f);
+		if (cmd != dev->lastcmd || dev->nostat) {
+			if (count < 3)
+				return -ENOMEM;
+			buf[idx++] = dev->lastcmd = cmd;
+		}
+		buf[idx++] = ev->data.control.param & 0x7f;
+		buf[idx++] = ev->data.control.value & 0x7f;
+		return idx;
 	}
 }
 
@@ -465,6 +484,7 @@
 EXPORT_SYMBOL(snd_midi_event_init);
 EXPORT_SYMBOL(snd_midi_event_reset_encode);
 EXPORT_SYMBOL(snd_midi_event_reset_decode);
+EXPORT_SYMBOL(snd_midi_event_no_status);
 EXPORT_SYMBOL(snd_midi_event_encode);
 EXPORT_SYMBOL(snd_midi_event_encode_byte);
 EXPORT_SYMBOL(snd_midi_event_decode);
diff -Nru a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c
--- a/sound/core/seq/seq_ports.c	Sun Apr 28 20:10:37 2002
+++ b/sound/core/seq/seq_ports.c	Sun Apr 28 20:10:37 2002
@@ -431,7 +431,6 @@
 {
 	int err = 0;
 
-	snd_assert(port->owner, return -EFAULT);
 	if (! grp->count)
 		return -EINVAL;
 	grp->count--;
diff -Nru a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c
--- a/sound/core/seq/seq_queue.c	Sun Apr 28 20:10:32 2002
+++ b/sound/core/seq/seq_queue.c	Sun Apr 28 20:10:32 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	Sun Apr 28 20:10:33 2002
+++ b/sound/core/seq/seq_queue.h	Sun Apr 28 20:10:33 2002
@@ -51,7 +51,7 @@
 	spinlock_t check_lock;
 
 	/* clients which uses this queue (bitmap) */
-	unsigned long clients_bitmap[SNDRV_SEQ_MAX_CLIENTS/sizeof(unsigned long)];
+	unsigned int clients_bitmap[SNDRV_SEQ_MAX_CLIENTS/sizeof(unsigned int)];
 	unsigned int clients;	/* users of this queue */
 	struct semaphore timer_mutex;
 
diff -Nru a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c
--- a/sound/core/seq/seq_virmidi.c	Sun Apr 28 20:10:31 2002
+++ b/sound/core/seq/seq_virmidi.c	Sun Apr 28 20:10:31 2002
@@ -76,6 +76,7 @@
 		ev->dest.port = vmidi->port;
 		break;
 	}
+	ev->type = SNDRV_SEQ_EVENT_NONE;
 }
 
 /*
@@ -150,12 +151,13 @@
 
 	if (up) {
 		vmidi->trigger = 1;
-		if (!(vmidi->rdev->flags & SNDRV_VIRMIDI_SUBSCRIBE)) {
+		if (vmidi->seq_mode == SNDRV_VIRMIDI_SEQ_DISPATCH &&
+		    !(vmidi->rdev->flags & SNDRV_VIRMIDI_SUBSCRIBE)) {
 			snd_rawmidi_transmit_ack(substream, substream->runtime->buffer_size - substream->runtime->avail);
 			return;		/* ignored */
 		}
 		if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) {
-			if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, 0, 0) <= 0)
+			if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, 0, 0) < 0)
 				return;
 			vmidi->event.type = SNDRV_SEQ_EVENT_NONE;
 		}
@@ -174,7 +176,7 @@
 				pbuf += res;
 				count -= res;
 				if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) {
-					if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, 0, 0) <= 0)
+					if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, 0, 0) < 0)
 						return;
 					vmidi->event.type = SNDRV_SEQ_EVENT_NONE;
 				}
diff -Nru a/sound/core/sound.c b/sound/core/sound.c
--- a/sound/core/sound.c	Sun Apr 28 20:10:31 2002
+++ b/sound/core/sound.c	Sun Apr 28 20:10:31 2002
@@ -497,6 +497,12 @@
 #ifdef CONFIG_SND_VERBOSE_PRINTK
 EXPORT_SYMBOL(snd_verbose_printk);
 #endif
+#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_SND_VERBOSE_PRINTK)
+EXPORT_SYMBOL(snd_verbose_printd);
+#endif
+#if defined(CONFIG_SND_DEBUG) && !defined(CONFIG_SND_VERBOSE_PRINTK)
+EXPORT_SYMBOL(snd_printd);
+#endif
   /* wrappers */
 #ifdef CONFIG_SND_DEBUG_MEMORY
 EXPORT_SYMBOL(snd_wrapper_kmalloc);
diff -Nru a/sound/drivers/dummy.c b/sound/drivers/dummy.c
--- a/sound/drivers/dummy.c	Sun Apr 28 20:10:35 2002
+++ b/sound/drivers/dummy.c	Sun Apr 28 20:10:35 2002
@@ -52,6 +52,16 @@
 #define USE_PERIODS_MAX		2
 #endif
 
+#if 0 /* UDA1341 emulation */
+#define MAX_BUFFER_SIZE		(16380)
+#define USE_FORMATS		SNDRV_PCM_FMTBIT_S16_LE
+#define USE_CHANNELS_MIN	2
+#define USE_CHANNELS_MAX	2
+#define USE_PERIODS_MIN		2
+#define USE_PERIODS_MAX		255
+#endif
+
+
 /* defaults */
 #ifndef MAX_BUFFER_SIZE
 #define MAX_BUFFER_SIZE		(64*1024)
diff -Nru a/sound/drivers/mpu401/Makefile b/sound/drivers/mpu401/Makefile
--- a/sound/drivers/mpu401/Makefile	Sun Apr 28 20:10:39 2002
+++ b/sound/drivers/mpu401/Makefile	Sun Apr 28 20:10:39 2002
@@ -42,4 +42,6 @@
 obj-$(CONFIG_SND_TRIDENT) += snd-mpu401-uart.o
 obj-$(CONFIG_SND_YMFPCI) += snd-mpu401-uart.o
 
+obj-m := $(sort $(obj-m))
+
 include $(TOPDIR)/Rules.make
diff -Nru a/sound/drivers/mpu401/mpu401_uart.c b/sound/drivers/mpu401/mpu401_uart.c
--- a/sound/drivers/mpu401/mpu401_uart.c	Sun Apr 28 20:10:35 2002
+++ b/sound/drivers/mpu401/mpu401_uart.c	Sun Apr 28 20:10:35 2002
@@ -29,8 +29,6 @@
 #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>
 
diff -Nru a/sound/drivers/opl3/Makefile b/sound/drivers/opl3/Makefile
--- a/sound/drivers/opl3/Makefile	Sun Apr 28 20:10:30 2002
+++ b/sound/drivers/opl3/Makefile	Sun Apr 28 20:10:30 2002
@@ -65,4 +65,6 @@
   obj-$(CONFIG_SND_YMFPCI) += snd-opl3-synth.o
 endif
 
+obj-m := $(sort $(obj-m))
+
 include $(TOPDIR)/Rules.make
diff -Nru a/sound/drivers/opl3/opl3_lib.c b/sound/drivers/opl3/opl3_lib.c
--- a/sound/drivers/opl3/opl3_lib.c	Sun Apr 28 20:10:32 2002
+++ b/sound/drivers/opl3/opl3_lib.c	Sun Apr 28 20:10:32 2002
@@ -158,14 +158,13 @@
 		opl3->hardware = OPL3_HW_OPL2;
 	} else {
 		/*
-		 * Detect availability of OPL4 (_experimental_). Works probably
-		 * only after a cold boot. In addition the OPL4 port
-		 * of the chip may not be connected to the PC bus at all.
+		 * Detect availability of OPL4. Unfortunately, the OPL4
+		 * port of the chip may not be connected to the PC bus.
 		 */
 		snd_assert(opl3->r_port != 0, return -ENODEV);
-		opl3->command(opl3, OPL3_RIGHT | OPL3_REG_MODE, 0x00);
 		opl3->command(opl3, OPL3_RIGHT | OPL3_REG_MODE, OPL3_OPL3_ENABLE | OPL3_OPL4_ENABLE);
-		if (inb(opl3->l_port) == 0x02) {	/* Have a OPL4 */
+		/* All OPL4 registers are readable. */
+		if (inb(opl3->r_port + 1) == (OPL3_OPL3_ENABLE | OPL3_OPL4_ENABLE)) {
 			opl3->hardware = OPL3_HW_OPL4;
 		} else {
 			opl3->hardware = OPL3_HW_OPL3;
diff -Nru a/sound/i2c/Makefile b/sound/i2c/Makefile
--- a/sound/i2c/Makefile	Sun Apr 28 20:10:30 2002
+++ b/sound/i2c/Makefile	Sun Apr 28 20:10:30 2002
@@ -11,6 +11,14 @@
 snd-cs8427-objs := cs8427.o
 snd-tea6330t-objs := tea6330t.o
 
+ifeq ($(subst m,y,$(CONFIG_L3)),y)
+  subdir-$(CONFIG_L3) += l3
+endif
+ifeq ($(filter $(subdir-y),l3),l3)
+  subdir-m := l3
+  obj-y += l3/l3.o
+endif
+
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_INTERWAVE_STB) += snd-tea6330t.o snd-i2c.o
 obj-$(CONFIG_SND_ICE1712) += snd-cs8427.o snd-i2c.o
diff -Nru a/sound/i2c/cs8427.c b/sound/i2c/cs8427.c
--- a/sound/i2c/cs8427.c	Sun Apr 28 20:10:30 2002
+++ b/sound/i2c/cs8427.c	Sun Apr 28 20:10:30 2002
@@ -181,7 +181,9 @@
 	static unsigned char initvals2[] = {
 	  CS8427_REG_RECVERRMASK | CS8427_REG_AUTOINC,
 	  /* CS8427_REG_RECVERRMASK: unmask the input PLL clock, V, confidence, biphase, parity status bits */
-	  CS8427_UNLOCK | CS8427_V | CS8427_CONF | CS8427_BIP | CS8427_PAR,
+	  /* CS8427_UNLOCK | CS8427_V | CS8427_CONF | CS8427_BIP | CS8427_PAR,
+	  Why setting CS8427_V causes clicks and glitches? */
+	  CS8427_UNLOCK | CS8427_CONF | CS8427_BIP | CS8427_PAR,
 	  /* CS8427_REG_CSDATABUF:
 	     Registers 32-55 window to CS buffer
 	     Inhibit D->E transfers from overwriting first 5 bytes of CS data.
@@ -228,10 +230,10 @@
 		goto __fail;
 	}
 	/* Turn off CS8427 interrupt stuff that is not used in hardware */
-	memset(buf, 0, 8);
-	/* from address 9 to 16 */
+	memset(buf, 0, 7);
+	/* from address 9 to 15 */
 	buf[0] = 9;	/* register */
-	if ((err = snd_i2c_sendbytes(device, buf, 8)) != 8)
+	if ((err = snd_i2c_sendbytes(device, buf, 7)) != 7)
 		goto __fail;
 	/* send transfer initialization sequence */
 	memcpy(chip->regmap + (initvals2[0] & 0x7f), initvals2 + 1, 3);
diff -Nru a/sound/i2c/l3/Makefile b/sound/i2c/l3/Makefile
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/i2c/l3/Makefile	Sun Apr 28 20:10:39 2002
@@ -0,0 +1,14 @@
+#
+# Makefile for ALSA
+#
+
+O_TARGET = l3.o
+
+export-objs += uda1341.o
+
+snd-uda1341-objs := uda1341.o
+
+# Module Dependency
+obj-$(CONFIG_SND_SA11XX_UDA1341) += snd-uda1341.o
+
+include $(TOPDIR)/Rules.make
diff -Nru a/sound/i2c/l3/uda1341.c b/sound/i2c/l3/uda1341.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/i2c/l3/uda1341.c	Sun Apr 28 20:10:39 2002
@@ -0,0 +1,883 @@
+/*
+ * Philips UDA1341 mixer device driver
+ * Copyright (c) 2002 Tomas Kasparek <tomas.kasparek@seznam.cz>
+ *
+ * Portions are Copyright (C) 2000 Lernout & Hauspie Speech Products, N.V.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License.
+ *
+ * History:
+ *
+ * 2002-03-13	Tomas Kasparek	Initial release - based on uda1341.c from OSS
+ * 2002-03-28   Tomas Kasparek  basic mixer is working (volume, bass, treble)
+ * 2002-03-30   Tomas Kasparek  Proc filesystem support, complete mixer and DSP
+ *                              features support
+ * 2002-04-12	Tomas Kasparek	Proc interface update, code cleanup
+ */
+
+/* $Id: uda1341.c,v 1.2 2002/04/18 07:47:59 perex Exp $ */
+
+#include <sound/driver.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/ioctl.h>
+#include <linux/errno.h>
+
+#include <asm/uaccess.h>
+
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+#include <sound/info.h>
+
+#include <linux/l3/l3.h>
+
+#undef DEBUG_MODE
+#undef DEBUG_FUNCTION_NAMES
+#include <sound/uda1341.h>
+
+/* {{{ HW regs definition */
+
+#define STAT0                   0x00
+#define STAT1			0x80
+#define STAT_MASK               0x80
+
+#define DATA0_0			0x00
+#define DATA0_1			0x40
+#define DATA0_2			0x80
+#define DATA_MASK               0xc0
+
+#define IS_DATA0(x)     ((x) >= data0_0 && (x) <= data0_2)
+#define IS_DATA1(x)     ((x) == data1)
+#define IS_STATUS(x)    ((x) == stat0 || (x) == stat1)
+#define IS_EXTEND(x)   ((x) >= ext0 && (x) <= ext6)
+
+/* }}} */
+
+enum uda1341_regs_names {
+	stat0,
+	stat1,
+	data0_0,
+	data0_1,
+	data0_2,
+	data1,
+	ext0,
+	ext1,
+	ext2,
+	empty,
+	ext4,
+	ext5,
+	ext6,
+	uda1341_reg_last,
+};
+
+const char *uda1341_reg_names[] = {
+	"stat 0 ",
+	"stat 1 ",
+	"data 00",
+	"data 01",
+	"data 02",
+	"data 1 ",
+	"ext 0",
+	"ext 1",
+	"ext 2",
+	"empty",
+	"ext 4",
+	"ext 5",
+	"ext 6",
+};
+
+const int uda1341_enum_items[] = {
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+		2, //peak - before/after
+		4, //deemp - none/32/44.1/48
+		0,
+		4, //filter - flat/min/min/max
+		0, 0, 0,
+		4, //mixer - differ/line/mic/mixer
+		0, 0, 0, 0, 0,
+};
+
+const char ** uda1341_enum_names[] = {
+	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+	peak_names, //peak - before/after
+	deemp_names, //deemp - none/32/44.1/48
+	NULL,
+	filter_names, //filter - flat/min/min/max
+	NULL, NULL, NULL,
+	mixer_names, //mixer - differ/line/mic/mixer
+	NULL, NULL, NULL, NULL, NULL,
+};
+
+typedef int uda1341_cfg[CMD_LAST];
+
+typedef struct uda1341 uda1341_t;
+
+struct uda1341{
+	void (*write) (struct l3_client *uda1341, unsigned short reg, unsigned short val);
+	unsigned char (*read) (struct l3_client *uda1341, unsigned short reg);
+        
+	unsigned char   regs[uda1341_reg_last];
+	int		active;
+
+	spinlock_t reg_lock;
+
+	snd_card_t *card;
+
+	snd_info_entry_t *proc_entry;
+	snd_info_entry_t *proc_regs_entry;
+
+	uda1341_cfg cfg;
+};
+
+//hack for ALSA magic casting
+typedef struct l3_client l3_client_t;
+#define chip_t l3_client_t      
+
+static struct l3_client *uda1341=NULL;
+
+/* transfer 8bit integer into string with binary representation */
+void int2str_bin8(uint8_t val, char *buf){
+	const int size = sizeof(val) * 8;
+	int i;
+
+	for (i= 0; i < size; i++){
+		*(buf++) = (val >> (size - 1)) ? '1' : '0';
+		val <<= 1;
+	}
+	*buf = '\0'; //end the string with zero
+}
+
+/* {{{ HW manipulation routines */
+
+void snd_uda1341_codec_write(struct l3_client *clnt, unsigned short reg, unsigned short val)
+{
+	struct uda1341 *uda = clnt->driver_data;
+
+	int err=0;
+	unsigned char buf[2] = { 0xc0, 0xe0 }; // for EXT addressing
+
+	DEBUG_NAME(KERN_DEBUG "codec_write: reg: %s val: %d ", uda1341_reg_names[reg], val);
+        
+	uda->regs[reg] = val;
+
+	if (uda->active) {
+		DEBUG("O");
+		if (IS_DATA0(reg)) {
+			DEBUG(" D0 ");
+			err = l3_write(clnt, UDA1341_DATA0, (const unsigned char *)&val, 1);
+		} else if (IS_DATA1(reg)) {
+			DEBUG(" D1 ");                        
+			err = l3_write(clnt, UDA1341_DATA1, (const unsigned char *)&val, 1);
+		} else if (IS_STATUS(reg)) {
+			DEBUG(" S ");                        
+			err = l3_write(clnt, UDA1341_STATUS, (const unsigned char *)&val, 1);
+		} else if (IS_EXTEND(reg)) {
+			DEBUG(" E ");
+			buf[0] |= (reg - ext0) & 0x7;   //EXT address
+			buf[1] |= val;                  //EXT data
+			DEBUG("%x %x ", buf[0], buf[1]);
+			err = l3_write(clnt, UDA1341_DATA0, (const unsigned char *)buf, 2);
+		}
+                
+		if (err == 1 || err == 2)
+			DEBUG("K\n");
+		else
+			DEBUG(" Error: %d\n", err);
+	} else
+		printk(KERN_ERR "UDA1341 codec not active!\n");
+}
+
+unsigned char snd_uda1341_codec_read(struct l3_client *clnt, unsigned short reg)
+{
+	int err=0;
+	unsigned char val;
+
+	DEBUG_NAME(KERN_DEBUG "codec_read: reg: %d ", reg);
+	
+	DEBUG("O");
+
+	err = l3_read(clnt, reg, &val, 1);
+	
+	if (err == 1)
+		DEBUG("K\n");
+	else
+		DEBUG(" Error: %d\n", err);
+
+	return val & 63; //use just 6bits - the rest is address of the reg
+}
+
+static int snd_uda1341_valid_reg(struct l3_client *clnt, unsigned short reg)
+{
+	DEBUG_NAME(KERN_DEBUG "valid_reg\n");
+	return reg < uda1341_reg_last;
+}
+
+int snd_uda1341_update_bits(struct l3_client *clnt, unsigned short reg, unsigned short mask,
+                            unsigned short shift, unsigned short value, int flush)
+{
+	int change;
+	unsigned short old, new;
+	struct uda1341 *uda = clnt->driver_data;
+
+	DEBUG(KERN_DEBUG "update_bits: reg: %s mask: %d shift: %d val: %d\n",
+	      uda1341_reg_names[reg], mask, shift, value);
+        
+	if (!snd_uda1341_valid_reg(clnt, reg))
+		return -EINVAL;
+	spin_lock(&uda->reg_lock);
+	old = uda->regs[reg];
+	new = (old & ~(mask << shift)) | (value << shift);
+	change = old != new;
+	if (change) {
+		if (flush) uda->write(clnt, reg, new);
+		uda->regs[reg] = new;
+	}
+	spin_unlock(&uda->reg_lock);
+	return change;
+}
+
+int snd_uda1341_cfg_write(struct l3_client *clnt, unsigned short what,
+                          unsigned short value, int flush)
+{
+	struct uda1341 *uda = clnt->driver_data;
+	int ret = 0;
+
+	DEBUG_NAME(KERN_DEBUG "cfg_write what: %d value: %d\n", what, value);
+
+	uda->cfg[what] = value;
+        
+	switch(what) {
+	case CMD_RESET: ret = snd_uda1341_update_bits(clnt, data0_2, 1, 2, 1, flush);//MUTE
+		ret = snd_uda1341_update_bits(clnt, stat0, 1, 6, 1, flush);//RESET
+		ret = snd_uda1341_update_bits(clnt, stat0, 1, 6, 0, flush);// RESTORE
+		uda->cfg[CMD_RESET]=0;
+		break;
+	case CMD_FS: ret = snd_uda1341_update_bits(clnt, stat0, 3, 4, value, flush);
+		break;
+	case CMD_FORMAT: ret = snd_uda1341_update_bits(clnt, stat0, 7, 1, value, flush);
+		break;
+	case CMD_OGAIN: ret = snd_uda1341_update_bits(clnt, stat1, 1, 6, value, flush);
+		break;
+	case CMD_IGAIN: ret = snd_uda1341_update_bits(clnt, stat1, 1, 5, value, flush);
+		break;
+	case CMD_DAC: ret = snd_uda1341_update_bits(clnt, stat1, 1, 0, value, flush);
+		break;
+	case CMD_ADC: ret = snd_uda1341_update_bits(clnt, stat1, 1, 1, value, flush);
+		break;
+	case CMD_VOLUME: ret = snd_uda1341_update_bits(clnt, data0_0, 63, 0, value, flush);
+		break;
+	case CMD_BASS: ret = snd_uda1341_update_bits(clnt, data0_1, 15, 2, value, flush);
+		break;
+	case CMD_TREBBLE: ret = snd_uda1341_update_bits(clnt, data0_1, 3, 0, value, flush);
+		break;
+	case CMD_PEAK: ret = snd_uda1341_update_bits(clnt, data0_2, 1, 5, value, flush);
+		break;
+	case CMD_DEEMP: ret = snd_uda1341_update_bits(clnt, data0_2, 3, 3, value, flush);
+		break;
+	case CMD_MUTE: ret = snd_uda1341_update_bits(clnt, data0_2, 1, 2, value, flush);
+		break;
+	case CMD_FILTER: ret = snd_uda1341_update_bits(clnt, data0_2, 3, 0, value, flush);
+		break;
+	case CMD_CH1: ret = snd_uda1341_update_bits(clnt, ext0, 31, 0, value, flush);
+		break;
+	case CMD_CH2: ret = snd_uda1341_update_bits(clnt, ext1, 31, 0, value, flush);
+		break;
+	case CMD_MIC: ret = snd_uda1341_update_bits(clnt, ext2, 7, 2, value, flush);
+		break;
+	case CMD_MIXER: ret = snd_uda1341_update_bits(clnt, ext2, 3, 0, value, flush);
+		break;
+	case CMD_AGC: ret = snd_uda1341_update_bits(clnt, ext4, 1, 4, value, flush);
+		break;
+	case CMD_IG: ret = snd_uda1341_update_bits(clnt, ext4, 3, 0, value & 0x3, flush);
+		ret = snd_uda1341_update_bits(clnt, ext5, 31, 0, value >> 2, flush);
+		break;
+	case CMD_AGC_TIME: ret = snd_uda1341_update_bits(clnt, ext6, 7, 2, value, flush);
+		break;
+	case CMD_AGC_LEVEL: ret = snd_uda1341_update_bits(clnt, ext6, 3, 0, value, flush);
+		break;
+	default: ret = -EINVAL;	break;                                
+	}
+                
+	if (!uda->active) {
+		printk(KERN_ERR "UDA1341 codec not active!\n");                
+	}
+	return ret;
+}
+
+
+/* }}} */
+
+/* {{{ Proc interface */
+
+static void snd_uda1341_proc_read(snd_info_entry_t *entry, 
+				  snd_info_buffer_t * buffer)
+{
+	struct l3_client *clnt = snd_magic_cast(l3_client_t, entry->private_data, return);
+	struct uda1341 *uda = clnt->driver_data;
+	int peak;
+
+	peak = snd_uda1341_codec_read(clnt, UDA1341_DATA1);
+	
+	DEBUG_NAME(KERN_DEBUG "proc_read\n");
+
+	snd_iprintf(buffer, "%s\n\n", uda->card->longname);
+
+	// for information about computed values see UDA1341TS datasheet pages 15 - 21
+	snd_iprintf(buffer, "DAC power           : %s\n", uda->cfg[CMD_DAC] ? "on" : "off");
+	snd_iprintf(buffer, "ADC power           : %s\n", uda->cfg[CMD_ADC] ? "on" : "off");
+ 	snd_iprintf(buffer, "Clock frequency     : %s\n", fs_names[uda->cfg[CMD_FS]]);
+	snd_iprintf(buffer, "Data format         : %s\n\n", format_names[uda->cfg[CMD_FORMAT]]);
+
+	snd_iprintf(buffer, "Filter mode         : %s\n", filter_names[uda->cfg[CMD_FILTER]]);
+	snd_iprintf(buffer, "Mixer mode          : %s\n", mixer_names[uda->cfg[CMD_MIXER]]);
+	snd_iprintf(buffer, "De-emphasis         : %s\n", deemp_names[uda->cfg[CMD_DEEMP]]);	
+	snd_iprintf(buffer, "Peak detection pos. : %s\n", uda->cfg[CMD_PEAK] ? "after" : "before");
+	snd_iprintf(buffer, "Peak value          : %s\n\n", peak_value[peak]);		
+	
+	snd_iprintf(buffer, "Automatic Gain Ctrl : %s\n", uda->cfg[CMD_AGC] ? "on" : "off");
+	snd_iprintf(buffer, "AGC attack time     : %d ms\n", AGC_atime[uda->cfg[CMD_AGC_TIME]]);
+	snd_iprintf(buffer, "AGC decay time      : %d ns\n", AGC_dtime[uda->cfg[CMD_AGC_TIME]]);
+	snd_iprintf(buffer, "AGC output level    : %s dB\n\n", AGC_level[uda->cfg[CMD_AGC_LEVEL]]);
+
+	snd_iprintf(buffer, "Mute                : %s\n", uda->cfg[CMD_MUTE] ? "on" : "off");
+
+	if(uda->cfg[CMD_VOLUME] == 0)
+		snd_iprintf(buffer, "Volume              : 0 dB\n");
+	else if (uda->cfg[CMD_VOLUME] < 62)
+		snd_iprintf(buffer, "Volume              : %d dB\n", -1*uda->cfg[CMD_VOLUME] +1);
+	else
+		snd_iprintf(buffer, "Volume              : -INF dB\n");
+	snd_iprintf(buffer, "Bass                : %s\n", bass_values[uda->cfg[CMD_FILTER]][uda->cfg[CMD_BASS]]);
+	snd_iprintf(buffer, "Trebble             : %d dB\n", uda->cfg[CMD_FILTER] ? 2*uda->cfg[CMD_TREBBLE] : 0);
+	snd_iprintf(buffer, "Input Gain (6dB)    : %s\n", uda->cfg[CMD_IGAIN] ? "on" : "off");
+	snd_iprintf(buffer, "Output Gain (6dB)   : %s\n", uda->cfg[CMD_OGAIN] ? "on" : "off");
+	snd_iprintf(buffer, "Mic sensitivity     : %s\n", mic_sens_value[uda->cfg[CMD_MIC]]);
+
+	
+	if(uda->cfg[CMD_CH1] < 31)
+		snd_iprintf(buffer, "Mixer gain channel 1: -%d.%c dB\n",
+			    ((uda->cfg[CMD_CH1] >> 1) * 3) + (uda->cfg[CMD_CH1] & 1),
+			    uda->cfg[CMD_CH1] & 1 ? '5' : '0');
+	else
+		snd_iprintf(buffer, "Mixer gain channel 1: -INF dB\n");
+	if(uda->cfg[CMD_CH2] < 31)
+		snd_iprintf(buffer, "Mixer gain channel 2: -%d.%c dB\n",
+			    ((uda->cfg[CMD_CH2] >> 1) * 3) + (uda->cfg[CMD_CH2] & 1),
+			    uda->cfg[CMD_CH2] & 1 ? '5' : '0');
+	else
+		snd_iprintf(buffer, "Mixer gain channel 2: -INF dB\n");
+
+	if(uda->cfg[CMD_IG] > 5)
+		snd_iprintf(buffer, "Input Amp. Gain ch 2: %d.%c dB\n",
+			    (uda->cfg[CMD_IG] >> 1) -3, uda->cfg[CMD_IG] & 1 ? '5' : '0');
+	else
+		snd_iprintf(buffer, "Input Amp. Gain ch 2: %s dB\n",  ig_small_value[uda->cfg[CMD_IG]]);
+}
+
+static void snd_uda1341_proc_regs_read(snd_info_entry_t *entry, 
+				       snd_info_buffer_t * buffer)
+{
+	struct l3_client *clnt = snd_magic_cast(l3_client_t, entry->private_data, return);
+	struct uda1341 *uda = clnt->driver_data;		
+	int reg;
+	char buf[12];
+
+	DEBUG_NAME(KERN_DEBUG "proc_regs_read\n");
+        
+	spin_lock(&uda->reg_lock);
+	for (reg = 0; reg < uda1341_reg_last; reg ++) {
+		if (reg == empty)
+			continue;
+		int2str_bin8(uda->regs[reg], buf);
+		snd_iprintf(buffer, "%s = %s\n", uda1341_reg_names[reg], buf);
+	}
+
+	int2str_bin8(snd_uda1341_codec_read(clnt, UDA1341_DATA1), buf);
+	snd_iprintf(buffer, "DATA1 = %s\n", buf);
+	
+	spin_unlock(&uda->reg_lock);       
+}
+
+static void __devinit snd_uda1341_proc_init(snd_card_t *card, struct l3_client *clnt)
+{
+	snd_info_entry_t *entry;
+	struct uda1341 *uda = clnt->driver_data;
+
+	DEBUG_NAME(KERN_DEBUG "proc_init\n");
+        
+	if ((entry = snd_info_create_card_entry(card, "uda1341", card->proc_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->private_data = clnt;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->c.text.read_size = 512;
+		entry->c.text.read = snd_uda1341_proc_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	uda->proc_entry = entry;
+	if ((entry = snd_info_create_card_entry(card, "uda1341-regs", card->proc_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->private_data = clnt;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->c.text.read_size = 1024;
+		entry->c.text.read = snd_uda1341_proc_regs_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	uda->proc_regs_entry = entry;   
+}
+
+static void snd_uda1341_proc_done(struct l3_client *clnt)
+{
+	struct uda1341 *uda = clnt->driver_data;
+
+	DEBUG_NAME(KERN_DEBUG "proc_done\n");
+        
+	if (uda->proc_regs_entry) {
+		snd_info_unregister(uda->proc_regs_entry);
+		uda->proc_regs_entry = NULL;
+	}
+	if (uda->proc_entry) {
+		snd_info_unregister(uda->proc_entry);
+		uda->proc_entry = NULL;
+	}
+}
+
+/* }}} */
+
+/* {{{ Mixer controls setting */
+
+/* {{{ UDA1341 single functions */
+
+#define UDA1341_SINGLE(xname, where, reg, shift, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_uda1341_info_single, \
+  get: snd_uda1341_get_single, put: snd_uda1341_put_single, \
+  private_value: where | reg << 5 | (shift << 9) | (mask << 12) | (invert << 18) \
+}
+
+static int snd_uda1341_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 12) & 63;
+
+	DEBUG_NAME(KERN_DEBUG "info_single where: %ld\n", kcontrol->private_value & 31);
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_uda1341_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	struct l3_client *clnt = snd_kcontrol_chip(kcontrol);
+	uda1341_t *uda = clnt->driver_data;
+	int where = kcontrol->private_value & 31;        
+	int mask = (kcontrol->private_value >> 12) & 63;
+	int invert = (kcontrol->private_value >> 18) & 1;
+        
+	DEBUG_NAME(KERN_DEBUG "get_single where: %d (val: %d)\n", where, uda->cfg[where]);
+        
+	ucontrol->value.integer.value[0] = uda->cfg[where];
+	if (invert)
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+
+	return 0;
+}
+
+static int snd_uda1341_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	struct l3_client *clnt = snd_kcontrol_chip(kcontrol);
+	uda1341_t *uda = clnt->driver_data;
+	int where = kcontrol->private_value & 31;        
+	int reg = (kcontrol->private_value >> 5) & 15;
+	int shift = (kcontrol->private_value >> 9) & 7;
+	int mask = (kcontrol->private_value >> 12) & 63;
+	int invert = (kcontrol->private_value >> 18) & 1;
+	unsigned short val;
+
+	val = (ucontrol->value.integer.value[0] & mask);
+	if (invert)
+		val = mask - val;
+
+	DEBUG(KERN_DEBUG "put_single where: %d reg: %d mask: %d shift: %d inv: %d val: %d\n",
+	      where, reg, mask, shift, invert, val);
+        
+	uda->cfg[where] = val;
+	return snd_uda1341_update_bits(clnt, reg, mask, shift, val, FLUSH);
+}
+
+/* }}} */
+
+/* {{{ UDA1341 enum functions */
+
+#define UDA1341_ENUM(xname, where, reg, shift, mask, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_uda1341_info_enum, \
+  get: snd_uda1341_get_enum, put: snd_uda1341_put_enum, \
+  private_value: where | reg << 5 | (shift << 9) | (mask << 12) | (invert << 18) \
+}
+
+static int snd_uda1341_info_enum(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int where = kcontrol->private_value & 31;
+	const char **texts;
+	
+	DEBUG_NAME(KERN_DEBUG "info_enum where: %d\n", where);
+
+	// this register we dont handle this way
+	if (!uda1341_enum_items[where])
+		return -EINVAL;
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = uda1341_enum_items[where];
+
+	if (uinfo->value.enumerated.item >= uda1341_enum_items[where])
+		uinfo->value.enumerated.item = uda1341_enum_items[where] - 1;
+
+	texts = uda1341_enum_names[where];
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_uda1341_get_enum(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	struct l3_client *clnt = snd_kcontrol_chip(kcontrol);
+	uda1341_t *uda = clnt->driver_data;
+	int where = kcontrol->private_value & 31;        
+        
+	DEBUG_NAME(KERN_DEBUG "get_enum where: %d (val: %d)\n", where, uda->cfg[where]);
+        
+	ucontrol->value.enumerated.item[0] = uda->cfg[where];	
+	return 0;
+}
+
+static int snd_uda1341_put_enum(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	struct l3_client *clnt = snd_kcontrol_chip(kcontrol);
+	uda1341_t *uda = clnt->driver_data;
+	int where = kcontrol->private_value & 31;        
+	int reg = (kcontrol->private_value >> 5) & 15;
+	int shift = (kcontrol->private_value >> 9) & 7;
+	int mask = (kcontrol->private_value >> 12) & 63;
+
+	uda->cfg[where] = (ucontrol->value.enumerated.item[0] & mask);
+	
+	DEBUG(KERN_DEBUG "put_enum where: %d reg: %d mask: %d shift: %d val: %d\n",
+	      where, reg, mask, shift, uda->cfg[where]);
+        
+	return snd_uda1341_update_bits(clnt, reg, mask, shift, uda->cfg[where], FLUSH);
+}
+
+/* }}} */
+
+/* {{{ UDA1341 2regs functions */
+
+#define UDA1341_2REGS(xname, where, reg_1, reg_2, shift_1, shift_2, mask_1, mask_2, invert) \
+{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: (xname), info: snd_uda1341_info_2regs, \
+  get: snd_uda1341_get_2regs, put: snd_uda1341_put_2regs, \
+  private_value: where | (reg_1 << 5) | (reg_2 << 9) | (shift_1 << 13) | (shift_2 << 16) | \
+                         (mask_1 << 19) | (mask_2 << 25) | (invert << 31) \
+}
+
+
+static int snd_uda1341_info_2regs(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask_1 = (kcontrol->private_value >> 19) & 63;
+	int mask_2 = (kcontrol->private_value >> 25) & 63;
+	int mask;
+        
+	DEBUG_NAME(KERN_DEBUG "info_2regs where: %ld\n", kcontrol->private_value & 31);
+
+	mask = (mask_2 + 1) * (mask_1 + 1) - 1;
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+static int snd_uda1341_get_2regs(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	struct l3_client *clnt = snd_kcontrol_chip(kcontrol);
+	uda1341_t *uda = clnt->driver_data;
+	int where = kcontrol->private_value & 31;
+	int mask_1 = (kcontrol->private_value >> 19) & 63;
+	int mask_2 = (kcontrol->private_value >> 25) & 63;        
+	int invert = (kcontrol->private_value >> 31) & 1;
+	int mask;
+
+	DEBUG_NAME(KERN_DEBUG "get_2regs where: %d (val: %d)\n", where, uda->cfg[where]);
+
+	mask = (mask_2 + 1) * (mask_1 + 1) - 1;
+
+	ucontrol->value.integer.value[0] = uda->cfg[where];
+	if (invert)
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+	return 0;
+}
+
+static int snd_uda1341_put_2regs(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	struct l3_client *clnt = snd_kcontrol_chip(kcontrol);
+	uda1341_t *uda = clnt->driver_data;        
+	int where = kcontrol->private_value & 31;        
+	int reg_1 = (kcontrol->private_value >> 5) & 15;
+	int reg_2 = (kcontrol->private_value >> 9) & 15;        
+	int shift_1 = (kcontrol->private_value >> 13) & 7;
+	int shift_2 = (kcontrol->private_value >> 16) & 7;
+	int mask_1 = (kcontrol->private_value >> 19) & 63;
+	int mask_2 = (kcontrol->private_value >> 25) & 63;        
+	int invert = (kcontrol->private_value >> 31) & 1;
+	int mask;
+	unsigned short val1, val2, val;
+
+	val = ucontrol->value.integer.value[0];
+         
+	DEBUG_NAME(KERN_DEBUG "put_2regs where: %d reg1: %d reg2: %d mask1: %d mask2: %d "
+		   "shift1: %d shift2: %d inv: %d val: %d\n", where, reg_1, reg_2, mask_1, mask_2,
+		   shift_1, shift_2, invert, val);       
+
+	mask = (mask_2 + 1) * (mask_1 + 1) - 1;
+
+	val1 = val & mask_1;
+	val2 = (val / (mask_1 + 1)) & mask_2;        
+
+	if (invert) {
+		val1 = mask_1 - val1;
+		val2 = mask_2 - val2;
+	}
+
+	uda->cfg[where] = invert ? mask - val : val;
+        
+	//FIXME - return value
+	snd_uda1341_update_bits(clnt, reg_1, mask_1, shift_1, val1, FLUSH);
+	return snd_uda1341_update_bits(clnt, reg_2, mask_2, shift_2, val2, FLUSH);
+}
+
+/* }}} */
+  
+#define UDA1341_CONTROLS (sizeof(snd_uda1341_controls)/sizeof(snd_kcontrol_new_t))
+
+static snd_kcontrol_new_t snd_uda1341_controls[] = {
+	UDA1341_SINGLE("Master Playback Switch", CMD_MUTE, data0_2, 2, 1, 1),
+	UDA1341_SINGLE("Master Playback Volume", CMD_VOLUME, data0_0, 0, 63, 1),
+
+	UDA1341_SINGLE("Bass Playback Volume", CMD_BASS, data0_1, 2, 15, 0),
+	UDA1341_SINGLE("Treble Playback Volume", CMD_TREBBLE, data0_1, 0, 3, 0),
+
+	UDA1341_SINGLE("Input Gain Switch", CMD_IGAIN, stat1, 5, 1, 0),
+	UDA1341_SINGLE("Output Gain Switch", CMD_OGAIN, stat1, 6, 1, 0),
+
+	UDA1341_SINGLE("Mixer Gain Channel 1 Volume", CMD_CH1, ext0, 0, 31, 1),
+	UDA1341_SINGLE("Mixer Gain Channel 2 Volume", CMD_CH2, ext1, 0, 31, 1),
+
+	UDA1341_SINGLE("Mic Sensitivity Volume", CMD_MIC, ext2, 2, 7, 0),
+
+	UDA1341_SINGLE("AGC Output Level", CMD_AGC_LEVEL, ext6, 0, 3, 0),
+	UDA1341_SINGLE("AGC Time Constant", CMD_AGC_TIME, ext6, 2, 7, 0),
+	UDA1341_SINGLE("AGC Time Constant Switch", CMD_AGC, ext4, 4, 1, 0),
+
+	UDA1341_SINGLE("DAC Power", CMD_DAC, stat1, 0, 1, 0),
+	UDA1341_SINGLE("ADC Power", CMD_ADC, stat1, 1, 1, 0),
+
+	UDA1341_ENUM("Peak detection", CMD_PEAK, data0_2, 5, 1, 0),
+	UDA1341_ENUM("De-emphasis", CMD_DEEMP, data0_2, 3, 3, 0),
+	UDA1341_ENUM("Mixer mode", CMD_MIXER, ext2, 0, 3, 0),
+	UDA1341_ENUM("Filter mode", CMD_FILTER, data0_2, 0, 3, 0),
+
+	UDA1341_2REGS("Gain Input Amplifier Gain (channel 2)", CMD_IG, ext4, ext5, 0, 0, 3, 31, 0),
+};
+
+int __init snd_chip_uda1341_mixer_new(snd_card_t *card, struct l3_client **clnt)
+{
+	int idx, err;
+
+	DEBUG_NAME(KERN_DEBUG "uda1341 mixer_new\n");
+        
+	snd_assert(card != NULL, return -EINVAL);
+
+	uda1341 = snd_magic_kcalloc(l3_client_t, 0, GFP_KERNEL);
+	if (uda1341 == NULL)
+		return -ENOMEM;
+         
+	if ((err = l3_attach_client(uda1341, "l3-bit-sa1100-gpio", "snd-uda1341")))
+		return -ENODEV;
+
+	for (idx = 0; idx < UDA1341_CONTROLS; idx++) {
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_uda1341_controls[idx], uda1341))) < 0)
+			return err;
+	}
+
+	*clnt = uda1341;
+	strcpy(card->mixername, "UDA1341TS Mixer");
+	((uda1341_t *)uda1341->driver_data)->card = card;
+        
+	snd_uda1341_proc_init(card, uda1341);
+        
+	return 0;
+}
+
+void __init snd_chip_uda1341_mixer_del(snd_card_t *card)
+{
+	DEBUG_NAME(KERN_DEBUG "uda1341 mixer_del\n");
+        
+	snd_uda1341_proc_done(uda1341);
+	l3_detach_client(uda1341);
+
+	snd_magic_kfree(uda1341);
+	uda1341 = NULL;
+}
+
+/* }}} */
+
+/* {{{ L3 operations */
+
+static int uda1341_attach(struct l3_client *clnt)
+{
+	struct uda1341 *uda;
+
+	DEBUG_NAME(KERN_DEBUG "uda1341 attach\n");
+        
+	uda = snd_magic_kcalloc(uda1341_t, 0, GFP_KERNEL);
+	if (!uda)
+		return -ENOMEM;
+
+	memset(uda, 0, sizeof(*uda));
+
+	/* init fixed parts of my copy of registers */
+	uda->regs[stat0]   = STAT0;
+	uda->regs[stat1]   = STAT1;
+
+	uda->regs[data0_0] = DATA0_0;
+	uda->regs[data0_1] = DATA0_1;
+	uda->regs[data0_2] = DATA0_2;
+
+	uda->write = snd_uda1341_codec_write;
+	uda->read = snd_uda1341_codec_read;
+  
+	spin_lock_init(&uda->reg_lock);
+        
+	clnt->driver_data = uda;
+
+	//l3_open(clnt);
+	return 0;
+}
+
+static void uda1341_detach(struct l3_client *clnt)
+{
+	DEBUG_NAME(KERN_DEBUG "uda1341 detach\n");        
+	snd_magic_kfree(clnt->driver_data);
+}
+
+static int
+uda1341_command(struct l3_client *clnt, int cmd, void *arg)
+{
+	DEBUG_NAME(KERN_DEBUG "l3_command\n");
+
+	if (cmd != CMD_READ_REG)
+		return snd_uda1341_cfg_write(clnt, cmd, (int) arg, FLUSH);
+
+	return snd_uda1341_codec_read(clnt, (int) arg);
+}
+
+static int uda1341_open(struct l3_client *clnt)
+{
+	struct uda1341 *uda = clnt->driver_data;
+
+	DEBUG_NAME(KERN_DEBUG "uda1341 open\n");
+        
+	uda->active = 1;
+
+	/* init default configuration */
+	snd_uda1341_cfg_write(clnt, CMD_RESET, 0, REGS_ONLY);
+	snd_uda1341_cfg_write(clnt, CMD_FS, F256, FLUSH);       // unknown state after reset
+	snd_uda1341_cfg_write(clnt, CMD_FORMAT, LSB16, FLUSH);  // unknown state after reset
+	snd_uda1341_cfg_write(clnt, CMD_OGAIN, ON, FLUSH);      // default off after reset
+	snd_uda1341_cfg_write(clnt, CMD_IGAIN, ON, FLUSH);      // default off after reset
+	snd_uda1341_cfg_write(clnt, CMD_DAC, ON, FLUSH);    // ??? default value after reset
+	snd_uda1341_cfg_write(clnt, CMD_ADC, ON, FLUSH);    // ??? default value after reset
+	snd_uda1341_cfg_write(clnt, CMD_VOLUME, 20, FLUSH);     // default 0dB after reset
+	snd_uda1341_cfg_write(clnt, CMD_BASS, 0, REGS_ONLY);    // default value after reset
+	snd_uda1341_cfg_write(clnt, CMD_TREBBLE, 0, REGS_ONLY); // default value after reset
+	snd_uda1341_cfg_write(clnt, CMD_PEAK, AFTER, REGS_ONLY);// default value after reset
+	snd_uda1341_cfg_write(clnt, CMD_DEEMP, NONE, REGS_ONLY);// default value after reset
+	//at this moment should be QMUTED by h3600_audio_init
+	snd_uda1341_cfg_write(clnt, CMD_MUTE, OFF, REGS_ONLY);  // default value after reset
+	snd_uda1341_cfg_write(clnt, CMD_FILTER, MAX, FLUSH);    // defaul flat after reset
+	snd_uda1341_cfg_write(clnt, CMD_CH1, 31, FLUSH);        // default value after reset
+	snd_uda1341_cfg_write(clnt, CMD_CH2, 4, FLUSH);         // default value after reset
+	snd_uda1341_cfg_write(clnt, CMD_MIC, 4, FLUSH);         // default 0dB after reset
+	snd_uda1341_cfg_write(clnt, CMD_MIXER, MIXER, FLUSH);   // default doub.dif.mode          
+	snd_uda1341_cfg_write(clnt, CMD_AGC, OFF, FLUSH);       // default value after reset
+	snd_uda1341_cfg_write(clnt, CMD_IG, 0, FLUSH);          // unknown state after reset
+	snd_uda1341_cfg_write(clnt, CMD_AGC_TIME, 0, FLUSH);    // default value after reset
+	snd_uda1341_cfg_write(clnt, CMD_AGC_LEVEL, 0, FLUSH);   // default value after reset
+
+	return 0;
+}
+
+static void uda1341_close(struct l3_client *clnt)
+{
+	struct uda1341 *uda = clnt->driver_data;
+
+	DEBUG_NAME(KERN_DEBUG "uda1341 close\n");
+	uda->active = 0;
+}
+
+/* }}} */
+
+/* {{{ Module and L3 initialization */
+
+static struct l3_ops uda1341_ops = {
+	open:		uda1341_open,
+	command:	uda1341_command,
+	close:		uda1341_close,
+};
+
+static struct l3_driver uda1341_driver = {
+	name:		UDA1341_ALSA_NAME,
+	attach_client:	uda1341_attach,
+	detach_client:	uda1341_detach,
+	ops:		&uda1341_ops,
+	owner:		THIS_MODULE,
+};
+
+static int __init uda1341_init(void)
+{
+	return l3_add_driver(&uda1341_driver);
+}
+
+static void __exit uda1341_exit(void)
+{
+	l3_del_driver(&uda1341_driver);
+}
+
+module_init(uda1341_init);
+module_exit(uda1341_exit);
+
+MODULE_AUTHOR("Tomas Kasparek <tomas.kasparek@seznam.cz>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Philips UDA1341 CODEC driver for ALSA");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{UDA1341,UDA1341TS}}");
+
+EXPORT_SYMBOL(snd_chip_uda1341_mixer_new);
+EXPORT_SYMBOL(snd_chip_uda1341_mixer_del);
+
+/* }}} */
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * End:
+ */
diff -Nru a/sound/isa/ad1848/Makefile b/sound/isa/ad1848/Makefile
--- a/sound/isa/ad1848/Makefile	Sun Apr 28 20:10:34 2002
+++ b/sound/isa/ad1848/Makefile	Sun Apr 28 20:10:34 2002
@@ -16,4 +16,6 @@
 obj-$(CONFIG_SND_AD1848) += snd-ad1848.o snd-ad1848-lib.o
 obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-ad1848-lib.o
 
+obj-m := $(sort $(obj-m))
+
 include $(TOPDIR)/Rules.make
diff -Nru a/sound/isa/ad1848/ad1848_lib.c b/sound/isa/ad1848/ad1848_lib.c
--- a/sound/isa/ad1848/ad1848_lib.c	Sun Apr 28 20:10:35 2002
+++ b/sound/isa/ad1848/ad1848_lib.c	Sun Apr 28 20:10:35 2002
@@ -661,8 +661,15 @@
 			rev = snd_ad1848_in(chip, AD1848_MISC_INFO);
 			if (rev & 0x80) {
 				chip->hardware = AD1848_HW_CS4248;
-			} else if (rev & 0x0a) {
-				chip->hardware = AD1848_HW_CMI8330;
+			} else if ((rev & 0x0f) == 0x0a) {
+				snd_ad1848_out(chip, AD1848_MISC_INFO, 0x40);
+				for (i = 0; i < 16; ++i) {
+					if (snd_ad1848_in(chip, i) != snd_ad1848_in(chip, i + 16)) {
+						chip->hardware = AD1848_HW_CMI8330;
+						break;
+					}
+				}
+				snd_ad1848_out(chip, AD1848_MISC_INFO, 0x00);
 			}
 		}
 	}
diff -Nru a/sound/isa/cmi8330.c b/sound/isa/cmi8330.c
--- a/sound/isa/cmi8330.c	Sun Apr 28 20:10:31 2002
+++ b/sound/isa/cmi8330.c	Sun Apr 28 20:10:31 2002
@@ -176,7 +176,7 @@
 AD1848_DOUBLE("Master Playback Volume", 0, CMI8330_MASTVOL, CMI8330_MASTVOL, 4, 0, 15, 0),
 AD1848_SINGLE("Loud Playback Switch", 0, CMI8330_MUTEMUX, 6, 1, 1),
 AD1848_DOUBLE("PCM Playback Switch", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 7, 7, 1, 1),
-AD1848_DOUBLE("PCM Playback Volume", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 0, 0, 63, 0),
+AD1848_DOUBLE("PCM Playback Volume", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 0, 0, 63, 1),
 AD1848_DOUBLE("Line Playback Switch", 0, CMI8330_MUTEMUX, CMI8330_MUTEMUX, 4, 3, 1, 0),
 AD1848_DOUBLE("Line Playback Volume", 0, CMI8330_LINVOL, CMI8330_LINVOL, 4, 0, 15, 0),
 AD1848_DOUBLE("Line Capture Switch", 0, CMI8330_RMUX3D, CMI8330_RMUX3D, 2, 1, 1, 0),
diff -Nru a/sound/isa/cs423x/Makefile b/sound/isa/cs423x/Makefile
--- a/sound/isa/cs423x/Makefile	Sun Apr 28 20:10:34 2002
+++ b/sound/isa/cs423x/Makefile	Sun Apr 28 20:10:34 2002
@@ -25,4 +25,6 @@
 obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-cs4231-lib.o
 obj-$(CONFIG_SND_WAVEFRONT) += snd-cs4231-lib.o
 
+obj-m := $(sort $(obj-m))
+
 include $(TOPDIR)/Rules.make
diff -Nru a/sound/isa/cs423x/cs4236.c b/sound/isa/cs423x/cs4236.c
--- a/sound/isa/cs423x/cs4236.c	Sun Apr 28 20:10:31 2002
+++ b/sound/isa/cs423x/cs4236.c	Sun Apr 28 20:10:31 2002
@@ -70,6 +70,7 @@
 		"{IBM,Aptiva 2137 E24},"
 		"{IBM,IntelliStation M Pro},"
 		"{Intel,Marlin Spike Mobo CS4235},"
+		"{Intel PR440FX Onboard},"
 		"{Guillemot,MaxiSound 16 PnP},"
 		"{NewClear,3D},"
 		"{TerraTec,AudioSystem EWS64L/XL},"
@@ -209,6 +210,8 @@
 	ISAPNP_CS4232_WOMPU('C','S','C',0x0735,0x0000,0x0010),
 	/* some uknown CS4236B */
 	ISAPNP_CS4232('C','S','C',0x0b35,0x0000,0x0010,0x0003),
+	/* Intel PR440FX Onboard sound */
+	ISAPNP_CS4232('C','S','C',0x0b36,0x0000,0x0010,0x0003),
 	/* CS4235 on mainboard without MPU */
 	ISAPNP_CS4232_WOMPU('C','S','C',0x1425,0x0100,0x0110),
 	/* Gateway E1000 Onboard CS4236B */
@@ -298,7 +301,7 @@
 		return -EAGAIN;
 	if (snd_port[dev] != SNDRV_AUTO_PORT)
 		isapnp_resource_change(&pdev->resource[0], snd_port[dev], 4);
-	if (snd_fm_port[dev] != SNDRV_AUTO_PORT)
+	if (snd_fm_port[dev] != SNDRV_AUTO_PORT && snd_fm_port[dev] >= 0)
 		isapnp_resource_change(&pdev->resource[1], snd_fm_port[dev], 4);
 	if (snd_sb_port[dev] != SNDRV_AUTO_PORT)
 		isapnp_resource_change(&pdev->resource[2], snd_sb_port[dev], 16);
@@ -313,7 +316,8 @@
 		return -EBUSY;
 	}
 	snd_port[dev] = pdev->resource[0].start;
-	snd_fm_port[dev] = pdev->resource[1].start;
+	if (snd_fm_port[dev] >= 0)
+		snd_fm_port[dev] = pdev->resource[1].start;
 	snd_sb_port[dev] = pdev->resource[2].start;
 	snd_irq[dev] = pdev->irq_resource[0].start;
 	snd_dma1[dev] = pdev->dma_resource[0].start;
@@ -338,7 +342,7 @@
 	snd_cport[dev] = pdev->resource[0].start;
 	snd_printdd("isapnp CTRL: control port=0x%lx\n", snd_cport[dev]);
 	/* MPU initialization */
-	if (acard->mpu) {
+	if (acard->mpu && snd_mpu_port[dev] >= 0) {
 		pdev = acard->mpu;
 		if (pdev->prepare(pdev) < 0) {
 			acard->wss->deactivate(acard->wss);
@@ -347,7 +351,7 @@
 		}
 		if (snd_mpu_port[dev] != SNDRV_AUTO_PORT)
 			isapnp_resource_change(&pdev->resource[0], snd_mpu_port[dev], 2);
-		if (snd_mpu_irq[dev] != SNDRV_AUTO_IRQ)
+		if (snd_mpu_irq[dev] != SNDRV_AUTO_IRQ && snd_mpu_irq[dev] >= 0)
 			isapnp_resource_change(&pdev->irq_resource[0], snd_mpu_irq[dev], 1);
 		if (pdev->activate(pdev)<0) {
 			snd_mpu_port[dev] = SNDRV_AUTO_PORT;
@@ -355,7 +359,8 @@
 			printk(KERN_ERR IDENT " isapnp configure failed for MPU (out of resources?)\n");
 		} else {
 			snd_mpu_port[dev] = pdev->resource[0].start;
-			if (pdev->irq_resource[0].flags & IORESOURCE_IRQ) {
+			if ((pdev->irq_resource[0].flags & IORESOURCE_IRQ) &&
+			    snd_mpu_irq[dev] >= 0) {
 				snd_mpu_irq[dev] = pdev->irq_resource[0].start;
 			} else {
 				snd_mpu_irq[dev] = -1;	/* disable interrupt */
@@ -510,7 +515,7 @@
 		}
 	}
 
-	if (snd_mpu_irq[dev] >= 0 && snd_mpu_irq[dev] != SNDRV_AUTO_IRQ) {
+	if (snd_mpu_port[dev] != SNDRV_AUTO_PORT) {
 		if (snd_mpu401_uart_new(card, 0, MPU401_HW_CS4232,
 					snd_mpu_port[dev], 0,
 					snd_mpu_irq[dev],
diff -Nru a/sound/isa/gus/Makefile b/sound/isa/gus/Makefile
--- a/sound/isa/gus/Makefile	Sun Apr 28 20:10:39 2002
+++ b/sound/isa/gus/Makefile	Sun Apr 28 20:10:39 2002
@@ -35,4 +35,6 @@
   obj-$(CONFIG_SND_INTERWAVE_STB) += snd-gus-synth.o
 endif
 
+obj-m := $(sort $(obj-m))
+
 include $(TOPDIR)/Rules.make
diff -Nru a/sound/isa/sb/Makefile b/sound/isa/sb/Makefile
--- a/sound/isa/sb/Makefile	Sun Apr 28 20:10:35 2002
+++ b/sound/isa/sb/Makefile	Sun Apr 28 20:10:35 2002
@@ -34,4 +34,6 @@
   obj-$(CONFIG_SND_SBAWE) += snd-emu8000-synth.o
 endif
 
+obj-m := $(sort $(obj-m))
+
 include $(TOPDIR)/Rules.make
diff -Nru a/sound/isa/sb/sb_common.c b/sound/isa/sb/sb_common.c
--- a/sound/isa/sb/sb_common.c	Sun Apr 28 20:10:39 2002
+++ b/sound/isa/sb/sb_common.c	Sun Apr 28 20:10:39 2002
@@ -258,9 +258,9 @@
 	}
 	chip->dma8 = dma8;
 	if (dma16 >= 0) {
-		if (dma16 < 5 || dma16 > 7) {
-			/* Vibra has no 16bit DMA - no duplex */
-			dma16 = dma8;
+		if (hardware != SB_HW_ALS100 && (dma16 < 5 || dma16 > 7)) {
+			/* no duplex */
+			dma16 = -1;
 		} else if (request_dma(dma16, "SoundBlaster - 16bit")) {
 			snd_sbdsp_free(chip);
 			return -EBUSY;
diff -Nru a/sound/pci/Config.help b/sound/pci/Config.help
--- a/sound/pci/Config.help	Sun Apr 28 20:10:32 2002
+++ b/sound/pci/Config.help	Sun Apr 28 20:10:32 2002
@@ -22,6 +22,10 @@
   Say 'Y' or 'M' to include support for RME Digi96, Digi96/8 and
   Digi96/8 PRO/PAD/PST.
 
+CONFIG_SND_RME32
+  Say 'Y' or 'M' to include support for RME Digi32, Digi32/8 and
+  Digi32 PRO audio devices.
+
 CONFIG_SND_RME9652
   Say 'Y' or 'M' to include support for RME Hammerfall (RME Digi9652 /
   Digi9636) soundcards.
diff -Nru a/sound/pci/Config.in b/sound/pci/Config.in
--- a/sound/pci/Config.in	Sun Apr 28 20:10:35 2002
+++ b/sound/pci/Config.in	Sun Apr 28 20:10:35 2002
@@ -11,6 +11,7 @@
 dep_tristate 'EMU10K1 (SB Live!, E-mu APS)' CONFIG_SND_EMU10K1 $CONFIG_SND
 dep_tristate 'Korg 1212 IO' CONFIG_SND_KORG1212 $CONFIG_SND
 dep_tristate 'NeoMagic NM256AV/ZX' CONFIG_SND_NM256 $CONFIG_SND
+dep_tristate 'RME Digi32, 32/8, 32 PRO' CONFIG_SND_RME32 $CONFIG_SND
 dep_tristate 'RME Digi96, 96/8, 96/8 PRO' CONFIG_SND_RME96 $CONFIG_SND
 dep_tristate 'RME Digi9652 (Hammerfall)' CONFIG_SND_RME9652 $CONFIG_SND
 dep_tristate 'Trident 4D-Wave DX/NX; SiS 7018' CONFIG_SND_TRIDENT $CONFIG_SND
@@ -28,5 +29,16 @@
 dep_tristate 'S3 SonicVibes' CONFIG_SND_SONICVIBES $CONFIG_SND
 dep_tristate 'VIA 82C686A/B South Bridge' CONFIG_SND_VIA686 $CONFIG_SND
 dep_tristate 'VIA 8233 South Bridge' CONFIG_SND_VIA8233 $CONFIG_SND
+
+# define gameport if necessary
+if [ "$CONFIG_INPUT_GAMEPORT" != "n" ]; then
+  if [ "$CONFIG_SND_CS4281" = "y" \
+       -o "$CONFIG_SND_ES1938" = "y" \
+       -o "$CONFIG_SND_CS46XX" = "y" \
+       -o "$CONFIG_SND_SONICVIBES" = "y" \
+       -o "$CONFIG_SND_TRIDENT" = "y" ]; then
+    define_tristate CONFIG_INPUT_GAMEPORT y
+  fi
+fi
 
 endmenu
diff -Nru a/sound/pci/Makefile b/sound/pci/Makefile
--- a/sound/pci/Makefile	Sun Apr 28 20:10:30 2002
+++ b/sound/pci/Makefile	Sun Apr 28 20:10:30 2002
@@ -19,6 +19,7 @@
 snd-ice1712-objs := ice1712.o
 snd-intel8x0-objs := intel8x0.o
 snd-maestro3-objs := maestro3.o
+snd-rme32-objs := rme32.o
 snd-rme96-objs := rme96.o
 snd-sonicvibes-objs := sonicvibes.o
 snd-via686-objs := via686.o
@@ -36,6 +37,7 @@
 obj-$(CONFIG_SND_ICE1712) += snd-ice1712.o
 obj-$(CONFIG_SND_INTEL8X0) += snd-intel8x0.o
 obj-$(CONFIG_SND_MAESTRO3) += snd-maestro3.o
+obj-$(CONFIG_SND_RME32) += snd-rme32.o
 obj-$(CONFIG_SND_RME96) += snd-rme96.o
 obj-$(CONFIG_SND_SONICVIBES) += snd-sonicvibes.o
 obj-$(CONFIG_SND_VIA686) += snd-via686.o
diff -Nru a/sound/pci/ac97/Makefile b/sound/pci/ac97/Makefile
--- a/sound/pci/ac97/Makefile	Sun Apr 28 20:10:31 2002
+++ b/sound/pci/ac97/Makefile	Sun Apr 28 20:10:31 2002
@@ -28,4 +28,6 @@
 obj-$(CONFIG_SND_TRIDENT) += snd-ac97-codec.o
 obj-$(CONFIG_SND_YMFPCI) += snd-ac97-codec.o
 
+obj-m := $(sort $(obj-m))
+
 include $(TOPDIR)/Rules.make
diff -Nru a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c
--- a/sound/pci/ac97/ac97_codec.c	Sun Apr 28 20:10:35 2002
+++ b/sound/pci/ac97/ac97_codec.c	Sun Apr 28 20:10:35 2002
@@ -51,16 +51,19 @@
 static void snd_ac97_proc_init(snd_card_t * card, ac97_t * ac97);
 static void snd_ac97_proc_done(ac97_t * ac97);
 
-static int patch_wolfson(ac97_t * ac97);
-
+static int patch_wolfson00(ac97_t * ac97);
+static int patch_wolfson03(ac97_t * ac97);
+static int patch_wolfson04(ac97_t * ac97);
 static int patch_tritech_tr28028(ac97_t * ac97);
 static int patch_sigmatel_stac9708(ac97_t * ac97);
 static int patch_sigmatel_stac9721(ac97_t * ac97);
 static int patch_sigmatel_stac9744(ac97_t * ac97);
 static int patch_sigmatel_stac9756(ac97_t * ac97);
 static int patch_cirrus_cs4299(ac97_t * ac97);
+static int patch_cirrus_cs4205(ac97_t * ac97);
 static int patch_ad1819(ac97_t * ac97);
 static int patch_ad1881(ac97_t * ac97);
+static int patch_ad1886(ac97_t * ac97);
 
 typedef struct {
 	unsigned int id;
@@ -82,7 +85,7 @@
 { 0x54524100, 0xffffff00, "TriTech",		NULL },
 { 0x54584e00, 0xffffff00, "Texas Instruments",	NULL },
 { 0x57454300, 0xffffff00, "Winbond",		NULL },
-{ 0x574d4c00, 0xffffff00, "Wolfson",		patch_wolfson },
+{ 0x574d4c00, 0xffffff00, "Wolfson",		NULL },
 { 0x594d4800, 0xffffff00, "Yamaha",		NULL },
 { 0x83847600, 0xffffff00, "SigmaTel",		NULL },
 { 0x45838300, 0xffffff00, "ESS Technology",	NULL },
@@ -99,7 +102,7 @@
 { 0x41445340, 0xffffffff, "AD1881",		patch_ad1881 },
 { 0x41445348, 0xffffffff, "AD1881A",		patch_ad1881 },
 { 0x41445360, 0xffffffff, "AD1885",		patch_ad1881 },
-{ 0x41445361, 0xffffffff, "AD1886",		patch_ad1881 },
+{ 0x41445361, 0xffffffff, "AD1886",		patch_ad1886 },
 { 0x41445362, 0xffffffff, "AD1887",		patch_ad1881 },
 { 0x414c4300, 0xfffffff0, "RL5306",	 	NULL },
 { 0x414c4310, 0xfffffff0, "RL5382", 		NULL },
@@ -111,7 +114,7 @@
 { 0x42525928, 0xfffffff8, "CS4294",		NULL },
 { 0x43525930, 0xfffffff8, "CS4299",		patch_cirrus_cs4299 },
 { 0x43525948, 0xfffffff8, "CS4201",		NULL },
-{ 0x43525958, 0xfffffff8, "CS4205",		NULL },
+{ 0x43525958, 0xfffffff8, "CS4205",		patch_cirrus_cs4205 },
 { 0x43525960, 0xfffffff8, "CS4291",		NULL },
 { 0x48525300, 0xffffff00, "HMP9701",		NULL },
 { 0x49434501, 0xffffffff, "ICE1230",		NULL },
@@ -124,9 +127,9 @@
 { 0x54524123, 0xffffffff, "TR28602",		NULL }, // only guess --jk [TR28023 = eMicro EM28023 (new CT1297)]
 { 0x54584e20, 0xffffffff, "TLC320AD9xC",	NULL },
 { 0x57454301, 0xffffffff, "W83971D",		NULL },
-{ 0x574d4c00, 0xffffffff, "WM9701A",		NULL },
-{ 0x574d4c03, 0xffffffff, "WM9703/9704",	NULL },
-{ 0x574d4c04, 0xffffffff, "WM9704 (quad)",	NULL },
+{ 0x574d4c00, 0xffffffff, "WM9701A",		patch_wolfson00 },
+{ 0x574d4c03, 0xffffffff, "WM9703/9707",	patch_wolfson03 },
+{ 0x574d4c04, 0xffffffff, "WM9704 (quad)",	patch_wolfson04 },
 { 0x594d4800, 0xffffffff, "YMF743",		NULL },
 { 0x83847600, 0xffffffff, "STAC9700/83/84",	NULL },
 { 0x83847604, 0xffffffff, "STAC9701/3/4/5",	NULL },
@@ -156,6 +159,9 @@
 #define AC97_ID_STAC9744	0x83847644
 #define AC97_ID_STAC9756	0x83847656
 
+#define SPDIF_CS4205            0x68
+#define MODE_CS4205            0x5e
+
 static const char *snd_ac97_stereo_enhancements[] =
 {
   /*   0 */ "No 3D Stereo Enhancement",
@@ -722,8 +728,8 @@
 		new |= ucontrol->value.iec958.status[0] & (IEC958_AES0_PRO_FS|IEC958_AES0_PRO_EMPHASIS_5015);
 		switch (new & IEC958_AES0_PRO_FS) {
 		case IEC958_AES0_PRO_FS_44100: val |= 0<<12; break;
-		case IEC958_AES0_PRO_FS_32000: val |= 2<<12; break;
-		case IEC958_AES0_PRO_FS_48000: val |= 1<<12; break;
+		case IEC958_AES0_PRO_FS_48000: val |= 2<<12; break;
+		case IEC958_AES0_PRO_FS_32000: val |= 3<<12; break;
 		default:		       val |= 1<<12; break;
 		}
 		if ((new & IEC958_AES0_PRO_EMPHASIS) == IEC958_AES0_PRO_EMPHASIS_5015)
@@ -731,7 +737,7 @@
 	} else {
 		new |= ucontrol->value.iec958.status[0] & (IEC958_AES0_CON_EMPHASIS_5015|IEC958_AES0_CON_NOT_COPYRIGHT);
 		new |= ((ucontrol->value.iec958.status[1] & (IEC958_AES1_CON_CATEGORY|IEC958_AES1_CON_ORIGINAL)) << 8);
-		new |= ((ucontrol->value.iec958.status[3] & IEC958_AES3_CON_FS) << 8);
+		new |= ((ucontrol->value.iec958.status[3] & IEC958_AES3_CON_FS) << 24);
 		if ((new & IEC958_AES0_CON_EMPHASIS) == IEC958_AES0_CON_EMPHASIS_5015)
 			val |= 1<<3;
 		if (!(new & IEC958_AES0_CON_NOT_COPYRIGHT))
@@ -744,7 +750,21 @@
 		default:		       val |= 1<<12; break;
 		}
 	}
-	change = snd_ac97_update_bits(ac97, AC97_SPDIF, 0x3fff, val);
+
+	
+
+	if (ac97->flags & AC97_CS_SPDIF) {
+		int x = (val >> 12) & 0x03;
+		switch (x) {
+		case 0: x = 1; break;  // 44.1
+		case 2: x = 0; break;  // 48.0
+		default: x = 0; break; // illegal.
+		}
+		change = snd_ac97_update_bits(ac97, SPDIF_CS4205, 0x3fff, ((val & 0xcfff) | (x << 12)));
+	} else {
+		change = snd_ac97_update_bits(ac97, AC97_SPDIF, 0x3fff, val);
+	}
+
 	change |= ac97->spdif_status != new;
 	ac97->spdif_status = new;
 	spin_unlock(&ac97->reg_lock);
@@ -773,10 +793,16 @@
 		get: snd_ac97_spdif_default_get,
 		put: snd_ac97_spdif_default_put,
 	},
+
 	AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH),AC97_EXTENDED_STATUS, 2, 1, 0),
 	AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "AC97-SPSA",AC97_EXTENDED_STATUS, 4, 3, 0)
 };
 
+static const snd_kcontrol_new_t snd_ac97_cs4205_controls_spdif[2] = {
+    AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH),SPDIF_CS4205, 15, 1, 0),
+    AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "AC97-SPSA",MODE_CS4205, 0, 3, 0)
+};
+
 #define AD18XX_PCM_BITS(xname, codec, shift, mask) \
 { iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_ac97_ad18xx_pcm_info_bits, \
   get: snd_ac97_ad18xx_pcm_get_bits, put: snd_ac97_ad18xx_pcm_put_bits, \
@@ -1270,12 +1296,27 @@
 	}
 	
 	/* build S/PDIF controls */
-	if (ac97->ext_id & 0x0004) {
-		for (idx = 0; idx < 5; idx++)
-			if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_spdif[idx], ac97))) < 0)
-				return err;
-		/* consumer,PCM audio,no copyright,no preemphasis,PCM coder,original */
-		snd_ac97_write_cache(ac97, AC97_SPDIF, 0x2a20);
+	if (ac97->ext_id & AC97_EA_SPDIF) {
+		if (ac97->flags & AC97_CS_SPDIF) {
+			for (idx = 0; idx < 3; idx++)
+				if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_spdif[idx], ac97))) < 0)
+					return err;
+			for (idx = 0; idx < 2; idx++)
+				if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_cs4205_controls_spdif[idx], ac97))) < 0)
+					return err;
+		} else {
+			for (idx = 0; idx < 5; idx++)
+				if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_spdif[idx], ac97))) < 0)
+					return err;
+		}
+
+		/* set default PCM S/PDIF params */
+		/* consumer,PCM audio,no copyright,no preemphasis,PCM coder,original,48000Hz */
+	        if (ac97->flags & AC97_CS_SPDIF)
+			snd_ac97_write_cache(ac97, SPDIF_CS4205, 0x0a20);
+		else 
+			snd_ac97_write_cache(ac97, AC97_SPDIF, 0x2a20);
+
 		ac97->spdif_status = SNDRV_PCM_DEFAULT_CON_SPDIF;
 	}
 	
@@ -1511,6 +1552,7 @@
 	unsigned short val, tmp, ext;
 	static const char *spdif_slots[4] = { " SPDIF=3/4", " SPDIF=7/8", " SPDIF=6/9", " SPDIF=res" };
 	static const char *spdif_rates[4] = { " Rate=44.1kHz", " Rate=res", " Rate=48kHz", " Rate=32kHz" };
+	static const char *spdif_rates_cs4205[4] = { " Rate=48kHz", " Rate=44.1kHz", " Rate=res", " Rate=res" };
 
 	id = snd_ac97_read(ac97, AC97_VENDOR_ID1) << 16;
 	id |= snd_ac97_read(ac97, AC97_VENDOR_ID2);
@@ -1557,6 +1599,7 @@
 		    val & 0x0100 ? "Mic2" : "Mic1",
 		    val & 0x0080 ? "on" : "off");
 	ext = snd_ac97_read(ac97, AC97_EXTENDED_ID);
+
 	if (ext == 0)
 		return;
 	snd_iprintf(buffer, "Extended ID      : codec=%i rev=%i%s%s%s%s DSA=%i%s%s%s%s\n",
@@ -1605,8 +1648,12 @@
 		val = snd_ac97_read(ac97, AC97_PCM_MIC_ADC_RATE);
 		snd_iprintf(buffer, "PCM MIC ADC      : %iHz\n", val);
 	}
-	if (ext & 0x0004) {
-		val = snd_ac97_read(ac97, AC97_SPDIF);
+	if ((ext & 0x0004) || (ac97->flags & AC97_CS_SPDIF)) {
+	        if (ac97->flags & AC97_CS_SPDIF)
+			val = snd_ac97_read(ac97, SPDIF_CS4205);
+		else
+			val = snd_ac97_read(ac97, AC97_SPDIF);
+
 		snd_iprintf(buffer, "SPDIF Control    :%s%s%s%s Category=0x%x Generation=%i%s%s%s\n",
 			val & 0x0001 ? " PRO" : " Consumer",
 			val & 0x0002 ? " Non-audio" : " PCM",
@@ -1614,9 +1661,15 @@
 			val & 0x0008 ? " Preemph50/15" : "",
 			(val & 0x07f0) >> 4,
 			(val & 0x0800) >> 11,
-			spdif_rates[(val & 0x3000) >> 12],
-			val & 0x4000 ? " DRS" : "",
-			val & 0x8000 ? " Validity" : "");
+			(ac97->flags & AC97_CS_SPDIF) ?
+			    spdif_rates_cs4205[(val & 0x3000) >> 12] :
+			    spdif_rates[(val & 0x3000) >> 12],
+			(ac97->flags & AC97_CS_SPDIF) ?
+			    (val & 0x4000 ? " Validity" : "") :
+			    (val & 0x4000 ? " DRS" : ""),
+			(ac97->flags & AC97_CS_SPDIF) ?
+			    (val & 0x8000 ? " Enabled" : "") :
+			    (val & 0x8000 ? " Validity" : ""));
 	}
 }
 
@@ -1668,6 +1721,7 @@
 	ac97_t *ac97 = snd_magic_cast(ac97_t, entry->private_data, return);
 
 	if ((ac97->id & 0xffffff40) == AC97_ID_AD1881) {	// Analog Devices AD1881/85/86
+
 		int idx;
 		down(&ac97->spec.ad18xx.mutex);
 		for (idx = 0; idx < 3; idx++)
@@ -1733,9 +1787,42 @@
  *  Chip specific initialization
  */
 
-static int patch_wolfson(ac97_t * ac97)
+static int patch_wolfson00(ac97_t * ac97)
 {
-	// for all wolfson codecs (is this correct? --jk)
+	/* This sequence is suspect because it was designed for
+	   the WM9704, and is known to fail when applied to the
+	   WM9707.  If you're having trouble initializing a
+	   WM9700, this is the place to start looking.
+	   Randolph Bentson <bentson@holmsjoen.com> */
+
+	// WM9701A
+	snd_ac97_write_cache(ac97, 0x72, 0x0808);
+	snd_ac97_write_cache(ac97, 0x74, 0x0808);
+
+	// patch for DVD noise
+	snd_ac97_write_cache(ac97, 0x5a, 0x0200);
+
+	// init vol
+	snd_ac97_write_cache(ac97, 0x70, 0x0808);
+
+	snd_ac97_write_cache(ac97, AC97_SURROUND_MASTER, 0x0000);
+	return 0;
+}
+
+static int patch_wolfson03(ac97_t * ac97)
+{
+	/* This is known to work for the ViewSonic ViewPad 1000
+	   Randolph Bentson <bentson@holmsjoen.com> */
+
+	// WM9703/9707
+	snd_ac97_write_cache(ac97, 0x72, 0x0808);
+	snd_ac97_write_cache(ac97, 0x20, 0x8000);
+	return 0;
+}
+
+static int patch_wolfson04(ac97_t * ac97)
+{
+	// WM9704
 	snd_ac97_write_cache(ac97, 0x72, 0x0808);
 	snd_ac97_write_cache(ac97, 0x74, 0x0808);
 
@@ -1819,6 +1906,26 @@
 static int patch_cirrus_cs4299(ac97_t * ac97)
 {
 	ac97->flags |= AC97_HAS_PC_BEEP;	/* force the detection of PC Beep */
+	
+	return 0;
+}
+
+static int patch_cirrus_cs4205(ac97_t * ac97)
+{
+  /* Basically, the cs4205 has non-standard sp/dif registers.
+     WHY CAN'T ANYONE FOLLOW THE BLOODY SPEC?  *sigh*
+      - sp/dif EA ID is not set, but sp/dif is always present.
+        - enable/disable is spdif register bit 15.
+      - sp/dif control register is 0x68.  differs from AC97:
+        - valid is bit 14 (vs 15)
+	- no DRS
+	- only 44.1/48k [00 = 48, 01=44,1] (AC97 is 00=44.1, 10=48)
+      - sp/dif ssource select is in 0x5e bits 0,1.
+  */
+
+	ac97->flags |= AC97_CS_SPDIF; 
+        ac97->ext_id |= AC97_EA_SPDIF;	/* force the detection of spdif */
+	snd_ac97_write_cache(ac97, MODE_CS4205, 0x0080);
 	return 0;
 }
 
@@ -1936,6 +2043,15 @@
 	return 0;
 }
 
+static int patch_ad1886(ac97_t * ac97)
+{
+	patch_ad1881(ac97);
+	/* Presario700 workaround */
+	/* for Jack Sense/SPDIF Register misetting causing */
+	snd_ac97_write_cache(ac97, AC97_AD_JACK_SPDIF, 0x0010);
+	return 0;
+}
+
 /*
  *  PCM support
  */
@@ -1978,6 +2094,7 @@
 	} while (end_time - (signed long)jiffies >= 0);
 	snd_ac97_update(ac97, reg, tmp & 0xffff);
 	udelay(10);
+	// XXXX update spdif rate here too?
 	snd_ac97_update_bits(ac97, AC97_POWERDOWN, mask, 0);
 	end_time = jiffies + (HZ / 50);
 	do {
diff -Nru a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c
--- a/sound/pci/ali5451/ali5451.c	Sun Apr 28 20:10:38 2002
+++ b/sound/pci/ali5451/ali5451.c	Sun Apr 28 20:10:38 2002
@@ -32,6 +32,7 @@
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
+#include <linux/pci.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
diff -Nru a/sound/pci/als4000.c b/sound/pci/als4000.c
--- a/sound/pci/als4000.c	Sun Apr 28 20:10:38 2002
+++ b/sound/pci/als4000.c	Sun Apr 28 20:10:38 2002
@@ -47,6 +47,7 @@
 #include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/init.h>
+#include <linux/pci.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
diff -Nru a/sound/pci/cmipci.c b/sound/pci/cmipci.c
--- a/sound/pci/cmipci.c	Sun Apr 28 20:10:31 2002
+++ b/sound/pci/cmipci.c	Sun Apr 28 20:10:31 2002
@@ -21,6 +21,7 @@
 #include <asm/io.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
+#include <linux/pci.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/info.h>
@@ -105,12 +106,13 @@
 
 #define CM_REG_CHFORMAT		0x08
 
-#define CM_CHB3D5C		0x80000000	/* 5 channels */
-#define CM_CHB3D		0x20000000	/* 4,5,6 channels */
+#define CM_CHB3D5C		0x80000000	/* 5,6 channels */
+#define CM_CHB3D		0x20000000	/* 4 channels */
 
 #define CM_CHIP_MASK1		0x1f000000
 #define CM_CHIP_037		0x01000000
 
+#define CM_SPDIF_SELECT1	0x00080000	/* for model <= 037 ? */
 #define CM_AC3EN1		0x00100000	/* enable AC3: model 037 */
 #define CM_SPD24SEL		0x00020000	/* 24bit spdif: model 037 */
 #define CM_SPDIF_INVERSE	0x00010000
@@ -189,7 +191,7 @@
 #define CM_SFIL_MASK		0x30000000
 #define CM_TXVX			0x08000000
 #define CM_N4SPK3D		0x04000000	/* 4ch output */
-#define CM_SPDO5V		0x02000000	/* 5V spdif output */
+#define CM_SPDO5V		0x02000000	/* 5V spdif output (1 = 0.5v (coax)) */
 #define CM_SPDIF48K		0x01000000	/* write */
 #define CM_SPATUS48K		0x01000000	/* read */
 #define CM_ENDBDAC		0x00800000	/* enable dual dac */
@@ -204,7 +206,7 @@
 #define CM_VIDWPPRT		0x00002000
 #define CM_SFILENB		0x00001000
 #define CM_MMODE_MASK		0x00000E00
-#define CM_SPDIF_SELECT		0x00000100	/* for model > 039 ? */
+#define CM_SPDIF_SELECT2	0x00000100	/* for model > 039 ? */
 #define CM_ENCENTER		0x00000080	/* shared with FLINKON? */
 #define CM_FLINKON		0x00000080
 #define CM_FLINKOFF		0x00000040
@@ -556,8 +558,8 @@
 			snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D);
 			snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_CHB3D5C);
 		} else {
-			snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_CHB3D);
 			snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D5C);
+			snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_CHB3D);
 		}
 		if (channels == 6) {
 			snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_CHB3D6C);
@@ -2142,7 +2144,8 @@
 DEFINE_BIT_SWITCH_ARG(spdif_0, CM_REG_FUNCTRL1, CM_SPDF_0, 0);
 DEFINE_BIT_SWITCH_ARG(spdo_48k, CM_REG_MISC_CTRL, CM_SPDF_AC97|CM_SPDIF48K, 0);
 #endif
-DEFINE_BIT_SWITCH_ARG(spdif_in_1_2, CM_REG_MISC_CTRL, CM_SPDIF_SELECT, 0, 0);
+DEFINE_BIT_SWITCH_ARG(spdif_in_sel1, CM_REG_CHFORMAT, CM_SPDIF_SELECT1, 0, 0);
+DEFINE_BIT_SWITCH_ARG(spdif_in_sel2, CM_REG_MISC_CTRL, CM_SPDIF_SELECT2, 0, 0);
 DEFINE_BIT_SWITCH_ARG(spdif_enable, CM_REG_LEGACY_CTRL, CM_ENSPDOUT, 0, 0);
 DEFINE_BIT_SWITCH_ARG(spdo2dac, CM_REG_FUNCTRL1, CM_SPDO2DAC, 0, 1);
 DEFINE_BIT_SWITCH_ARG(spdi_valid, CM_REG_MISC, CM_SPDVALID, 1, 0);
@@ -2242,12 +2245,13 @@
 static snd_kcontrol_new_t snd_cmipci_old_mixer_switches[] __devinitdata = {
 	DEFINE_MIXER_SWITCH("IEC958 Mix Analog", spdif_dac_out),
 	DEFINE_MIXER_SWITCH("IEC958 In Phase Inverse", spdi_phase),
+	DEFINE_MIXER_SWITCH("IEC958 In Select", spdif_in_sel1),
 };
 
 /* only for model 039 or later */
 static snd_kcontrol_new_t snd_cmipci_extra_mixer_switches[] __devinitdata = {
 	DEFINE_MIXER_SWITCH("Line-In As Bass", line_bass),
-	DEFINE_MIXER_SWITCH("IEC958 In Select", spdif_in_1_2),
+	DEFINE_MIXER_SWITCH("IEC958 In Select", spdif_in_sel2),
 	DEFINE_MIXER_SWITCH("IEC958 In Phase Inverse", spdi_phase2),
 };
 
diff -Nru a/sound/pci/cs4281.c b/sound/pci/cs4281.c
--- a/sound/pci/cs4281.c	Sun Apr 28 20:10:35 2002
+++ b/sound/pci/cs4281.c	Sun Apr 28 20:10:35 2002
@@ -24,6 +24,7 @@
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
+#include <linux/pci.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/control.h>
diff -Nru a/sound/pci/cs46xx/cs46xx.c b/sound/pci/cs46xx/cs46xx.c
--- a/sound/pci/cs46xx/cs46xx.c	Sun Apr 28 20:10:39 2002
+++ b/sound/pci/cs46xx/cs46xx.c	Sun Apr 28 20:10:39 2002
@@ -26,6 +26,7 @@
 */
 
 #include <sound/driver.h>
+#include <linux/pci.h>
 #include <linux/time.h>
 #include <sound/core.h>
 #include <sound/cs46xx.h>
diff -Nru a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c
--- a/sound/pci/cs46xx/cs46xx_lib.c	Sun Apr 28 20:10:31 2002
+++ b/sound/pci/cs46xx/cs46xx_lib.c	Sun Apr 28 20:10:31 2002
@@ -34,6 +34,7 @@
 #include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
+#include <linux/pci.h>
 #include <linux/pm.h>
 #include <linux/init.h>
 #include <linux/slab.h>
diff -Nru a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c
--- a/sound/pci/emu10k1/emu10k1.c	Sun Apr 28 20:10:30 2002
+++ b/sound/pci/emu10k1/emu10k1.c	Sun Apr 28 20:10:30 2002
@@ -21,6 +21,7 @@
 
 #include <sound/driver.h>
 #include <linux/init.h>
+#include <linux/pci.h>
 #include <linux/time.h>
 #include <sound/core.h>
 #include <sound/emu10k1.h>
diff -Nru a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c
--- a/sound/pci/emu10k1/emu10k1_main.c	Sun Apr 28 20:10:36 2002
+++ b/sound/pci/emu10k1/emu10k1_main.c	Sun Apr 28 20:10:36 2002
@@ -29,6 +29,7 @@
 #include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
+#include <linux/pci.h>
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
 #include <sound/core.h>
@@ -165,7 +166,7 @@
 	memset(emu->silent_page, 0, PAGE_SIZE);
 	silent_page = emu->silent_page_dmaaddr << 1;
 	for (idx = 0; idx < MAXPAGES; idx++)
-		emu->ptb_pages[idx] = silent_page | idx;
+		emu->ptb_pages[idx] = cpu_to_le32(silent_page | idx);
 	snd_emu10k1_ptr_write(emu, PTB, 0, emu->ptb_pages_dmaaddr);
 	snd_emu10k1_ptr_write(emu, TCB, 0, 0);	/* taken from original driver */
 	snd_emu10k1_ptr_write(emu, TCBS, 0, 4);	/* taken from original driver */
@@ -630,9 +631,9 @@
 	
 	emu->fx8010.fxbus_mask = 0x303f;
 	if (extin_mask == 0)
-		extin_mask = 0x1fcf;
+		extin_mask = 0x3fcf;
 	if (extout_mask == 0)
-		extout_mask = 0x3fff;
+		extout_mask = 0x1fff;
 	emu->fx8010.extin_mask = extin_mask;
 	emu->fx8010.extout_mask = extout_mask;
 
diff -Nru a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c
--- a/sound/pci/emu10k1/emumixer.c	Sun Apr 28 20:10:34 2002
+++ b/sound/pci/emu10k1/emumixer.c	Sun Apr 28 20:10:34 2002
@@ -353,7 +353,10 @@
 {
 	emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
 
-	ucontrol->value.integer.value[0] = inl(emu->port + HCFG) & HCFG_GPOUT0 ? 0 : 1;
+	if (emu->audigy)
+		ucontrol->value.integer.value[0] = inl(emu->port + A_IOCFG) & A_IOCFG_GPOUT0 ? 1 : 0;
+	else
+		ucontrol->value.integer.value[0] = inl(emu->port + HCFG) & HCFG_GPOUT0 ? 1 : 0;
         return 0;
 }
 
@@ -363,20 +366,32 @@
 	unsigned long flags;
 	emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
 	unsigned int reg, val;
-	int change;
+	int change = 0;
 
 	spin_lock_irqsave(&emu->reg_lock, flags);
+	if (emu->audigy) {
+		reg = inl(emu->port + A_IOCFG);
+		val = ucontrol->value.integer.value[0] ? A_IOCFG_GPOUT0 : 0;
+		change = (reg & A_IOCFG_GPOUT0) != val;
+		if (change) {
+			reg &= ~A_IOCFG_GPOUT0;
+			reg |= val;
+			outl(reg | val, emu->port + A_IOCFG);
+		}
+	}
 	reg = inl(emu->port + HCFG);
-	val = ucontrol->value.integer.value[0] & 1 ? 0 : HCFG_GPOUT0;
-	change = (reg & HCFG_GPOUT0) != val;
-	reg &= ~HCFG_GPOUT0;
-	reg |= val;
-	outl(reg | val, emu->port + HCFG);
+	val = ucontrol->value.integer.value[0] ? HCFG_GPOUT0 : 0;
+	change |= (reg & HCFG_GPOUT0) != val;
+	if (change) {
+		reg &= ~HCFG_GPOUT0;
+		reg |= val;
+		outl(reg | val, emu->port + HCFG);
+	}
 	spin_unlock_irqrestore(&emu->reg_lock, flags);
         return change;
 }
 
-static snd_kcontrol_new_t snd_emu10k1_shared_spdif =
+static snd_kcontrol_new_t snd_emu10k1_shared_spdif __devinitdata =
 {
 	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
 	name:		"SB Live Analog/Digital Output Jack",
@@ -385,68 +400,14 @@
 	put:		snd_emu10k1_shared_spdif_put
 };
 
-#if 0 // XXX: not working yet..
-/*
- * Audigy analog / digital switches
- */
-static int audigy_output_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
-{
-	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
-	uinfo->count = 1;
-	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = 1;
-	return 0;
-}
-
-static int audigy_output_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
-{
-	emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
-	unsigned int mask = (unsigned int)kcontrol->private_value;
-
-	ucontrol->value.integer.value[0] = inl(emu->port + A_IOCFG) & mask ? 0 : 1;
-        return 0;
-}
-
-static int audigy_output_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
-{
-	unsigned long flags;
-	emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
-	unsigned int mask = (unsigned int)kcontrol->private_value;
-	unsigned int reg, oreg;
-	int change;
-
-	spin_lock_irqsave(&emu->reg_lock, flags);
-	reg = oreg = inl(emu->port + A_IOCFG);
-	reg &= ~mask;
-	reg |= ucontrol->value.integer.value[0] & 1 ? 0 : mask;
-	change = (reg != oreg);
-	if (change)
-		outl(reg, emu->port + A_IOCFG);
-	spin_unlock_irqrestore(&emu->reg_lock, flags);
-        return change;
-}
-
-static snd_kcontrol_new_t audigy_output_analog =
-{
-	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
-	name:		"Audigy Analog Output Switch",
-	info:		audigy_output_info,
-	get:		audigy_output_get,
-	put:		audigy_output_put,
-	private_value:	0x40,
-};
-
-static snd_kcontrol_new_t audigy_output_digital =
+static snd_kcontrol_new_t snd_audigy_shared_spdif __devinitdata =
 {
 	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
-	name:		"Audigy Digital Output Switch",
-	info:		audigy_output_info,
-	get:		audigy_output_get,
-	put:		audigy_output_put,
-	private_value:	0x04,
+	name:		"Audigy Analog/Digital Output Jack",
+	info:		snd_emu10k1_shared_spdif_info,
+	get:		snd_emu10k1_shared_spdif_get,
+	put:		snd_emu10k1_shared_spdif_put
 };
-#endif // XXX
-
 
 /*
  */
@@ -528,16 +489,10 @@
 	}
 
 	if (emu->audigy) {
-#if 0 // XXX
-		if ((kctl = snd_ctl_new1(&audigy_output_analog, emu)) == NULL)
-			return -ENOMEM;
-		if ((err = snd_ctl_add(card, kctl)))
-			return err;
-		if ((kctl = snd_ctl_new1(&audigy_output_digital, emu)) == NULL)
+		if ((kctl = snd_ctl_new1(&snd_audigy_shared_spdif, emu)) == NULL)
 			return -ENOMEM;
 		if ((err = snd_ctl_add(card, kctl)))
 			return err;
-#endif // XXX
 	} else {
 		if ((kctl = snd_ctl_new1(&snd_emu10k1_shared_spdif, emu)) == NULL)
 			return -ENOMEM;
diff -Nru a/sound/pci/emu10k1/memory.c b/sound/pci/emu10k1/memory.c
--- a/sound/pci/emu10k1/memory.c	Sun Apr 28 20:10:38 2002
+++ b/sound/pci/emu10k1/memory.c	Sun Apr 28 20:10:38 2002
@@ -31,7 +31,7 @@
  * aligned pages in others
  */
 #define __set_ptb_entry(emu,page,addr) \
-	((emu)->ptb_pages[page] = ((addr) << 1) | (page))
+	((emu)->ptb_pages[page] = cpu_to_le32(((addr) << 1) | (page)))
 
 #define UNIT_PAGES		(PAGE_SIZE / EMUPAGESIZE)
 #define MAX_ALIGN_PAGES		(MAXPAGES / UNIT_PAGES)
diff -Nru a/sound/pci/ens1370.c b/sound/pci/ens1370.c
--- a/sound/pci/ens1370.c	Sun Apr 28 20:10:33 2002
+++ b/sound/pci/ens1370.c	Sun Apr 28 20:10:33 2002
@@ -24,6 +24,7 @@
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
+#include <linux/pci.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/control.h>
@@ -1290,11 +1291,23 @@
 	ensoniq->u.es1371.ac97 = NULL;
 }
 
+static struct {
+	unsigned short vid;		/* vendor ID */
+	unsigned short did;		/* device ID */
+	unsigned char rev;		/* revision */
+} es1371_spdif_present[] = {
+	{ vid: PCI_VENDOR_ID_ENSONIQ, did: PCI_DEVICE_ID_ENSONIQ_CT5880, rev: CT5880REV_CT5880_C },
+	{ vid: PCI_VENDOR_ID_ENSONIQ, did: PCI_DEVICE_ID_ENSONIQ_CT5880, rev: CT5880REV_CT5880_D },
+	{ vid: PCI_VENDOR_ID_ENSONIQ, did: PCI_DEVICE_ID_ENSONIQ_CT5880, rev: CT5880REV_CT5880_E },
+	{ vid: PCI_VENDOR_ID_ENSONIQ, did: PCI_DEVICE_ID_ENSONIQ_ES1371, rev: ES1371REV_CT5880_A },
+	{ vid: PCI_ANY_ID, did: PCI_ANY_ID, rev: 0 }
+};
+
 static int snd_ensoniq_1371_mixer(ensoniq_t * ensoniq)
 {
 	snd_card_t *card = ensoniq->card;
 	ac97_t ac97;
-	int err;
+	int err, idx;
 
 	memset(&ac97, 0, sizeof(ac97));
 	ac97.write = snd_es1371_codec_write;
@@ -1303,8 +1316,13 @@
 	ac97.private_free = snd_ensoniq_mixer_free_ac97;
 	if ((err = snd_ac97_mixer(card, &ac97, &ensoniq->u.es1371.ac97)) < 0)
 		return err;
-	if (ensoniq->rev >= ES1371REV_ES1373_A)
-		snd_ctl_add(card, snd_ctl_new1(&snd_es1371_mixer_spdif, ensoniq));
+	for (idx = 0; es1371_spdif_present[idx].vid != (unsigned short)PCI_ANY_ID; idx++)
+		if (ensoniq->pci->vendor == es1371_spdif_present[idx].vid &&
+		    ensoniq->pci->device == es1371_spdif_present[idx].did &&
+		    ensoniq->rev == es1371_spdif_present[idx].rev) {
+			snd_ctl_add(card, snd_ctl_new1(&snd_es1371_mixer_spdif, ensoniq));
+			break;
+		}
 	return 0;
 }
 
diff -Nru a/sound/pci/es1938.c b/sound/pci/es1938.c
--- a/sound/pci/es1938.c	Sun Apr 28 20:10:37 2002
+++ b/sound/pci/es1938.c	Sun Apr 28 20:10:37 2002
@@ -50,6 +50,7 @@
 #include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/init.h>
+#include <linux/pci.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/control.h>
diff -Nru a/sound/pci/es1968.c b/sound/pci/es1968.c
--- a/sound/pci/es1968.c	Sun Apr 28 20:10:37 2002
+++ b/sound/pci/es1968.c	Sun Apr 28 20:10:37 2002
@@ -100,6 +100,7 @@
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
+#include <linux/pci.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
diff -Nru a/sound/pci/fm801.c b/sound/pci/fm801.c
--- a/sound/pci/fm801.c	Sun Apr 28 20:10:37 2002
+++ b/sound/pci/fm801.c	Sun Apr 28 20:10:37 2002
@@ -23,6 +23,7 @@
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/init.h>
+#include <linux/pci.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -471,7 +472,7 @@
 	status = inw(FM801_REG(chip, IRQ_STATUS));
 	if ((status & (FM801_IRQ_PLAYBACK|FM801_IRQ_CAPTURE|FM801_IRQ_MPU|FM801_IRQ_VOLUME)) == 0)
 		return;
-	if (chip->pcm && (status & FM801_IRQ_PLAYBACK)) {
+	if (chip->pcm && (status & FM801_IRQ_PLAYBACK) && chip->playback_substream) {
 		spin_lock(&chip->reg_lock);
 		chip->ply_buf++;
 		chip->ply_pos += chip->ply_count;
@@ -486,7 +487,7 @@
 		spin_unlock(&chip->reg_lock);
 		snd_pcm_period_elapsed(chip->playback_substream);
 	}
-	if (chip->pcm && (status & FM801_IRQ_CAPTURE)) {
+	if (chip->pcm && (status & FM801_IRQ_CAPTURE) && chip->capture_substream) {
 		spin_lock(&chip->reg_lock);
 		chip->cap_buf++;
 		chip->cap_pos += chip->cap_count;
diff -Nru a/sound/pci/ice1712.c b/sound/pci/ice1712.c
--- a/sound/pci/ice1712.c	Sun Apr 28 20:10:37 2002
+++ b/sound/pci/ice1712.c	Sun Apr 28 20:10:37 2002
@@ -30,6 +30,7 @@
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
+#include <linux/pci.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/control.h>
@@ -1158,6 +1159,7 @@
 	else
 		nval |= 0x04;
 	if (val != nval) {
+		reg[1] = nval;
 		if (snd_i2c_sendbytes(ice->cs8427, reg, 2) != 2) {
 			snd_i2c_unlock(ice->i2c);
 			return -EREMOTE;
diff -Nru a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c
--- a/sound/pci/intel8x0.c	Sun Apr 28 20:10:37 2002
+++ b/sound/pci/intel8x0.c	Sun Apr 28 20:10:37 2002
@@ -30,6 +30,7 @@
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
+#include <linux/pci.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -49,6 +50,7 @@
 		"{Intel,82901AB},"
 		"{Intel,82801BA},"
 		"{Intel,ICH3},"
+		"{Intel,ICH4},"
 		"{Intel,MX440},"
 		"{SiS,SI7012},"
 		"{NVidia,NForce Audio},"
@@ -116,6 +118,9 @@
 #ifndef PCI_DEVICE_ID_INTEL_ICH3
 #define PCI_DEVICE_ID_INTEL_ICH3	0x2485
 #endif
+#ifndef PCI_DEVICE_ID_INTEL_ICH4
+#define PCI_DEVICE_ID_INTEL_ICH4	0x24c5
+#endif
 #ifndef PCI_DEVICE_ID_SI_7012
 #define PCI_DEVICE_ID_SI_7012		0x7012
 #endif
@@ -205,7 +210,7 @@
 
 typedef struct {
 	unsigned long reg_offset;
-	u32 *bdbar;			/* CPU address (32bit) */
+	u32 *bdbar;				/* CPU address (32bit) */
 	unsigned int bdbar_addr;		/* PCI bus address (32bit) */
 	snd_pcm_substream_t *substream;
 	unsigned int physbuf;			/* physical address (32bit) */
@@ -230,6 +235,8 @@
 
 struct _snd_intel8x0 {
 	unsigned int device_type;
+	char ac97_name[32];
+	char ctrl_name[32];
 
 	unsigned long dma_playback_size;
 	unsigned long dma_capture_size;
@@ -284,6 +291,7 @@
 	{ 0x8086, 0x2425, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL },	/* 82901AB */
 	{ 0x8086, 0x2445, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL },	/* 82801BA */
 	{ 0x8086, 0x2485, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL },	/* ICH3 */
+	{ 0x8086, 0x24c5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL },	/* ICH4 */
 	{ 0x8086, 0x7195, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL },	/* 440MX */
 	{ 0x1039, 0x7012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_SIS },	/* SI7012 */
 	{ 0x10de, 0x01b1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL },	/* NFORCE */
@@ -470,9 +478,16 @@
 	intel8x0_t *chip = snd_magic_cast(intel8x0_t, dev_id, return);
 	unsigned int status;
 
+	spin_lock(&chip->reg_lock);
 	status = inl(ICHREG(chip, GLOB_STA));
-	if ((status & (ICH_MCINT | ICH_POINT | ICH_PIINT)) == 0)
+	if ((status & (ICH_MCINT | ICH_POINT | ICH_PIINT)) == 0) {
+		spin_unlock(&chip->reg_lock);
 		return;
+	}
+	/* ack first */
+	outl(status & (ICH_MCINT | ICH_POINT | ICH_PIINT), ICHREG(chip, GLOB_STA));
+	spin_unlock(&chip->reg_lock);
+
 	if (status & ICH_POINT)
 		snd_intel8x0_update(chip, &chip->playback);
 	if (status & ICH_PIINT)
@@ -1218,11 +1233,11 @@
 	chip->playback.size = chip->playback.fragsize = INTEL8X0_TESTBUF_SIZE;
 	chip->playback.substream = NULL; /* don't process interrupts */
 
-	spin_lock_irqsave(&chip->reg_lock, flags);
 	/* set rate */
 	snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, 48000);
 	snd_intel8x0_setup_periods(chip, &chip->playback);
 	port = chip->bmport + chip->playback.reg_offset;
+	spin_lock_irqsave(&chip->reg_lock, flags);
 	outb(ICH_IOCE | ICH_STARTBM, port + ICH_REG_PI_CR); /* trigger */
 	do_gettimeofday(&start_time);
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
@@ -1256,16 +1271,18 @@
 	else
 		t += stop_time.tv_usec - start_time.tv_usec;
 	if (t == 0) {
-		snd_printk("?? calculation error..\n");
+		snd_printk(KERN_ERR "?? calculation error..\n");
 		return;
 	}
 	pos = (pos / 4) * 1000;
 	pos = (pos / t) * 1000 + ((pos % t) * 1000) / t;
-	if ((pos > 40000 && pos < 47500) ||
-	    (pos > 48500 && pos < 50000)) {
+	if (pos < 40000 || pos >= 60000) 
+		/* abnormal value. hw problem? */
+		printk(KERN_INFO "intel8x0: measured clock %ld rejected\n", pos);
+	else if (pos < 47500 || pos > 48500)
+		/* not 48000Hz, tuning the clock.. */
 		chip->ac97->clock = (chip->ac97->clock * 48000) / pos;
-		printk(KERN_INFO "intel8x0: clocking to %d\n", chip->ac97->clock);
-	}
+	printk(KERN_INFO "intel8x0: clocking to %d\n", chip->ac97->clock);
 }
 
 static int snd_intel8x0_dev_free(snd_device_t *device)
@@ -1275,16 +1292,15 @@
 }
 
 static int __devinit snd_intel8x0_create(snd_card_t * card,
-				      struct pci_dev *pci,
-				      unsigned long device_type,
-				      intel8x0_t ** r_intel8x0)
+					 struct pci_dev *pci,
+					 unsigned long device_type,
+					 intel8x0_t ** r_intel8x0)
 {
 	intel8x0_t *chip;
 	int err;
 	static snd_device_ops_t ops = {
 		dev_free:	snd_intel8x0_dev_free,
 	};
-	char name[32];
 
 	*r_intel8x0 = NULL;
 
@@ -1301,15 +1317,15 @@
 	chip->pci = pci;
 	chip->irq = -1;
 	chip->port = pci_resource_start(pci, 0);
-	sprintf(name, "%s - AC'97", card->shortname);
-	if ((chip->res_port = request_region(chip->port, 256, name)) == NULL) {
+	sprintf(chip->ac97_name, "%s - AC'97", card->shortname);
+	if ((chip->res_port = request_region(chip->port, 256, chip->ac97_name)) == NULL) {
 		snd_intel8x0_free(chip);
 		snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->port, chip->port + 256 - 1);
 		return -EBUSY;
 	}
-	sprintf(name, "%s - Controller", card->shortname);
+	sprintf(chip->ctrl_name, "%s - Controller", card->shortname);
 	chip->bmport = pci_resource_start(pci, 1);
-	if ((chip->res_bmport = request_region(chip->bmport, 64, name)) == NULL) {
+	if ((chip->res_bmport = request_region(chip->bmport, 64, chip->ctrl_name)) == NULL) {
 		snd_intel8x0_free(chip);
 		snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->bmport, chip->bmport + 64 - 1);
 		return -EBUSY;
@@ -1393,6 +1409,7 @@
 	{ PCI_DEVICE_ID_INTEL_82901, "Intel ICH 82901AB" },
 	{ PCI_DEVICE_ID_INTEL_440MX, "Intel 440MX" },
 	{ PCI_DEVICE_ID_INTEL_ICH3, "Intel ICH3" },
+	{ PCI_DEVICE_ID_INTEL_ICH4, "Intel ICH4" },
 	{ PCI_DEVICE_ID_SI_7012, "SiS SI7012" },
 	{ PCI_DEVICE_ID_NVIDIA_MCP_AUDIO, "NVidia NForce" },
 	{ 0x764d, "AMD AMD8111" },
diff -Nru a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c
--- a/sound/pci/korg1212/korg1212.c	Sun Apr 28 20:10:30 2002
+++ b/sound/pci/korg1212/korg1212.c	Sun Apr 28 20:10:30 2002
@@ -23,6 +23,7 @@
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/init.h>
+#include <linux/pci.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/info.h>
diff -Nru a/sound/pci/maestro3.c b/sound/pci/maestro3.c
--- a/sound/pci/maestro3.c	Sun Apr 28 20:10:30 2002
+++ b/sound/pci/maestro3.c	Sun Apr 28 20:10:30 2002
@@ -36,6 +36,7 @@
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
+#include <linux/pci.h>
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
 #include <sound/core.h>
diff -Nru a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c
--- a/sound/pci/nm256/nm256.c	Sun Apr 28 20:10:34 2002
+++ b/sound/pci/nm256/nm256.c	Sun Apr 28 20:10:34 2002
@@ -29,6 +29,7 @@
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
+#include <linux/pci.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/info.h>
diff -Nru a/sound/pci/rme32.c b/sound/pci/rme32.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/pci/rme32.c	Sun Apr 28 20:10:39 2002
@@ -0,0 +1,1705 @@
+/*
+ *   ALSA driver for RME Digi32, Digi32/8 and Digi32 PRO audio interfaces
+ *
+ *	Copyright (c) 2002 Martin Langer <martin-langer@gmx.de>
+ *
+ *      Thanks to :        Anders Torger <torger@ludd.luth.se>,
+ *                         Henk Hesselink <henk@anda.nl>
+ *                         for writing the digi96-driver 
+ *                         and RME for all informations.
+ *
+ *   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.
+ * 
+ * 
+ *   ToDo: - Switching to Mono (32, 32/8, 32Pro)
+ *         - ADAT (32/8)
+ *         - Doublespeed-Bit, 96kHz (32Pro)
+ *         - DAC (32Pro)
+ *         - full duplex should be possible
+ *         - testing, testing, testing ....
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/asoundef.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
+
+EXPORT_NO_SYMBOLS;
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for RME Digi32 soundcard.");
+MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
+MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+MODULE_PARM_DESC(snd_id, "ID string for RME Digi32 soundcard.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable RME Digi32 soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_AUTHOR("Martin Langer <martin-langer@gmx.de>");
+MODULE_DESCRIPTION("RME Digi32, Digi32/8, Digi32 PRO");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{RME,Digi32}," "{RME,Digi32/8}," "{RME,Digi32 PRO}}");
+
+/* Defines for RME Digi32 series */
+#define RME32_DRIVER_VERSION "0.3.2"
+#define RME32_SPDIF_NCHANNELS 2
+
+/* Playback and capture buffer size */
+#define RME32_BUFFER_SIZE 0x20000
+
+/* IO area size */
+#define RME32_IO_SIZE 0x30000
+
+/* IO area offsets */
+#define RME32_IO_DATA_BUFFER        0x0
+#define RME32_IO_CONTROL_REGISTER   0x20000
+#define RME32_IO_GET_POS            0x20000
+#define RME32_IO_CONFIRM_ACTION_IRQ 0x20004
+#define RME32_IO_RESET_POS          0x20100
+
+/* Write control register bits */
+#define RME32_WCR_START     (1 << 0)
+#define RME32_WCR_MONO      (1 << 1)
+#define RME32_WCR_MODE24    (1 << 2)
+#define RME32_WCR_SEL       (1 << 3)
+#define RME32_WCR_FREQ_0    (1 << 4)
+#define RME32_WCR_FREQ_1    (1 << 5)
+#define RME32_WCR_INP_0     (1 << 6)
+#define RME32_WCR_INP_1     (1 << 7)
+#define RME32_WCR_RESET     (1 << 8)
+#define RME32_WCR_MUTE      (1 << 9)
+#define RME32_WCR_PRO       (1 << 10)
+#define RME32_WCR_DS_BM     (1 << 11)	/* only PRO-Version */
+#define RME32_WCR_ADAT      (1 << 12)	/* only Adat-Version */
+#define RME32_WCR_AUTOSYNC  (1 << 13)
+#define RME32_WCR_PD        (1 << 14)	/* only PRO-Version */
+#define RME32_WCR_EMP       (1 << 15)	/* only PRO-Version */
+
+#define RME32_WCR_BITPOS_FREQ_0 4
+#define RME32_WCR_BITPOS_FREQ_1 5
+#define RME32_WCR_BITPOS_INP_0 6
+#define RME32_WCR_BITPOS_INP_1 7
+
+/* Read control register bits */
+#define RME32_RCR_AUDIO_ADDR_MASK 0x10001
+#define RME32_RCR_LOCK      (1 << 23)
+#define RME32_RCR_ERF       (1 << 26)
+#define RME32_RCR_FREQ_0    (1 << 27)
+#define RME32_RCR_FREQ_1    (1 << 28)
+#define RME32_RCR_FREQ_2    (1 << 29)
+#define RME32_RCR_KMODE     (1 << 30)
+#define RME32_RCR_IRQ       (1 << 31)
+
+#define RME32_RCR_BITPOS_F0 27
+#define RME32_RCR_BITPOS_F1 28
+#define RME32_RCR_BITPOS_F2 29
+
+/* Input types */
+#define RME32_INPUT_OPTICAL 0
+#define RME32_INPUT_COAXIAL 1
+#define RME32_INPUT_INTERNAL 2
+#define RME32_INPUT_XLR 3
+
+/* Clock modes */
+#define RME32_CLOCKMODE_SLAVE 0
+#define RME32_CLOCKMODE_MASTER_32 1
+#define RME32_CLOCKMODE_MASTER_44 2
+#define RME32_CLOCKMODE_MASTER_48 3
+
+/* Block sizes in bytes */
+#define RME32_BLOCK_SIZE 8192
+
+/* Hardware revisions */
+#define RME32_32_REVISION 192
+#define RME32_328_REVISION_OLD 100
+#define RME32_328_REVISION_NEW 101
+#define RME32_PRO_REVISION_WITH_8412 192
+#define RME32_PRO_REVISION_WITH_8414 150
+
+
+/* PCI vendor/device ID's */
+#ifndef PCI_VENDOR_ID_XILINX_RME
+# define PCI_VENDOR_ID_XILINX_RME 0xea60
+#endif
+#ifndef PCI_DEVICE_ID_DIGI32
+# define PCI_DEVICE_ID_DIGI32 0x9896
+#endif
+#ifndef PCI_DEVICE_ID_DIGI32_8
+# define PCI_DEVICE_ID_DIGI32_8 0x9898
+#endif
+#ifndef PCI_DEVICE_ID_DIGI32_PRO
+# define PCI_DEVICE_ID_DIGI32_PRO 0x9897
+#endif
+
+typedef struct snd_rme32 {
+	spinlock_t lock;
+	int irq;
+	unsigned long port;
+	struct resource *res_port;
+	unsigned long iobase;
+
+	u32 wcreg;		/* cached write control register value */
+	u32 wcreg_spdif;	/* S/PDIF setup */
+	u32 wcreg_spdif_stream;	/* S/PDIF setup (temporary) */
+	u32 rcreg;		/* cached read control register value */
+
+	u8 rev;			/* card revision number */
+
+	pid_t capture_pid;
+	pid_t playback_pid;
+
+	snd_pcm_substream_t *playback_substream;
+	snd_pcm_substream_t *capture_substream;
+
+	int playback_frlog;	/* log2 of framesize */
+	int capture_frlog;
+
+	size_t playback_periodsize;	/* in bytes, zero if not used */
+	size_t capture_periodsize;	/* in bytes, zero if not used */
+
+	snd_pcm_uframes_t playback_last_appl_ptr;
+	size_t playback_ptr;
+	size_t capture_ptr;
+
+	snd_card_t *card;
+	snd_pcm_t *spdif_pcm;
+	snd_pcm_t *adat_pcm;
+	struct pci_dev *pci;
+	snd_info_entry_t *proc_entry;
+	snd_kcontrol_t *spdif_ctl;
+} rme32_t;
+
+static struct pci_device_id snd_rme32_ids[] __devinitdata = {
+	{PCI_VENDOR_ID_XILINX_RME, PCI_DEVICE_ID_DIGI32,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0,},
+	{PCI_VENDOR_ID_XILINX_RME, PCI_DEVICE_ID_DIGI32_8,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0,},
+	{PCI_VENDOR_ID_XILINX_RME, PCI_DEVICE_ID_DIGI32_PRO,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0,},
+	{0,}
+};
+
+MODULE_DEVICE_TABLE(pci, snd_rme32_ids);
+
+#define RME32_ISWORKING(rme32) ((rme32)->wcreg & RME32_WCR_START)
+#define RME32_PRO_WITH_8414(rme32) ((rme32)->pci->device == PCI_DEVICE_ID_DIGI32_PRO && (rme32)->rev == RME32_PRO_REVISION_WITH_8414)
+
+static int snd_rme32_playback_prepare(snd_pcm_substream_t * substream);
+
+static int snd_rme32_capture_prepare(snd_pcm_substream_t * substream);
+
+static int
+snd_rme32_playback_trigger(snd_pcm_substream_t * substream, int cmd);
+
+static int
+snd_rme32_capture_trigger(snd_pcm_substream_t * substream, int cmd);
+
+static snd_pcm_uframes_t
+snd_rme32_playback_pointer(snd_pcm_substream_t * substream);
+
+static snd_pcm_uframes_t
+snd_rme32_capture_pointer(snd_pcm_substream_t * substream);
+
+static void __init snd_rme32_proc_init(rme32_t * rme32);
+
+static void snd_rme32_proc_done(rme32_t * rme32);
+
+static int snd_rme32_create_switches(snd_card_t * card, rme32_t * rme32);
+
+static inline unsigned int snd_rme32_playback_ptr(rme32_t * rme32)
+{
+
+	return (readl(rme32->iobase + RME32_IO_GET_POS)
+		& RME32_RCR_AUDIO_ADDR_MASK) >> rme32->playback_frlog;
+}
+
+static inline unsigned int snd_rme32_capture_ptr(rme32_t * rme32)
+{
+	return (readl(rme32->iobase + RME32_IO_GET_POS)
+		& RME32_RCR_AUDIO_ADDR_MASK) >> rme32->capture_frlog;
+}
+
+static int snd_rme32_playback_silence(snd_pcm_substream_t * substream, int channel,	/* not used (interleaved data) */
+				      snd_pcm_uframes_t pos,
+				      snd_pcm_uframes_t count)
+{
+	rme32_t *rme32 = _snd_pcm_substream_chip(substream);
+	count <<= rme32->playback_frlog;
+	pos <<= rme32->playback_frlog;
+	memset_io(rme32->iobase + RME32_IO_DATA_BUFFER + pos, 0, count);
+	return 0;
+}
+
+static int snd_rme32_playback_copy(snd_pcm_substream_t * substream, int channel,	/* not used (interleaved data) */
+				   snd_pcm_uframes_t pos,
+				   void *src, snd_pcm_uframes_t count)
+{
+	rme32_t *rme32 = _snd_pcm_substream_chip(substream);
+	count <<= rme32->playback_frlog;
+	pos <<= rme32->playback_frlog;
+	copy_from_user_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos,
+			    src, count);
+	return 0;
+}
+
+static int snd_rme32_capture_copy(snd_pcm_substream_t * substream, int channel,	/* not used (interleaved data) */
+				  snd_pcm_uframes_t pos,
+				  void *dst, snd_pcm_uframes_t count)
+{
+	rme32_t *rme32 = _snd_pcm_substream_chip(substream);
+	count <<= rme32->capture_frlog;
+	pos <<= rme32->capture_frlog;
+	copy_to_user_fromio(dst,
+			    rme32->iobase + RME32_IO_DATA_BUFFER + pos,
+			    count);
+	return 0;
+}
+
+/*
+ * Digital output capabilites (S/PDIF)
+ */
+static snd_pcm_hardware_t snd_rme32_playback_spdif_info = {
+	info:		(SNDRV_PCM_INFO_MMAP |
+			 SNDRV_PCM_INFO_MMAP_VALID |
+			 SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE),
+	formats:	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE),
+	rates:		(SNDRV_PCM_RATE_32000 |
+			 SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000),
+	rate_min:	32000,
+	rate_max:	48000,
+	channels_min:	2,
+	channels_max:	2,
+	buffer_bytes_max: RME32_BUFFER_SIZE,
+	period_bytes_min: RME32_BLOCK_SIZE,
+	period_bytes_max: RME32_BLOCK_SIZE,
+	periods_min:	RME32_BUFFER_SIZE / RME32_BLOCK_SIZE,
+	periods_max:	RME32_BUFFER_SIZE / RME32_BLOCK_SIZE,
+	fifo_size:	0,
+};
+
+/*
+ * Digital input capabilites (S/PDIF)
+ */
+static snd_pcm_hardware_t snd_rme32_capture_spdif_info = {
+	info:		(SNDRV_PCM_INFO_MMAP |
+			 SNDRV_PCM_INFO_MMAP_VALID |
+			 SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE),
+	formats:	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE),
+	rates:		(SNDRV_PCM_RATE_32000 |
+			 SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000),
+	rate_min:	32000,
+	rate_max:	48000,
+	channels_min:	2,
+	channels_max:	2,
+	buffer_bytes_max: RME32_BUFFER_SIZE,
+	period_bytes_min: RME32_BLOCK_SIZE,
+	period_bytes_max: RME32_BLOCK_SIZE,
+	periods_min:	RME32_BUFFER_SIZE / RME32_BLOCK_SIZE,
+	periods_max:	RME32_BUFFER_SIZE / RME32_BLOCK_SIZE,
+	fifo_size:	0,
+};
+
+static int snd_rme32_playback_getrate(rme32_t * rme32)
+{
+	int rate;
+
+	rate = ((rme32->wcreg >> RME32_WCR_BITPOS_FREQ_0) & 1) +
+	       (((rme32->wcreg >> RME32_WCR_BITPOS_FREQ_1) & 1) << 1);
+	switch (rate) {
+	case 1:
+		rate = 32000;
+		break;
+	case 2:
+		rate = 44100;
+		break;
+	case 3:
+		rate = 48000;
+		break;
+	default:
+		return -1;
+	}
+	return rate;
+}
+
+static int snd_rme32_capture_getrate(rme32_t * rme32, int *is_adat)
+{
+	int n;
+	*is_adat = 0;
+
+	if (rme32->rcreg & RME32_RCR_ERF) {
+		return -1;
+	}
+
+	n = ((rme32->rcreg >> RME32_RCR_BITPOS_F0) & 1) +
+	    (((rme32->rcreg >> RME32_RCR_BITPOS_F1) & 1) << 1) +
+	    (((rme32->rcreg >> RME32_RCR_BITPOS_F2) & 1) << 2);
+
+	if (RME32_PRO_WITH_8414(rme32))
+		switch (n) {	/* supporting the CS8414 */
+		case 0:
+		case 1:
+		case 2:
+			return -1;
+		case 3:
+			return 96000;
+		case 4:
+			return 88200;
+		case 5:
+			return 48000;
+		case 6:
+			return 44100;
+		case 7:
+			return 32000;
+		default:
+			return -1;
+			break;
+	} else
+		switch (n) {	/* supporting the CS8412 */
+		case 0:
+			return -1;
+		case 1:
+			return 48000;
+		case 2:
+			return 44100;
+		case 3:
+			return 32000;
+		case 4:
+			return 48000;
+		case 5:
+			return 44100;
+		case 6:
+			return 44056;
+		case 7:
+			return 32000;
+		default:
+			break;
+		}
+	return -1;
+}
+
+static int snd_rme32_playback_setrate(rme32_t * rme32, int rate)
+{
+	switch (rate) {
+	case 32000:
+		rme32->wcreg = (rme32->wcreg | RME32_WCR_FREQ_0) & ~RME32_WCR_FREQ_1;
+		break;
+	case 44100:
+		rme32->wcreg = (rme32->wcreg | RME32_WCR_FREQ_1) & ~RME32_WCR_FREQ_0;
+		break;
+	case 48000:
+		rme32->wcreg = (rme32->wcreg | RME32_WCR_FREQ_0) | RME32_WCR_FREQ_1;
+		break;
+	default:
+		return -EINVAL;
+	}
+	writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
+	return 0;
+}
+
+static int snd_rme32_setclockmode(rme32_t * rme32, int mode)
+{
+	switch (mode) {
+	case RME32_CLOCKMODE_SLAVE:
+		rme32->wcreg = (rme32->wcreg & ~RME32_WCR_FREQ_0) & ~RME32_WCR_FREQ_1;
+		break;
+	case RME32_CLOCKMODE_MASTER_32:
+		rme32->wcreg = (rme32->wcreg | RME32_WCR_FREQ_0) & ~RME32_WCR_FREQ_1;
+		break;
+	case RME32_CLOCKMODE_MASTER_44:
+		rme32->wcreg = (rme32->wcreg & ~RME32_WCR_FREQ_0) | RME32_WCR_FREQ_1;
+		break;
+	case RME32_CLOCKMODE_MASTER_48:
+		rme32->wcreg = (rme32->wcreg | RME32_WCR_FREQ_0) | RME32_WCR_FREQ_1;
+		break;
+	default:
+		return -EINVAL;
+	}
+	writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
+	return 0;
+}
+
+static int snd_rme32_getclockmode(rme32_t * rme32)
+{
+	return ((rme32->wcreg >> RME32_WCR_BITPOS_FREQ_0) & 1) +
+	    (((rme32->wcreg >> RME32_WCR_BITPOS_FREQ_1) & 1) << 1);
+}
+
+static int snd_rme32_setinputtype(rme32_t * rme32, int type)
+{
+	switch (type) {
+	case RME32_INPUT_OPTICAL:
+		rme32->wcreg = (rme32->wcreg & ~RME32_WCR_INP_0) & ~RME32_WCR_INP_1;
+		break;
+	case RME32_INPUT_COAXIAL:
+		rme32->wcreg = (rme32->wcreg | RME32_WCR_INP_0) & ~RME32_WCR_INP_1;
+		break;
+	case RME32_INPUT_INTERNAL:
+		rme32->wcreg = (rme32->wcreg & ~RME32_WCR_INP_0) | RME32_WCR_INP_1;
+		break;
+	case RME32_INPUT_XLR:
+		rme32->wcreg = (rme32->wcreg | RME32_WCR_INP_0) | RME32_WCR_INP_1;
+		break;
+	default:
+		return -EINVAL;
+	}
+	writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
+	return 0;
+}
+
+static int snd_rme32_getinputtype(rme32_t * rme32)
+{
+	return ((rme32->wcreg >> RME32_WCR_BITPOS_INP_0) & 1) +
+	    (((rme32->wcreg >> RME32_WCR_BITPOS_INP_1) & 1) << 1);
+}
+
+static void
+snd_rme32_setframelog(rme32_t * rme32, int n_channels, int is_playback)
+{
+	int frlog;
+
+	if (n_channels == 2) {
+		frlog = 1;
+	} else {
+		/* assume 8 channels */
+		frlog = 3;
+	}
+	if (is_playback) {
+		frlog += (rme32->wcreg & RME32_WCR_MODE24) ? 2 : 1;
+		rme32->playback_frlog = frlog;
+	} else {
+		frlog += (rme32->wcreg & RME32_WCR_MODE24) ? 2 : 1;
+		rme32->capture_frlog = frlog;
+	}
+}
+
+static int snd_rme32_setformat(rme32_t * rme32, int format)
+{
+	switch (format) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		rme32->wcreg &= ~RME32_WCR_MODE24;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		rme32->wcreg |= RME32_WCR_MODE24;
+		break;
+	default:
+		return -EINVAL;
+	}
+	writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
+	return 0;
+}
+
+static int
+snd_rme32_playback_hw_params(snd_pcm_substream_t * substream,
+			     snd_pcm_hw_params_t * params)
+{
+	rme32_t *rme32 = _snd_pcm_substream_chip(substream);
+	int err;
+
+	if ((err = snd_pcm_lib_malloc_pages(substream,
+				      params_buffer_bytes(params))) < 0)
+		return err;
+	spin_lock_irq(&rme32->lock);
+	if ((err = snd_rme32_playback_setrate(rme32, params_rate(params))) < 0) {
+		spin_unlock_irq(&rme32->lock);
+		return err;
+	}
+	if ((err = snd_rme32_setformat(rme32, params_format(params))) < 0) {
+		spin_unlock_irq(&rme32->lock);
+		return err;
+	}
+
+	snd_rme32_setframelog(rme32, params_channels(params), 1);
+	if (rme32->capture_periodsize != 0) {
+		if (params_period_size(params) << rme32->playback_frlog != rme32->capture_periodsize) {
+			spin_unlock_irq(&rme32->lock);
+			return -EBUSY;
+		}
+	}
+	rme32->playback_periodsize = params_period_size(params) << rme32->playback_frlog;
+	/* S/PDIF setup */
+	if ((rme32->wcreg & RME32_WCR_ADAT) == 0) {
+		rme32->wcreg &= ~(RME32_WCR_PRO | RME32_WCR_EMP);
+		writel(rme32->wcreg |=
+		       rme32->wcreg_spdif_stream,
+		       rme32->iobase + RME32_IO_CONTROL_REGISTER);
+	}
+	spin_unlock_irq(&rme32->lock);
+
+	return 0;
+}
+
+static int snd_rme32_playback_hw_free(snd_pcm_substream_t * substream)
+{
+	snd_pcm_lib_free_pages(substream);
+	return 0;
+}
+
+static int
+snd_rme32_capture_hw_params(snd_pcm_substream_t * substream,
+			    snd_pcm_hw_params_t * params)
+{
+	unsigned long flags;
+	rme32_t *rme32 = _snd_pcm_substream_chip(substream);
+	int err, isadat;
+
+	if ((err = snd_pcm_lib_malloc_pages(substream,
+				      params_buffer_bytes(params))) < 0)
+		return err;
+	spin_lock_irqsave(&rme32->lock, flags);
+	/* enable AutoSync for record-preparing */
+	rme32->wcreg |= RME32_WCR_AUTOSYNC;
+	writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
+
+	if ((err = snd_rme32_setformat(rme32, params_format(params))) < 0) {
+		spin_unlock_irqrestore(&rme32->lock, flags);
+		return err;
+	}
+	if ((err = snd_rme32_playback_setrate(rme32, params_rate(params))) < 0) {
+		spin_unlock_irqrestore(&rme32->lock, flags);
+		return err;
+	}
+	if (params_rate(params) != snd_rme32_capture_getrate(rme32, &isadat)) {
+		spin_unlock_irqrestore(&rme32->lock, flags);
+		return -EBUSY;
+	}
+	/* AutoSync off for recording */
+	rme32->wcreg &= ~RME32_WCR_AUTOSYNC;
+	writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
+
+	snd_rme32_setframelog(rme32, params_channels(params), 0);
+	if (rme32->playback_periodsize != 0) {
+		if (params_period_size(params) << rme32->capture_frlog !=
+		    rme32->playback_periodsize) {
+			spin_unlock_irqrestore(&rme32->lock, flags);
+			return -EBUSY;
+		}
+	}
+	rme32->capture_periodsize =
+	    params_period_size(params) << rme32->capture_frlog;
+	spin_unlock_irqrestore(&rme32->lock, flags);
+
+	return 0;
+}
+
+static int snd_rme32_capture_hw_free(snd_pcm_substream_t * substream)
+{
+	snd_pcm_lib_free_pages(substream);
+	return 0;
+}
+
+static void snd_rme32_playback_start(rme32_t * rme32, int from_pause)
+{
+	if (!from_pause) {
+		writel(0, rme32->iobase + RME32_IO_RESET_POS);
+		rme32->playback_last_appl_ptr = 0;
+		rme32->playback_ptr = 0;
+	}
+
+	rme32->wcreg |= RME32_WCR_START;
+	writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
+}
+
+static void snd_rme32_capture_start(rme32_t * rme32, int from_pause)
+{
+	if (!from_pause) {
+		writel(0, rme32->iobase + RME32_IO_RESET_POS);
+	}
+
+	rme32->wcreg |= RME32_WCR_START;
+	writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
+}
+
+static void snd_rme32_playback_stop(rme32_t * rme32)
+{
+	/*
+	 * Check if there is an unconfirmed IRQ, if so confirm it, or else
+	 * the hardware will not stop generating interrupts
+	 */
+	rme32->rcreg = readl(rme32->iobase + RME32_IO_CONTROL_REGISTER);
+	if (rme32->rcreg & RME32_RCR_IRQ) {
+		writel(0, rme32->iobase + RME32_IO_CONFIRM_ACTION_IRQ);
+	}
+	rme32->wcreg &= ~RME32_WCR_START;
+	rme32->wcreg |= RME32_WCR_MUTE;
+	writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
+}
+
+static void snd_rme32_capture_stop(rme32_t * rme32)
+{
+	rme32->rcreg = readl(rme32->iobase + RME32_IO_CONTROL_REGISTER);
+	if (rme32->rcreg & RME32_RCR_IRQ) {
+		writel(0, rme32->iobase + RME32_IO_CONFIRM_ACTION_IRQ);
+	}
+	rme32->wcreg &= ~RME32_WCR_START;
+	writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
+}
+
+static void
+snd_rme32_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	rme32_t *rme32 = (rme32_t *) dev_id;
+
+	rme32->rcreg = readl(rme32->iobase + RME32_IO_CONTROL_REGISTER);
+	if (!(rme32->rcreg & RME32_RCR_IRQ)) {
+		return;
+	} else {
+		if (rme32->capture_substream) {
+			snd_pcm_period_elapsed(rme32->capture_substream);
+		}
+		if (rme32->playback_substream) {
+			snd_pcm_period_elapsed(rme32->playback_substream);
+		}
+		writel(0, rme32->iobase + RME32_IO_CONFIRM_ACTION_IRQ);
+	}
+}
+
+static unsigned int period_bytes[] = { RME32_BLOCK_SIZE };
+
+
+#define PERIOD_BYTES sizeof(period_bytes) / sizeof(period_bytes[0])
+
+static snd_pcm_hw_constraint_list_t hw_constraints_period_bytes = {
+	count:PERIOD_BYTES,
+	list:period_bytes,
+	mask:0
+};
+
+static int snd_rme32_playback_spdif_open(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	rme32_t *rme32 = _snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	snd_pcm_set_sync(substream);
+
+	spin_lock_irqsave(&rme32->lock, flags);
+	rme32->wcreg &= ~RME32_WCR_ADAT;
+	writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
+	rme32->playback_substream = substream;
+	rme32->playback_last_appl_ptr = 0;
+	rme32->playback_ptr = 0;
+
+	runtime->hw = snd_rme32_playback_spdif_info;
+	rme32->playback_pid = current->pid;
+	rme32->playback_substream = substream;
+
+	spin_unlock_irqrestore(&rme32->lock, flags);
+
+	snd_pcm_hw_constraint_minmax(runtime,
+				     SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+				     RME32_BUFFER_SIZE, RME32_BUFFER_SIZE);
+	snd_pcm_hw_constraint_list(runtime, 0,
+				   SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+				   &hw_constraints_period_bytes);
+
+	rme32->wcreg_spdif_stream = rme32->wcreg_spdif;
+	rme32->spdif_ctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+	snd_ctl_notify(rme32->card, SNDRV_CTL_EVENT_MASK_VALUE |
+		       SNDRV_CTL_EVENT_MASK_INFO, &rme32->spdif_ctl->id);
+	return 0;
+}
+
+static int snd_rme32_capture_spdif_open(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	int isadat;
+	rme32_t *rme32 = _snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	rme32->rcreg = readl(rme32->iobase + RME32_IO_CONTROL_REGISTER);
+	if (snd_rme32_capture_getrate(rme32, &isadat) < 0) {
+		/* no input */
+		return -EIO;
+	}
+	if (isadat) {
+		/* ADAT input */
+		return -EBUSY;
+	}
+	snd_pcm_set_sync(substream);
+
+	spin_lock_irqsave(&rme32->lock, flags);
+
+	rme32->capture_pid = current->pid;
+	rme32->capture_substream = substream;
+	rme32->capture_ptr = 0;
+	spin_unlock_irqrestore(&rme32->lock, flags);
+
+	runtime->hw = snd_rme32_capture_spdif_info;
+
+	snd_pcm_hw_constraint_minmax(runtime,
+				     SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+				     RME32_BUFFER_SIZE, RME32_BUFFER_SIZE);
+	snd_pcm_hw_constraint_list(runtime, 0,
+				   SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+				   &hw_constraints_period_bytes);
+
+	return 0;
+}
+
+
+static int snd_rme32_playback_close(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	rme32_t *rme32 = _snd_pcm_substream_chip(substream);
+	int spdif = 0;
+
+	spin_lock_irqsave(&rme32->lock, flags);
+	rme32->playback_pid = -1;
+	rme32->playback_substream = NULL;
+	rme32->playback_periodsize = 0;
+	spdif = (rme32->wcreg & RME32_WCR_ADAT) == 0;
+	spin_unlock_irqrestore(&rme32->lock, flags);
+	if (spdif) {
+		rme32->spdif_ctl->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+		snd_ctl_notify(rme32->card, SNDRV_CTL_EVENT_MASK_VALUE |
+			       SNDRV_CTL_EVENT_MASK_INFO,
+			       &rme32->spdif_ctl->id);
+	}
+	return 0;
+}
+
+static int snd_rme32_capture_close(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	rme32_t *rme32 = _snd_pcm_substream_chip(substream);
+
+	spin_lock_irqsave(&rme32->lock, flags);
+	rme32->capture_pid = -1;
+	rme32->capture_substream = NULL;
+	rme32->capture_periodsize = 0;
+	spin_unlock_irqrestore(&rme32->lock, flags);
+	return 0;
+}
+
+static int snd_rme32_playback_prepare(snd_pcm_substream_t * substream)
+{
+	rme32_t *rme32 = _snd_pcm_substream_chip(substream);
+	unsigned long flags;
+
+	spin_lock_irqsave(&rme32->lock, flags);
+	if (RME32_ISWORKING(rme32)) {
+		snd_rme32_playback_stop(rme32);
+	}
+	writel(0, rme32->iobase + RME32_IO_RESET_POS);
+	rme32->wcreg &= ~RME32_WCR_MUTE;
+	writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
+	spin_unlock_irqrestore(&rme32->lock, flags);
+	return 0;
+}
+
+static int snd_rme32_capture_prepare(snd_pcm_substream_t * substream)
+{
+	rme32_t *rme32 = _snd_pcm_substream_chip(substream);
+	unsigned long flags;
+
+	spin_lock_irqsave(&rme32->lock, flags);
+	if (RME32_ISWORKING(rme32)) {
+		snd_rme32_capture_stop(rme32);
+	}
+	writel(0, rme32->iobase + RME32_IO_RESET_POS);
+	spin_unlock_irqrestore(&rme32->lock, flags);
+	return 0;
+}
+
+static int
+snd_rme32_playback_trigger(snd_pcm_substream_t * substream, int cmd)
+{
+	rme32_t *rme32 = _snd_pcm_substream_chip(substream);
+	spin_lock(&rme32->lock);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		if (!RME32_ISWORKING(rme32)) {
+			if (substream != rme32->playback_substream) {
+				return -EBUSY;
+			}
+			snd_rme32_playback_start(rme32, 0);
+		}
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+		if (RME32_ISWORKING(rme32)) {
+			if (substream != rme32->playback_substream) {
+				return -EBUSY;
+			}
+			snd_rme32_playback_stop(rme32);
+		}
+		break;
+
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		if (RME32_ISWORKING(rme32)) {
+			snd_rme32_playback_stop(rme32);
+		}
+		break;
+
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		if (!RME32_ISWORKING(rme32)) {
+			snd_rme32_playback_start(rme32, 1);
+		}
+		break;
+
+	default:
+		return -EINVAL;
+	}
+	spin_unlock(&rme32->lock);
+	return 0;
+}
+
+static int
+snd_rme32_capture_trigger(snd_pcm_substream_t * substream, int cmd)
+{
+	rme32_t *rme32 = _snd_pcm_substream_chip(substream);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		if (!RME32_ISWORKING(rme32)) {
+			if (substream != rme32->capture_substream) {
+				return -EBUSY;
+			}
+			snd_rme32_capture_start(rme32, 0);
+		}
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+		if (RME32_ISWORKING(rme32)) {
+			if (substream != rme32->capture_substream) {
+				return -EBUSY;
+			}
+			snd_rme32_capture_stop(rme32);
+		}
+		break;
+
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		if (RME32_ISWORKING(rme32)) {
+			snd_rme32_capture_stop(rme32);
+		}
+		break;
+
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		if (!RME32_ISWORKING(rme32)) {
+			snd_rme32_capture_start(rme32, 1);
+		}
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static snd_pcm_uframes_t
+snd_rme32_playback_pointer(snd_pcm_substream_t * substream)
+{
+	rme32_t *rme32 = _snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_pcm_sframes_t diff;
+	size_t bytes;
+
+
+	if (runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) {
+		diff = runtime->control->appl_ptr -
+		       rme32->playback_last_appl_ptr;
+		rme32->playback_last_appl_ptr = runtime->control->appl_ptr;
+		if (diff != 0 && diff < -(snd_pcm_sframes_t) (runtime->boundary >> 1)) {
+			diff += runtime->boundary;
+		}
+		bytes = diff << rme32->playback_frlog;
+		if (bytes > RME32_BUFFER_SIZE - rme32->playback_ptr) {
+			memcpy_toio(rme32->iobase + RME32_IO_DATA_BUFFER + rme32->playback_ptr,
+				    runtime->dma_area + rme32->playback_ptr,
+				    RME32_BUFFER_SIZE - rme32->playback_ptr);
+			bytes -= RME32_BUFFER_SIZE - rme32->playback_ptr;
+			if (bytes > RME32_BUFFER_SIZE) {
+				bytes = RME32_BUFFER_SIZE;
+			}
+			memcpy_toio(rme32->iobase + RME32_IO_DATA_BUFFER,
+				    runtime->dma_area, bytes);
+			rme32->playback_ptr = bytes;
+		} else if (bytes != 0) {
+			memcpy_toio(rme32->iobase + RME32_IO_DATA_BUFFER + rme32->playback_ptr,
+				    runtime->dma_area + rme32->playback_ptr, bytes);
+			rme32->playback_ptr += bytes;
+		}
+	}
+	return snd_rme32_playback_ptr(rme32);
+}
+
+static snd_pcm_uframes_t
+snd_rme32_capture_pointer(snd_pcm_substream_t * substream)
+{
+	rme32_t *rme32 = _snd_pcm_substream_chip(substream);
+	return snd_rme32_capture_ptr(rme32);
+}
+
+static snd_pcm_ops_t snd_rme32_playback_spdif_ops = {
+	open:		snd_rme32_playback_spdif_open,
+	close:		snd_rme32_playback_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_rme32_playback_hw_params,
+	hw_free:	snd_rme32_playback_hw_free,
+	prepare:	snd_rme32_playback_prepare,
+	trigger:	snd_rme32_playback_trigger,
+	pointer:	snd_rme32_playback_pointer,
+	copy:		snd_rme32_playback_copy,
+	silence:	snd_rme32_playback_silence,
+};
+
+static snd_pcm_ops_t snd_rme32_capture_spdif_ops = {
+	open:		snd_rme32_capture_spdif_open,
+	close:		snd_rme32_capture_close,
+	ioctl:		snd_pcm_lib_ioctl,
+	hw_params:	snd_rme32_capture_hw_params,
+	hw_free:	snd_rme32_capture_hw_free,
+	prepare:	snd_rme32_capture_prepare,
+	trigger:	snd_rme32_capture_trigger,
+	pointer:	snd_rme32_capture_pointer,
+	copy:		snd_rme32_capture_copy,
+};
+
+static void snd_rme32_free(void *private_data)
+{
+	rme32_t *rme32 = (rme32_t *) private_data;
+
+	if (rme32 == NULL) {
+		return;
+	}
+	if (rme32->irq >= 0) {
+		snd_rme32_playback_stop(rme32);
+		snd_rme32_capture_stop(rme32);
+		snd_rme32_proc_done(rme32);
+		free_irq(rme32->irq, (void *) rme32);
+		rme32->irq = -1;
+	}
+	if (rme32->iobase) {
+		iounmap((void *) rme32->iobase);
+		rme32->iobase = 0;
+	}
+	if (rme32->res_port != NULL) {
+		release_resource(rme32->res_port);
+		rme32->res_port = NULL;
+	}
+}
+
+static void snd_rme32_free_spdif_pcm(snd_pcm_t * pcm)
+{
+	rme32_t *rme32 = (rme32_t *) pcm->private_data;
+	rme32->spdif_pcm = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __init snd_rme32_create(rme32_t * rme32)
+{
+	struct pci_dev *pci = rme32->pci;
+	int err;
+
+	rme32->irq = -1;
+
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+
+	rme32->port = pci_resource_start(rme32->pci, 0);
+
+	if ((rme32->res_port = request_mem_region(rme32->port, RME32_IO_SIZE, "RME32")) == NULL) {
+		snd_printk("unable to grab memory region 0x%lx-0x%lx\n",
+			   rme32->port, rme32->port + RME32_IO_SIZE - 1);
+		return -EBUSY;
+	}
+
+	if (request_irq(pci->irq, snd_rme32_interrupt, SA_INTERRUPT | SA_SHIRQ, "RME32", (void *) rme32)) {
+		snd_printk("unable to grab IRQ %d\n", pci->irq);
+		return -EBUSY;
+	}
+	rme32->irq = pci->irq;
+
+	spin_lock_init(&rme32->lock);
+	if ((rme32->iobase = (unsigned long) ioremap_nocache(rme32->port, RME32_IO_SIZE)) == 0) {
+		snd_printk("unable to remap memory region 0x%lx-0x%lx\n",
+			   rme32->port, rme32->port + RME32_IO_SIZE - 1);
+		return -ENOMEM;
+	}
+
+	/* read the card's revision number */
+	pci_read_config_byte(pci, 8, &rme32->rev);
+
+	/* set up ALSA pcm device for S/PDIF */
+	if ((err = snd_pcm_new(rme32->card, "Digi32 IEC958", 0, 1, 1, &rme32->spdif_pcm)) < 0) {
+		return err;
+	}
+	rme32->spdif_pcm->private_data = rme32;
+	rme32->spdif_pcm->private_free = snd_rme32_free_spdif_pcm;
+	strcpy(rme32->spdif_pcm->name, "Digi32 IEC958");
+	snd_pcm_set_ops(rme32->spdif_pcm, SNDRV_PCM_STREAM_PLAYBACK,
+			&snd_rme32_playback_spdif_ops);
+	snd_pcm_set_ops(rme32->spdif_pcm, SNDRV_PCM_STREAM_CAPTURE,
+			&snd_rme32_capture_spdif_ops);
+
+	rme32->spdif_pcm->info_flags = 0;
+
+	snd_pcm_lib_preallocate_pages_for_all(rme32->spdif_pcm,
+					      RME32_BUFFER_SIZE,
+					      RME32_BUFFER_SIZE,
+					      GFP_KERNEL);
+
+	/* set up ALSA pcm device for ADAT */
+	if (pci->device == PCI_DEVICE_ID_DIGI32) {
+		/* ADAT is not available on the base model */
+		rme32->adat_pcm = NULL;
+	}
+
+	rme32->playback_periodsize = 0;
+	rme32->capture_periodsize = 0;
+
+	/* make sure playback/capture is stopped, if by some reason active */
+	snd_rme32_playback_stop(rme32);
+	snd_rme32_capture_stop(rme32);
+
+	/* reset buffer pointer */
+	writel(0, rme32->iobase + RME32_IO_RESET_POS);
+
+	/* set default values in registers */
+	rme32->wcreg = RME32_WCR_SEL |	 /* normal playback */
+		       RME32_WCR_INP_0 | /* input select */
+		       RME32_WCR_MUTE;	 /* setting muting on */
+	writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
+
+
+	/* init switch interface */
+	if ((err = snd_rme32_create_switches(rme32->card, rme32)) < 0) {
+		return err;
+	}
+
+	/* init proc interface */
+	snd_rme32_proc_init(rme32);
+
+	rme32->playback_pid = -1;
+	rme32->capture_pid = -1;
+	rme32->capture_substream = NULL;
+	rme32->playback_substream = NULL;
+
+	return 0;
+}
+
+/*
+ * proc interface
+ */
+
+static void
+snd_rme32_proc_read(snd_info_entry_t * entry, snd_info_buffer_t * buffer)
+{
+	int n;
+	rme32_t *rme32 = (rme32_t *) entry->private_data;
+
+	rme32->rcreg = readl(rme32->iobase + RME32_IO_CONTROL_REGISTER);
+
+	snd_iprintf(buffer, rme32->card->longname);
+	snd_iprintf(buffer, " (index #%d)\n", rme32->card->number + 1);
+
+	snd_iprintf(buffer, "\nGeneral settings\n");
+	snd_iprintf(buffer, "  driver version: %s\n",
+		    RME32_DRIVER_VERSION);
+	if (RME32_PRO_WITH_8414(rme32)) {
+		snd_iprintf(buffer, "  receiver: CS8414\n");
+	} else {
+		snd_iprintf(buffer, "  receiver: CS8412\n");
+	}
+	if (rme32->wcreg & RME32_WCR_MODE24) {
+		snd_iprintf(buffer, "  format: 24 bit");
+	} else {
+		snd_iprintf(buffer, "  format: 16 bit");
+	}
+	if (rme32->wcreg & RME32_WCR_MONO) {
+		snd_iprintf(buffer, ", Mono\n");
+	} else {
+		snd_iprintf(buffer, ", Stereo\n");
+	}
+
+	snd_iprintf(buffer, "\nInput settings\n");
+	switch (snd_rme32_getinputtype(rme32)) {
+	case RME32_INPUT_OPTICAL:
+		snd_iprintf(buffer, "  input: optical");
+		break;
+	case RME32_INPUT_COAXIAL:
+		snd_iprintf(buffer, "  input: coaxial");
+		break;
+	case RME32_INPUT_INTERNAL:
+		snd_iprintf(buffer, "  input: internal");
+		break;
+	case RME32_INPUT_XLR:
+		snd_iprintf(buffer, "  input: XLR");
+		break;
+	}
+	if (snd_rme32_capture_getrate(rme32, &n) < 0) {
+		snd_iprintf(buffer, "\n  sample rate: no valid signal\n");
+	} else {
+		if (n) {
+			snd_iprintf(buffer, " (8 channels)\n");
+		} else {
+			snd_iprintf(buffer, " (2 channels)\n");
+		}
+		snd_iprintf(buffer, "  sample rate: %d Hz\n",
+			    snd_rme32_capture_getrate(rme32, &n));
+	}
+
+	snd_iprintf(buffer, "\nOutput settings\n");
+	if (rme32->wcreg & RME32_WCR_SEL) {
+		snd_iprintf(buffer, "  output signal: normal playback");
+	} else {
+		snd_iprintf(buffer, "  output signal: same as input");
+	}
+	if (rme32->wcreg & RME32_WCR_MUTE) {
+		snd_iprintf(buffer, " (muted)\n");
+	} else {
+		snd_iprintf(buffer, "\n");
+	}
+
+	/* master output frequency */
+	if (!
+	    ((!(rme32->wcreg & RME32_WCR_FREQ_0))
+	     && (!(rme32->wcreg & RME32_WCR_FREQ_1)))) {
+		snd_iprintf(buffer, "  sample rate: %d Hz\n",
+			    snd_rme32_playback_getrate(rme32));
+	}
+	if (rme32->wcreg & RME32_RCR_KMODE) {
+		snd_iprintf(buffer, "  clock mode: slave\n");
+	} else {
+		snd_iprintf(buffer, "  clock mode: master\n");
+	}
+	if (rme32->wcreg & RME32_WCR_PRO) {
+		snd_iprintf(buffer, "  format: AES/EBU (professional)\n");
+	} else {
+		snd_iprintf(buffer, "  format: IEC958 (consumer)\n");
+	}
+	if (rme32->wcreg & RME32_WCR_EMP) {
+		snd_iprintf(buffer, "  emphasis: on\n");
+	} else {
+		snd_iprintf(buffer, "  emphasis: off\n");
+	}
+}
+
+static void __init snd_rme32_proc_init(rme32_t * rme32)
+{
+	snd_info_entry_t *entry;
+
+	if ((entry = snd_info_create_card_entry(rme32->card, "rme32", rme32->card->proc_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->private_data = rme32;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->c.text.read_size = 256;
+		entry->c.text.read = snd_rme32_proc_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	rme32->proc_entry = entry;
+}
+
+static void snd_rme32_proc_done(rme32_t * rme32)
+{
+	if (rme32->proc_entry) {
+		snd_info_unregister(rme32->proc_entry);
+		rme32->proc_entry = NULL;
+	}
+}
+
+/*
+ * control interface
+ */
+
+static int
+snd_rme32_info_loopback_control(snd_kcontrol_t * kcontrol,
+				snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+static int
+snd_rme32_get_loopback_control(snd_kcontrol_t * kcontrol,
+			       snd_ctl_elem_value_t * ucontrol)
+{
+	rme32_t *rme32 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+
+	spin_lock_irqsave(&rme32->lock, flags);
+	ucontrol->value.integer.value[0] =
+	    rme32->wcreg & RME32_WCR_SEL ? 0 : 1;
+	spin_unlock_irqrestore(&rme32->lock, flags);
+	return 0;
+}
+static int
+snd_rme32_put_loopback_control(snd_kcontrol_t * kcontrol,
+			       snd_ctl_elem_value_t * ucontrol)
+{
+	rme32_t *rme32 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned int val;
+	int change;
+
+	val = ucontrol->value.integer.value[0] ? 0 : RME32_WCR_SEL;
+	spin_lock_irqsave(&rme32->lock, flags);
+	val = (rme32->wcreg & ~RME32_WCR_SEL) | val;
+	change = val != rme32->wcreg;
+	writel(rme32->wcreg =
+	       val, rme32->iobase + RME32_IO_CONTROL_REGISTER);
+	spin_unlock_irqrestore(&rme32->lock, flags);
+	return change;
+}
+
+static int
+snd_rme32_info_inputtype_control(snd_kcontrol_t * kcontrol,
+				 snd_ctl_elem_info_t * uinfo)
+{
+	static char *_texts[5] =
+	    { "Optical", "Coaxial", "Internal", "XLR" };
+	rme32_t *rme32 = _snd_kcontrol_chip(kcontrol);
+	char *texts[4] = { _texts[0], _texts[1], _texts[2], _texts[3] };
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	switch (rme32->pci->device) {
+	case PCI_DEVICE_ID_DIGI32:
+	case PCI_DEVICE_ID_DIGI32_8:
+		uinfo->value.enumerated.items = 3;
+		break;
+	case PCI_DEVICE_ID_DIGI32_PRO:
+		uinfo->value.enumerated.items = 4;
+		break;
+	default:
+		snd_BUG();
+		break;
+	}
+	if (uinfo->value.enumerated.item >
+	    uinfo->value.enumerated.items - 1) {
+		uinfo->value.enumerated.item =
+		    uinfo->value.enumerated.items - 1;
+	}
+	strcpy(uinfo->value.enumerated.name,
+	       texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+static int
+snd_rme32_get_inputtype_control(snd_kcontrol_t * kcontrol,
+				snd_ctl_elem_value_t * ucontrol)
+{
+	rme32_t *rme32 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int items = 3;
+
+	spin_lock_irqsave(&rme32->lock, flags);
+	ucontrol->value.enumerated.item[0] = snd_rme32_getinputtype(rme32);
+
+	switch (rme32->pci->device) {
+	case PCI_DEVICE_ID_DIGI32:
+	case PCI_DEVICE_ID_DIGI32_8:
+		items = 3;
+		break;
+	case PCI_DEVICE_ID_DIGI32_PRO:
+		items = 4;
+		break;
+	default:
+		snd_BUG();
+		break;
+	}
+	if (ucontrol->value.enumerated.item[0] >= items) {
+		ucontrol->value.enumerated.item[0] = items - 1;
+	}
+
+	spin_unlock_irqrestore(&rme32->lock, flags);
+	return 0;
+}
+static int
+snd_rme32_put_inputtype_control(snd_kcontrol_t * kcontrol,
+				snd_ctl_elem_value_t * ucontrol)
+{
+	rme32_t *rme32 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned int val;
+	int change, items = 3;
+
+	switch (rme32->pci->device) {
+	case PCI_DEVICE_ID_DIGI32:
+	case PCI_DEVICE_ID_DIGI32_8:
+		items = 3;
+		break;
+	case PCI_DEVICE_ID_DIGI32_PRO:
+		items = 4;
+		break;
+	default:
+		snd_BUG();
+		break;
+	}
+	val = ucontrol->value.enumerated.item[0] % items;
+
+	spin_lock_irqsave(&rme32->lock, flags);
+	change = val != snd_rme32_getinputtype(rme32);
+	snd_rme32_setinputtype(rme32, val);
+	spin_unlock_irqrestore(&rme32->lock, flags);
+	return change;
+}
+
+static int
+snd_rme32_info_clockmode_control(snd_kcontrol_t * kcontrol,
+				 snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[4] = { "Slave", "Master (32.0 kHz)", "Master (44.1 kHz)", "Master (48.0 kHz)" };
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 4;
+	if (uinfo->value.enumerated.item > 4) {
+		uinfo->value.enumerated.item = 4;
+	}
+	strcpy(uinfo->value.enumerated.name,
+	       texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+static int
+snd_rme32_get_clockmode_control(snd_kcontrol_t * kcontrol,
+				snd_ctl_elem_value_t * ucontrol)
+{
+	rme32_t *rme32 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+
+	spin_lock_irqsave(&rme32->lock, flags);
+	ucontrol->value.enumerated.item[0] = snd_rme32_getclockmode(rme32);
+	spin_unlock_irqrestore(&rme32->lock, flags);
+	return 0;
+}
+static int
+snd_rme32_put_clockmode_control(snd_kcontrol_t * kcontrol,
+				snd_ctl_elem_value_t * ucontrol)
+{
+	rme32_t *rme32 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	unsigned int val;
+	int change;
+
+	val = ucontrol->value.enumerated.item[0] % 3;
+	spin_lock_irqsave(&rme32->lock, flags);
+	change = val != snd_rme32_getclockmode(rme32);
+	snd_rme32_setclockmode(rme32, val);
+	spin_unlock_irqrestore(&rme32->lock, flags);
+	return change;
+}
+
+static u32 snd_rme32_convert_from_aes(snd_aes_iec958_t * aes)
+{
+	u32 val = 0;
+	val |= (aes->status[0] & IEC958_AES0_PROFESSIONAL) ? RME32_WCR_PRO : 0;
+	if (val & RME32_WCR_PRO)
+		val |= (aes->status[0] & IEC958_AES0_PRO_EMPHASIS_5015) ? RME32_WCR_EMP : 0;
+	else
+		val |= (aes->status[0] & IEC958_AES0_CON_EMPHASIS_5015) ? RME32_WCR_EMP : 0;
+	return val;
+}
+
+static void snd_rme32_convert_to_aes(snd_aes_iec958_t * aes, u32 val)
+{
+	aes->status[0] = ((val & RME32_WCR_PRO) ? IEC958_AES0_PROFESSIONAL : 0);
+	if (val & RME32_WCR_PRO)
+		aes->status[0] |= (val & RME32_WCR_EMP) ? IEC958_AES0_PRO_EMPHASIS_5015 : 0;
+	else
+		aes->status[0] |= (val & RME32_WCR_EMP) ? IEC958_AES0_CON_EMPHASIS_5015 : 0;
+}
+
+static int snd_rme32_control_spdif_info(snd_kcontrol_t * kcontrol,
+					snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_rme32_control_spdif_get(snd_kcontrol_t * kcontrol,
+				       snd_ctl_elem_value_t * ucontrol)
+{
+	rme32_t *rme32 = _snd_kcontrol_chip(kcontrol);
+
+	snd_rme32_convert_to_aes(&ucontrol->value.iec958,
+				 rme32->wcreg_spdif);
+	return 0;
+}
+
+static int snd_rme32_control_spdif_put(snd_kcontrol_t * kcontrol,
+				       snd_ctl_elem_value_t * ucontrol)
+{
+	rme32_t *rme32 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	u32 val;
+
+	val = snd_rme32_convert_from_aes(&ucontrol->value.iec958);
+	spin_lock_irqsave(&rme32->lock, flags);
+	change = val != rme32->wcreg_spdif;
+	rme32->wcreg_spdif = val;
+	spin_unlock_irqrestore(&rme32->lock, flags);
+	return change;
+}
+
+static int snd_rme32_control_spdif_stream_info(snd_kcontrol_t * kcontrol,
+					       snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_rme32_control_spdif_stream_get(snd_kcontrol_t * kcontrol,
+					      snd_ctl_elem_value_t *
+					      ucontrol)
+{
+	rme32_t *rme32 = _snd_kcontrol_chip(kcontrol);
+
+	snd_rme32_convert_to_aes(&ucontrol->value.iec958,
+				 rme32->wcreg_spdif_stream);
+	return 0;
+}
+
+static int snd_rme32_control_spdif_stream_put(snd_kcontrol_t * kcontrol,
+					      snd_ctl_elem_value_t *
+					      ucontrol)
+{
+	rme32_t *rme32 = _snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int change;
+	u32 val;
+
+	val = snd_rme32_convert_from_aes(&ucontrol->value.iec958);
+	spin_lock_irqsave(&rme32->lock, flags);
+	change = val != rme32->wcreg_spdif_stream;
+	rme32->wcreg_spdif_stream = val;
+	rme32->wcreg &= ~(RME32_WCR_PRO | RME32_WCR_EMP);
+	writel(rme32->wcreg |= val, rme32->iobase + RME32_IO_CONTROL_REGISTER);
+	spin_unlock_irqrestore(&rme32->lock, flags);
+	return change;
+}
+
+static int snd_rme32_control_spdif_mask_info(snd_kcontrol_t * kcontrol,
+					     snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_rme32_control_spdif_mask_get(snd_kcontrol_t * kcontrol,
+					    snd_ctl_elem_value_t *
+					    ucontrol)
+{
+	ucontrol->value.iec958.status[0] = kcontrol->private_value;
+	return 0;
+}
+
+static snd_kcontrol_new_t snd_rme32_controls[] = {
+	{
+		iface:	SNDRV_CTL_ELEM_IFACE_PCM,
+		name:	SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
+		info:	snd_rme32_control_spdif_info,
+		get:	snd_rme32_control_spdif_get,
+		put:	snd_rme32_control_spdif_put
+	},
+	{
+		access:	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+		iface:	SNDRV_CTL_ELEM_IFACE_PCM,
+		name:	SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM),
+		info:	snd_rme32_control_spdif_stream_info,
+		get:	snd_rme32_control_spdif_stream_get,
+		put:	snd_rme32_control_spdif_stream_put
+	},
+	{
+		access:	SNDRV_CTL_ELEM_ACCESS_READ,
+		iface:	SNDRV_CTL_ELEM_IFACE_MIXER,
+		name:	SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK),
+		info:	snd_rme32_control_spdif_mask_info,
+		get:	snd_rme32_control_spdif_mask_get,
+		private_value:	IEC958_AES0_PROFESSIONAL | IEC958_AES0_CON_EMPHASIS
+	},
+	{
+		access:	SNDRV_CTL_ELEM_ACCESS_READ,
+		iface:	SNDRV_CTL_ELEM_IFACE_MIXER,
+		name:	SNDRV_CTL_NAME_IEC958("", PLAYBACK, PRO_MASK),
+		info:	snd_rme32_control_spdif_mask_info,
+		get:	snd_rme32_control_spdif_mask_get,
+		private_value:	IEC958_AES0_PROFESSIONAL | IEC958_AES0_PRO_EMPHASIS
+	},
+	{
+		iface:	SNDRV_CTL_ELEM_IFACE_PCM,
+		name:	"Input Connector",
+		info:	snd_rme32_info_inputtype_control,
+		get:	snd_rme32_get_inputtype_control,
+		put:	snd_rme32_put_inputtype_control
+	},
+	{
+		iface:	SNDRV_CTL_ELEM_IFACE_PCM,
+		name:	"Loopback Input",
+		info:	snd_rme32_info_loopback_control,
+		get:	snd_rme32_get_loopback_control,
+		put:	snd_rme32_put_loopback_control
+	},
+	{
+		iface:	SNDRV_CTL_ELEM_IFACE_PCM,
+		name:	"Clock Mode",
+		info:	snd_rme32_info_clockmode_control,
+		get:	snd_rme32_get_clockmode_control,
+		put:	snd_rme32_put_clockmode_control
+	}
+};
+
+static int snd_rme32_create_switches(snd_card_t * card, rme32_t * rme32)
+{
+	int idx, err;
+	snd_kcontrol_t *kctl;
+
+	for (idx = 0; idx < 7; idx++) {
+		if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme32_controls[idx], rme32))) < 0)
+			return err;
+		if (idx == 1)	/* IEC958 (S/PDIF) Stream */
+			rme32->spdif_ctl = kctl;
+	}
+
+	return 0;
+}
+
+/*
+ * Card initialisation
+ */
+
+static void snd_rme32_card_free(snd_card_t * card)
+{
+	snd_rme32_free(card->private_data);
+}
+
+static int __init
+snd_rme32_probe(struct pci_dev *pci, const struct pci_device_id *id)
+{
+	static int dev = 0;
+	rme32_t *rme32;
+	snd_card_t *card;
+	int err;
+
+	for (; dev < SNDRV_CARDS; dev++) {
+		if (!snd_enable[dev]) {
+			dev++;
+			return -ENOENT;
+		}
+		break;
+	}
+	if (dev >= SNDRV_CARDS) {
+		return -ENODEV;
+	}
+	if ((card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE,
+				 sizeof(rme32_t))) == NULL)
+		return -ENOMEM;
+	card->private_free = snd_rme32_card_free;
+	rme32 = (rme32_t *) card->private_data;
+	rme32->card = card;
+	rme32->pci = pci;
+	if ((err = snd_rme32_create(rme32)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	strcpy(card->driver, "Digi32");
+	switch (rme32->pci->device) {
+	case PCI_DEVICE_ID_DIGI32:
+		strcpy(card->shortname, "RME Digi32");
+		break;
+	case PCI_DEVICE_ID_DIGI32_8:
+		strcpy(card->shortname, "RME Digi32/8");
+		break;
+	case PCI_DEVICE_ID_DIGI32_PRO:
+		strcpy(card->shortname, "RME Digi32 PRO");
+		break;
+	}
+	sprintf(card->longname, "%s (Rev. %d) at 0x%lx, irq %d",
+		card->shortname, rme32->rev, rme32->port, rme32->irq);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	pci_set_drvdata(pci, card);
+	dev++;
+	return 0;
+}
+
+static void __exit snd_rme32_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+	name:		"RME Digi32",
+	id_table:	snd_rme32_ids,
+	probe:		snd_rme32_probe,
+	remove:		snd_rme32_remove,
+};
+
+static int __init alsa_card_rme32_init(void)
+{
+	int err;
+
+	if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+		snd_printk("No RME Digi32 cards found\n");
+#endif
+		return err;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_rme32_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_rme32_init)
+module_exit(alsa_card_rme32_exit)
+
+#ifndef MODULE
+
+static int __init alsa_card_rme32_setup(char *str)
+{
+	static unsigned __initdata nr_dev = 0;
+
+	if (nr_dev >= SNDRV_CARDS)
+		return 0;
+	(void) (get_option(&str, &snd_enable[nr_dev]) == 2 &&
+		get_option(&str, &snd_index[nr_dev]) == 2 &&
+		get_id(&str, &snd_id[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-rme32=", alsa_card_rme32_setup);
+
+#endif				/* ifndef MODULE */
diff -Nru a/sound/pci/rme96.c b/sound/pci/rme96.c
--- a/sound/pci/rme96.c	Sun Apr 28 20:10:35 2002
+++ b/sound/pci/rme96.c	Sun Apr 28 20:10:35 2002
@@ -27,6 +27,7 @@
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/init.h>
+#include <linux/pci.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/info.h>
diff -Nru a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c
--- a/sound/pci/rme9652/rme9652.c	Sun Apr 28 20:10:36 2002
+++ b/sound/pci/rme9652/rme9652.c	Sun Apr 28 20:10:36 2002
@@ -24,6 +24,7 @@
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/init.h>
+#include <linux/pci.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/control.h>
diff -Nru a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c
--- a/sound/pci/sonicvibes.c	Sun Apr 28 20:10:32 2002
+++ b/sound/pci/sonicvibes.c	Sun Apr 28 20:10:32 2002
@@ -26,6 +26,7 @@
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/init.h>
+#include <linux/pci.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
diff -Nru a/sound/pci/trident/trident.c b/sound/pci/trident/trident.c
--- a/sound/pci/trident/trident.c	Sun Apr 28 20:10:30 2002
+++ b/sound/pci/trident/trident.c	Sun Apr 28 20:10:30 2002
@@ -23,6 +23,7 @@
 
 #include <sound/driver.h>
 #include <linux/init.h>
+#include <linux/pci.h>
 #include <linux/time.h>
 #include <sound/core.h>
 #include <sound/trident.h>
diff -Nru a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c
--- a/sound/pci/trident/trident_main.c	Sun Apr 28 20:10:31 2002
+++ b/sound/pci/trident/trident_main.c	Sun Apr 28 20:10:31 2002
@@ -30,6 +30,7 @@
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/init.h>
+#include <linux/pci.h>
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
 #include <sound/core.h>
diff -Nru a/sound/pci/via686.c b/sound/pci/via686.c
--- a/sound/pci/via686.c	Sun Apr 28 20:10:34 2002
+++ b/sound/pci/via686.c	Sun Apr 28 20:10:34 2002
@@ -24,6 +24,7 @@
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
+#include <linux/pci.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
diff -Nru a/sound/pci/via8233.c b/sound/pci/via8233.c
--- a/sound/pci/via8233.c	Sun Apr 28 20:10:34 2002
+++ b/sound/pci/via8233.c	Sun Apr 28 20:10:34 2002
@@ -25,6 +25,7 @@
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
+#include <linux/pci.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
diff -Nru a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c
--- a/sound/pci/ymfpci/ymfpci.c	Sun Apr 28 20:10:35 2002
+++ b/sound/pci/ymfpci/ymfpci.c	Sun Apr 28 20:10:35 2002
@@ -21,6 +21,7 @@
 
 #include <sound/driver.h>
 #include <linux/init.h>
+#include <linux/pci.h>
 #include <linux/time.h>
 #include <sound/core.h>
 #include <sound/ymfpci.h>
diff -Nru a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c
--- a/sound/pci/ymfpci/ymfpci_main.c	Sun Apr 28 20:10:38 2002
+++ b/sound/pci/ymfpci/ymfpci_main.c	Sun Apr 28 20:10:38 2002
@@ -29,6 +29,7 @@
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/init.h>
+#include <linux/pci.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/control.h>
@@ -1464,6 +1465,82 @@
 YMFPCI_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), 0, YDSXGR_SPDIFINCTRL)
 };
 
+
+/*
+ * GPIO
+ */
+
+static int snd_ymfpci_get_gpio_out(ymfpci_t *chip, int pin)
+{
+	u16 reg, ctrl;
+	unsigned long flags;
+
+	reg = ~(1 << pin) & 0xff;
+	ctrl = 0xff00 | reg;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_ymfpci_writew(chip, YDSXGR_GPIOFUNCENABLE, ctrl);
+	ctrl = snd_ymfpci_readw(chip, YDSXGR_GPIOINSTATUS);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return (ctrl >> pin) & 1;
+}
+
+static int snd_ymfpci_set_gpio_out(ymfpci_t *chip, int pin, int enable)
+{
+	u16 reg, ctrl;
+	unsigned long flags;
+
+	reg = ~(1 << pin) & 0xff;
+	ctrl = (reg << 8) | reg;
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	snd_ymfpci_writew(chip, YDSXGR_GPIOFUNCENABLE, ctrl);
+	snd_ymfpci_writew(chip, YDSXGR_GPIOOUTCTRL, enable << pin);
+	ctrl = 0xff00 | reg;
+	snd_ymfpci_writew(chip, YDSXGR_GPIOFUNCENABLE, ctrl);
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+	return 0;
+}
+
+static int snd_ymfpci_gpio_sw_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_ymfpci_gpio_sw_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+	int pin = (int)kcontrol->private_value;
+	ucontrol->value.integer.value[0] = snd_ymfpci_get_gpio_out(chip, pin);
+	return 0;
+}
+
+static int snd_ymfpci_gpio_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
+	int pin = (int)kcontrol->private_value;
+
+	if (snd_ymfpci_get_gpio_out(chip, pin) != ucontrol->value.integer.value[0]) {
+		snd_ymfpci_set_gpio_out(chip, pin, !!ucontrol->value.integer.value[0]);
+		ucontrol->value.integer.value[0] = snd_ymfpci_get_gpio_out(chip, pin);
+		return 1;
+	}
+	return 0;
+}
+
+static snd_kcontrol_new_t snd_ymfpci_rear_shared __devinitdata = {
+	name: "Shared Rear/Line-In Switch",
+	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
+	info: snd_ymfpci_gpio_sw_info,
+	get: snd_ymfpci_gpio_sw_get,
+	put: snd_ymfpci_gpio_sw_put,
+	private_value: 2,
+};
+
+
 /*
  *  Mixer routines
  */
@@ -1505,6 +1582,13 @@
 		return err;
 	kctl->id.device = chip->pcm_spdif->device;
 	chip->spdif_pcm_ctl = kctl;
+
+	/*
+	 * shared rear/line-in
+	 * FIXME: should be only on supported models - checking subdevice
+	 */
+	if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_ymfpci_rear_shared, chip))) < 0)
+		return err;
 
 	return 0;
 }
diff -Nru a/sound/ppc/awacs.c b/sound/ppc/awacs.c
--- a/sound/ppc/awacs.c	Sun Apr 28 20:10:37 2002
+++ b/sound/ppc/awacs.c	Sun Apr 28 20:10:37 2002
@@ -24,6 +24,7 @@
 #include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/init.h>
+#include <linux/delay.h>
 #include <sound/core.h>
 #include "pmac.h"
 
diff -Nru a/sound/sound_core.c b/sound/sound_core.c
--- a/sound/sound_core.c	Sun Apr 28 20:10:35 2002
+++ b/sound/sound_core.c	Sun Apr 28 20:10:35 2002
@@ -229,7 +229,7 @@
  
 int register_sound_special(struct file_operations *fops, int unit)
 {
-	const int chain = unit % (SOUND_STEP-1);
+	const int chain = unit % SOUND_STEP;
 	int max_unit = 128 + chain;
 	const char *name;
 	char _name[16];
@@ -243,6 +243,7 @@
 		if (unit >= SOUND_STEP)
 			goto __unknown;
 		max_unit = unit + 1;
+		break;
 	    case 2:
 		name = "midi";
 		break;
@@ -382,7 +383,7 @@
 
 void unregister_sound_special(int unit)
 {
-	sound_remove_unit(&chains[unit&15], unit);
+	sound_remove_unit(&chains[unit % SOUND_STEP], unit);
 }
  
 EXPORT_SYMBOL(unregister_sound_special);
